Zonemaster-Backend/lib/Zonemaster/Backend/Log.pm
use strict;
use warnings;
package Zonemaster::Backend::Log;
use English qw( $PID );
use POSIX;
use JSON::PP;
use IO::Handle;
use Log::Any::Adapter::Util ();
use Carp;
use Data::Dumper;
use base qw(Log::Any::Adapter::Base);
my $default_level = Log::Any::Adapter::Util::numeric_level('info');
sub init {
my ($self) = @_;
if ( defined $self->{log_level} && $self->{log_level} =~ /\D/ ) {
$self->{log_level} = lc $self->{log_level};
my $numeric_level = Log::Any::Adapter::Util::numeric_level( $self->{log_level} );
if ( !defined($numeric_level) ) {
croak "Error: Unrecognized log level " . $self->{log_level} . "\n";
}
$self->{log_level} = $numeric_level;
}
$self->{log_level} //= $default_level;
my $fd;
if ( !exists $self->{file} || $self->{file} eq '-') {
if ( $self->{stderr} ) {
$fd = fileno(STDERR);
} else {
$fd = fileno(STDOUT);
}
} else {
open( $fd, '>>', $self->{file} ) or croak "Can't open log file: $!";
}
$self->{handle} = IO::Handle->new_from_fd( $fd, "w" ) or croak "Can't fdopen file: $!";
$self->{handle}->autoflush(1);
if ( !exists $self->{formatter} ) {
if ( $self->{json} ) {
$self->{formatter} = \&format_json;
} else {
$self->{formatter} = \&format_text;
}
}
}
sub format_text {
my ($self, $log_params) = @_;
my $msg;
$msg .= sprintf "%s ", $log_params->{timestamp};
delete $log_params->{timestamp};
$msg .= sprintf(
"[%d] [%s] [%s] %s",
delete $log_params->{pid},
uc delete $log_params->{level},
delete $log_params->{category},
delete $log_params->{message}
);
if ( %$log_params ) {
local $Data::Dumper::Indent = 0;
local $Data::Dumper::Terse = 1;
my $data = Dumper($log_params);
$msg .= " Extra parameters: $data";
}
return $msg
}
sub format_json {
my ($self, $log_params) = @_;
my $js = JSON::PP->new;
$js->canonical( 1 );
return $js->encode( $log_params );
}
sub structured {
my ($self, $level, $category, $string, @items) = @_;
my $log_level = Log::Any::Adapter::Util::numeric_level($level);
return if $log_level > $self->{log_level};
my %log_params = (
timestamp => strftime( "%FT%TZ", gmtime ),
level => $level,
category => $category,
message => $string,
pid => $PID,
);
for my $item ( @items ) {
if (ref($item) eq 'HASH') {
for my $key (keys %$item) {
$log_params{$key} = $item->{$key};
}
}
}
my $msg = $self->{formatter}->($self, \%log_params);
$self->{handle}->print($msg . "\n");
}
# From Log::Any::Adapter::File
foreach my $method ( Log::Any::Adapter::Util::detection_methods() ) {
no strict 'refs';
my $base = substr($method,3);
my $method_level = Log::Any::Adapter::Util::numeric_level( $base );
*{$method} = sub {
return !!( $method_level <= $_[0]->{log_level} );
};
}
1;