#pragma once
#include "../pp.h"
#include "../string.h"
#include "../function.h"
#include <time.h>
#include <string>
#include <memory>
#include <vector>
#include <ostream>
#include <cstdlib>
#include <chrono>
namespace
panda {
namespace
log
{
struct
Module;
}}
extern
panda::
log
::Module panda_log_module;
namespace
panda {
namespace
log
{
#define PANDA_LOG_CODE_POINT panda::log::CodePoint{__FILE__, __LINE__, __func__}
#define panda_should_log(...) PANDA_PP_VFUNC(PANDA_SHOULD_LOG, __VA_ARGS__)
#define PANDA_SHOULD_LOG1(lvl) PANDA_SHOULD_LOG2(lvl, panda_log_module)
#define PANDA_SHOULD_LOG2(lvl, mod) ((lvl) >= (mod).level())
#define panda_should_rlog(lvl) PANDA_SHOULD_LOG2(lvl, ::panda_log_module)
#define panda_log(...) PANDA_LOG(__VA_ARGS__) // proxy to expand args
#define PANDA_LOG(lvl, ...) PANDA_PP_VFUNC(PANDA_LOG, PANDA_PP_VJOIN(lvl, __VA_ARGS__)) // separate first arg to detect empty args
#define PANDA_LOG1(lvl) PANDA_LOG2(lvl, default_message)
#define PANDA_LOG2(lvl, msg) PANDA_LOG3(lvl, panda_log_module, msg)
#define PANDA_LOG3(lvl, mod, msg) do { \
if
(PANDA_SHOULD_LOG2(lvl, mod)) { \
std::ostream&
log
= panda::
log
::details::get_os(); \
panda::static_if<panda::
log
::details::IsEval<panda::
log
::details::getf(#msg)>::value>([&](
auto
) { \
(panda::
log
::details::LambdaStream&)
log
<< msg; \
})(panda::
log
::details::Unique1{}); \
panda::static_if<!panda::
log
::details::IsEval<panda::
log
::details::getf(#msg)>::value>([&](
auto
) { \
log
<< msg; \
})(panda::
log
::details::Unique2{}); \
panda::
log
::details::do_log(
log
, lvl, &(mod), PANDA_LOG_CODE_POINT); \
} \
}
while
(0)
#define panda_log_verbose_debug(...) panda_log(panda::log::Level::VerboseDebug, __VA_ARGS__)
#define panda_log_debug(...) panda_log(panda::log::Level::Debug, __VA_ARGS__)
#define panda_log_info(...) panda_log(panda::log::Level::Info, __VA_ARGS__)
#define panda_log_notice(...) panda_log(panda::log::Level::Notice, __VA_ARGS__)
#define panda_log_warn(...) panda_log(panda::log::Level::Warning, __VA_ARGS__)
#define panda_log_warning(...) panda_log(panda::log::Level::Warning, __VA_ARGS__)
#define panda_log_error(...) panda_log(panda::log::Level::Error, __VA_ARGS__)
#define panda_log_critical(...) panda_log(panda::log::Level::Critical, __VA_ARGS__)
#define panda_log_alert(...) panda_log(panda::log::Level::Alert, __VA_ARGS__)
#define panda_log_emergency(...) panda_log(panda::log::Level::Emergency, __VA_ARGS__)
#define panda_rlog(level, msg) panda_log(level, ::panda_log_module, msg)
#define panda_rlog_verbose_debug(msg) panda_rlog(panda::log::Level::VerboseDebug, msg)
#define panda_rlog_debug(msg) panda_rlog(panda::log::Level::Debug, msg)
#define panda_rlog_info(msg) panda_rlog(panda::log::Level::Info, msg)
#define panda_rlog_notice(msg) panda_rlog(panda::log::Level::Notice, msg)
#define panda_rlog_warn(msg) panda_rlog(panda::log::Level::Warning, msg)
#define panda_rlog_warning(msg) panda_rlog(panda::log::Level::Warning, msg)
#define panda_rlog_error(msg) panda_rlog(panda::log::Level::Error, msg)
#define panda_rlog_critical(msg) panda_rlog(panda::log::Level::Critical, msg)
#define panda_rlog_alert(msg) panda_rlog(panda::log::Level::Alert, msg)
#define panda_rlog_emergency(msg) panda_rlog(panda::log::Level::Emergency, msg)
#define panda_log_ctor(...) PANDA_PP_VFUNC(PANDA_LOG_CTOR, __VA_ARGS__)
#define panda_log_dtor(...) PANDA_PP_VFUNC(PANDA_LOG_DTOR, __VA_ARGS__)
#define PANDA_LOG_CTOR0() PANDA_LOG_CTOR1(panda_log_module)
#define PANDA_LOG_CTOR1(mod) PANDA_LOG3(panda::log::Level::VerboseDebug, mod, __func__ << " [ctor]")
#define PANDA_LOG_DTOR0() PANDA_LOG_DTOR1(panda_log_module)
#define PANDA_LOG_DTOR1(mod) PANDA_LOG3(panda::log::Level::VerboseDebug, mod, __func__ << " [dtor]")
#define panda_rlog_ctor() panda_log_ctor(::panda_log_module)
#define panda_rlog_dtor() panda_log_dtor(::panda_log_module)
#define panda_debug_v(var) panda_log_debug(#var << " = " << (var))
#define PANDA_ASSERT(var, msg) if(!(auto assert_value = var)) { panda_log_emergency("assert failed: " << #var << " is " << assert_value << msg) }
extern
string_view default_message;
enum
class
Level {
VerboseDebug = 0,
Debug,
Info,
Notice,
Warning,
Error,
Critical,
Alert,
Emergency
};
struct
CodePoint {
string_view file;
uint32_t line;
string_view func;
};
struct
Module;
struct
Info {
Info () : level(), module(), line() {}
Info (Level level,
const
Module* module,
const
string_view& file, uint32_t line,
const
string_view& func,
const
string_view& program_name)
: level(level), module(module), file(file), line(line), func(func), program_name{program_name} {}
using
time_point = std::chrono::system_clock::time_point;
Level level;
const
Module* module;
string_view file;
uint32_t line;
string_view func;
time_point
time
;
string_view program_name;
};
struct
IFormatter : AtomicRefcnt {
virtual
string format (std::string&,
const
Info&)
const
= 0;
virtual
~IFormatter () {}
};
using
IFormatterSP = iptr<IFormatter>;
struct
ILogger : AtomicRefcnt {
virtual
void
log_format (std::string&,
const
Info&,
const
IFormatter&);
virtual
void
log
(
const
string&,
const
Info&);
virtual
~ILogger () = 0;
};
using
ILoggerSP = iptr<ILogger>;
using
format_fn = function<string(std::string&,
const
Info&)>;
using
logger_format_fn = function<
void
(std::string&,
const
Info&,
const
IFormatter&)>;
using
logger_fn = function<
void
(
const
string&,
const
Info&)>;
inline
ILoggerSP make_logger (std::nullptr_t) {
return
{}; }
inline
ILoggerSP make_logger (ILoggerSP l) {
return
l; }
ILoggerSP make_logger (
const
logger_fn& f);
ILoggerSP make_logger (
const
logger_format_fn& f);
inline
IFormatterSP make_formatter (std::nullptr_t) {
return
{}; }
inline
IFormatterSP make_formatter (
const
IFormatterSP& f) {
return
f; }
IFormatterSP make_formatter (
const
format_fn& f);
IFormatterSP make_formatter (string_view pattern);
struct
ILoggerFromAny {
ILoggerSP value;
ILoggerFromAny () {}
template
<
class
T> ILoggerFromAny (T&& l) : value(make_logger(std::forward<T>(l))) {}
};
struct
IFormatterFromAny {
IFormatterSP value;
IFormatterFromAny () {}
template
<
class
T> IFormatterFromAny (T&& f) : value(make_formatter(std::forward<T>(f))) {}
};
struct
Module {
using
Modules = std::vector<Module*>;
Module (
const
string& name, Level level = Level::Warning);
Module (
const
string& name, Module& parent, Level level = Level::Warning);
Module (
const
string& name, Module* parent, Level level = Level::Warning);
Module (
const
string& name, std::nullptr_t, Level level = Level::Warning);
Module (
const
Module&) =
delete
;
Module (Module&&) =
delete
;
Module& operator= (
const
Module&) =
delete
;
const
string& name ()
const
;
const
Module* parent ()
const
;
Level level ()
const
;
const
Modules& children ()
const
;
bool
passthrough ()
const
;
void
set_level (Level);
void
set_logger (ILoggerFromAny,
bool
passthrough =
false
);
void
set_formatter (IFormatterFromAny);
ILoggerSP get_logger ();
IFormatterSP get_formatter ();
virtual
~Module ();
void
init();
private
:
Module* _parent;
Level _level;
Modules _children;
string _name;
bool
_passthrough =
false
;
void
_set_effective_logger (
const
ILoggerSP&);
void
_set_effective_formatter (
const
IFormatterSP&);
};
void
set_level (Level, string_view module =
""
);
void
set_logger (ILoggerFromAny l);
void
set_formatter (IFormatterFromAny f);
void
set_program_name(
const
string& value)
noexcept
;
ILoggerSP get_logger ();
IFormatterSP get_formatter ();
Module* get_module (string_view);
std::vector<Module*> get_modules ();
namespace
details {
std::ostream& get_os ();
std::ostream& get_os_tmp ();
bool
do_log (std::ostream&, Level,
const
Module*,
const
CodePoint&);
template
<
char
T>
struct
IsEval : std::false_type {};
template
<>
struct
IsEval<
'['
> : std::true_type {};
static
constexpr
inline
char
getf (
const
char
* s) {
return
*s; }
struct
LambdaStream : std::ostream {};
struct
Unique1 {};
struct
Unique2 {};
template
<
class
T>
std::enable_if_t<panda::has_call_operator<T>::value, LambdaStream&> operator<< (LambdaStream& os, T&& f) {
f();
return
os;
}
}
struct
escaped { string_view src; };
struct
prettify_json {
string_view src;
std::string str;
size_t
unfold_limit;
explicit
prettify_json (
const
string_view& sv,
size_t
unfold_limit = 1) : src(sv), unfold_limit(unfold_limit) {}
template
<
typename
T,
typename
= std::enable_if_t<!std::is_same<std::decay_t<T>, string>::value && !std::is_same<std::decay_t<T>, string_view>::value, T>>
explicit
prettify_json (T&& v,
size_t
unfold_limit = 1) : unfold_limit(unfold_limit) {
auto
& os = details::get_os_tmp();
os << v;
set_from_os(os);
}
private
:
void
set_from_os(std::ostream&);
};
std::ostream& operator<< (std::ostream&,
const
escaped&);
std::ostream& operator<< (std::ostream&,
const
prettify_json&);
}}
extern
panda::
log
::Module panda_log_module;