Zachary Hall
8fdf30878b
Some checks failed
Build / build-gentoo (push) Successful in 1m22s
Build / download-system-deps (push) Successful in 1m37s
Build / get-source-code (push) Successful in 4m30s
Build / build-appimage (push) Successful in 1m15s
Build / build-android (push) Failing after 4m34s
Build / build-windows (push) Failing after 4m33s
291 lines
9 KiB
C++
291 lines
9 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(-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(-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);
|
|
}
|