File Coverage

blib/lib/Log/Log4perl/Layout/GELF.pm
Criterion Covered Total %
statement 44 44 100.0
branch 7 8 87.5
condition 3 6 50.0
subroutine 11 11 100.0
pod 2 2 100.0
total 67 71 94.3


line stmt bran cond sub pod time code
1             ##################################################
2             package Log::Log4perl::Layout::GELF;
3             ##################################################
4              
5 3     3   102924 use 5.006;
  3         12  
  3         135  
6 3     3   557 use strict;
  3         6  
  3         149  
7 3     3   18 use warnings;
  3         11  
  3         94  
8              
9 3     3   5583 use JSON::XS;
  3         46705  
  3         502  
10 3     3   7635 use IO::Compress::Gzip qw( gzip $GzipError );
  3         269965  
  3         509  
11 3     3   5955 use Log::Log4perl;
  3         219799  
  3         22  
12              
13 3     3   161 use base qw(Log::Log4perl::Layout::PatternLayout);
  3         4  
  3         1681  
14              
15             # We need to define our own cspecs
16             $Log::Log4perl::ALLOW_CODE_IN_CONFIG_FILE = 1;
17              
18             =head1 NAME
19              
20             Log::Log4perl::Layout::GELF - Log4perl for graylog2
21              
22             =head1 VERSION
23              
24             Version 0.03
25              
26             =cut
27              
28             our $VERSION = '0.03';
29              
30             =head1 SYNOPSIS
31              
32             Log4perl implementation of GELF. When used with
33             Log::Log4perl::Appender::Socket you can log directly
34             to a graylog2 server.
35              
36             =cut
37              
38             =head1 What is graylog?
39              
40             Graylog is log management server that can be used to run analytics,
41             alerting, monitoring and perform powerful searches over your whole
42             log base. Need to debug a failing request? Just run a quick filter
43             search to find it and see what errors it produced. Want to see all
44             messages a certain API consumer is consuming in real time? Create
45             streams for every consumer and have them always only one click away.
46              
47             =cut
48              
49             =head1 Configuration Sample
50              
51             Code snippet. Replace the ip with your graylog server.
52              
53             use Log::Log4perl
54             my $logger_conf = {
55             'log4perl.logger.graylog' => "DEBUG, SERVER",
56             'log4perl.appender.SERVER' => "Log::Log4perl::Appender::Socket",
57             'log4perl.appender.SERVER.PeerAddr' => '10.211.1.94',
58             'log4perl.appender.SERVER.PeerPort' => "12201",
59             'log4perl.appender.SERVER.Proto' => "udp",
60             'log4perl.appender.SERVER.layout' => "GELF"
61             };
62             Log::Log4perl->init( $logger_conf );
63             my $LOGGER = Log::Log4perl->get_logger('graylog');
64             $LOGGER->debug("Debug log");
65             ...
66             =cut
67              
68             =head1 SUBROUTINES/METHODS
69              
70             =head2 new
71            
72             Can take most of options that Log::Log4perl::Layout::PatternLayout can.
73            
74             Additional Options:
75             PlainText - outputs plaintext and not gzipped files.
76            
77             =cut
78             sub new {
79 5     5 1 66656 my $class = shift;
80 5   33     98 $class = ref ($class) || $class;
81            
82 5 100       32 my $options = ref $_[0] eq "HASH" ? shift : {};
83            
84             # Creating object to make changes easier
85 5         70 my $gelf_format = {
86             "version" => "1.0",
87             "host" => "%H",
88             "short_message" => "%m{chomp}",
89             "timestamp" => "%Z", # custom cspec
90             "level"=> "%Y", # custom cspec
91             "facility"=> "%M",
92             "file"=> "%F",
93             "line"=> "%L",
94             "_pid" => "%P",
95             };
96             # make a JSON string
97 5         110 my $conversion_pattern = encode_json($gelf_format);
98            
99 5         25 $options->{ConversionPattern} = { value => $conversion_pattern } ;
100            
101             # Since we are building on top of PatternLayout, we can define our own
102             # own patterns using a "cspec".
103 10     10   589 $options->{cspec} = {
104             'Z' => { value => sub {return time } },
105 5         57 'Y' => { value => \&_level_converter } ,
106             };
107            
108 5         60 my $self = $class->SUPER::new($options);
109            
110             # to help with debugging. you can skip the bzipping.
111 5         3381 $self->{PlainText} = 0;
112 5 100       30 if(defined $options->{PlainText}->{value} ){
113 1         3 $self->{PlainText} = $options->{PlainText}->{value};
114             }
115 5         54 return $self;
116             }
117              
118              
119             # Maps over the syslog levels from Log4perl levels.
120              
121             # Syslog Levels for Reference
122             # 0 Emergency: system is unusable
123             # 1 Alert: action must be taken immediately
124             # 2 Critical: critical conditions
125             # 3 Error: error conditions
126             # 4 Warning: warning conditions
127             # 5 Notice: normal but significant condition
128             # 6 Informational: informational messages
129             # 7 Debug: debug-level messages
130             sub _level_converter {
131 10     10   855 my ($layout, $message, $category, $priority, $caller_level) = @_;
132             # TODO Replace with a case statement
133 10         65 my $levels = {
134             "DEBUG" => 7,
135             "INFO" => 6,
136             "NOTICE"=> 5,
137             "WARN" => 4,
138             "ERROR" => 3,
139             "FATAL" => 2
140             };
141 10         49 return $levels->{$priority};
142             }
143              
144             =head2 render
145            
146             Wraps the Log::Log4perl::Layout::PatternLayout return value so we can
147             gzip the JSON string.
148            
149             =cut
150              
151             sub render {
152 10     10 1 7744 my($self, $message, $category, $priority, $caller_level) = @_;
153 10         64 my $encoded_message = $self->SUPER::render($message, $category, $priority, $caller_level);
154            
155             # makes debugging easier
156 10 100 66     860 if( defined $self->{PlainText} && $self->{PlainText} ){
157 5         19 return $encoded_message;
158             }
159            
160             # Graylog2 servers require gzipped messesages.
161 5         8 my $gzipped_message;
162 5 50       28 gzip \$encoded_message => \$gzipped_message or die "gzip failed: $GzipError\n";
163 5         14305 return $gzipped_message;
164             }
165             1;