File Coverage

blib/lib/Log/Radis.pm
Criterion Covered Total %
statement 48 52 92.3
branch 17 20 85.0
condition 4 6 66.6
subroutine 10 11 90.9
pod 2 2 100.0
total 81 91 89.0


line stmt bran cond sub pod time code
1 1     1   77279 use strictures 2;
  1         8  
  1         33  
2              
3             package Log::Radis;
4              
5             # ABSTRACT: Radis is a graylog logging radio through a redis database
6              
7 1     1   597 use Moo 2;
  1         6378  
  1         4  
8 1     1   1563 use Redis 1.980;
  1         32589  
  1         32  
9 1     1   5 use JSON 2.90 qw(encode_json);
  1         15  
  1         5  
10 1     1   92 use Time::HiRes 1.9726;
  1         13  
  1         6  
11 1     1   444 use Sys::Hostname ;
  1         721  
  1         46  
12 1     1   4 use Carp qw(croak carp);
  1         1  
  1         36  
13 1     1   3 use Scalar::Util qw(blessed);
  1         0  
  1         534  
14              
15             our $GELF_SPEC_VERSION = '1.1';
16             our $HOSTNAME = hostname();
17              
18             our $VERSION = '0.002'; # VERSION
19              
20              
21              
22             has server => (
23             is => 'ro',
24             default => 'localhost:6379',
25             );
26              
27              
28             has reconnect => (
29             is => 'ro',
30             default => 5,
31             );
32              
33              
34             has every => (
35             is => 'ro',
36             default => 1,
37             );
38              
39              
40             has queue => (
41             is => 'ro',
42             default => 'graylog-radis:queue',
43             );
44              
45              
46             has redis => (
47             is => 'lazy',
48             isa => sub {
49             (blessed($_[0]) and ($_[0]->isa('Redis') or $_[0]->can('lpush'))) or
50             croak "Must be an instance of Redis".
51             " or a blessed reference implementing the 'lpush' method";
52             },
53             builder => sub {
54 0     0   0 my $self = shift;
55 0         0 return Redis->new(
56             server => $self->server,
57             reconnect => $self->reconnect,
58             every => $self->every,
59             );
60             }
61             );
62              
63              
64             my %levels = (
65             fatal => 1,
66             emerg => 1,
67             emergency => 1,
68              
69             alert => 2,
70              
71             crit => 2,
72             critical => 3,
73              
74             error => 4,
75             err => 4,
76              
77             warn => 5,
78             warning => 5,
79              
80             note => 6,
81             notice => 6,
82              
83             info => 7,
84              
85             debug => 8,
86              
87             trace => 9,
88             core => 9,
89             );
90              
91              
92             sub log {
93 25     25 1 24122 my $self = shift;
94 25         40 my ($level, $message, %gelf) = @_;
95              
96 25 100       55 croak "log message without level" unless defined $level;
97 24 100       41 croak "log message without message" unless defined $message;
98              
99             # replace level with numeric code, if needed
100 23 50       72 $level = $levels{lc($level)} unless $level =~ m{^\d$};
101              
102             # addiotional field are only allowed with a prefixed underscore
103             # and strip off all unallowed chars
104             %gelf = map {
105             m{^_[\w\.\-]+$}i
106             ?
107             (
108             lc($_)
109             ,
110             $gelf{$_}
111             )
112             :
113             (
114             '_'.s{[^\w\.\-]+}{}gr
115             ,
116 5 50       27 $gelf{$_}
117             )
118 23         41 } grep { defined $gelf{$_} } keys %gelf;
  5         10  
119              
120             # graylog omit the id field automatically
121 23 50       34 if (exists $gelf{_id}) {
122 0         0 carp "log message with id is not allowed";
123 0         0 delete $gelf{_id};
124             }
125              
126             # preserve params, which are allowed by client
127             # including some mispelled ones
128 23 100       32 $gelf{host} = delete $gelf{_hostname} if defined $gelf{_hostname};
129 23 100       30 $gelf{host} = delete $gelf{_host} if defined $gelf{_host};
130 23 100       29 $gelf{timestamp} = delete $gelf{_time} if defined $gelf{_time};
131 23 100       29 $gelf{timestamp} = delete $gelf{_timestamp} if defined $gelf{_timestamp};
132              
133             # hostname defaults to system hostname...
134 23   66     60 $gelf{host} //= $HOSTNAME;
135              
136             # ...and timestamp with milliseconds by default
137 23   66     64 $gelf{timestamp} //= Time::HiRes::time();
138              
139             # graylog seems to have problems with float values in json
140             # so force string, which works fine
141 23         130 $gelf{timestamp} = ''.$gelf{timestamp};
142              
143 23         24 $gelf{short_message} = $message;
144 23         22 $gelf{version} = $GELF_SPEC_VERSION;
145 23         15 $gelf{level} = $level;
146              
147 23         38 $self->push(\%gelf);
148             }
149              
150              
151             sub push {
152 24     24 1 1785 my ($self, $gelf) = @_;
153 24 100       38 if (ref $gelf eq 'HASH') {
154 23         92 $gelf = encode_json($gelf);
155             }
156 24         417 $self->redis->lpush($self->queue, $gelf);
157             }
158              
159             1;
160              
161             __END__