File Coverage

blib/lib/Log/Dispatch/Gelf.pm
Criterion Covered Total %
statement 66 66 100.0
branch 10 12 83.3
condition 13 18 72.2
subroutine 16 16 100.0
pod 0 2 0.0
total 105 114 92.1


line stmt bran cond sub pod time code
1             package Log::Dispatch::Gelf;
2 3     3   2768 use 5.010;
  3         17  
3 3     3   18 use strict;
  3         6  
  3         75  
4 3     3   17 use warnings;
  3         17  
  3         157  
5              
6             our $VERSION = '1.0.0';
7              
8 3     3   18 use base qw(Log::Dispatch::Output);
  3         5  
  3         3012  
9 3     3   20550 use Params::Validate qw(validate SCALAR HASHREF CODEREF);
  3         6  
  3         168  
10              
11 3     3   1037616 use Sys::Hostname;
  3         1026087  
  3         238  
12 3     3   27 use JSON;
  3         9  
  3         29  
13 3     3   3827 use Time::HiRes qw(time);
  3         5399  
  3         18  
14              
15             sub new {
16 7     7 0 8508 my $proto = shift;
17 7   33     41 my $class = ref $proto || $proto;
18              
19 7         16 my $self = bless {}, $class;
20              
21 7         34 $self->_basic_init(@_);
22 7         642 $self->_init(@_);
23              
24 2         11 return $self;
25             }
26              
27             sub _init {
28 7     7   11 my $self = shift;
29              
30 7         21 Params::Validate::validation_options(allow_extra => 1);
31             my %p = validate(
32             @_,
33             {
34             send_sub => { type => CODEREF, optional => 1 },
35             additional_fields => { type => HASHREF, optional => 1 },
36             host => { type => SCALAR, optional => 1 },
37             socket => {
38             type => HASHREF,
39             optional => 1,
40             callbacks => {
41             protocol_is_tcp_or_udp_or_default => sub {
42 4     4   8 my ($socket) = @_;
43              
44 4   100     18 $socket->{protocol} //= 'udp';
45 4 100       40 die 'socket protocol must be tcp or udp' unless $socket->{protocol} =~ /^tcp|udp$/;
46             },
47             host_must_be_set => sub {
48 4     4   6 my ($socket) = @_;
49              
50 4 100 100     61 die 'socket host must be set' unless exists $socket->{host} && length $socket->{host} > 0;
51             },
52             port_must_be_number_or_default => sub {
53 5     5   10 my ($socket) = @_;
54              
55 5   100     16 $socket->{port} //= 12201;
56 5 100       64 die 'socket port must be integer' unless $socket->{port} =~ /^\d+$/;
57             }
58             }
59             }
60             }
61 7         382 );
62              
63 3 100 66     55 if (!defined $p{socket} && !defined $p{send_sub}) {
64 1         31 die 'Must be set socket or send_sub';
65             }
66              
67 2   33     12 $self->{host} = $p{host} // hostname();
68 2   100     40 $self->{additional_fields} = $p{additional_fields} // {};
69 2         6 $self->{send_sub} = $p{send_sub};
70 2         4 $self->{gelf_version} = '1.1';
71              
72 2 100       8 if ($p{socket}) {
73 1         3 my $socket = $self->_create_socket($p{socket});
74              
75             $self->{send_sub} = sub {
76 1     1   87 my ($msg) = @_;
77              
78 1         4 $socket->send($msg);
79 1         774 };
80             }
81              
82 2         3 my $i = 0;
83 2         25 $self->{number_of_loglevel}{$_} = $i++ for qw(emergency alert critical error warning notice info debug);
84              
85 2         7 return;
86             }
87              
88             sub _create_socket {
89 1     1   2 my ($self, $socket_opts) = @_;
90              
91 1         6 require IO::Socket::INET;
92             return IO::Socket::INET->new(
93             PeerAddr => $socket_opts->{host},
94             PeerPort => $socket_opts->{port},
95             Proto => $socket_opts->{protocol},
96 1 0       5 ) or die "Cannot create socket: $!";
97             }
98              
99             sub log_message {
100 2     2 0 354 my ($self, %p) = @_;
101 2         29 (my $short_message = $p{message}) =~ s/\n.*//s;
102              
103 2         4 my %additional_fields;
104 2         4 while (my ($key, $value) = each %{ $self->{additional_fields} }) {
  3         16  
105 1         4 $additional_fields{"_$key"} = $value;
106             }
107              
108             my $log_unit = {
109             version => $self->{gelf_version},
110             host => $self->{host},
111             short_message => $short_message,
112             timestamp => time(),
113             level => $self->{number_of_loglevel}{ $p{level} },
114             full_message => $p{message},
115 2         43 %additional_fields,
116             };
117              
118 2         12 $self->{send_sub}->(to_json($log_unit, { canonical => 1 }) . "\n");
119              
120 2         109 return;
121             }
122              
123             1;
124             __END__