Files
jsix/src/libraries/kutil/logger.cpp
F in Chat for Tabs 8f529046a9 [project] Lose the battle between tabs & spaces
I'm a tabs guy. I like tabs, it's an elegant way to represent
indentation instead of brute-forcing it. But I have to admit that the
world seems to be going towards spaces, and tooling tends not to play
nice with tabs. So here we go, changing the whole repo to spaces since
I'm getting tired of all the inconsistent formatting.
2021-08-01 17:46:16 -07:00

174 lines
4.0 KiB
C++

#include "kutil/assert.h"
#include "kutil/constexpr_hash.h"
#include "kutil/logger.h"
#include "kutil/memory.h"
#include "kutil/printf.h"
namespace kutil {
namespace logs {
#define LOG(name, lvl) \
const log::area_t name = #name ## _h; \
const char * name ## _name = #name;
#include "j6/tables/log_areas.inc"
#undef LOG
}
namespace log {
using kutil::memset;
using kutil::memcpy;
logger *logger::s_log = nullptr;
const char *logger::s_level_names[] = {"", "debug", "info", "warn", "error", "fatal"};
logger::logger(logger::immediate_cb output) :
m_buffer(nullptr, 0),
m_immediate(output),
m_flush(nullptr),
m_sequence(0)
{
memset(&m_levels, 0, sizeof(m_levels));
memset(&m_names, 0, sizeof(m_names));
s_log = this;
}
logger::logger(uint8_t *buffer, size_t size, logger::immediate_cb output) :
m_buffer(buffer, size),
m_immediate(output),
m_flush(nullptr),
m_sequence(0)
{
memset(&m_levels, 0, sizeof(m_levels));
memset(&m_names, 0, sizeof(m_names));
s_log = this;
#define LOG(name, lvl) \
register_area(logs::name, logs::name ## _name, log::level::lvl);
#include "j6/tables/log_areas.inc"
#undef LOG
}
void
logger::set_level(area_t area, level l)
{
unsigned uarea = static_cast<unsigned>(area);
uint8_t ulevel = static_cast<uint8_t>(l) & 0x0f;
uint8_t &flags = m_levels[uarea / 2];
if (uarea & 1)
flags = (flags & 0x0f) | (ulevel << 4);
else
flags = (flags & 0xf0) | ulevel;
}
level
logger::get_level(area_t area)
{
unsigned uarea = static_cast<unsigned>(area);
uint8_t &flags = m_levels[uarea / 2];
if (uarea & 1)
return static_cast<level>((flags & 0xf0) >> 4);
else
return static_cast<level>(flags & 0x0f);
}
void
logger::register_area(area_t area, const char *name, level verbosity)
{
m_names[area] = name;
set_level(area, verbosity);
}
void
logger::output(level severity, area_t area, const char *fmt, va_list args)
{
uint8_t buffer[256];
entry *header = reinterpret_cast<entry *>(buffer);
header->bytes = sizeof(entry);
header->area = area;
header->severity = severity;
header->sequence = m_sequence++;
header->bytes +=
vsnprintf(header->message, sizeof(buffer) - sizeof(entry), fmt, args);
kutil::scoped_lock lock {m_lock};
if (m_immediate) {
buffer[header->bytes] = 0;
m_immediate(area, severity, header->message);
return;
}
uint8_t *out;
size_t n = m_buffer.reserve(header->bytes, reinterpret_cast<void**>(&out));
if (n < sizeof(entry)) {
m_buffer.commit(0); // Cannot even write the header, abort
return;
}
if (n < header->bytes)
header->bytes = n;
memcpy(out, buffer, n);
m_buffer.commit(n);
if (m_flush)
m_flush();
}
size_t
logger::get_entry(void *buffer, size_t size)
{
kutil::scoped_lock lock {m_lock};
void *out;
size_t out_size = m_buffer.get_block(&out);
if (out_size == 0 || out == 0)
return 0;
kassert(out_size >= sizeof(entry), "Couldn't read a full entry");
if (out_size < sizeof(entry))
return 0;
entry *ent = reinterpret_cast<entry *>(out);
if (size >= ent->bytes) {
memcpy(buffer, out, ent->bytes);
m_buffer.consume(ent->bytes);
}
return ent->bytes;
}
#define LOG_LEVEL_FUNCTION(name) \
void name (area_t area, const char *fmt, ...) { \
logger *l = logger::s_log; \
if (!l) return; \
level limit = l->get_level(area); \
if (limit == level::none || level::name < limit) return; \
va_list args; \
va_start(args, fmt); \
l->output(level::name, area, fmt, args); \
va_end(args); \
}
LOG_LEVEL_FUNCTION(debug);
LOG_LEVEL_FUNCTION(info);
LOG_LEVEL_FUNCTION(warn);
LOG_LEVEL_FUNCTION(error);
void fatal(area_t area, const char *fmt, ...)
{
logger *l = logger::s_log;
if (!l) return;
va_list args;
va_start(args, fmt);
l->output(level::fatal, area, fmt, args);
va_end(args);
kassert(false, "log::fatal");
}
} // namespace log
} // namespace kutil