#include #include #include #include #include "assert.h" #include "logger.h" #include "objects/system.h" #include "objects/thread.h" // The logger is initialized _before_ global constructors are called, // so that we can start log output immediately. Keep its constructor // from being called here so as to not overwrite the previous initialization. static util::no_construct __g_logger_storage; log::logger &g_logger = __g_logger_storage.value; namespace log { namespace { } // anon namespace logger *logger::s_log = nullptr; logger::logger() : m_buffer {nullptr, 0}, m_start {0}, m_end {0}, m_count {0} { memset(&m_levels, 0, sizeof(m_levels)); s_log = this; } logger::logger(util::buffer data) : m_buffer {data}, m_start {0}, m_end {0}, m_count {0} { kassert((data.count & (data.count - 1)) == 0, "log buffer size must be a power of two"); memset(&m_levels, 0, sizeof(m_levels)); s_log = this; #define LOG(name, lvl) \ set_level(logs::name, log::level::lvl); #include #undef LOG } void logger::output(level severity, logs area, const char *fmt, va_list args) { static constexpr size_t buffer_len = 256; static constexpr size_t message_len = buffer_len - sizeof(entry); char buffer[buffer_len]; entry *header = reinterpret_cast(buffer); size_t size = sizeof(entry); size += util::vformat({header->message, message_len}, fmt, args); util::scoped_lock lock {m_lock}; while (free() < size) { // Remove old entries until there's enough space const entry *first = util::at(m_buffer, start()); m_start += first->bytes; } header->id = ++m_count; header->bytes = size; header->severity = severity; header->area = area; memcpy(util::at(m_buffer, end()), buffer, size); m_end += size; m_waiting.clear(); } size_t logger::get_entry(uint64_t seen, void *buffer, size_t size) { util::scoped_lock lock {m_lock}; while (seen == m_count) { lock.release(); m_waiting.wait(); lock.reacquire(); } size_t off = m_start; entry *ent = util::at(m_buffer, off); while (seen >= ent->id) { off += ent->bytes; kassert(off < m_end, "Got to the end while looking for new log entry"); ent = util::at(m_buffer, off); } if (size >= ent->bytes) memcpy(buffer, ent, ent->bytes); return ent->bytes; } #define LOG_LEVEL_FUNCTION(name) \ void name (logs area, const char *fmt, ...) { \ logger *l = logger::s_log; \ if (!l) return; \ level limit = l->get_level(area); \ if (level::name > limit) return; \ va_list args; \ va_start(args, fmt); \ l->output(level::name, area, fmt, args); \ va_end(args); \ } LOG_LEVEL_FUNCTION(spam); LOG_LEVEL_FUNCTION(verbose); LOG_LEVEL_FUNCTION(info); LOG_LEVEL_FUNCTION(warn); LOG_LEVEL_FUNCTION(error); void fatal(logs 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