2#include "gaia/config/config_core.h"
8#include "gaia/cnt/darray_ext.h"
11#ifndef GAIA_LOG_BUFFER_SIZE
12 #define GAIA_LOG_BUFFER_SIZE 32 * 1024
15#ifndef GAIA_LOG_BUFFER_ENTRIES
16 #define GAIA_LOG_BUFFER_ENTRIES 2048
22 using LogLevelType = uint8_t;
25 enum class LogLevel : LogLevelType {
32 inline LogLevelType g_logLevelMask = (LogLevelType)LogLevel::Debug | (LogLevelType)LogLevel::Info |
33 (LogLevelType)LogLevel::Warning | (LogLevelType)LogLevel::Error;
38 inline void log_enable(LogLevel level,
bool value) {
40 gaia::util::g_logLevelMask |= ((LogLevelType)level);
42 gaia::util::g_logLevelMask &= ~((LogLevelType)level);
48 inline bool is_logging_enabled(LogLevel level) {
49 return ((LogLevelType)level & g_logLevelMask) != 0;
53 using LogLineFunc = void (*)(LogLevel,
const char*);
55 using LogFunc = void (*)(LogLevel,
const char*, va_list);
57 using LogFlushFunc = void (*)();
61 inline constexpr uint32_t LOG_BUFFER_SIZE = GAIA_LOG_BUFFER_SIZE;
62 inline constexpr uint32_t LOG_RECORD_LIMIT = GAIA_LOG_BUFFER_ENTRIES;
64 inline FILE* get_log_out(LogLevel level) {
65 const auto mask = (LogLevelType)level & ((LogLevelType)LogLevel::Error | (LogLevelType)LogLevel::Warning);
67 return mask != 0 ? stderr : stdout;
73 inline void log_line(LogLevel level,
const char* msg) {
74 FILE* out = get_log_out(level);
76 static constexpr const char* colors[] = {
83 const auto lvl = (uint32_t)level;
84 const auto idx = GAIA_CLZ(lvl);
85 fprintf(out,
"%s%s\033[0m\n", colors[idx], msg);
87 inline LogLineFunc g_log_line_func = log_line;
97 char m_buffer[LOG_BUFFER_SIZE];
98 LogRecord m_recs[LOG_RECORD_LIMIT];
99 uint32_t m_buffer_pos = 0;
100 uint32_t m_recs_pos = 0;
108 void log(LogLevel level, uint32_t len,
const char* msg) {
109 FILE* out = get_log_out(level);
110 const bool is_assert = out == stderr;
113 if (is_assert || len >= detail::LOG_BUFFER_SIZE) {
118 g_log_line_func(level, msg);
124 if (m_buffer_pos + len > detail::LOG_BUFFER_SIZE || m_recs_pos >= detail::LOG_RECORD_LIMIT)
128 auto& rec = m_recs[m_recs_pos];
129 rec.offset = m_buffer_pos;
130 rec.level = (LogLevelType)level;
131 memcpy(m_buffer + m_buffer_pos, msg, len);
141 for (
size_t i = 0; i < m_recs_pos; ++i) {
142 const auto& rec = m_recs[i];
143 g_log_line_func((LogLevel)rec.level, &m_buffer[rec.offset]);
154 setvbuf(stdout,
nullptr, _IOFBF, 4096);
155 setvbuf(stderr,
nullptr, _IOFBF, 4096);
162 LogBuffer(
const LogBuffer&) =
delete;
163 LogBuffer(LogBuffer&&) =
delete;
164 LogBuffer& operator=(
const LogBuffer&) =
delete;
165 LogBuffer& operator=(LogBuffer&&) =
delete;
168 inline LogBuffer* g_log() {
169 static LogBuffer* s_log =
nullptr;
170 if (s_log ==
nullptr) {
171 s_log =
new LogBuffer();
173 static struct LogAtExit {
174 LogAtExit() =
default;
176 if (s_log !=
nullptr) {
183 LogAtExit(
const LogAtExit&) =
delete;
184 LogAtExit(LogAtExit&&) =
delete;
185 LogAtExit& operator=(
const LogAtExit&) =
delete;
186 LogAtExit& operator=(LogAtExit&&) =
delete;
193 inline void log_cached(LogLevel level,
const char* fmt, va_list args) {
196 GAIA_CLANG_WARNING_PUSH()
197 GAIA_GCC_WARNING_PUSH()
198 GAIA_CLANG_WARNING_DISABLE("-Wformat-nonliteral")
199 GAIA_GCC_WARNING_DISABLE("-Wformat-nonliteral")
201 va_copy(args_copy, args);
202 int l = vsnprintf(
nullptr, 0, fmt, args_copy);
207 const auto len = (uint32_t)l;
208 cnt::darray_ext<
char, 1024> msg(len + 1);
210 va_copy(args_copy, args);
211 vsnprintf(msg.data(), msg.size(), fmt, args_copy);
213 GAIA_GCC_WARNING_POP()
214 GAIA_CLANG_WARNING_POP()
222 g_log()->log(level, msg.size(), msg.data());
226 inline
void log_flush_cached() {
233 inline void log_default(LogLevel level,
const char* fmt, va_list args) {
236 GAIA_CLANG_WARNING_PUSH()
237 GAIA_GCC_WARNING_PUSH()
238 GAIA_CLANG_WARNING_DISABLE("-Wformat-nonliteral")
239 GAIA_GCC_WARNING_DISABLE("-Wformat-nonliteral")
241 va_copy(args_copy, args);
242 int l = vsnprintf(
nullptr, 0, fmt, args_copy);
247 const auto len = (uint32_t)l;
248 cnt::darray_ext<
char, 1024> msg(len + 1);
250 va_copy(args_copy, args);
251 vsnprintf(msg.data(), msg.size(), fmt, args_copy);
253 GAIA_GCC_WARNING_POP()
254 GAIA_CLANG_WARNING_POP()
259 g_log_line_func(level, msg.data());
265 inline
void log_flush_default() {}
267 inline LogFunc g_log_func = log_default;
268 inline LogFlushFunc g_log_flush_func = log_flush_default;
275 inline void set_log_func(LogFunc func) {
276 detail::g_log_func = func !=
nullptr ? func : detail::log_default;
281 inline void set_log_line_func(LogLineFunc func) {
282 detail::g_log_line_func = func !=
nullptr ? func : detail::log_line;
287 inline void set_log_flush_func(LogFlushFunc func) {
288 detail::g_log_flush_func = func !=
nullptr ? func : detail::log_flush_default;
294 inline void log(LogLevel level,
const char* fmt, ...) {
295 if (!is_logging_enabled(level))
300 detail::g_log_func(level, fmt, args);
305 inline void log_flush() {
306 detail::g_log_flush_func();
314typedef void (*gaia_log_line_func_t)(gaia::util::LogLevelType level,
const char* msg);
315inline void gaia_set_log_func(gaia_log_line_func_t func) {
316 gaia::util::set_log_line_func((gaia::util::LogLineFunc)func);
319inline void gaia_log(uint8_t level,
const char* msg) {
320 gaia::util::log((gaia::util::LogLevel)level,
"%s", msg);
323typedef void (*gaia_log_flush_func_t)();
324inline void gaia_set_flush_func(gaia_log_flush_func_t func) {
325 gaia::util::set_log_flush_func((gaia::util::LogFlushFunc)func);
328inline void gaia_flush_logs() {
329 gaia::util::log_flush();
332inline void gaia_log_enable(gaia::util::LogLevelType level,
bool value) {
333 gaia::util::log_enable((gaia::util::LogLevel)level, value);
336inline bool gaia_is_logging_enabled(gaia::util::LogLevelType level) {
337 return gaia::util::is_logging_enabled((gaia::util::LogLevel)level);
342#define GAIA_LOG_D(...) gaia::util::log(gaia::util::LogLevel::Debug, __VA_ARGS__)
343#define GAIA_LOG_N(...) gaia::util::log(gaia::util::LogLevel::Info, __VA_ARGS__)
344#define GAIA_LOG_W(...) gaia::util::log(gaia::util::LogLevel::Warning, __VA_ARGS__)
345#define GAIA_LOG_E(...) gaia::util::log(gaia::util::LogLevel::Error, __VA_ARGS__)