add a way to set a log consumer
this is a private API to capture filament's logs.
This commit is contained in:
committed by
Benjamin Doherty
parent
c81ece5c3c
commit
9fa3cbfcde
@@ -69,6 +69,13 @@ public:
|
||||
ostream& dec() noexcept;
|
||||
ostream& hex() noexcept;
|
||||
|
||||
/*! @cond PRIVATE */
|
||||
// Sets a consumer of the log. The consumer is invoked on flush() and replaces the default.
|
||||
// Thread safe and reentrant.
|
||||
using ConsumerCallback = void(*)(void*, char const*);
|
||||
void setConsumer(ConsumerCallback consumer, void* user) noexcept;
|
||||
/*! @endcond */
|
||||
|
||||
protected:
|
||||
ostream& print(const char* format, ...) noexcept;
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#include <emscripten/console.h>
|
||||
#endif
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace utils {
|
||||
namespace io {
|
||||
|
||||
@@ -52,6 +54,12 @@ private:
|
||||
ostream& LogStream::flush() noexcept {
|
||||
std::lock_guard const lock(mImpl->mLock);
|
||||
Buffer& buf = getBuffer();
|
||||
char const* const str = buf.get();
|
||||
if (UTILS_UNLIKELY(!str)) {
|
||||
// this can happen if the log hasn't been written ever
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
int prio = ANDROID_LOG_UNKNOWN;
|
||||
switch (mPriority) {
|
||||
@@ -61,20 +69,20 @@ ostream& LogStream::flush() noexcept {
|
||||
case LOG_INFO: prio = ANDROID_LOG_INFO; break;
|
||||
case LOG_VERBOSE: prio = ANDROID_LOG_VERBOSE; break;
|
||||
}
|
||||
__android_log_write(prio, UTILS_LOG_TAG, buf.get());
|
||||
__android_log_write(prio, UTILS_LOG_TAG, str);
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
switch (mPriority) {
|
||||
case LOG_DEBUG:
|
||||
case LOG_WARNING:
|
||||
case LOG_INFO:
|
||||
_emscripten_out(buf.get());
|
||||
_emscripten_out(str);
|
||||
break;
|
||||
case LOG_ERROR:
|
||||
_emscripten_err(buf.get());
|
||||
_emscripten_err(str);
|
||||
break;
|
||||
case LOG_VERBOSE:
|
||||
#ifndef NFIL_DEBUG
|
||||
_emscripten_out(buf.get());
|
||||
_emscripten_out(str);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -83,14 +91,14 @@ ostream& LogStream::flush() noexcept {
|
||||
case LOG_DEBUG:
|
||||
case LOG_WARNING:
|
||||
case LOG_INFO:
|
||||
fprintf(stdout, "%s", buf.get());
|
||||
fprintf(stdout, "%s", str);
|
||||
break;
|
||||
case LOG_ERROR:
|
||||
fprintf(stderr, "%s", buf.get());
|
||||
fprintf(stderr, "%s", str);
|
||||
break;
|
||||
case LOG_VERBOSE:
|
||||
#ifndef NDEBUG
|
||||
fprintf(stdout, "%s", buf.get());
|
||||
fprintf(stdout, "%s", str);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -24,9 +24,12 @@
|
||||
#include <utils/PrivateImplementation-impl.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
template class utils::PrivateImplementation<utils::io::ostream_>;
|
||||
@@ -35,7 +38,31 @@ namespace utils::io {
|
||||
|
||||
ostream::~ostream() = default;
|
||||
|
||||
void ostream::setConsumer(ConsumerCallback consumer, void* user) noexcept {
|
||||
auto* const pImpl = mImpl;
|
||||
std::lock_guard const lock(pImpl->mLock);
|
||||
pImpl->mConsumer = { consumer, user };
|
||||
}
|
||||
|
||||
ostream& flush(ostream& s) noexcept {
|
||||
auto* const pImpl = s.mImpl;
|
||||
pImpl->mLock.lock();
|
||||
auto const callback = pImpl->mConsumer;
|
||||
if (UTILS_UNLIKELY(callback.first)) {
|
||||
auto& buf = s.getBuffer();
|
||||
char const* const data = buf.get();
|
||||
if (UTILS_LIKELY(data)) {
|
||||
char* const str = strdup(data);
|
||||
buf.reset();
|
||||
pImpl->mLock.unlock();
|
||||
// call ConsumerCallback without lock held
|
||||
callback.first(callback.second, str);
|
||||
::free(str);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
pImpl->mLock.unlock();
|
||||
|
||||
return s.flush();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace utils::io {
|
||||
struct ostream_ {
|
||||
std::mutex mLock;
|
||||
ostream::Buffer mData;
|
||||
std::pair<ostream::ConsumerCallback, void*> mConsumer{};
|
||||
bool mShowHex = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -18,8 +18,33 @@
|
||||
|
||||
#include <utils/sstream.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
using namespace utils;
|
||||
using namespace utils::io;
|
||||
|
||||
TEST(ostream, setConsumer) {
|
||||
slog.d.setConsumer(+[](void*, char const*) {
|
||||
GTEST_FAIL();
|
||||
}, nullptr);
|
||||
// we test that we don't crash if the log is empty and that we don't call the consumer.
|
||||
flush(slog.d);
|
||||
|
||||
slog.d.setConsumer(nullptr, nullptr);
|
||||
slog.d << "hello world";
|
||||
// we test that after resetting the consumer, it's not called on flush.
|
||||
flush(slog.d);
|
||||
|
||||
const char* str = "hello world!";
|
||||
slog.d.setConsumer(+[](void* user, char const* str) {
|
||||
ASSERT_STREQ(str, (const char*)user);
|
||||
}, (void*)str);
|
||||
slog.d << str;
|
||||
// we test that the comsumer is called with the right string
|
||||
flush(slog.d);
|
||||
slog.d.setConsumer(nullptr, nullptr);
|
||||
}
|
||||
|
||||
TEST(sstream, EmptyString) {
|
||||
sstream ss;
|
||||
EXPECT_STREQ("", ss.c_str());
|
||||
|
||||
Reference in New Issue
Block a user