File Coverage

blib/lib/Log/Log4perl/Appender/Fluent.pm
Criterion Covered Total %
statement 15 44 34.0
branch 0 18 0.0
condition 0 9 0.0
subroutine 5 7 71.4
pod 1 2 50.0
total 21 80 26.2


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             =head1 NAME
4              
5             Log::Log4perl::Appender::Fluent - log appender writing to Fluentd
6              
7             =head1 SYNOPSIS
8              
9             log4perl.category = INFO, Fluentd
10             # ...
11             log4perl.appender.Fluentd = Log::Log4perl::Appender::Fluent
12             log4perl.appender.Fluentd.host = fluentd.example.net
13             # this port is default for Fluentd
14             #log4perl.appender.Fluentd.port = 24224
15             log4perl.appender.Fluentd.hostname_field = source_host
16             log4perl.appender.Fluentd.tag_prefix = example
17             # these two options prevent the message from being stringified
18             log4perl.appender.Fluentd.layout = Log::Log4perl::Layout::NoopLayout
19             log4perl.appender.Fluentd.warp_message = 0
20              
21             =head1 DESCRIPTION
22              
23             Log::Log4perl::Appender::Fluent is a L appender plugin that
24             provides output to Fluentd daemon. The plugin supports sending simple string
25             messages, but it works way better when is provided with
26             L or L object, because the
27             structure of the message will be preserved.
28              
29             =cut
30              
31             package Log::Log4perl::Appender::Fluent;
32              
33 1     1   125856 use warnings;
  1         4  
  1         36  
34 1     1   7 use strict;
  1         2  
  1         36  
35              
36 1     1   5 use base qw{Log::Log4perl::Appender};
  1         6  
  1         152  
37 1     1   1117 use Fluent::Logger;
  1         104763  
  1         47  
38 1     1   23 use Sys::Hostname;
  1         2  
  1         496  
39              
40             #-----------------------------------------------------------------------------
41              
42             our $VERSION = '0.04';
43              
44             #-----------------------------------------------------------------------------
45              
46             =head1 USAGE
47              
48             Following options are available in L config:
49              
50             =cut
51              
52             #-----------------------------------------------------------------------------
53              
54             =over
55              
56             =item I (default: I)
57              
58             Path to UNIX socket, where Fluentd listens. If specified, communication with
59             Fluentd instance will go through this socket, otherwise TCP protocol will be
60             used.
61              
62             =item I, I (default: C, C<24224>)
63              
64             Fluentd instance's address. If neither host/port nor socket is specified,
65             due to default values, TCP communication will take place.
66              
67             =item I (default: C)
68              
69             Communication with Fluentd imposes using hashes as messages. This option
70             tells how should be named key if the message is not
71             a L/L object.
72              
73             =item I (default: I)
74              
75             Fluentd on its own doesn't provide the information where the record comes
76             from. Setting I will make this module to add (replace)
77             necessary field in messages.
78              
79             =item I, I (default: I, I)
80              
81             These options, similarly to I, specify where to put message's
82             category and level.
83              
84             =item I, I (default: I, I)
85              
86             If I is set, this will be the tag for messages. If I is set,
87             message will have the tag set to this prefix plus message's category. If
88             neither I nor I is set, message's tag is equal to category.
89              
90             I has the precedence from these two if both set.
91              
92             =back
93              
94             =cut
95              
96             sub new {
97 0     0 1   my ($class, %options) = @_;
98              
99 0   0       my $self = bless {
      0        
      0        
100             unix => $options{socket},
101             tcp => {
102             host => $options{host} || 'localhost',
103             port => $options{port} || 24224,
104             },
105             message_field => $options{message_field} || 'message',
106             hostname_field => $options{hostname_field},
107             tag_prefix => $options{tag_prefix},
108             tag => $options{tag},
109              
110             fluent => undef,
111             }, $class;
112              
113 0 0         if ($self->{unix}) {
114 0           $self->{fluent} = new Fluent::Logger(
115             socket => $self->{unix},
116             );
117             } else {
118 0           $self->{fluent} = new Fluent::Logger(
119             host => $self->{tcp}{host},
120             port => $self->{tcp}{port},
121             );
122             }
123              
124 0           return $self;
125             }
126              
127             sub log {
128 0     0 0   my ($self, %params) = @_;
129              
130 0           my $msg = $params{message};
131 0           my $category = $params{log4p_category};
132 0           my $level = $params{log4p_level};
133              
134             # possibly strip one array level
135 0 0 0       $msg = $msg->[0] if ref $msg eq 'ARRAY' && @$msg == 1;
136              
137             # repack message
138 0 0         if (eval { $msg->isa('Log::Message::JSON') }) {
  0 0          
139             # strip Log::Message::JSON blessing
140             # NOTE: the resulting hash(ref) should be tied to Tie::IxHash, but there's
141             # a bug in Data::MessagePack 0.38 (XS version)
142 0           $msg = { %$msg };
143 0           } elsif (eval { $msg->DOES("Log::Message::Structured") }) {
144             # Log::Message::Structured support
145             # such a message:
146             # * is a Moose object
147             # * has Log::Message::Structured role
148             # * has method as_hash()
149 0           $msg = $msg->as_hash;
150             } else {
151 0           $msg = { $self->{message_field} => $msg };
152             }
153              
154             # add (replace?) fields: hostname, category (facility), level (importance)
155 0 0         if ($self->{hostname_field}) {
156 0           $msg->{ $self->{hostname_field} } = hostname();
157             }
158 0 0         if ($self->{category_field}) {
159 0           $msg->{ $self->{category_field} } = $category;
160             }
161 0 0         if ($self->{level_field}) {
162 0           $msg->{ $self->{level_field} } = $level;
163             }
164              
165 0           my $tag;
166 0 0         if ($self->{tag}) {
    0          
167 0           $tag = $self->{tag};
168             } elsif ($self->{tag_prefix}) {
169 0           $tag = "$self->{tag_prefix}.$category";
170             } else {
171 0           $tag = $category;
172             }
173              
174             # TODO: what if error? there was carp() somewhere
175 0           $self->{fluent}->post($tag, $msg);
176             }
177              
178             #-----------------------------------------------------------------------------
179              
180             =head1 NOTES
181              
182             If the destination host is unavailable, this module may print error messages
183             using C.
184              
185             =head1 AUTHOR
186              
187             Stanislaw Klekot, C<< >>
188              
189             =head1 LICENSE AND COPYRIGHT
190              
191             Copyright 2012 Stanislaw Klekot.
192              
193             This program is free software; you can redistribute it and/or modify it
194             under the terms of either: the GNU General Public License as published
195             by the Free Software Foundation; or the Artistic License.
196              
197             See http://dev.perl.org/licenses/ for more information.
198              
199             =head1 SEE ALSO
200              
201             http://fluentd.org/, L, L,
202             L.
203              
204             =cut
205              
206             #-----------------------------------------------------------------------------
207             1;
208             # vim:ft=perl