File Coverage

blib/lib/Log/Deep.pm
Criterion Covered Total %
statement 216 220 98.1
branch 61 78 78.2
condition 41 71 57.7
subroutine 39 39 100.0
pod 24 24 100.0
total 381 432 88.1


line stmt bran cond sub pod time code
1             package Log::Deep;
2              
3             # Created on: 2008-10-19 04:44:02
4             # Create by: ivan
5             # $Id$
6             # $Revision$, $HeadURL$, $Date$
7             # $Revision$, $Source$, $Date$
8              
9 3     3   92876 use strict;
  3         8  
  3         78  
10 3     3   10 use warnings;
  3         4  
  3         68  
11 3     3   1270 use version;
  3         4683  
  3         16  
12 3     3   203 use Carp qw/croak longmess/;
  3         4  
  3         199  
13 3     3   1635 use List::MoreUtils qw/any/;
  3         29674  
  3         18  
14 3     3   3147 use Readonly;
  3         7223  
  3         149  
15 3     3   1359 use Clone qw/clone/;
  3         6450  
  3         152  
16 3     3   2518 use Data::Dump::Streamer;
  3         1251573  
  3         37  
17 3     3   2564 use POSIX qw/strftime/;
  3         18978  
  3         20  
18 3     3   3456 use Fcntl qw/SEEK_END/;
  3         5  
  3         148  
19 3     3   972 use English qw/ -no_match_vars /;
  3         4882  
  3         19  
20 3     3   1267 use base qw/Exporter/;
  3         6  
  3         7616  
