File Coverage

blib/lib/Treex/Core/Log.pm
Criterion Covered Total %
statement 80 127 62.9
branch 9 38 23.6
condition 5 16 31.2
subroutine 20 24 83.3
pod 9 11 81.8
total 123 216 56.9


line stmt bran cond sub pod time code
1             package Treex::Core::Log;
2             $Treex::Core::Log::VERSION = '2.20160630';
3 31     31   39771 use strict;
  31         166  
  31         947  
4 31     31   174 use warnings;
  31         74  
  31         887  
5              
6 31     31   517 use 5.008;
  31         116  
7 31     31   1557 use utf8;
  31         90  
  31         218  
8 31     31   16498 use English '-no_match_vars';
  31         95445  
  31         196  
9              
10 31     31   10649 use Carp qw(cluck);
  31         77  
  31         1706  
11              
12 31     31   13761 use IO::Handle;
  31         143289  
  31         1404  
13 31     31   14978 use Readonly;
  31         105479  
  31         1796  
14 31     31   13474 use Time::HiRes qw(time);
  31         30101  
  31         170  
15              
16 31     31   5315 use Exporter;
  31         80  
  31         1069  
17 31     31   188 use base 'Exporter';
  31         71  
  31         5748  
18             our @EXPORT = qw(log_fatal log_warn log_info log_debug log_memory running_time); ## no critic (ProhibitAutomaticExportation)
19              
20             $Carp::CarpLevel = 1;
21              
22 31     31   3910 binmode STDOUT, ":encoding(utf-8)";
  31         161  
  31         244  
