#include "log.hpp" #include #include namespace Looper::Log { std::set LogStream::global_outputs; int LogStream::log_level = 0; std::set LogStream::get_used_outputs() { std::set used_outputs; for (auto my_output : outputs) { used_outputs.insert(my_output); } for (auto global_output : global_outputs) { used_outputs.insert(global_output); } return used_outputs; } void LogStream::writec(const char chr) { bool is_newline = (chr == '\n' || chr == '\r'); if (my_log_level < log_level) { return; } writeprefix(); if (nested) { for (auto &stream : streams) { stream->writec(chr); } } else { std::set used_outputs = get_used_outputs(); for (auto &file : used_outputs) { fwrite(&chr, 1, 1, file); } } if (is_newline) { need_prefix = true; } } void LogStream::writeprefix() { if (need_prefix) { need_prefix = false; for (auto name : names) { writec('['); writes(name); writes("] "); } } } void LogStream::writes(const char *msg) { while (*msg != '\0') { writec(*(msg++)); } } void LogStream::writesn(const char *msg, size_t n) { for (size_t i = 0; i < n && msg[i] != '\0'; i++) { writec(msg[i]); } } void LogStream::writes(std::string msg) { LogStream::writesn(msg.c_str(), msg.size()); } void LogStream::writeln(const char *msg) { LogStream::writes(msg); LogStream::writec('\n'); } void LogStream::writeln_n(const char *msg, size_t n) { LogStream::writesn(msg, n); LogStream::writec('\n'); } void LogStream::writeln(std::string msg) { LogStream::writes(msg); LogStream::writec('\n'); } void LogStream::writef(const char *fmt, ...) { va_list args; va_start(args, fmt); vwritef(fmt, args); va_end(args); } void LogStream::vwritef(const char *fmt, va_list args) { va_list args_backup; va_copy(args_backup, args); size_t n = vsnprintf(NULL, 0, fmt, args); va_end(args); va_copy(args, args_backup); size_t bufsize = n + 1; char *buf = (char*)malloc(bufsize); memset(buf, 0, bufsize); if (buf == NULL) { va_end(args); va_end(args_backup); throw std::exception(); } n = vsnprintf(buf, bufsize, fmt, args); va_end(args_backup); LogStream::writes(buf); free(buf); } void LogStream::vwritefln(const char *fmt, va_list args) { vwritef(fmt, args); writec('\n'); } void LogStream::writefln(const char *fmt, ...) { va_list args; va_start(args, fmt); vwritefln(fmt, args); va_end(args); } LogStream::LogStream(std::initializer_list names, int log_level, bool nested) : names(names), need_prefix(true), my_log_level(log_level), nested(nested) { } LogStream::LogStream(std::initializer_list names, std::initializer_list streams, int log_level) : LogStream(names, log_level, true) { this->streams = std::set(streams); } LogStream::LogStream(std::initializer_list names, std::initializer_list outputs, int log_level) : LogStream(names, log_level, false) { this->outputs = std::set(outputs); } static LogStream *debug_stream; static LogStream *info_stream; static LogStream *warning_stream; static LogStream *error_stream; void init_logging() { debug_stream = new LogStream({"DEBUG"}, {stderr}, -1); info_stream = new LogStream({"INFO"}, {stdout}, 0); warning_stream = new LogStream({"WARNING"}, {stderr}, 1); error_stream = new LogStream({"ERROR"}, {stderr}, 2); } LogStream &get_log_stream_by_level(int level) { switch (level) { case -1: { return *debug_stream; } break; case 0: { return *info_stream; } break; case 1: { return *warning_stream; } break; case 2: { return *error_stream; } break; default: { return *info_stream; } break; } } }