21              
22             our $VERSION = version->new('0.3.5');
23              
24             Readonly my @LOG_LEVELS => qw/info message debug warn error fatal/;
25              
26             sub new {
27 4     4 1 574 my $class = shift;
28 4         9 my %param = @_;
29 4         6 my $self = {};
30              
31 4         7 bless $self, $class;
32              
33 4         33 $self->{dump} = Data::Dump::Streamer->new()->Indent(0)->Names('DATA');
34              
35             # set up log levels
36 4 100       267 if (!$param{-level}) {
37 2         9 $self->level(qw/warn error fatal/);
38             }
39             else {
40 2 100       8 $self->level(ref $param{-level} eq 'ARRAY' ? @{$param{-level}} : $param{-level});
  1         3  
41             }
42              
43             # set up the log file parameters
44 4         44 $self->{file} = $param{-file};
45 4         8 $self->{log_dir} = $param{-log_dir};
46 4         8 $self->{log_name} = $param{-name};
47 4         7 $self->{date_fmt} = $param{-date_fmt};
48 4   50     16 $self->{style} = $param{-style} || 'none';
49              
50             # set up the maximum random session id
51 4   50     23 $self->{rand_max} = $param{-rand_max} || 10_000;
52              
53             # set up tracked variables
54             # Configuration variables - These are only recorded with calls to session()
55 4   50     18 $self->{vars_config} = $param{-vars_config} || {};
56 4         7 $self->{vars_config}{ENV} = \%ENV;
57              
58             # runtime varibles - These are recorded with every log message
59 4   50     16 $self->{vars} = $param{-vars} || {};
60              
61 4 50       16 if ($param{-catchwarn}) {
62 0         0 $self->catch_warnings(1);
63             }
64              
65             # check if we are starting a session or not
66 4 50       10 if ($param{-nosession}) {
67 0         0 $self->{session} = $param{-session_id};
68             }
69             else {
70 4         16 $self->session($param{-session_id});
71             }
72              
73 4         16 return $self;
74             }
75              
76             sub info {
77 2     2 1 664 my ($self, @params) = @_;
78              
79 2 100       4 return if !$self->is_info;
80              
81 1 50 33     5 if (!ref $params[0] || ref $params[0] ne 'HASH') {
82 1         2 unshift @params, {};
83             }
84              
85 1         3 $params[0]{-level} = 'info';
86              
87 1         3 return $self->record(@params);
88             }
89              
90             sub message {
91 2     2 1 636 my ($self, @params) = @_;
92              
93 2 100       5 return if !$self->is_message;
94              
95 1 50 33     5 if (!ref $params[0] || ref $params[0] ne 'HASH') {
96 1         3 unshift @params, {};
97             }
98              
99 1         3 $params[0]{-level} = 'message';
100              
101 1         2 return $self->record(@params);
102             }
103              
104             sub debug {
105 2     2 1 819 my ($self, @params) = @_;
106              
107 2 100       5 return if !$self->is_debug;
108              
109 1 50 33     6 if (!ref $params[0] || ref $params[0] ne 'HASH') {
110 1         2 unshift @params, {};
111             }
112              
113 1         3 $params[0]{-level} = 'debug';
114              
115 1         3 return $self->record(@params);
116             }
117              
118             sub warn {
119 2     2 1 749 my ($self, @params) = @_;
120              
121 2 50       6 return if !$self->is_warn;
122              
123 2 100 66     10 if (!ref $params[0] || ref $params[0] ne 'HASH') {
124 1         2 unshift @params, {};
125             }
126              
127 2         5 $params[0]{-level} = 'warn';
128              
129 2         6 return $self->record(@params);
130             }
131              
132             sub error {
133 1     1 1 544 my ($self, @params) = @_;
134              
135 1 50       3 return if !$self->is_error;
136              
137 1 50 33     8 if (!ref $params[0] || ref $params[0] ne 'HASH') {
138 1         3 unshift @params, {};
139             }
140              
141 1         3 $params[0]{-level} = 'error';
142              
143 1         3 my $ans = $self->record(@params);
144 1         3 $self->flush;
145              
146 1         3 return $ans;
147             }
148              
149             sub fatal {
150 1     1 1 611 my ($self, @params) = @_;
151              
152 1 50 33     6 if (!ref $params[0] || ref $params[0] ne 'HASH') {
153 1         2 unshift @params, {};
154             }
155              
156 1         3 $params[0]{-level} = 'fatal';
157              
158 1         2 $self->record(@params);
159              
160 1         21 croak join ' ', @params[ 1 .. @params -1 ];
161              
162 0         0 return;
163             }
164              
165             sub security {
166 1     1 1 670 my ($self, @params) = @_;
167              
168 1 50 33     11 if (!ref $params[0] || ref $params[0] ne 'HASH') {
169 1         2 unshift @params, {};
170             }
171              
172 1         3 $params[0]{-level} = 'security';
173              
174 1         4 return $self->record(@params);
175             }
176              
177             sub record {
178 13     13 1 20 my ($self, $data, @message) = @_;
179 13         18 my $dump = $self->{dump};
180              
181             # check that a session has been created
182 13 50       37 $self->session($data->{-session_id}) if !$self->{session_id};
183              
184 13   100     38 my $level = $data->{-level} || '(none)';
185 13         21 delete $data->{-level};
186              
187 13         19 my $configs = $data->{-write_configs};
188 13         22 delete $data->{-write_configs};
189              
190             my $param = {
191             data => $data,
192             vars => $self->{vars},
193 13         27 };
194              
195             # add all the config variables to the variables to be logged
196 13 100       20 if ($configs) {
197 5         4 for my $var ( keys %{ $self->{vars_config} } ) {
  5         16  
198 5         11 $param->{vars}{$var} = $self->{vars_config}{$var};
199             }
200             }
201              
202             # set up
203 13         395 $param->{stack} = substr longmess, 0, 1_000;
204 13         3313 $param->{stack} =~ s/^\s+[^\n]*Log::Deep::[^\n]*\n//gxms;
205 13         43 $param->{stack} =~ s/\A\s at [^\n]*\n\s+//gxms;
206 13         21 $param->{stack} =~ s/\n[^\n]+\Z/\n.../xms;
207              
208             my @log = (
209             strftime('%Y-%m-%d %H:%M:%S', localtime),
210             $self->{session_id},
211 13         681 $level,
212             (join ' ', @message),
213             $dump->Data($param)->Out(),
214             );
215              
216             # make each part safe for outputting to one line
217 13         70657 for my $col (@log) {
218 75         69 chomp $col;
219             # quote all back slashes
220 75         104 $col =~ s{\\}{\\\\}g;
221             # quote all new lines
222 75         90 $col =~ s/\n/\\n/g;
223             }
224              
225 13         35 my $log = $self->log_handle();
226 13         15 print {$log} join ',', @log;
  13         107  
227 13         13 print {$log} "\n";
  13         18  
228              
229 13         18 $self->{log_session_count}++;
230              
231 13         44 return ;
232             }
233              
234             sub log_handle {
235 13     13 1 23 my $self = shift;
236              
237 13 100       35 if ( !$self->{handle} ) {
238 12   50     43 $self->{log_dir} ||= $ENV{TMP} || '/tmp';
      66        
239 12   50     43 $self->{log_name} ||= (split m{/}, $0)[-1] || 'deep';
      66        
240 12   100     32 $self->{date_fmt} ||= '%Y-%m-%d';
241 12         484 $self->{log_date} = strftime $self->{date_fmt}, localtime;
242              
243 12   66     53 my $file = $self->{file} || "$self->{log_dir}/$self->{log_name}_$self->{log_date}.log";
244              
245             # guarentee that there is a new line before we start writing
246 12         15 my $missing = 0;
247 12 100 100     156 if ( !$self->{reopening} && -s $file ) {
248 1 50       26 open my $fh, '<', $file or die "Could not open the log file $file to check that it ends in a new line: $OS_ERROR\n";
249 1         10 seek $fh, -20, SEEK_END;
250 1         13 my $end = <$fh>;
251 1         6 $missing = $end =~ /\n$/;
252 1         8 close $fh;
253             }
254              
255 12 50       482 open my $fh, '>>', $file or die "Could not open log file $file: $OS_ERROR\n";
256 12         25 $self->{file} = $file;
257 12         23 $self->{handle} = $fh;
258              
259 12 100       34 if ($missing) {
260 1         23 print {$fh} "\n";
  1         8  
261             }
262             }
263              
264 13         23 return $self->{handle};
265             }
266              
267             sub session {
268 5     5 1 694 my ($self, $session_id) = @_;
269              
270 5 100       11 if ( ! defined $session_id ) {
271 4 50 33     13 return if defined $self->{log_session_count} && $self->{log_session_count} == 0;
272             }
273              
274             # use the supplied session id or create a new session id
275 5   33     82 $self->{session_id} = $session_id || int rand $self->{rand_max};
276              
277 5         30 $self->record({ -write_configs => 1 }, '"START"');
278              
279 5         6 $self->{log_session_count} = 0;
280              
281 5         6 return;
282             }
283              
284             sub level {
285 19     19 1 1890 my ($self, @level) = @_;
286              
287 19   100     64 $self->{level} ||= { map { $_ => 0 } @LOG_LEVELS };
  24         106  
288              
289             # if not called with any parameters return the level hash
290 19 100       172 return clone $self->{level} if !@level;
291              
292             # return log state if asked about that state
293 8 50       22 return $self->{level}{$level[1]} if $level[0] eq '-log';
294              
295             # Set a log state if requested
296 8 100       19 return $self->{level}{$level[1]} = 1 if $level[0] eq '-set';
297              
298             # Unset a log state if requested
299 7 100       15 return $self->{level}{$level[1]} = 0 if $level[0] eq '-unset';
300              
301             # if there is only one parameter that is a single digit set the all levels of that digit and higher
302 6 100 100     26 if (@level == 1 && $level[0] =~ /^\d$/) {
303 2         2 my $i = 0;
304 2         6 for my $log_level (@LOG_LEVELS) {
305 12 100       71 $self->{level}{$log_level} = $i++ >= $level[0] ? 1 : 0;
306             }
307              
308 2         27 return clone $self->{level};
309             }
310              
311             # if the is one parameter and it is a string turn on that level and highter
312 4 100 66 3   17 if ( @level == 1 && any { $_ eq $level[0] } @LOG_LEVELS ) {
  3         23  
313              
314             # flag that we have found the starting level
315 1         6 my $found = 0;
316              
317 1         2 for my $log_level (@LOG_LEVELS) {
318              
319             # flag that we have the start level
320 6 100       19 $found = 1 if $log_level eq $level[0];
321              
322             # mark the current level appropriatly
323 6 100       28 $self->{level}{$log_level} = $found ? 1 : 0;
324             }
325              
326 1         19 return clone $self->{level};
327             }
328              
329             # set all levels passed in as active levels.
330 3         6 for my $level (@level) {
331 8         10 $self->{level}{$level} = 1;
332             }
333              
334 3         33 return clone $self->{level};
335             }
336              
337             sub enable {
338 9     9 1 2255 my ($self, @levels) = @_;
339              
340 9         17 for my $level (@levels) {
341 9         17 $self->{level}{$level} = 1;
342             }
343 9         11 return;
344             }
345              
346             sub disable {
347 6     6 1 13 my ($self, @levels) = @_;
348              
349 6         8 for my $level (@levels) {
350 6         12 $self->{level}{$level} = 0;
351             }
352              
353 6         8 return;
354             }
355              
356 4     4 1 18 sub is_info { return $_[0]->{level}{info} }
357 4     4 1 14 sub is_message { return $_[0]->{level}{message} }
358 4     4 1 16 sub is_debug { return $_[0]->{level}{debug} }
359 4     4 1 14 sub is_warn { return $_[0]->{level}{warn} }
360 3     3 1 13 sub is_error { return $_[0]->{level}{error} }
361 2     2 1 6 sub is_fatal { return $_[0]->{level}{fatal} }
362 1     1 1 3 sub is_security { return 1 }
363              
364             sub file {
365 15     15 1 748 my ($self) = @_;
366              
367 15         45 return $self->{file};
368             }
369              
370             sub catch_warnings {
371 2     2 1 1247 my ($self, $action) = @_;
372              
373 2 100 66     20 if ( $action == 1 && !$self->{old_warn_handle} ) {
    50 33        
374             # save old handle
375 1         3 $self->{old_warn_handle} = $SIG{__WARN__};
376              
377             # install a redirect of all warnings to $self->warn
378             $SIG{__WARN__} = sub {
379 1     1   2 my $data = {};
380 1 50       3 if ( ref $_[0] ) {
381             # record the error reference for better display
382             # using the error in the message just stringifys it
383 0         0 $data->{ERROR_OBJ} = $_[0];
384             }
385 1         6 $self->warn( $data, $_[0] );
386             }
387 1         5 }
388             elsif ( $action == 0 && $self->{old_warn_handle} ) {
389 1         3 $SIG{__WARN__} = $self->{old_warn_handle};
390 1         6 delete $self->{old_warn_handle};
391             }
392              
393 2   100     13 return $self->{old_warn_handle} && 1;
394             }
395              
396             sub flush {
397 13     13 1 777 my ($self) = @_;
398              
399 13 100       27 return if ! exists $self->{handle};
400              
401 9         219 close $self->{handle};
402 9         28 delete $self->{handle};
403 9         11 $self->{reopening} = 1;
404              
405 9         13 return;
406             }
407              
408             sub DESTROY {
409 4     4   1817 my ($self) = @_;
410              
411 4 100       13 if ($self->{handle}) {
412 3         88 close $self->{handle};
413             }
414              
415 4         26 return;
416             }
417              
418             1;
419              
420             __END__