looper/log.cpp
2024-05-02 14:52:11 -07:00

198 lines
No EOL
5.9 KiB
C++

#include "log.hpp"
#include <stdarg.h>
#include <string.h>
#include "util.hpp"
#ifdef __ANDROID__
#include <android/log.h>
#endif
namespace Looper::Log {
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;
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::string line;
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 {
#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<FILE*> 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) {
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) {
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<std::string> 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<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
}
static LogStream *debug_stream;
static LogStream *info_stream;
static LogStream *warning_stream;
static LogStream *error_stream;
void init_logging() {
#ifdef __ANDROID__
debug_stream = new LogStream({"DEBUG"}, {ANDROID_LOG_DEBUG}, -1);
info_stream = new LogStream({"INFO"}, {ANDROID_LOG_INFO}, 0);
warning_stream = new LogStream({"WARNING"}, {ANDROID_LOG_WARN}, 1);
error_stream = new LogStream({"ERROR"}, {ANDROID_LOG_ERROR}, 2);
#else
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);
#endif
}
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;
}
}
}
void write_log(int level, const char *log) {
LOG(level).writes(log);
}
void write_logln(int level, const char *log) {
LOG(level).writeln(log);
}