File Coverage

xs/XLog.xsi
Criterion Covered Total %
statement 29 29 100.0
branch 38 76 50.0
condition n/a
subroutine n/a
pod n/a
total 67 105 63.8


line stmt bran cond sub pod time code
1             MODE: INLINE
2             #include "util.h"
3             using namespace xs::xlog;
4              
5             namespace {
6             static bool no_format_warnings;
7              
8             struct PerlObjectFormatter : IFormatter, Backref {
9             string format (std::string& msg, const Info& i) const override {
10             if (!is_perl_thread()) throw std::logic_error("can't call pure-perl formatting callback: log() called from perl-foreign thread");
11             Object o = xs::out(this);
12             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));
13             return xs::in(ret);
14             }
15            
16             ~PerlObjectFormatter () { Backref::dtor(); }
17             };
18            
19             struct PerlObjectLogger : ILogger, Backref {
20             void log_format (std::string& msg, const Info& i, const IFormatter& fmt) override {
21             if (!is_perl_thread()) throw std::logic_error("can't call pure-perl logging callback: log() called from perl-foreign thread");
22             Object o = xs::out(this);
23             auto sub = o.method("log_format");
24             if (!sub) return ILogger::log_format(msg, i, fmt);
25             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));
26             }
27            
28             void log (const string& msg, const Info& info) override {
29             if (!is_perl_thread()) throw std::logic_error("can't call pure-perl logging callback: log() called from perl-foreign thread");
30             Object o = xs::out(this);
31             o.call("log", xs::out(msg), xs::out(info.level));
32             }
33            
34             ~PerlObjectLogger () { Backref::dtor(); }
35             };
36            
37             struct NoWarningsGuard {
38             NoWarningsGuard (bool enabled) : _enabled(enabled), _cop_warnings(), _dowarn() {
39             if (!_enabled) return;
40             _cop_warnings = PL_curcop->cop_warnings;
41             _dowarn = PL_dowarn;
42             PL_curcop->cop_warnings = pWARN_STD;
43             PL_dowarn &= ~G_WARN_ON;
44             }
45            
46             ~NoWarningsGuard () {
47             if (!_enabled) return;
48             PL_curcop->cop_warnings = _cop_warnings;
49             PL_dowarn = _dowarn;
50             }
51            
52             private:
53             bool _enabled;
54             decltype(PL_curcop->cop_warnings) _cop_warnings;
55             U8 _dowarn;
56             };
57             }
58              
59             namespace xs {
60             template struct Typemap : Typemap {};
61             template struct Typemap : Typemap {};
62             }
63              
64             #undef PANDA_LOG_CODE_POINT
65             #define PANDA_LOG_CODE_POINT make_code_point()
66              
67             static inline CV* get_context_sub () {
68             int depth = 0;
69             auto ctx = caller_cx(depth, nullptr);
70             while (ctx) {
71             if (CxTYPE(ctx) == CXt_SUB) return ctx->blk_sub.cv;
72             ctx = caller_cx(++depth, nullptr);
73             }
74             return nullptr;
75             }
76              
77             static inline CodePoint make_code_point () {
78             auto cop = PL_curcop;
79            
80             string_view func;
81             auto cv = get_context_sub();
82             if (cv) {
83             GV* gv = CvGV(cv);
84             if (gv) func = string_view(GvNAME(gv), GvNAMELEN(gv));
85             }
86            
87             return CodePoint{CopFILE(cop), CopLINE(cop), func};
88             }
89              
90             static inline Sv format_args (SV** args, int items) {
91             if (!items) return Simple(default_message);
92            
93             if (items == 1) {
94             Sv arg(args[0]);
95             if (arg.is_simple()) { return arg; }
96             else { return Simple(SvPV_nolen(args[0])); }
97             }
98            
99             NoWarningsGuard g(no_format_warnings); (void)g;
100            
101             STRLEN patlen;
102             auto pat = SvPV(args[0], patlen);
103             Sv ret = Simple::create(patlen * 1.5);
104             bool stub = false;
105             sv_vcatpvfn(ret, pat, patlen, nullptr, args + 1, items - 1, &stub);
106             return ret;
107             }
108              
109             static inline void peep_args (SV**& args, I32& items, const Module*& module, Sub& sub) {
110             if (items && SvROK(args[0])) {
111             auto first = SvRV(args[0]);
112             if (has_module(first)) {
113             module = xs::in(first);
114             ++args;
115             --items;
116             }
117             else if (SvTYPE(first) == SVt_PVCV) {
118             if (items > 1) throw exception("no arguments should follow subref when logging");
119             sub = first;
120             }
121             }
122              
123             if (!module) module = resolve_module(0); // auto detect module by namespace
124             }
125              
126             template
127             void forward(Log&& log, SV**& args, I32& items, Sub& sub) {
128             Simple msg;
129             if (sub) {
130             Sv ret = sub.call();
131             SV* sv = ret;
132             msg = format_args(&sv, 1);
133             }
134             else {
135             msg = format_args(args, items);
136             }
137             log << msg.as_string();
138             }
139              
140             static void xslog (Level level, SV** args, I32 items) {
141             const Module* module = nullptr;
142             Sub sub;
143             peep_args(args, items, module, sub);
144             panda_log(level, *module, [&]{ forward(log, args, items, sub); });
145             }
146              
147             MODULE = XLog PACKAGE = XLog
148             PROTOTYPES: DISABLE
149              
150             BOOT {
151 16 50         Stash stash(__PACKAGE__);
152            
153 88 50         xs::exp::create_constants(stash, {
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    100          
    0          
154             {"VERBOSE_DEBUG", (int)Level::VerboseDebug},
155             {"DEBUG", (int)Level::Debug},
156             {"INFO", (int)Level::Info},
157             {"NOTICE", (int)Level::Notice},
158             {"WARNING", (int)Level::Warning},
159             {"ERROR", (int)Level::Error},
160             {"CRITICAL", (int)Level::Critical},
161             {"ALERT", (int)Level::Alert},
162             {"EMERGENCY", (int)Level::Emergency},
163 80 50         });
164            
165 16 50         auto root_module = xs::out(&::panda_log_module);
166 8           root_module.readonly(1);
167 8 50         stash.add_const_sub("default_format", xs::out(default_format));
    50          
168 8 50         stash.add_const_sub("root", root_module);
169 8 50         stash.store("root_module", root_module);
170            
171 32 50         xs::at_perl_destroy([]{
172 8 50         if (dyn_cast(get_logger().get())) set_logger(nullptr);
    50          
    0          
173 8 50         if (dyn_cast(get_formatter().get())) set_formatter(nullptr);
    50          
    0          
174 24 50         });
175             }
176              
177 31 50         void set_level (Level level, string_view module = {})
178              
179 55 50         void set_logger (ILoggerSP logger)
    50          
180              
181             void set_formatter (IFormatterSP fmt) : ALIAS(set_format=1) {
182             (void)ix;
183 52 50         set_formatter(fmt);
    50          
184             }
185              
186 2           ILoggerSP get_logger ()
187 1 50          
188 2           IFormatterSP get_formatter ()
189 1 50          
190 6           Module* get_module (string_view name)
191 3 50          
192             void log (Level level, ...) {
193 64           xslog(level, &ST(1), items-1);
194 64 100         if (!(PL_op->op_spare & 1)) xlog::optimize();
195             }
196              
197             Module* resolve_module (size_t depth = 0) {
198 40           RETVAL = resolve_module(depth);
199 20 50         }
200              
201             void disable_format_warnings () : ALIAS(enable_format_warnings=1) {
202 2           no_format_warnings = !ix;
203             }
204              
205             void __logXXX (...) : ALIAS(verbose_debug=0, debug=1, info=2, notice=3, warning=4, error=5, critical=6, alert=7, emergency=8) {
206 104           Level level = Level((int)Level::VerboseDebug + ix);
207 104           xslog(level, &ST(0), items);
208 102 100         if (!(PL_op->op_spare & 1)) xlog::optimize(level);
209             }