—package
Treex::Core::Log;
$Treex::Core::Log::VERSION
=
'2.20210102'
;
use
strict;
use
warnings;
use
5.008;
use
utf8;
use
IO::Handle;
use
Readonly;
use
Exporter;
our
@EXPORT
=
qw(log_fatal log_warn log_info log_debug log_memory running_time)
;
## no critic (ProhibitAutomaticExportation)
$Carp::CarpLevel
= 1;
binmode
STDOUT,
":encoding(utf-8)"
;
binmode
STDERR,
":encoding(utf-8)"
;
# Autoflush after every Perl statement should enforce that INFO and FATALs are ordered correctly.
{
#my $oldfh = select(STDERR);
#$| = 1;
#select($oldfh);
*STDERR
->autoflush();
}
my
@ERROR_LEVEL_NAMES
=
qw(ALL DEBUG INFO WARN FATAL)
;
Readonly
my
%ERROR_LEVEL_VALUE
=>
map
{
$ERROR_LEVEL_NAMES
[
$_
] =>
$_
} (0 ..
$#ERROR_LEVEL_NAMES
);
#Readonly my %ERROR_LEVEL_VALUE => (
# 'ALL' => 0,
# 'DEBUG' => 1,
# 'INFO' => 2,
# 'WARN' => 3,
# 'FATAL' => 4,
#);
enum
'ErrorLevel'
=> [
keys
%ERROR_LEVEL_VALUE
];
# how many characters of a string-eval are to be shown in the output
$Carp::MaxEvalLen
= 100;
my
$unfinished_line
;
# By default report only messages with INFO or higher level
my
$current_error_level_value
=
$ERROR_LEVEL_VALUE
{
'INFO'
};
# Time when treex was executed.
our
$init_time
=
time
();
# returns time elapsed from $init_time.
sub
running_time
{
return
sprintf
(
'%10.3f'
,
time
() -
$init_time
);
}
# allows to suppress messages with lower than given importance
sub
log_set_error_level {
my
$new_error_level
=
uc
(
shift
);
if
( not
defined
$ERROR_LEVEL_VALUE
{
$new_error_level
} ) {
log_fatal(
"Unacceptable errorlevel: $new_error_level"
);
}
$current_error_level_value
=
$ERROR_LEVEL_VALUE
{
$new_error_level
};
return
;
}
sub
get_error_level {
return
$ERROR_LEVEL_NAMES
[
$current_error_level_value
];
}
# fatal error messages can't be suppressed
sub
log_fatal {
my
$message
=
shift
;
if
(
$unfinished_line
) {
STDERR
"\n"
;
$unfinished_line
= 0;
}
my
$line
=
"TREEX-FATAL:"
. running_time() .
":\t$message\n\n"
;
if
(
$current_error_level_value
<=
$ERROR_LEVEL_VALUE
{
'DEBUG'
} ) {
if
(
$OS_ERROR
) {
$line
.=
"PERL ERROR MESSAGE: $OS_ERROR\n"
;
}
if
(
$EVAL_ERROR
) {
$line
.=
"PERL EVAL ERROR MESSAGE: $EVAL_ERROR\n"
;
}
}
$line
.=
"PERL STACK:"
;
cluck
$line
;
run_hooks(
'FATAL'
);
die
"\n"
;
}
# TODO: redesign API - $carp, $no_print_stack
sub
log_warn {
my
(
$message
,
$carp
) =
@_
;
if
(
$current_error_level_value
<=
$ERROR_LEVEL_VALUE
{
'WARN'
} ) {
my
$line
=
""
;
if
(
$unfinished_line
) {
$line
=
"\n"
;
$unfinished_line
= 0;
}
$line
.=
"TREEX-WARN:"
. running_time() .
":\t$message\n"
;
if
(
$carp
) {
Carp::carp
$line
;
}
else
{
STDERR
$line
;
}
}
run_hooks(
'WARN'
);
return
;
}
sub
log_debug {
my
(
$message
,
$no_print_stack
) =
@_
;
if
(
$current_error_level_value
<=
$ERROR_LEVEL_VALUE
{
'DEBUG'
} ) {
my
$line
=
""
;
if
(
$unfinished_line
) {
$line
=
"\n"
;
$unfinished_line
= 0;
}
$line
.=
"TREEX-DEBUG:"
. running_time() .
":\t$message\n"
;
if
(
$no_print_stack
) {
STDERR
$line
;
}
else
{
Carp::cluck
$line
;
}
}
run_hooks(
'DEBUG'
);
return
;
}
sub
log_info {
my
(
$message
,
$arg_ref
) =
@_
;
if
(
$current_error_level_value
<=
$ERROR_LEVEL_VALUE
{
'INFO'
} ) {
my
$same_line
=
defined
$arg_ref
&&
$arg_ref
->{same_line};
my
$line
=
""
;
if
(
$unfinished_line
&& !
$same_line
) {
$line
=
"\n"
;
$unfinished_line
= 0;
}
if
( !
$same_line
|| !
$unfinished_line
) {
$line
.=
"TREEX-INFO:"
. running_time() .
":\t"
;
}
$line
.=
$message
;
if
(
$same_line
) {
$unfinished_line
= 1;
}
else
{
$line
.=
"\n"
;
}
STDERR
$line
;
if
(
$same_line
) {
STDERR->flush;
}
}
run_hooks(
'INFO'
);
return
;
}
sub
progress {
# progress se pres ntred neposila, protoze by se stejne neflushoval
return
if
$current_error_level_value
>
$ERROR_LEVEL_VALUE
{
'INFO'
};
if
( not
$unfinished_line
) {
STDERR
"TREEX-PROGRESS:"
. running_time() .
":\t"
;
}
STDERR
"*"
;
STDERR->flush;
$unfinished_line
= 1;
return
;
}
# ---------- HOOKS -----------------
my
%hooks
;
# subroutines can be associated with reported events
sub
add_hook {
my
(
$level
,
$subroutine
) =
@_
;
$hooks
{
$level
} ||= [];
push
@{
$hooks
{
$level
} },
$subroutine
;
return
scalar
(@{
$hooks
{
$level
}}) - 1;
}
sub
del_hook {
my
(
$level
,
$pos
) =
@_
;
$hooks
{
$level
} ||= [];
if
(
$pos
< 0 ||
$pos
>=
scalar
(@{
$hooks
{
$level
}}) ) {
return
;
}
splice
(@{
$hooks
{
$level
}},
$pos
, 1);
return
;
}
sub
run_hooks {
my
(
$level
) =
@_
;
foreach
my
$subroutine
( @{
$hooks
{
$level
} } ) {
&$subroutine
;
}
return
;
}
1;
__END__
=encoding utf-8
=head1 NAME
Treex::Core::Log - logger tailored for the needs of Treex
=head1 VERSION
version 2.20210102
=head1 SYNOPSIS
use Treex::Core::Log;
Treex::Core::Log::log_set_error_level('DEBUG');
sub epilog {
print STDERR "I'm going to cease!";
}
Treex::Core::Log::add_hook('FATAL',&epilog());
sub test_value {
my $value = shift;
log_fatal "Negative values are unacceptable" if $ARGV < 0;
log_warn "Zero value is suspicious" if $ARGV == 0;
log_debug "test: value=$value";
}
=head1 DESCRIPTION
C<Treex::Core::Log> is a logger developed with the Treex system.
It uses more or less standard leveled set of reporting functions,
printing the messages at C<STDERR>.
Note that this module might be completely substituted
by more elaborate solutions such as L<Log::Log4perl> in the
whole Treex in the future
=head2 Error levels
Specifying error level can be used for suppressing
reports with lower severity. This module supports four
ordered levels of report severity (plus a special value
comprising them all).
=over 4
=item FATAL
=item WARN
=item INFO - the default value
=item DEBUG
=item ALL
=back
The current error level can be accessed by the following functions:
=over 4
=item log_set_error_level($error_level)
=item get_error_level()
=back
=head2 Basic reporting functions
All the following functions are exported by default.
=over 4
=item log_fatal($message)
print the message, print the Perl stack too, and exit
=item log_warn($message)
=item log_info($message)
=item log_debug($message)
=back
=head2 Other reporting functions
=over 4
=item log_memory
print the consumed memory
=item progress
print another asterisk in a 'progress bar' composed of asterisks
=back
=head2 Hooks
Another functions can be called prior to reporting events, by
hooking a function on a certain error level event.
=over 4
=item add_hook($level, &hook_subroutine)
add the subroutine to the list of subroutines called prior
to reporting events with the given level
=item run_hooks($level)
run all subroutines for the given error level
=back
=head1 AUTHOR
Zdeněk Žabokrtský <zabokrtsky@ufal.mff.cuni.cz>
=head1 COPYRIGHT AND LICENSE
Copyright © 2011 by Institute of Formal and Applied Linguistics, Charles University in Prague
This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.