File Coverage

blib/lib/Treex/Core/Log.pm
Criterion Covered Total %
statement 89 127 70.0
branch 14 38 36.8
condition 5 16 31.2
subroutine 21 24 87.5
pod 9 11 81.8
total 138 216 63.8


line stmt bran cond sub pod time code
1             package Treex::Core::Log;
2             $Treex::Core::Log::VERSION = '2.20210102';
3 31     31   138867 use strict;
  31         75  
  31         963  
4 31     31   166 use warnings;
  31         65  
  31         784  
5              
6 31     31   504 use 5.008;
  31         108  
7 31     31   2044 use utf8;
  31         118  
  31         199  
8 31     31   18004 use English '-no_match_vars';
  31         104093  
  31         185  
9              
10 31     31   11436 use Carp qw(cluck);
  31         72  
  31         1750  
11              
12 31     31   13824 use IO::Handle;
  31         150990  
  31         1780  
13 31     31   17287 use Readonly;
  31         124141  
  31         1963  
14 31     31   14645 use Time::HiRes qw(time);
  31         37144  
  31         181  
15              
16 31     31   5474 use Exporter;
  31         93  
  31         1209  
17 31     31   202 use base 'Exporter';
  31         89  
  31         8109  
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   4200 binmode STDOUT, ":encoding(utf-8)";
  31         141  
  31         251  
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   2223 use Moose::Util::TypeConstraints;
  31         856456  
  31         383  
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 46     46 0 647 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 7     7 1 5402 my $new_error_level = uc(shift);
70 7 50       49 if ( not defined $ERROR_LEVEL_VALUE{$new_error_level} ) {
71 0         0 log_fatal("Unacceptable errorlevel: $new_error_level");
72             }
73 7         85 $current_error_level_value = $ERROR_LEVEL_VALUE{$new_error_level};
74 7         43 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 11     11 1 27 my $message = shift;
84 11 50       38 if ($unfinished_line) {
85 0         0 print STDERR "\n";
86 0         0 $unfinished_line = 0;
87             }
88 11         32 my $line = "TREEX-FATAL:" . running_time() . ":\t$message\n\n";
89 11 50       122 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 11         167 $line .= "PERL STACK:";
98 11         3044 cluck $line;
99 11         3355 run_hooks('FATAL');
100 11         144 die "\n";
101             }
102              
103             # TODO: redesign API - $carp, $no_print_stack
104              
105             sub log_warn {
106 3     3 1 2377 my ( $message, $carp ) = @_;
107 3 100       19 if ( $current_error_level_value <= $ERROR_LEVEL_VALUE{'WARN'} ) {
108 1         10 my $line = "";
109 1 50       4 if ($unfinished_line) {
110 0         0 $line = "\n";
111 0         0 $unfinished_line = 0;
112             }
113 1         5 $line .= "TREEX-WARN:" . running_time() . ":\t$message\n";
114              
115 1 50       6 if ($carp) {
116 0         0 Carp::carp $line;
117             }
118             else {
119 1         147 print STDERR $line;
120             }
121             }
122 3         31 run_hooks('WARN');
123 3         11 return;
124             }
125              
126             sub log_debug {
127 25     25 1 57 my ( $message, $no_print_stack ) = @_;
128 25 50       158 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 25         259 run_hooks('DEBUG');
144 25         58 return;
145             }
146              
147             sub log_info {
148 46     46 1 141 my ( $message, $arg_ref ) = @_;
149 46 100       320 if ( $current_error_level_value <= $ERROR_LEVEL_VALUE{'INFO'} ) {
150 34   33     378 my $same_line = defined $arg_ref && $arg_ref->{same_line};
151 34         60 my $line = "";
152 34 50 33     95 if ( $unfinished_line && !$same_line ) {
153 0         0 $line = "\n";
154 0         0 $unfinished_line = 0;
155             }
156 34 50 33     97 if ( !$same_line || !$unfinished_line ) {
157 34         104 $line .= "TREEX-INFO:" . running_time() . ":\t";
158             }
159 34         78 $line .= $message;
160              
161 34 50       97 if ($same_line) {
162 0         0 $unfinished_line = 1;
163             }
164             else {
165 34         59 $line .= "\n";
166             }
167              
168 34         1380 print STDERR $line;
169 34 50       174 if ($same_line) {
170 0         0 STDERR->flush;
171             }
172             }
173 46         363 run_hooks('INFO');
174 46         131 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 1144 my ( $level, $subroutine ) = @_;
194 2   100     14 $hooks{$level} ||= [];
195 2         4 push @{ $hooks{$level} }, $subroutine;
  2         7  
196 2         4 return scalar(@{$hooks{$level}}) - 1;
  2         5  
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 85     85 1 239 my ($level) = @_;
212 85         157 foreach my $subroutine ( @{ $hooks{$level} } ) {
  85         281  
213 3         56 &$subroutine;
214             }
215 85         235 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.20210102
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.