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-2015 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   68 use strict;
  14         21  
  14         17501  
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 873 sub prefix { $_[0]->{'prefix'} }
31 37     37 0 79 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   48 my $self = shift;
68 29         53 my ($prefix, $penalty) = @_;
69 29         104 $self->{'prefix'} = $prefix; # Prefix info to prepend
70 29         89 $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 88 my $self = shift;
103 68         127 my ($prio) = @_;
104 68   50     236 my $level = $level{lc(substr($prio, 0, 1))} || 8;
105 68         185 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 87 my $self = shift;
131 68         115 my ($channel, $prio, $msg) = @_;
132 68         183 $self->write($channel, $self->priority($prio), $self->prefix_msg($msg));
133 68         356 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 94 my $self = shift;
155 73         107 my ($priority, $level) = @_;
156 73         277 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 117 my $self = shift;
182 37         60 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         144 require Carp;
195              
196 37         114 my $skip = $offset + 2 + $self->penalty;
197 37         56 $Carp::CarpLevel += $skip;
198 37         108 my $original = $str->str; # Original user message
199 37         7491 my $msg = &$fn('__MESSAGE__');
200 37         1642 $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         66 chomp($msg); # Remove final "\n" added
209              
210 37 100       140 if ($msg =~ s/^(.*?)\n//) {
211 10         28 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       35 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       33 my $index = $] >= 5.008 ? 1 : 0;
237              
238 10         102 $first =~ s/(at (.+) line \d+)$//;
239 10         22 my $bad = $1;
240 10         46 my @stack = split(/\n/, $msg);
241 10 100       71 my ($at) = $stack[$index] =~ /(at \S+ line \d+)$/
242             if defined $stack[$index];
243 10 100       35 $at = "$bad (Log::Agent could not fix it)" unless $at;
244 10         22 $first .= $at;
245 10         44 $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         87 $str->set_str($msg); # Change original message inplace
253             }
254              
255 37         99 $msg = $str->str;
256              
257             # Another Carp workaround kludge.
258 37         103 $msg =~ s/ at .*\d\.at / at /;
259              
260 37         108 $msg =~ s/__MESSAGE__/$original/;
261 37         111 $str->set_str($msg);
262              
263 37         112 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 3 my $self = shift;
302 2         3 my ($str) = @_;
303 2         5 $self->emit('error', 'critical', $str);
304 2         6 die "$str\n";
305             }
306              
307             #
308             # logerr
309             #
310             # Log error
311             #
312             sub logerr {
313 12     12 1 19 my $self = shift;
314 12         18 my ($str) = @_;
315 12         47 $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 29 my $self = shift;
337 17         26 my ($str) = @_;
338 17         80 $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 30 my $self = shift;
348 18         26 my ($str) = @_;
349 18         57 $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 10 my $self = shift;
359 5         12 my ($chan, $prio, $level, $str) = @_;
360 5         17 $self->write($chan, $self->map_pri($prio, $level),
361             $self->prefix_msg($str));
362             }
363              
364             1; # for require
365             __END__