23             binmode STDERR, ":encoding(utf-8)";
24              
25             # Autoflush after every Perl statement should enforce that INFO and FATALs are ordered correctly.
26             {
27              
28             #my $oldfh = select(STDERR);
29             #$| = 1;
30             #select($oldfh);
31             *STDERR->autoflush();
32             }
33              
34              
35             my @ERROR_LEVEL_NAMES = qw(ALL DEBUG INFO WARN FATAL);
36             Readonly my %ERROR_LEVEL_VALUE => map {$ERROR_LEVEL_NAMES[$_] => $_} (0 .. $#ERROR_LEVEL_NAMES);
37              
38             #Readonly my %ERROR_LEVEL_VALUE => (
39             # 'ALL' => 0,
40             # 'DEBUG' => 1,
41             # 'INFO' => 2,
42             # 'WARN' => 3,
43             # 'FATAL' => 4,
44             #);
45              
46              
47 31     31   2193 use Moose::Util::TypeConstraints;
  31         745735  
  31         378  
48             enum 'ErrorLevel' => [keys %ERROR_LEVEL_VALUE];
49              
50             # how many characters of a string-eval are to be shown in the output
51             $Carp::MaxEvalLen = 100;
52              
53             my $unfinished_line;
54              
55             # By default report only messages with INFO or higher level
56             my $current_error_level_value = $ERROR_LEVEL_VALUE{'INFO'};
57              
58             # Time when treex was executed.
59             our $init_time = time ();
60              
61             # returns time elapsed from $init_time.
62             sub running_time
63             {
64 35     35 0 575 return sprintf('%10.3f', time() - $init_time);
65             }
66              
67             # allows to suppress messages with lower than given importance
68             sub log_set_error_level {
69 6     6 1 2303 my $new_error_level = uc(shift);
70 6 50       41 if ( not defined $ERROR_LEVEL_VALUE{$new_error_level} ) {
71 0         0 log_fatal("Unacceptable errorlevel: $new_error_level");
72             }
73 6         85 $current_error_level_value = $ERROR_LEVEL_VALUE{$new_error_level};
74 6         51 return;
75             }
76              
77             sub get_error_level {
78 0     0 1 0 return $ERROR_LEVEL_NAMES[$current_error_level_value];
79             }
80              
81             # fatal error messages can't be suppressed
82             sub log_fatal {
83 10     10 1 42188 my $message = shift;
84 10 50       32 if ($unfinished_line) {
85 0         0 print STDERR "\n";
86 0         0 $unfinished_line = 0;
87             }
88 10         34 my $line = "TREEX-FATAL:" . running_time() . ":\t$message\n\n";
89 10 50       66 if ( $current_error_level_value <= $ERROR_LEVEL_VALUE{'DEBUG'} ) {
90 0 0       0 if ($OS_ERROR) {
91 0         0 $line .= "PERL ERROR MESSAGE: $OS_ERROR\n";
92             }
93 0 0       0 if ($EVAL_ERROR) {
94 0         0 $line .= "PERL EVAL ERROR MESSAGE: $EVAL_ERROR\n";
95             }
96             }
97 10         99 $line .= "PERL STACK:";
98 10         2042 cluck $line;
99 10         4184 run_hooks('FATAL');
100 10         107 die "\n";
101             }
102              
103             # TODO: redesign API - $carp, $no_print_stack
104              
105             sub log_warn {
106 2     2 1 1984 my ( $message, $carp ) = @_;
107 2 50       10 if ( $current_error_level_value <= $ERROR_LEVEL_VALUE{'WARN'} ) {
108 0         0 my $line = "";
109 0 0       0 if ($unfinished_line) {
110 0         0 $line = "\n";
111 0         0 $unfinished_line = 0;
112             }
113 0         0 $line .= "TREEX-WARN:" . running_time() . ":\t$message\n";
114              
115 0 0       0 if ($carp) {
116 0         0 Carp::carp $line;
117             }
118             else {
119 0         0 print STDERR $line;
120             }
121             }
122 2         23 run_hooks('WARN');
123 2         6 return;
124             }
125              
126             sub log_debug {
127 0     0 1 0 my ( $message, $no_print_stack ) = @_;
128 0 0       0 if ( $current_error_level_value <= $ERROR_LEVEL_VALUE{'DEBUG'} ) {
129 0         0 my $line = "";
130 0 0       0 if ($unfinished_line) {
131 0         0 $line = "\n";
132 0         0 $unfinished_line = 0;
133             }
134 0         0 $line .= "TREEX-DEBUG:" . running_time() . ":\t$message\n";
135              
136 0 0       0 if ($no_print_stack) {
137 0         0 print STDERR $line;
138             }
139             else {
140 0         0 Carp::cluck $line;
141             }
142             }
143 0         0 run_hooks('DEBUG');
144 0         0 return;
145             }
146              
147             sub log_info {
148 25     25 1 79 my ( $message, $arg_ref ) = @_;
149 25 50       215 if ( $current_error_level_value <= $ERROR_LEVEL_VALUE{'INFO'} ) {
150 25   33     283 my $same_line = defined $arg_ref && $arg_ref->{same_line};
151 25         47 my $line = "";
152 25 50 33     90 if ( $unfinished_line && !$same_line ) {
153 0         0 $line = "\n";
154 0         0 $unfinished_line = 0;
155             }
156 25 50 33     104 if ( !$same_line || !$unfinished_line ) {
157 25         83 $line .= "TREEX-INFO:" . running_time() . ":\t";
158             }
159 25         61 $line .= $message;
160              
161 25 50       72 if ($same_line) {
162 0         0 $unfinished_line = 1;
163             }
164             else {
165 25         55 $line .= "\n";
166             }
167              
168 25         1388 print STDERR $line;
169 25 50       102 if ($same_line) {
170 0         0 STDERR->flush;
171             }
172             }
173 25         117 run_hooks('INFO');
174 25         62 return;
175             }
176              
177             sub progress { # progress se pres ntred neposila, protoze by se stejne neflushoval
178 0 0   0 1 0 return if $current_error_level_value > $ERROR_LEVEL_VALUE{'INFO'};
179 0 0       0 if ( not $unfinished_line ) {
180 0         0 print STDERR "TREEX-PROGRESS:" . running_time() . ":\t";
181             }
182 0         0 print STDERR "*";
183 0         0 STDERR->flush;
184 0         0 $unfinished_line = 1;
185 0         0 return;
186             }
187              
188             # ---------- HOOKS -----------------
189              
190             my %hooks; # subroutines can be associated with reported events
191              
192             sub add_hook {
193 2     2 1 1040 my ( $level, $subroutine ) = @_;
194 2   100     16 $hooks{$level} ||= [];
195 2         4 push @{ $hooks{$level} }, $subroutine;
  2         6  
196 2         5 return scalar(@{$hooks{$level}}) - 1;
  2         6  
197             }
198              
199             sub del_hook {
200 0     0 0 0 my ( $level, $pos ) = @_;
201 0   0     0 $hooks{$level} ||= [];
202 0 0 0     0 if ( $pos < 0 || $pos >= scalar(@{$hooks{$level}}) ) {
  0         0  
203 0         0 return;
204             }
205 0         0 splice(@{$hooks{$level}}, $pos, 1);
  0         0  
206              
207 0         0 return;
208             }
209              
210             sub run_hooks {
211 37     37 1 101 my ($level) = @_;
212 37         70 foreach my $subroutine ( @{ $hooks{$level} } ) {
  37         136  
213 3         47 &$subroutine;
214             }
215 37         207 return;
216             }
217              
218             1;
219              
220             __END__
221              
222              
223             =encoding utf-8
224              
225             =head1 NAME
226              
227             Treex::Core::Log - logger tailored for the needs of Treex
228              
229             =head1 VERSION
230              
231             version 2.20160630
232              
233             =head1 SYNOPSIS
234              
235             use Treex::Core::Log;
236              
237             Treex::Core::Log::log_set_error_level('DEBUG');
238              
239             sub epilog {
240             print STDERR "I'm going to cease!";
241             }
242             Treex::Core::Log::add_hook('FATAL',&epilog());
243              
244             sub test_value {
245             my $value = shift;
246             log_fatal "Negative values are unacceptable" if $ARGV < 0;
247             log_warn "Zero value is suspicious" if $ARGV == 0;
248             log_debug "test: value=$value";
249             }
250              
251              
252              
253             =head1 DESCRIPTION
254              
255             C<Treex::Core::Log> is a logger developed with the Treex system.
256             It uses more or less standard leveled set of reporting functions,
257             printing the messages at C<STDERR>.
258              
259              
260             Note that this module might be completely substituted
261             by more elaborate solutions such as L<Log::Log4perl> in the
262             whole Treex in the future
263              
264              
265             =head2 Error levels
266              
267              
268             Specifying error level can be used for suppressing
269             reports with lower severity. This module supports four
270             ordered levels of report severity (plus a special value
271             comprising them all).
272              
273             =over 4
274              
275             =item FATAL
276              
277             =item WARN
278              
279             =item INFO - the default value
280              
281             =item DEBUG
282              
283             =item ALL
284              
285             =back
286              
287             The current error level can be accessed by the following functions:
288              
289             =over 4
290              
291             =item log_set_error_level($error_level)
292              
293             =item get_error_level()
294              
295             =back
296              
297              
298              
299             =head2 Basic reporting functions
300              
301             All the following functions are exported by default.
302              
303             =over 4
304              
305             =item log_fatal($message)
306              
307             print the message, print the Perl stack too, and exit
308              
309             =item log_warn($message)
310              
311             =item log_info($message)
312              
313             =item log_debug($message)
314              
315             =back
316              
317              
318              
319             =head2 Other reporting functions
320              
321             =over 4
322              
323             =item log_memory
324              
325             print the consumed memory
326              
327             =item progress
328              
329             print another asterisk in a 'progress bar' composed of asterisks
330              
331             =back
332              
333              
334              
335              
336             =head2 Hooks
337              
338             Another functions can be called prior to reporting events, by
339             hooking a function on a certain error level event.
340              
341             =over 4
342              
343             =item add_hook($level, &hook_subroutine)
344              
345             add the subroutine to the list of subroutines called prior
346             to reporting events with the given level
347              
348             =item run_hooks($level)
349              
350             run all subroutines for the given error level
351              
352             =back
353              
354              
355              
356             =head1 AUTHOR
357              
358             ZdenÄ›k Žabokrtský <zabokrtsky@ufal.mff.cuni.cz>
359              
360             =head1 COPYRIGHT AND LICENSE
361              
362             Copyright © 2011 by Institute of Formal and Applied Linguistics, Charles University in Prague
363              
364             This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.