File Coverage

blib/lib/Log/Agent/Driver.pm
Criterion Covered Total %
statement 62 92 67.3
branch 8 10 80.0
condition 1 2 50.0
subroutine 13 22 59.0
pod 15 20 75.0
total 99 146 67.8


line stmt bran cond sub pod time code
1             ###########################################################################
2             #
3             # Driver.pm
4             #
5             # Copyright (C) 1999 Raphael Manfredi.
6             # Copyright (C) 2002-2017 Mark Rogaski, mrogaski@cpan.org;
7             # all rights reserved.
8             #
9             # See the README file included with the
10             # distribution for license information.
11             #
12             ##########################################################################
13            
14 14     14   104 use strict;
  14         30  
  14         18339  
15            
16             ########################################################################
17             package Log::Agent::Driver;
18            
19             #
20             # Ancestor for all Log::Agent drivers.
21             #
22            
23             #
24             # Common attribute acccess, initialized via _init().
25             #
26             # prefix the common (static) string info to prepend to messages
27             # penalty the skip Carp penalty to offset to the fixed one
28             #
29            
30 93     93 0 709 sub prefix { $_[0]->{'prefix'} }
31 37     37 0 93 sub penalty { $_[0]->{'penalty'} }
32            
33             #
34             # is_deferred
35             #
36             # Report routine as being deferred
37             #
38             sub is_deferred {
39 0     0 0 0 require Carp;
40 0         0 Carp::confess("deferred");
41             }
42            
43             #
44             # ->make -- deferred
45             #
46             # Creation routine.
47             #
48             sub make {
49 0     0 1 0 &is_deferred;
50             }
51            
52             #
53             # ->channel_eq
54             #
55             # Compare two channels and return true if they go to the same output.
56             #
57             sub channel_eq {
58 0     0 1 0 &is_deferred;
59             }
60            
61             #
62             # ->_init
63             #
64             # Common initilization routine
65             #
66             sub _init {
67 29     29   53 my $self = shift;
68 29         62 my ($prefix, $penalty) = @_;
69 29         106 $self->{'prefix'} = $prefix; # Prefix info to prepend
70 29         75 $self->{'penalty'} = $penalty; # Carp stack skip penalty
71             }
72            
73             #
74             # ->add_penalty -- "exported" only to Log::Agent::Driver::Datum
75             #
76             # Add offset to current driver penalty
77             #
78             sub add_penalty {
79 0     0 0 0 my $self = shift;
80 0         0 my ($offset) = @_;
81 0         0 $self->{penalty} += $offset;
82             }
83            
84             my %level = (
85             'c' => 1,
86             'e' => 2,
87             'w' => 4,
88             'n' => 6,
89             );
90            
91             #
92             # ->priority -- frozen
93             #
94             # Return proper priority for emit() based on one of the following strings:
95             # "critical", "error", "warning", "notice". Those correspond to the hardwired
96             # strings for logconfess()/logdie(), logerr(), logwarn() and logsay().
97             #
98             # This routine is intended to be "frozen", i.e. it MUST NOT be redefined.
99             # Redefine map_pri() if needed, or don't call it in the first place.
100             #
101             sub priority {
102 68     68 1 133 my $self = shift;
103 68         119 my ($prio) = @_;
104 68   50     280 my $level = $level{lc(substr($prio, 0, 1))} || 8;
105 68         203 return $self->map_pri($prio, $level);
106             }
107            
108             #
109             # ->write -- deferred
110             #
111             # Write log entry, physically.
112             # A trailing "\n" is to be added if needed.
113             #
114             # $channel is one of 'debug', 'output', 'error' and can be used to determine
115             # where the emission of the log message should be done.
116             #
117             sub write {
118 0     0 1 0 my $self = shift;
119 0         0 my ($channel, $priority, $logstring) = @_;
120 0         0 &is_deferred;
121             }
122            
123             #
124             # ->emit -- may be redefined
125             #
126             # Routine to call to emit log, resolve priority and prefix logstring.
127             # Ulitimately calls ->write() to perform the physical write.
128             #
129             sub emit {
130 68     68 1 131 my $self = shift;
131 68         143 my ($channel, $prio, $msg) = @_;
132 68         172 $self->write($channel, $self->priority($prio), $self->prefix_msg($msg));
133 68         408 return;
134             }
135            
136            
137             #
138             # ->map_pri -- may be redefined
139             #
140             # Convert a ("priority", level) tupple to a single priority token suitable
141             # for `emit'.
142             #
143             # This is driver-specific: drivers may ignore priority altogether thanks to
144             # the previous level-based filtering done (-trace and -debug switches in the
145             # Log::Agent configuration), choose to give precedence to levels over priority
146             # when "priority:level" was specified, or always ignore levels and only use
147             # "priority".
148             #
149             # The default is to ignore "priority" and "levels", which is suitable to basic
150             # drivers. Only those (ala syslog) which rely on post-filtering need to be
151             # concerned.
152             #
153             sub map_pri {
154 73     73 1 107 my $self = shift;
155 73         132 my ($priority, $level) = @_;
156 73         242 return ''; # ignored for basic drivers
157             }
158            
159             #
160             # ->prefix_msg -- deferred
161             #
162             # Prefix message with driver-specific string, if necessary.
163             #
164             # This routine may or may not use common attributes like the fixed
165             # static prefix or the process's pid.
166             #
167             sub prefix_msg {
168 0     0 1 0 my $self = shift;
169 0         0 my ($str) = @_;
170 0         0 &is_deferred;
171             }
172            
173             #
174             # ->carpmess
175             #
176             # Utility routine for logconfess and logcroak which builds the "die" message
177             # by calling the appropriate routine in Carp, and offseting the stack
178             # according to our call stack configuration, plus any offset.
179             #
180             sub carpmess {
181 37     37 0 98 my $self = shift;
182 37         71 my ($offset, $str, $fn) = @_;
183            
184             #
185             # While confessing, we have basically tell $fn() to skip 2 stack frames:
186             # this call, and our caller chain back to Log::Agent (calls within the
187             # same hierarchy are automatically stripped by Carp).
188             #
189             # To that, we add any additional penalty level, as told us by the creation
190             # routine of each driver, which accounts for extra levels used before
191             # calling us.
192             #
193            
194 37         133 require Carp;
195            
196 37         119 my $skip = $offset + 2 + $self->penalty;
197 37         61 $Carp::CarpLevel += $skip;
198 37         94 my $original = $str->str; # Original user message
199 37         6045 my $msg = &$fn('__MESSAGE__');
200 37         1720 $Carp::CarpLevel -= $skip;
201            
202             #
203             # If we have a newline in the message, we have a full stack trace.
204             # Replace the original message string with the first line, and
205             # append the remaining.
206             #
207            
208 37         81 chomp($msg); # Remove final "\n" added
209            
210 37 100       194 if ($msg =~ s/^(.*?)\n//) {
211 10         34 my $first = $1;
212            
213             #
214             # Patch incorrect computation by Carp, which occurs when we request
215             # a short message and we get a long one. In that case, what we
216             # want is the first line of the extra message.
217             #
218             # This bug manifests when the whole call chain above Log::Agent
219             # lies in "main". When objects are involved, it seems to work
220             # correctly.
221             #
222             # The kludge here is valid for perl 5.005_03. If some day Carp is
223             # fixed, we will have to test for the Perl version. The right fix,
224             # I believe, would be to have Carp skip frame first, and not last
225             # as it currently does.
226             # -- RAM, 30/09/2000
227             #
228            
229 10 50       36 if ($fn == \&Carp::shortmess) { # Kludge alert!!
230            
231             #
232             # And things just got a little uglier with 5.8.0
233             #
234             # -- mrogaski, 1 Aug 2002
235             #
236 10 50       29 my $index = $] >= 5.008 ? 1 : 0;
237            
238 10         67 $first =~ s/(at (.+) line \d+)$//;
239 10         26 my $bad = $1;
240 10         37 my @stack = split(/\n/, $msg);
241 10 100       95 my ($at) = $stack[$index] =~ /(at \S+ line \d+)$/
242             if defined $stack[$index];
243 10 100       37 $at = "$bad (Log::Agent could not fix it)" unless $at;
244 10         78 $first .= $at;
245 10         40 $str->set_str($first);
246             } else {
247 0         0 $str->set_str($first);
248 0         0 $str->append_last("\n");
249 0         0 $str->append_last($msg); # Stack at the very tail of message
250             }
251             } else {
252 27         83 $str->set_str($msg); # Change original message inplace
253             }
254            
255 37         95 $msg = $str->str;
256            
257             # Another Carp workaround kludge.
258 37         125 $msg =~ s/ at .*\d\.at / at /;
259            
260 37         125 $msg =~ s/__MESSAGE__/$original/;
261 37         109 $str->set_str($msg);
262            
263 37         170 return $str;
264             }
265            
266             #
267             # ->logconfess
268             #
269             # Confess fatal error
270             # Error is logged, and then we confess.
271             #
272             sub logconfess {
273 0     0 1 0 my $self = shift;
274 0         0 my ($str) = @_;
275 0         0 my $msg = $self->carpmess(0, $str, \&Carp::longmess);
276 0         0 $self->emit('error', 'critical', $msg);
277 0         0 die "$msg\n";
278             }
279            
280             #
281             # ->logxcroak
282             #
283             # Fatal error, from the perspective of the caller.
284             # Error is logged, and then we confess.
285             #
286             sub logxcroak {
287 0     0 1 0 my $self = shift;
288 0         0 my ($offset, $str) = @_;
289 0         0 my $msg = $self->carpmess($offset, $str, \&Carp::shortmess);
290 0         0 $self->emit('error', 'critical', $msg);
291 0         0 die "$msg\n";
292             }
293            
294             #
295             # ->logdie
296             #
297             # Fatal error
298             # Error is logged, and then we die.
299             #
300             sub logdie {
301 2     2 1 5 my $self = shift;
302 2         4 my ($str) = @_;
303 2         6 $self->emit('error', 'critical', $str);
304 2         8 die "$str\n";
305             }
306            
307             #
308             # logerr
309             #
310             # Log error
311             #
312             sub logerr {
313 12     12 1 32 my $self = shift;
314 12         23 my ($str) = @_;
315 12         44 $self->emit('error', 'error', $str);
316             }
317            
318             #
319             # ->logxcarp
320             #
321             # Log warning, from the perspective of the caller.
322             #
323             sub logxcarp {
324 0     0 1 0 my $self = shift;
325 0         0 my ($offset, $str) = @_;
326 0         0 my $msg = $self->carpmess($offset, $str, \&Carp::shortmess);
327 0         0 $self->emit('error', 'warning', $msg);
328             }
329            
330             #
331             # logwarn
332             #
333             # Log warning
334             #
335             sub logwarn {
336 17     17 1 31 my $self = shift;
337 17         33 my ($str) = @_;
338 17         47 $self->emit('error', 'warning', $str);
339             }
340            
341             #
342             # logsay
343             #
344             # Log message at the "notice" level.
345             #
346             sub logsay {
347 18     18 1 37 my $self = shift;
348 18         36 my ($str) = @_;
349 18         56 $self->emit('output', 'notice', $str);
350             }
351            
352             #
353             # logwrite
354             #
355             # Emit the message to the specified channel
356             #
357             sub logwrite {
358 5     5 1 11 my $self = shift;
359 5         14 my ($chan, $prio, $level, $str) = @_;
360 5         19 $self->write($chan, $self->map_pri($prio, $level),
361             $self->prefix_msg($str));
362             }
363            
364             1; # for require
365             __END__