looper/log.cpp
Zachary Hall 27113f6443
Some checks failed
Build / build-gentoo (push) Failing after 22s
Build / download-system-deps (push) Successful in 3m16s
Build / get-source-code (push) Successful in 12m8s
Build / build-appimage (push) Successful in 3m58s
Build / build-android (push) Failing after 2m59s
Build / build-windows (push) Failing after 7m22s
WTF - Asserting the return value of a function causes it to not run?!
2024-12-08 09:55:27 -08:00

293 lines
9.1 KiB
C++

#include "log.hpp"
#include <stdarg.h>
#include <string.h>
#include "util.hpp"
#ifdef __ANDROID__
#include <android/log.h>
#endif
#include <filesystem>
using namespace std::filesystem;
namespace Looper::Log {
const std::locale LogStream::c_locale = std::locale("C");
std::set<FILE*> LogStream::global_outputs;
int LogStream::log_level =
#ifdef DEBUG_MODE
-2;
#else
0;
#endif
std::set<FILE*> LogStream::get_used_outputs() {
std::set<FILE*> used_outputs;
if (my_log_level >= log_level) {
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) {
if (nested) {
for (auto &stream : streams) {
stream->writec(chr);
}
} else {
#ifdef __ANDROID__
bool is_newline = chr == '\n' || chr == '\r';
if (!is_newline) {
line += chr;
} else {
for (auto logPriority : android_outputs) {
__android_log_print(logPriority, "Looper", "%s", line.c_str());
}
}
#endif
std::set<FILE*> used_outputs = get_used_outputs();
for (auto &file : used_outputs) {
fwrite(&chr, 1, 1, file);
fflush(file);
}
}
}
void LogStream::writec(const char chr) {
log_mutex_guard guard(log_mutex);
bool is_newline = (chr == '\n' || chr == '\r');
writeprefix();
_writec(chr);
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<std::string> names, int log_level, bool nested, void *discriminator)
: names(names),
need_prefix(true),
my_log_level(log_level),
nested(nested)
{
}
LogStream &LogStream::substream(std::string name) {
if (!this->substreams.contains(name)) {
this->substreams[name] = new LogStream({name}, {this}, my_log_level);
}
return *this->substreams[name];
}
LogStream::LogStream(std::initializer_list<std::string> names, std::initializer_list<LogStream*> streams, int log_level)
: LogStream(names, log_level, true, nullptr)
{
this->streams = std::set(streams);
}
#ifdef __ANDROID__
LogStream::LogStream(std::initializer_list<std::string> names, std::initializer_list<std::variant<FILE*, android_LogPriority>> outputs, int log_level)
#else
LogStream::LogStream(std::initializer_list<std::string> names, std::initializer_list<FILE*> outputs, int log_level)
#endif
: LogStream(names, log_level, false, nullptr)
{
#ifdef __ANDROID__
std::set<FILE*> file_outputs;
std::set<android_LogPriority> android_outputs;
for (auto output : outputs) {
android_LogPriority *logPriority = std::get_if<android_LogPriority>(&output);
FILE **file = std::get_if<FILE*>(&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*>();
}
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<std::recursive_mutex> guard(log_stream_mutex);
make_log_stream(-2, fn);
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<std::recursive_mutex> guard(log_stream_mutex);
pop_log_stream(-2);
pop_log_stream(-1);
pop_log_stream(0);
pop_log_stream(1);
pop_log_stream(2);
}
LogStream *_init_logging_normal(int id) {
std::string name = get_log_name_by_idx(id);
#ifdef __ANDROID__
std::map<int, android_LogPriority> stream_files = {
{-2, ANDROID_LOG_DEBUG},
{-1, ANDROID_LOG_DEBUG},
{0, ANDROID_LOG_INFO},
{1, ANDROID_LOG_WARN},
{2, ANDROID_LOG_ERROR}
};
#else
std::map<int, FILE*> stream_files = {
{-2, stderr},
{-1, stderr},
{0, stdout},
{1, stderr},
{2, stderr}
};
#endif
return new LogStream({name}, {stream_files[id]}, id);
}
std::string get_log_name_by_idx(int idx) {
static std::map<int, std::string> stream_names = {
{-2, "TRACE"},
{-1, "DEBUG"},
{0, "INFO"},
{1, "WARNING"},
{2, "ERROR"},
{3, "FATAL"}
};
return stream_names[idx];
}
static FILE *log_file;
void deinit_logging() {
fclose(log_file);
}
void init_logging() {
path prefs_path(get_prefs_path());
path log_dir = prefs_path / path("looper") / path("logs");
create_directories(log_dir);
log_file = fopen((log_dir / path("looper.log")).c_str(), "a");
LogStream::global_outputs.insert(log_file);
init_logging_custom(_init_logging_normal);
atexit(deinit_logging);
}
LogStream &get_log_stream_by_level(int level) {
return *log_streams[level].top();
}
}
extern "C" void write_log(int level, const char *log) {
LOG(level).writes(log);
}
extern "C" void write_logln(int level, const char *log) {
LOG(level).writeln(log);
}