kendryte-standalone-sdk/lib/nncase/v1/include/nncase/runtime/aixlog.hpp

1252 lines
32 KiB
C++

/***
__ __ _ _ __ __ ___
/ _\ ( )( \/ )( ) / \ / __)
/ \ )( ) ( / (_/\( O )( (_ \
\_/\_/(__)(_/\_)\____/ \__/ \___/
version 1.5.0
https://github.com/badaix/aixlog
This file is part of aixlog
Copyright (C) 2017-2021 Johannes Pohl
This software may be modified and distributed under the terms
of the MIT license. See the LICENSE file for details.
***/
/// inspired by "eater":
/// https://stackoverflow.com/questions/2638654/redirect-c-stdclog-to-syslog-on-unix
#ifndef AIX_LOG_HPP
#define AIX_LOG_HPP
#ifndef _WIN32
#define HAS_SYSLOG_ 1
#endif
#ifdef __APPLE__
#ifdef __MAC_OS_X_VERSION_MAX_ALLOWED
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1012
#define HAS_APPLE_UNIFIED_LOG_ 1
#endif
#endif
#endif
#include <algorithm>
#include <cctype>
#include <chrono>
#include <cstdio>
#include <ctime>
#include <fstream>
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <mutex>
#include <sstream>
#include <thread>
#include <vector>
#ifdef __ANDROID__
#include <android/log.h>
#endif
#ifdef _WIN32
#include <Windows.h>
// ERROR macro is defined in Windows header
// To avoid conflict between these macro and declaration of ERROR / DEBUG in SEVERITY enum
// We save macro and undef it
#pragma push_macro("ERROR")
#pragma push_macro("DEBUG")
#undef ERROR
#undef DEBUG
#endif
#ifdef HAS_APPLE_UNIFIED_LOG_
#include <os/log.h>
#endif
#ifdef HAS_SYSLOG_
#include <syslog.h>
#endif
#ifdef __ANDROID__
// fix for bug "Android NDK __func__ definition is inconsistent with glibc and C++99"
// https://bugs.chromium.org/p/chromium/issues/detail?id=631489
#ifdef __GNUC__
#define AIXLOG_INTERNAL__FUNC __FUNCTION__
#else
#define AIXLOG_INTERNAL__FUNC __func__
#endif
#else
#define AIXLOG_INTERNAL__FUNC __func__
#endif
/// Internal helper macros (exposed, but shouldn't be used directly)
#define AIXLOG_INTERNAL__LOG_SEVERITY(SEVERITY_) std::clog << static_cast<AixLog::Severity>(SEVERITY_) << TAG()
#define AIXLOG_INTERNAL__LOG_SEVERITY_TAG(SEVERITY_, TAG_) std::clog << static_cast<AixLog::Severity>(SEVERITY_) << TAG(TAG_)
#define AIXLOG_INTERNAL__ONE_COLOR(FG_) AixLog::Color::FG_
#define AIXLOG_INTERNAL__TWO_COLOR(FG_, BG_) AixLog::TextColor(AixLog::Color::FG_, AixLog::Color::BG_)
// https://stackoverflow.com/questions/3046889/optional-parameters-with-c-macros
#define AIXLOG_INTERNAL__VAR_PARM(PARAM1_, PARAM2_, FUNC_, ...) FUNC_
#define AIXLOG_INTERNAL__LOG_MACRO_CHOOSER(...) AIXLOG_INTERNAL__VAR_PARM(__VA_ARGS__, AIXLOG_INTERNAL__LOG_SEVERITY_TAG, AIXLOG_INTERNAL__LOG_SEVERITY, )
#define AIXLOG_INTERNAL__COLOR_MACRO_CHOOSER(...) AIXLOG_INTERNAL__VAR_PARM(__VA_ARGS__, AIXLOG_INTERNAL__TWO_COLOR, AIXLOG_INTERNAL__ONE_COLOR, )
/// External logger macros
// usage: LOG(SEVERITY) or LOG(SEVERITY, TAG)
// e.g.: LOG(NOTICE) or LOG(NOTICE, "my tag")
#ifndef WIN32
#define LOG(...) AIXLOG_INTERNAL__LOG_MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__) << TIMESTAMP << FUNC
#endif
// usage: COLOR(TEXT_COLOR, BACKGROUND_COLOR) or COLOR(TEXT_COLOR)
// e.g.: COLOR(yellow, blue) or COLOR(red)
#define COLOR(...) AIXLOG_INTERNAL__COLOR_MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)
#define FUNC AixLog::Function(AIXLOG_INTERNAL__FUNC, __FILE__, __LINE__)
#define TAG AixLog::Tag
#define COND AixLog::Conditional
#define TIMESTAMP AixLog::Timestamp(std::chrono::system_clock::now())
// stijnvdb: sorry! :) LOG(SEV, "tag") was not working for Windows and I couldn't figure out how to fix it for windows without potentially breaking everything
// else...
// https://stackoverflow.com/questions/3046889/optional-parameters-with-c-macros (Jason Deng)
#ifdef WIN32
#define LOG_2(severity, tag) AIXLOG_INTERNAL__LOG_SEVERITY_TAG(severity, tag)
#define LOG_1(severity) AIXLOG_INTERNAL__LOG_SEVERITY(severity)
#define LOG_0() LOG_1(0)
#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(...) FUNC_RECOMPOSER((__VA_ARGS__, LOG_2, LOG_1, FUNC_, ...))
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(__VA_ARGS__())
#define LOG(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__) << TIMESTAMP << FUNC
#endif
/**
* @brief
* Severity of the log message
*/
enum SEVERITY
{
TRACE = 0,
DEBUG = 1,
INFO = 2,
NOTICE = 3,
WARNING = 4,
ERROR = 5,
FATAL = 6
};
namespace AixLog
{
/**
* @brief
* Severity of the log message
*
* Mandatory parameter for the LOG macro
*/
enum class Severity : std::int8_t
{
// Mapping table from AixLog to other loggers. Boost is just for information.
// https://chromium.googlesource.com/chromium/mini_chromium/+/master/base/logging.cc
//
// Aixlog Boost Syslog Android macOS EventLog Syslog Desc
//
// trace trace DEBUG VERBOSE DEBUG INFORMATION
// debug debug DEBUG DEBUG DEBUG INFORMATION debug-level message
// info info INFO INFO INFO SUCCESS informational message
// notice NOTICE INFO INFO SUCCESS normal, but significant, condition
// warning warning WARNING WARN DEFAULT WARNING warning conditions
// error error ERROR ERROR ERROR ERROR error conditions
// fatal fatal CRIT FATAL FAULT ERROR critical conditions
// ALERT action must be taken immediately
// EMERG system is unusable
trace = SEVERITY::TRACE,
debug = SEVERITY::DEBUG,
info = SEVERITY::INFO,
notice = SEVERITY::NOTICE,
warning = SEVERITY::WARNING,
error = SEVERITY::ERROR,
fatal = SEVERITY::FATAL
};
static Severity to_severity(std::string severity, Severity def = Severity::info)
{
std::transform(severity.begin(), severity.end(), severity.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
if (severity == "trace")
return Severity::trace;
else if (severity == "debug")
return Severity::debug;
else if (severity == "info")
return Severity::info;
else if (severity == "notice")
return Severity::notice;
else if (severity == "warning")
return Severity::warning;
else if (severity == "error")
return Severity::error;
else if (severity == "fatal")
return Severity::fatal;
else
return def;
}
static std::string to_string(Severity logSeverity)
{
switch (logSeverity)
{
case Severity::trace:
return "Trace";
case Severity::debug:
return "Debug";
case Severity::info:
return "Info";
case Severity::notice:
return "Notice";
case Severity::warning:
return "Warn";
case Severity::error:
return "Error";
case Severity::fatal:
return "Fatal";
default:
std::stringstream ss;
ss << static_cast<int>(logSeverity);
return ss.str();
}
}
/**
* @brief
* Color constants used for console colors
*/
enum class Color
{
none = 0,
NONE = 0,
black = 1,
BLACK = 1,
red = 2,
RED = 2,
green = 3,
GREEN = 3,
yellow = 4,
YELLOW = 4,
blue = 5,
BLUE = 5,
magenta = 6,
MAGENTA = 6,
cyan = 7,
CYAN = 7,
white = 8,
WHITE = 8
};
/**
* @brief
* Encapsulation of foreground and background color
*/
struct TextColor
{
TextColor(Color foreground = Color::none, Color background = Color::none) : foreground(foreground), background(background)
{
}
Color foreground;
Color background;
};
/**
* @brief
* For Conditional logging of a log line
*/
struct Conditional
{
using EvalFunc = std::function<bool()>;
Conditional() : func_([](void) { return true; })
{
}
Conditional(const EvalFunc& func) : func_(func)
{
}
Conditional(bool value) : func_([value](void) { return value; })
{
}
virtual ~Conditional() = default;
virtual bool is_true() const
{
return func_();
}
protected:
EvalFunc func_;
};
/**
* @brief
* Timestamp of a log line
*
* to_string will convert the time stamp into a string, using the strftime syntax
*/
struct Timestamp
{
using time_point_sys_clock = std::chrono::time_point<std::chrono::system_clock>;
Timestamp(std::nullptr_t) : is_null_(true)
{
}
Timestamp() : Timestamp(nullptr)
{
}
Timestamp(const time_point_sys_clock& time_point) : time_point(time_point), is_null_(false)
{
}
Timestamp(time_point_sys_clock&& time_point) : time_point(std::move(time_point)), is_null_(false)
{
}
virtual ~Timestamp() = default;
explicit operator bool() const
{
return !is_null_;
}
/// strftime format + proprietary "#ms" for milliseconds
std::string to_string(const std::string& format = "%Y-%m-%d %H-%M-%S.#ms") const
{
std::time_t now_c = std::chrono::system_clock::to_time_t(time_point);
struct ::tm now_tm = localtime_xp(now_c);
char buffer[256];
strftime(buffer, sizeof buffer, format.c_str(), &now_tm);
std::string result(buffer);
size_t pos = result.find("#ms");
if (pos != std::string::npos)
{
int ms_part = std::chrono::time_point_cast<std::chrono::milliseconds>(time_point).time_since_epoch().count() % 1000;
char ms_str[4];
if (snprintf(ms_str, 4, "%03d", ms_part) >= 0)
result.replace(pos, 3, ms_str);
}
return result;
}
time_point_sys_clock time_point;
private:
bool is_null_;
inline std::tm localtime_xp(std::time_t timer) const
{
std::tm bt;
#if defined(__unix__)
localtime_r(&timer, &bt);
#elif defined(_MSC_VER)
localtime_s(&bt, &timer);
#else
static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx);
bt = *std::localtime(&timer);
#endif
return bt;
}
};
/**
* @brief
* Tag (string) for log line
*/
struct Tag
{
Tag(std::nullptr_t) : text(""), is_null_(true)
{
}
Tag() : Tag(nullptr)
{
}
Tag(const char* text) : text(text), is_null_(false)
{
}
Tag(const std::string& text) : text(text), is_null_(false)
{
}
Tag(std::string&& text) : text(std::move(text)), is_null_(false)
{
}
virtual ~Tag() = default;
explicit operator bool() const
{
return !is_null_;
}
bool operator<(const Tag& other) const
{
return (text < other.text);
}
std::string text;
private:
bool is_null_;
};
/**
* @brief
* Capture function, file and line number of the log line
*/
struct Function
{
Function(const std::string& name, const std::string& file, size_t line) : name(name), file(file), line(line), is_null_(false)
{
}
Function(std::string&& name, std::string&& file, size_t line) : name(std::move(name)), file(std::move(file)), line(line), is_null_(false)
{
}
Function(std::nullptr_t) : name(""), file(""), line(0), is_null_(true)
{
}
Function() : Function(nullptr)
{
}
virtual ~Function() = default;
explicit operator bool() const
{
return !is_null_;
}
std::string name;
std::string file;
size_t line;
private:
bool is_null_;
};
/**
* @brief
* Collection of a log line's meta data
*/
struct Metadata
{
Metadata() : severity(Severity::trace), tag(nullptr), function(nullptr), timestamp(nullptr)
{
}
Severity severity;
Tag tag;
Function function;
Timestamp timestamp;
};
class Filter
{
public:
Filter()
{
}
Filter(Severity severity)
{
add_filter(severity);
}
bool match(const Metadata& metadata) const
{
if (tag_filter_.empty())
return true;
auto iter = tag_filter_.find(metadata.tag);
if (iter != tag_filter_.end())
return (metadata.severity >= iter->second);
iter = tag_filter_.find("*");
if (iter != tag_filter_.end())
return (metadata.severity >= iter->second);
return false;
}
void add_filter(const Tag& tag, Severity severity)
{
tag_filter_[tag] = severity;
}
void add_filter(Severity severity)
{
tag_filter_["*"] = severity;
}
void add_filter(const std::string& filter)
{
auto pos = filter.find(":");
if (pos != std::string::npos)
add_filter(filter.substr(0, pos), to_severity(filter.substr(pos + 1)));
else
add_filter(to_severity(filter));
}
private:
std::map<Tag, Severity> tag_filter_;
};
/**
* @brief
* Abstract log sink
*
* All log sinks must inherit from this Sink
*/
struct Sink
{
Sink(const Filter& filter) : filter(filter)
{
}
virtual ~Sink() = default;
virtual void log(const Metadata& metadata, const std::string& message) = 0;
Filter filter;
};
/// ostream operators << for the meta data structs
static std::ostream& operator<<(std::ostream& os, const Severity& log_severity);
static std::ostream& operator<<(std::ostream& os, const Timestamp& timestamp);
static std::ostream& operator<<(std::ostream& os, const Tag& tag);
static std::ostream& operator<<(std::ostream& os, const Function& function);
static std::ostream& operator<<(std::ostream& os, const Conditional& conditional);
static std::ostream& operator<<(std::ostream& os, const Color& color);
static std::ostream& operator<<(std::ostream& os, const TextColor& text_color);
using log_sink_ptr = std::shared_ptr<Sink>;
/**
* @brief
* Main Logger class with "Log::init"
*
* Don't use it directly, but call once "Log::init" with your log sink instances.
* The Log class will simply redirect clog to itself (as a streambuf) and
* forward whatever went to clog to the log sink instances
*/
class Log : public std::basic_streambuf<char, std::char_traits<char>>
{
public:
static Log& instance()
{
static Log instance_;
return instance_;
}
/// Without "init" every LOG(X) will simply go to clog
static void init(const std::vector<log_sink_ptr> log_sinks = {})
{
Log::instance().log_sinks_.clear();
for (const auto& sink : log_sinks)
Log::instance().add_logsink(sink);
}
template <typename T, typename... Ts>
static std::shared_ptr<T> init(Ts&&... params)
{
std::shared_ptr<T> sink = Log::instance().add_logsink<T>(std::forward<Ts>(params)...);
init({sink});
return sink;
}
template <typename T, typename... Ts>
std::shared_ptr<T> add_logsink(Ts&&... params)
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
static_assert(std::is_base_of<Sink, typename std::decay<T>::type>::value, "type T must be a Sink");
std::shared_ptr<T> sink = std::make_shared<T>(std::forward<Ts>(params)...);
log_sinks_.push_back(sink);
return sink;
}
void add_logsink(const log_sink_ptr& sink)
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
log_sinks_.push_back(sink);
}
void remove_logsink(const log_sink_ptr& sink)
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
log_sinks_.erase(std::remove(log_sinks_.begin(), log_sinks_.end(), sink), log_sinks_.end());
}
protected:
Log() noexcept : last_buffer_(nullptr), do_log_(true)
{
std::clog.rdbuf(this);
std::clog << Severity() << Tag() << Function() << Conditional() << AixLog::Color::NONE << std::flush;
}
virtual ~Log()
{
sync();
}
int sync() override
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
if (!get_stream().str().empty())
{
if (do_log_)
{
for (const auto& sink : log_sinks_)
{
if (sink->filter.match(metadata_))
sink->log(metadata_, get_stream().str());
}
}
get_stream().str("");
get_stream().clear();
}
return 0;
}
int overflow(int c) override
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
if (c != EOF)
{
if (c == '\n')
sync();
else if (do_log_)
get_stream() << static_cast<char>(c);
}
else
{
sync();
}
return c;
}
private:
friend std::ostream& operator<<(std::ostream& os, const Severity& log_severity);
friend std::ostream& operator<<(std::ostream& os, const Timestamp& timestamp);
friend std::ostream& operator<<(std::ostream& os, const Tag& tag);
friend std::ostream& operator<<(std::ostream& os, const Function& function);
friend std::ostream& operator<<(std::ostream& os, const Conditional& conditional);
std::stringstream& get_stream()
{
auto id = std::this_thread::get_id();
if ((last_buffer_ == nullptr) || (last_id_ != id))
{
last_id_ = id;
last_buffer_ = &(buffer_[id]);
}
return *last_buffer_;
}
/// one buffer per thread to avoid mixed log lines
std::map<std::thread::id, std::stringstream> buffer_;
/// the last thread id
std::thread::id last_id_;
/// the last buffer
std::stringstream* last_buffer_ = nullptr;
Metadata metadata_;
bool do_log_;
std::vector<log_sink_ptr> log_sinks_;
std::recursive_mutex mutex_;
};
/**
* @brief
* Null log sink
*
* Discards all log messages
*/
struct SinkNull : public Sink
{
SinkNull() : Sink(Filter())
{
}
void log(const Metadata& /*metadata*/, const std::string& /*message*/) override
{
}
};
/**
* @brief
* Abstract log sink with support for formatting log message
*
* "format" in the c'tor defines a log pattern.
* For every log message, these placeholders will be substituded:
* - strftime syntax is used to format the logging time stamp (%Y, %m, %d, ...)
* - #ms: milliseconds part of the logging time stamp with leading zeros
* - #severity: log severity
* - #tag_func: the log tag. If empty, the function
* - #tag: the log tag
* - #function: the function
* - #message: the log message
*/
struct SinkFormat : public Sink
{
SinkFormat(const Filter& filter, const std::string& format) : Sink(filter), format_(format)
{
}
virtual void set_format(const std::string& format)
{
format_ = format;
}
void log(const Metadata& metadata, const std::string& message) override = 0;
protected:
virtual void do_log(std::ostream& stream, const Metadata& metadata, const std::string& message) const
{
std::string result = format_;
if (metadata.timestamp)
result = metadata.timestamp.to_string(result);
size_t pos = result.find("#severity");
if (pos != std::string::npos)
result.replace(pos, 9, to_string(metadata.severity));
pos = result.find("#color_severity");
if (pos != std::string::npos)
{
std::stringstream ss;
ss << TextColor(Color::RED) << to_string(metadata.severity) << TextColor(Color::NONE);
result.replace(pos, 15, ss.str());
}
pos = result.find("#tag_func");
if (pos != std::string::npos)
result.replace(pos, 9, metadata.tag ? metadata.tag.text : (metadata.function ? metadata.function.name : "log"));
pos = result.find("#tag");
if (pos != std::string::npos)
result.replace(pos, 4, metadata.tag ? metadata.tag.text : "");
pos = result.find("#function");
if (pos != std::string::npos)
result.replace(pos, 9, metadata.function ? metadata.function.name : "");
pos = result.find("#message");
if (pos != std::string::npos)
{
result.replace(pos, 8, message);
stream << result << std::endl;
}
else
{
if (result.empty() || (result.back() == ' '))
stream << result << message << std::endl;
else
stream << result << " " << message << std::endl;
}
}
std::string format_;
};
/**
* @brief
* Formatted logging to cout
*/
struct SinkCout : public SinkFormat
{
SinkCout(const Filter& filter, const std::string& format = "%Y-%m-%d %H-%M-%S.#ms [#severity] (#tag_func)") : SinkFormat(filter, format)
{
}
void log(const Metadata& metadata, const std::string& message) override
{
do_log(std::cout, metadata, message);
}
};
/**
* @brief
* Formatted logging to cerr
*/
struct SinkCerr : public SinkFormat
{
SinkCerr(const Filter& filter, const std::string& format = "%Y-%m-%d %H-%M-%S.#ms [#severity] (#tag_func)") : SinkFormat(filter, format)
{
}
void log(const Metadata& metadata, const std::string& message) override
{
do_log(std::cerr, metadata, message);
}
};
/**
* @brief
* Formatted logging to file
*/
struct SinkFile : public SinkFormat
{
SinkFile(const Filter& filter, const std::string& filename, const std::string& format = "%Y-%m-%d %H-%M-%S.#ms [#severity] (#tag_func)")
: SinkFormat(filter, format)
{
ofs.open(filename.c_str(), std::ofstream::out | std::ofstream::trunc);
}
~SinkFile() override
{
ofs.close();
}
void log(const Metadata& metadata, const std::string& message) override
{
do_log(ofs, metadata, message);
}
protected:
mutable std::ofstream ofs;
};
#ifdef _WIN32
/**
* @brief
* Windows: Logging to OutputDebugString
*
* Not tested due to unavailability of Windows
*/
struct SinkOutputDebugString : public Sink
{
SinkOutputDebugString(const Filter& filter) : Sink(filter)
{
}
void log(const Metadata& metadata, const std::string& message) override
{
#ifdef UNICODE
std::wstring wide = std::wstring(message.begin(), message.end());
OutputDebugString(wide.c_str());
#else
OutputDebugString(message.c_str());
#endif
}
};
#endif
#ifdef HAS_APPLE_UNIFIED_LOG_
/**
* @brief
* macOS: Logging to Apples system logger
*/
struct SinkUnifiedLogging : public Sink
{
SinkUnifiedLogging(const Filter& filter) : Sink(filter)
{
}
os_log_type_t get_os_log_type(Severity severity) const
{
// https://developer.apple.com/documentation/os/os_log_type_t?language=objc
switch (severity)
{
case Severity::trace:
case Severity::debug:
return OS_LOG_TYPE_DEBUG;
case Severity::info:
case Severity::notice:
return OS_LOG_TYPE_INFO;
case Severity::warning:
return OS_LOG_TYPE_DEFAULT;
case Severity::error:
return OS_LOG_TYPE_ERROR;
case Severity::fatal:
return OS_LOG_TYPE_FAULT;
default:
return OS_LOG_TYPE_DEFAULT;
}
}
void log(const Metadata& metadata, const std::string& message) override
{
os_log_with_type(OS_LOG_DEFAULT, get_os_log_type(metadata.severity), "%{public}s", message.c_str());
}
};
#endif
#ifdef HAS_SYSLOG_
/**
* @brief
* UNIX: Logging to syslog
*/
struct SinkSyslog : public Sink
{
SinkSyslog(const char* ident, const Filter& filter) : Sink(filter)
{
openlog(ident, LOG_PID, LOG_USER);
}
~SinkSyslog() override
{
closelog();
}
int get_syslog_priority(Severity severity) const
{
// http://unix.superglobalmegacorp.com/Net2/newsrc/sys/syslog.h.html
switch (severity)
{
case Severity::trace:
case Severity::debug:
return LOG_DEBUG;
case Severity::info:
return LOG_INFO;
case Severity::notice:
return LOG_NOTICE;
case Severity::warning:
return LOG_WARNING;
case Severity::error:
return LOG_ERR;
case Severity::fatal:
return LOG_CRIT;
default:
return LOG_INFO;
}
}
void log(const Metadata& metadata, const std::string& message) override
{
syslog(get_syslog_priority(metadata.severity), "%s", message.c_str());
}
};
#endif
#ifdef __ANDROID__
/**
* @brief
* Android: Logging to android log
*
* Use logcat to read the logs
*/
struct SinkAndroid : public Sink
{
SinkAndroid(const std::string& ident, const Filter& filter) : Sink(filter), ident_(ident)
{
}
android_LogPriority get_android_prio(Severity severity) const
{
// https://developer.android.com/ndk/reference/log_8h.html
switch (severity)
{
case Severity::trace:
return ANDROID_LOG_VERBOSE;
case Severity::debug:
return ANDROID_LOG_DEBUG;
case Severity::info:
case Severity::notice:
return ANDROID_LOG_INFO;
case Severity::warning:
return ANDROID_LOG_WARN;
case Severity::error:
return ANDROID_LOG_ERROR;
case Severity::fatal:
return ANDROID_LOG_FATAL;
default:
return ANDROID_LOG_UNKNOWN;
}
}
void log(const Metadata& metadata, const std::string& message) override
{
std::string tag = metadata.tag ? metadata.tag.text : (metadata.function ? metadata.function.name : "");
std::string log_tag;
if (!ident_.empty() && !tag.empty())
log_tag = ident_ + "." + tag;
else if (!ident_.empty())
log_tag = ident_;
else if (!tag.empty())
log_tag = tag;
else
log_tag = "log";
__android_log_write(get_android_prio(metadata.severity), log_tag.c_str(), message.c_str());
}
protected:
std::string ident_;
};
#endif
#ifdef _WIN32
/**
* @brief
* Windows: Logging to event logger
*
* Not tested due to unavailability of Windows
*/
struct SinkEventLog : public Sink
{
SinkEventLog(const std::string& ident, const Filter& filter) : Sink(filter)
{
#ifdef UNICODE
std::wstring wide = std::wstring(ident.begin(), ident.end()); // stijnvdb: RegisterEventSource expands to RegisterEventSourceW which takes wchar_t
event_log = RegisterEventSource(NULL, wide.c_str());
#else
event_log = RegisterEventSource(NULL, ident.c_str());
#endif
}
WORD get_type(Severity severity) const
{
// https://msdn.microsoft.com/de-de/library/windows/desktop/aa363679(v=vs.85).aspx
switch (severity)
{
case Severity::trace:
case Severity::debug:
return EVENTLOG_INFORMATION_TYPE;
case Severity::info:
case Severity::notice:
return EVENTLOG_SUCCESS;
case Severity::warning:
return EVENTLOG_WARNING_TYPE;
case Severity::error:
case Severity::fatal:
return EVENTLOG_ERROR_TYPE;
default:
return EVENTLOG_INFORMATION_TYPE;
}
}
void log(const Metadata& metadata, const std::string& message) override
{
#ifdef UNICODE
std::wstring wide = std::wstring(message.begin(), message.end());
// We need this temp variable because we cannot take address of rValue
const auto* c_str = wide.c_str();
ReportEvent(event_log, get_type(metadata.severity), 0, 0, NULL, 1, 0, &c_str, NULL);
#else
const auto* c_str = message.c_str();
ReportEvent(event_log, get_type(metadata.severity), 0, 0, NULL, 1, 0, &c_str, NULL);
#endif
}
protected:
HANDLE event_log;
};
#endif
/**
* @brief
* Log to the system's native sys logger
*
* - Android: Android log
* - macOS: unified log
* - Windows: event log
* - Unix: syslog
*/
struct SinkNative : public Sink
{
SinkNative(const std::string& ident, const Filter& filter) : Sink(filter), log_sink_(nullptr), ident_(ident)
{
#ifdef __ANDROID__
log_sink_ = std::make_shared<SinkAndroid>(ident_, filter);
#elif HAS_APPLE_UNIFIED_LOG_
log_sink_ = std::make_shared<SinkUnifiedLogging>(filter);
#elif _WIN32
log_sink_ = std::make_shared<SinkEventLog>(ident, filter);
#elif HAS_SYSLOG_
log_sink_ = std::make_shared<SinkSyslog>(ident_.c_str(), filter);
#else
/// will not throw or something. Use "get_logger()" to check for success
log_sink_ = nullptr;
#endif
}
virtual log_sink_ptr get_logger()
{
return log_sink_;
}
void log(const Metadata& metadata, const std::string& message) override
{
if (log_sink_ != nullptr)
log_sink_->log(metadata, message);
}
protected:
log_sink_ptr log_sink_;
std::string ident_;
};
/**
* @brief
* Forward log messages to a callback function
*
* Pass the callback function to the c'tor.
* This can be any function that matches the signature of "callback_fun"
* Might also be a lambda function
*/
struct SinkCallback : public Sink
{
using callback_fun = std::function<void(const Metadata& metadata, const std::string& message)>;
SinkCallback(const Filter& filter, callback_fun callback) : Sink(filter), callback_(callback)
{
}
void log(const Metadata& metadata, const std::string& message) override
{
if (callback_)
callback_(metadata, message);
}
private:
callback_fun callback_;
};
/**
* @brief
* ostream << operator for "Severity"
*
* Severity must be the first thing that is logged into clog, since it will reset the loggers metadata.
*/
static std::ostream& operator<<(std::ostream& os, const Severity& log_severity)
{
Log* log = dynamic_cast<Log*>(os.rdbuf());
if (log != nullptr)
{
std::lock_guard<std::recursive_mutex> lock(log->mutex_);
if (log->metadata_.severity != log_severity)
{
log->sync();
log->metadata_.severity = log_severity;
log->metadata_.timestamp = nullptr;
log->metadata_.tag = nullptr;
log->metadata_.function = nullptr;
log->do_log_ = true;
}
}
else
{
os << to_string(log_severity);
}
return os;
}
static std::ostream& operator<<(std::ostream& os, const Timestamp& timestamp)
{
Log* log = dynamic_cast<Log*>(os.rdbuf());
if (log != nullptr)
{
std::lock_guard<std::recursive_mutex> lock(log->mutex_);
log->metadata_.timestamp = timestamp;
}
else if (timestamp)
{
os << timestamp.to_string();
}
return os;
}
static std::ostream& operator<<(std::ostream& os, const Tag& tag)
{
Log* log = dynamic_cast<Log*>(os.rdbuf());
if (log != nullptr)
{
std::lock_guard<std::recursive_mutex> lock(log->mutex_);
log->metadata_.tag = tag;
}
else if (tag)
{
os << tag.text;
}
return os;
}
static std::ostream& operator<<(std::ostream& os, const Function& function)
{
Log* log = dynamic_cast<Log*>(os.rdbuf());
if (log != nullptr)
{
std::lock_guard<std::recursive_mutex> lock(log->mutex_);
log->metadata_.function = function;
}
else if (function)
{
os << function.name;
}
return os;
}
static std::ostream& operator<<(std::ostream& os, const Conditional& conditional)
{
Log* log = dynamic_cast<Log*>(os.rdbuf());
if (log != nullptr)
{
std::lock_guard<std::recursive_mutex> lock(log->mutex_);
log->do_log_ = conditional.is_true();
}
return os;
}
static std::ostream& operator<<(std::ostream& os, const TextColor& text_color)
{
os << "\033[";
if ((text_color.foreground == Color::none) && (text_color.background == Color::none))
os << "0"; // reset colors if no params
if (text_color.foreground != Color::none)
{
os << 29 + static_cast<int>(text_color.foreground);
if (text_color.background != Color::none)
os << ";";
}
if (text_color.background != Color::none)
os << 39 + static_cast<int>(text_color.background);
os << "m";
return os;
}
static std::ostream& operator<<(std::ostream& os, const Color& color)
{
os << TextColor(color);
return os;
}
} // namespace AixLog
#ifdef _WIN32
// We restore the ERROR Windows macro
#pragma pop_macro("ERROR")
#pragma pop_macro("DEBUG")
#endif
#endif // AIX_LOG_HPP