MODE: INLINE
#include "util.h"
using namespace xs::xlog;
namespace {
static bool no_format_warnings;
struct PerlObjectFormatter : IFormatter, Backref {
string format (std::string& msg, const Info& i) const override {
if (!is_perl_thread()) throw std::logic_error("can't call pure-perl formatting callback: log() called from perl-foreign thread");
Object o = xs::out(this);
auto ret = o.call("format", xs::out(msg), xs::out(i.level), xs::out(i.module->name()), xs::out(i.file), xs::out(i.line), xs::out(i.func));
return xs::in<string>(ret);
}
~PerlObjectFormatter () { Backref::dtor(); }
};
struct PerlObjectLogger : ILogger, Backref {
void log_format (std::string& msg, const Info& i, const IFormatter& fmt) override {
if (!is_perl_thread()) throw std::logic_error("can't call pure-perl logging callback: log() called from perl-foreign thread");
Object o = xs::out(this);
auto sub = o.method("log_format");
if (!sub) return ILogger::log_format(msg, i, fmt);
sub.call(o.ref(), xs::out(msg), xs::out(i.level), xs::out(i.module->name()), xs::out(i.file), xs::out(i.line), xs::out(i.func), xs::out(&fmt));
}
void log (const string& msg, const Info& info) override {
if (!is_perl_thread()) throw std::logic_error("can't call pure-perl logging callback: log() called from perl-foreign thread");
Object o = xs::out(this);
o.call("log", xs::out(msg), xs::out(info.level));
}
~PerlObjectLogger () { Backref::dtor(); }
};
struct NoWarningsGuard {
NoWarningsGuard (bool enabled) : _enabled(enabled), _cop_warnings(), _dowarn() {
if (!_enabled) return;
_cop_warnings = PL_curcop->cop_warnings;
_dowarn = PL_dowarn;
PL_curcop->cop_warnings = pWARN_STD;
PL_dowarn &= ~G_WARN_ON;
}
~NoWarningsGuard () {
if (!_enabled) return;
PL_curcop->cop_warnings = _cop_warnings;
PL_dowarn = _dowarn;
}
private:
bool _enabled;
decltype(PL_curcop->cop_warnings) _cop_warnings;
U8 _dowarn;
};
}
namespace xs {
template <class TYPE> struct Typemap<PerlObjectLogger*, TYPE> : Typemap<ILogger*, TYPE> {};
template <class TYPE> struct Typemap<PerlObjectFormatter*, TYPE> : Typemap<IFormatter*, TYPE> {};
}
#undef PANDA_LOG_CODE_POINT
#define PANDA_LOG_CODE_POINT make_code_point()
static inline CV* get_context_sub () {
int depth = 0;
auto ctx = caller_cx(depth, nullptr);
while (ctx) {
if (CxTYPE(ctx) == CXt_SUB) return ctx->blk_sub.cv;
ctx = caller_cx(++depth, nullptr);
}
return nullptr;
}
static inline CodePoint make_code_point () {
auto cop = PL_curcop;
string_view func;
auto cv = get_context_sub();
if (cv) {
GV* gv = CvGV(cv);
if (gv) func = string_view(GvNAME(gv), GvNAMELEN(gv));
}
return CodePoint{CopFILE(cop), CopLINE(cop), func};
}
static inline Sv format_args (SV** args, int items) {
if (!items) return Simple(default_message);
if (items == 1) {
Sv arg(args[0]);
if (arg.is_simple()) { return arg; }
else { return Simple(SvPV_nolen(args[0])); }
}
NoWarningsGuard g(no_format_warnings); (void)g;
STRLEN patlen;
auto pat = SvPV(args[0], patlen);
Sv ret = Simple::create(patlen * 1.5);
bool stub = false;
sv_vcatpvfn(ret, pat, patlen, nullptr, args + 1, items - 1, &stub);
return ret;
}
static inline void peep_args (SV**& args, I32& items, const Module*& module, Sub& sub) {
if (items && SvROK(args[0])) {
auto first = SvRV(args[0]);
if (has_module(first)) {
module = xs::in<Module*>(first);
++args;
--items;
}
else if (SvTYPE(first) == SVt_PVCV) {
if (items > 1) throw exception("no arguments should follow subref when logging");
sub = first;
}
}
if (!module) module = resolve_module(0); // auto detect module by namespace
}
template<typename Log>
void forward(Log&& log, SV**& args, I32& items, Sub& sub) {
Simple msg;
if (sub) {
Sv ret = sub.call();
SV* sv = ret;
msg = format_args(&sv, 1);
}
else {
msg = format_args(args, items);
}
log << msg.as_string<string_view>();
}
static void xslog (Level level, SV** args, I32 items) {
const Module* module = nullptr;
Sub sub;
peep_args(args, items, module, sub);
panda_log(level, *module, [&]{ forward(log, args, items, sub); });
}
MODULE = XLog PACKAGE = XLog
PROTOTYPES: DISABLE
BOOT {
Stash stash(__PACKAGE__);
xs::exp::create_constants(stash, {
{"VERBOSE_DEBUG", (int)Level::VerboseDebug},
{"DEBUG", (int)Level::Debug},
{"INFO", (int)Level::Info},
{"NOTICE", (int)Level::Notice},
{"WARNING", (int)Level::Warning},
{"ERROR", (int)Level::Error},
{"CRITICAL", (int)Level::Critical},
{"ALERT", (int)Level::Alert},
{"EMERGENCY", (int)Level::Emergency},
});
auto root_module = xs::out(&::panda_log_module);
root_module.readonly(1);
stash.add_const_sub("default_format", xs::out(default_format));
stash.add_const_sub("root", root_module);
stash.store("root_module", root_module);
xs::at_perl_destroy([]{
if (dyn_cast<PerlObjectLogger*>(get_logger().get())) set_logger(nullptr);
if (dyn_cast<PerlObjectFormatter*>(get_formatter().get())) set_formatter(nullptr);
});
}
void set_level (Level level, string_view module = {})
void set_logger (ILoggerSP logger)
void set_formatter (IFormatterSP fmt) : ALIAS(set_format=1) {
(void)ix;
set_formatter(fmt);
}
ILoggerSP get_logger ()
IFormatterSP get_formatter ()
Module* get_module (string_view name)
void log (Level level, ...) {
xslog(level, &ST(1), items-1);
if (!(PL_op->op_spare & 1)) xlog::optimize();
}
Module* resolve_module (size_t depth = 0) {
RETVAL = resolve_module(depth);
}
void disable_format_warnings () : ALIAS(enable_format_warnings=1) {
no_format_warnings = !ix;
}
void __logXXX (...) : ALIAS(verbose_debug=0, debug=1, info=2, notice=3, warning=4, error=5, critical=6, alert=7, emergency=8) {
Level level = Level((int)Level::VerboseDebug + ix);
xslog(level, &ST(0), items);
if (!(PL_op->op_spare & 1)) xlog::optimize(level);
}