#include "log.hpp" #include #include #include "util.hpp" #ifdef __ANDROID__ #include #endif namespace Looper::Log { std::set LogStream::global_outputs; int LogStream::log_level = #ifdef DEBUG_MODE -2; #else 0; #endif 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; } std::recursive_mutex log_mutex; void LogStream::writec(const char chr) { log_mutex_guard guard(log_mutex); 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 { #ifdef __ANDROID__ if (!is_newline) { line += chr; } else { for (auto logPriority : android_outputs) { __android_log_print(logPriority, "Looper", "%s", line.c_str()); } } #endif 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() { log_mutex_guard guard(log_mutex); if (need_prefix) { need_prefix = false; for (auto name : names) { writec('['); writes(name); writes("] "); } } } void LogStream::writes(const char *msg) { log_mutex_guard guard(log_mutex); while (*msg != '\0') { writec(*(msg++)); } } void LogStream::writesn(const char *msg, size_t n) { log_mutex_guard guard(log_mutex); 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) { log_mutex_guard guard(log_mutex); LogStream::writes(msg); LogStream::writec('\n'); } void LogStream::writeln_n(const char *msg, size_t n) { log_mutex_guard guard(log_mutex); LogStream::writesn(msg, n); LogStream::writec('\n'); } void LogStream::writeln(std::string msg) { log_mutex_guard guard(log_mutex); LogStream::writes(msg); LogStream::writec('\n'); } void LogStream::writef(const char *fmt, ...) { log_mutex_guard guard(log_mutex); va_list args; va_start(args, fmt); vwritef(fmt, args); va_end(args); } void LogStream::vwritef(const char *fmt, va_list args) { log_mutex_guard guard(log_mutex); const char *buf = vcformat(fmt, args); va_end(args); if (buf == NULL) { throw std::exception(); } LogStream::writes(buf); free((void*)buf); } void LogStream::vwritefln(const char *fmt, va_list args) { log_mutex_guard guard(log_mutex); vwritef(fmt, args); writec('\n'); } void LogStream::writefln(const char *fmt, ...) { log_mutex_guard guard(log_mutex); va_list args; va_start(args, fmt); vwritefln(fmt, args); va_end(args); } void LogStream::write_level_prefix(size_t level) { log_mutex_guard guard(log_mutex); for (size_t i = 0; i < level; i++) { writes(" "); } } void LogStream::write_level(size_t level, const char *msg) { log_mutex_guard guard(log_mutex); write_level_prefix(level); writeln(msg); } void LogStream::write_level(size_t level, std::string msg) { log_mutex_guard guard(log_mutex); write_level_prefix(level); writeln(msg); } void LogStream::vwritef_level(size_t level, const char *fmt, va_list args) { log_mutex_guard guard(log_mutex); write_level_prefix(level); vwritefln(fmt, args); } void LogStream::writef_level(size_t level, const char *fmt, ...) { log_mutex_guard guard(log_mutex); va_list args; va_start(args, fmt); vwritef_level(level, fmt, args); va_end(args); } LogStream::LogStream(std::initializer_list names, int log_level, bool nested, void *discriminator) : 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, nullptr) { this->streams = std::set(streams); } #ifdef __ANDROID__ LogStream::LogStream(std::initializer_list names, std::initializer_list> outputs, int log_level) #else LogStream::LogStream(std::initializer_list names, std::initializer_list outputs, int log_level) #endif : LogStream(names, log_level, false, nullptr) { #ifdef __ANDROID__ std::set file_outputs; std::set android_outputs; for (auto output : outputs) { android_LogPriority *logPriority = std::get_if(&output); FILE **file = std::get_if(&output); if (logPriority != nullptr) { android_outputs.insert(*logPriority); } else if (file != nullptr){ file_outputs.insert(*file); } } this->android_outputs = android_outputs; this->outputs = file_outputs; #else this->outputs = std::set(outputs); #endif } LogStreamMap log_streams; void make_log_stream(int stream_id, log_stream_creation_function_t fn) { if (!log_streams.contains(stream_id)) { log_streams[stream_id] = std::stack(); } LogStream *new_stream = fn(stream_id); log_streams[stream_id].push(new_stream); } std::recursive_mutex log_stream_mutex; void init_logging_custom(log_stream_creation_function_t fn) { std::lock_guard guard(log_stream_mutex); make_log_stream(-1, fn); make_log_stream(0, fn); make_log_stream(1, fn); make_log_stream(2, fn); } void pop_log_stream(int i) { log_streams[i].pop(); } void pop_log_streams() { std::lock_guard guard(log_stream_mutex); pop_log_stream(-1); pop_log_stream(0); pop_log_stream(1); pop_log_stream(2); } LogStream *_init_logging_normal(int id) { std::map stream_names = { {-1, "DEBUG"}, {0, "INFO"}, {1, "WARNING"}, {2, "ERROR"} }; #ifdef __ANDROID__ std::map stream_files = { {-1, ANDROID_LOG_DEBUG}, {0, ANDROID_LOG_INFO}, {1, ANDROID_LOG_WARN}, {2, ANDROID_LOG_ERROR} }; #else std::map stream_files = { {-1, stderr}, {0, stdout}, {1, stderr}, {2, stderr} }; #endif return new LogStream({stream_names[id]}, {stream_files[id]}, id); } void init_logging() { init_logging_custom(_init_logging_normal); } LogStream &get_log_stream_by_level(int level) { return *log_streams[level].top(); } } void write_log(int level, const char *log) { LOG(level).writes(log); } void write_logln(int level, const char *log) { LOG(level).writeln(log); }