File Coverage

lib/Beekeeper/Logger.pm
Criterion Covered Total %
statement 42 89 47.1
branch 0 26 0.0
condition 0 3 0.0
subroutine 14 16 87.5
pod 0 2 0.0
total 56 136 41.1


line stmt bran cond sub pod time code
1             package Beekeeper::Logger;
2              
3 1     1   954 use strict;
  1         3  
  1         30  
4 1     1   5 use warnings;
  1         2  
  1         47  
5              
6             our $VERSION = '0.08';
7              
8 1     1   5 use constant LOG_FATAL => 1;
  1         1  
  1         77  
9 1     1   7 use constant LOG_ALERT => 2;
  1         3  
  1         47  
10 1     1   6 use constant LOG_CRIT => 3;
  1         11  
  1         51  
11 1     1   6 use constant LOG_ERROR => 4;
  1         2  
  1         52  
12 1     1   6 use constant LOG_WARN => 5;
  1         2  
  1         43  
13 1     1   5 use constant LOG_NOTICE => 6;
  1         2  
  1         97  
14 1     1   7 use constant LOG_INFO => 7;
  1         2  
  1         55  
15 1     1   6 use constant LOG_DEBUG => 8;
  1         11  
  1         50  
16 1     1   5 use constant LOG_TRACE => 9;
  1         2  
  1         58  
17              
18 1     1   7 use JSON::XS;
  1         2  
  1         49  
19 1     1   5 use Exporter 'import';
  1         1  
  1         36  
20 1     1   6 use Time::HiRes;
  1         2  
  1         5  
21              
22             my $JSON;
23              
24             our @EXPORT_OK = qw(
25             LOG_FATAL
26             LOG_ALERT
27             LOG_CRIT
28             LOG_ERROR
29             LOG_WARN
30             LOG_NOTICE
31             LOG_INFO
32             LOG_DEBUG
33             LOG_TRACE
34             %LOG_LABEL
35             );
36              
37             our %EXPORT_TAGS = ('log_levels' => \@EXPORT_OK );
38              
39             our %LOG_LABEL = (
40             &LOG_FATAL => 'fatal',
41             &LOG_ALERT => 'alert',
42             &LOG_CRIT => 'critical',
43             &LOG_ERROR => 'error',
44             &LOG_WARN => 'warning',
45             &LOG_NOTICE => 'notice',
46             &LOG_INFO => 'info',
47             &LOG_DEBUG => 'debug',
48             &LOG_TRACE => 'trace',
49             );
50              
51              
52             sub new {
53 0     0 0   my $class = shift;
54              
55 0           my $self = {
56             worker_class => undef,
57             foreground => undef,
58             log_file => undef,
59             service => undef,
60             host => undef,
61             pool => undef,
62             _BUS => undef,
63             @_
64             };
65              
66 0 0         unless ($self->{service}) {
67             # Make an educated guess based on worker class
68 0           my $service = lc $self->{worker_class};
69 0           $service =~ s/::/-/g;
70 0           $service =~ s/-worker$//;
71              
72 0           $self->{service} = $service;
73             }
74              
75 0 0         unless ($self->{log_file}) {
76             # Use a single log file per service
77 0           my $dir = '/var/log';
78 0           my $user = getpwuid($>);
79 0           my $file = $self->{service} . '.log';
80 0           ($user) = ($user =~ m/(\w+)/); # untaint
81              
82 0 0         $self->{log_file} = (-d "$dir/$user") ? "$dir/$user/$file" : "$dir/$file";
83             }
84              
85 0 0         unless ($self->{foreground}) {
86              
87 0           my $log_file = $self->{log_file};
88              
89             # If running as root temporarily restore uid and gid to allow opening
90 0 0         local $> = $< if ($< == 0);
91 0 0         local $) = $( if ($( == 0);
92              
93 0 0         if (open(my $fh, '>>', $log_file)) {
94             # Send STDERR and STDOUT to log file
95 0 0         open(STDERR, '>&', $fh) or die "Can't redirect STDERR to $log_file: $!";
96 0 0         open(STDOUT, '>&', $fh) or die "Can't redirect STDOUT to $log_file: $!";
97             }
98             else {
99             # Probably no permissions to open the log file
100 0           warn "Can't open log file $log_file: $!";
101             }
102             }
103              
104 0           bless $self, $class;
105 0           return $self;
106             }
107              
108             sub log {
109 0     0 0   my ($self, $level, @msg) = @_;
110              
111 0 0         my $msg = join(' ', map { defined $_ ? "$_" : 'undef' } @msg );
  0            
112 0           chomp($msg);
113              
114 0           my $now = Time::HiRes::time;
115 0           my $ms = int(($now * 1000) % 1000);
116 0           my @t = reverse((localtime)[0..5]); $t[0] += 1900; $t[1]++;
  0            
  0            
117 0           my $tstamp = sprintf("%4d-%02d-%02d %02d:%02d:%02d.%03d", @t, $ms);
118              
119             ## 1. Log to local file
120              
121 0           print STDERR "[$tstamp][$$][$LOG_LABEL{$level}] $msg\n";
122              
123             ## 2. Log to topic
124              
125 0           my $bus = $self->{_BUS};
126 0 0 0       return unless $bus && $bus->{is_connected};
127              
128             # JSON-RPC notification
129              
130 0 0         $JSON = JSON::XS->new->utf8->allow_blessed->convert_blessed unless defined $JSON;
131              
132             my $json = $JSON->encode({
133             jsonrpc => '2.0',
134             method => $LOG_LABEL{$level},
135             params => {
136             level => $level,
137             service => $self->{service},
138             host => $self->{host},
139             pool => $self->{pool},
140 0           pid => $$,
141             message => $msg,
142             tstamp => $now,
143             }
144             });
145              
146 0           local $@;
147              
148 0           eval {
149              
150 0           my $service = $self->{service};
151 0           $service =~ tr|.|/|;
152              
153 0           $bus->publish(
154             topic => "log/$level/$service",
155             payload => \$json,
156             );
157             };
158              
159 0 0         if ($@) {
160 0           my $msg = $@; chomp($msg);
  0            
161 0           print STDERR "[$tstamp][$$][$LOG_LABEL{$level}] $msg\n";
162             }
163             }
164              
165             1;
166              
167             __END__