File Coverage

blib/lib/Log/Dispatch/Scribe.pm
Criterion Covered Total %
statement 89 92 96.7
branch 25 36 69.4
condition 17 33 51.5
subroutine 12 12 100.0
pod 2 2 100.0
total 145 175 82.8


line stmt bran cond sub pod time code
1             package Log::Dispatch::Scribe;
2              
3 1     1   3055 use strict;
  1         2  
  1         56  
4 1     1   5 use warnings;
  1         3  
  1         62  
5              
6             our $VERSION = '0.05';
7              
8 1     1   3838 use Log::Dispatch 2.00;
  1         53019  
  1         34  
9 1     1   12 use base qw(Log::Dispatch::Output);
  1         3  
  1         1060  
10              
11 1     1   1345 use Scribe::Thrift::scribe;
  1         1  
  1         36  
12 1     1   6 use Thrift::Socket;
  1         2  
  1         26  
13 1     1   7 use Thrift::FramedTransport;
  1         1  
  1         24  
14 1     1   6 use Thrift::BinaryProtocol;
  1         2  
  1         1618  
15              
16             sub new {
17 1     1 1 859 my($proto, %params) = @_;
18 1   33     11 my $self = bless {}, ref $proto || $proto;
19              
20 1         10 $self->_basic_init(%params);
21 1         173 $self->_init(%params);
22              
23 1         4 return $self;
24             }
25              
26             sub _init {
27 1     1   2 my $self = shift;
28 1         6 my %params = @_;
29              
30 1         2 $self->{retry_plan_a} = 'buffer';
31 1         2 $self->{retry_plan_b} = 'discard';
32 1         3 for my $plan (qw/a b/) {
33 2         3 my $retry_plan = "retry_plan_$plan";
34 2 50       9 $self->{$retry_plan} = $params{$retry_plan} if defined $params{$retry_plan};
35 2 50       14 die "retry_plan_$plan must be one of 'die', 'wait_forever', 'wait_count', 'discard', 'buffer'"
36             unless $self->{$retry_plan} =~ m/^(?:die|wait_forever|wait_count|discard|buffer)$/;
37             }
38 1   50     6 $self->{retry_delay} = $params{retry_delay} || 10;
39 1   50     3 $self->{retry_count} = $params{retry_count} || 100;
40 1   50     4 $self->{retry_buffer_size} = $params{retry_buffer_size} || 1000;
41 1         3 $self->{_retry_buffer} = [];
42 1   50     4 $self->{default_category} = $params{default_category} || 'none';
43 1         2 $self->{category} = $params{category};
44              
45 1         3 eval {
46 1   50     11 my $socket = Thrift::Socket->new($params{host} || 'localhost', $params{port} || 1463);
      50        
47 1         13 $self->{transport} = Thrift::FramedTransport->new($socket);
48 1         16 my $proto = Thrift::BinaryProtocol->new($self->{transport});
49            
50 1         13 $self->{client} = Scribe::Thrift::scribeClient->new($proto, $proto);
51             };
52 1 50       24 if ($@) {
53 0 0 0     0 if (ref($@) && $@->isa('Thrift::TException')) {
54 0         0 die $@->{message};
55             }
56             else {
57 0         0 die $@;
58             }
59             }
60              
61             }
62              
63             sub log_message {
64 10     10 1 8918 my $self = shift;
65 10         38 my %params = @_;
66              
67 10         14 my $append = 1;
68 10         14 my $looping = 1;
69 10         17 my $count = $self->{retry_count};
70 10         30 while ($looping) {
71 17         420 eval {
72 17 100       184 $self->{transport}->open() unless $self->{transport}->isOpen();
73 17   33     368 my $cat = $self->{category} || $params{category} || $params{log4p_category} || $self->{default_category};
74 9         59 push(@{$self->{_retry_buffer}},
  10         59  
75             Scribe::Thrift::LogEntry->new({ category => $cat, message => $params{message} }))
76 17 100 100     62 if $append && @{$self->{_retry_buffer}} <= $self->{retry_buffer_size};
77 17         267 my $result = $self->{client}->Log($self->{_retry_buffer});
78 4 50       64 die "TRY_LATER" if $result == Scribe::Thrift::ResultCode::TRY_LATER;
79              
80 4         23 $self->{_retry_buffer} = [];
81 4         21 $looping = 0;
82             };
83 17 100       452 if ($@) {
84 13         23 my $msg = $@;
85 13 50 33     115 if (ref($msg) && $msg->isa('Thrift::TException')) {
86 13         30 $msg = $msg->{message};
87             }
88 13         34 my $retry_plan = $self->{'retry_plan_a'};
89 13 100 100     21 if ( @{$self->{_retry_buffer}} > $self->{retry_buffer_size}
  13   66     219  
90             || ($retry_plan eq 'wait_count' && $count < 0) ) {
91 3         8 $retry_plan = $self->{'retry_plan_b'}
92             }
93 13 100       46 die $msg if $retry_plan eq 'die';
94              
95 11 100       54 if ($retry_plan eq 'wait_forever') {
    100          
    100          
    50          
96 3         7 $append = 0;
97 3         2999367 sleep($self->{retry_delay});
98             }
99             elsif ($retry_plan eq 'wait_count') {
100 4 50       12 die "Retry limit reached following failure: $msg" if $count < 0;
101 4         9 $append = 0;
102 4         4001549 sleep($self->{retry_delay});
103 4         75 $count--;
104             }
105             elsif ($retry_plan eq 'buffer') {
106 1 50       2 die "Full buffer following failure: $msg" if @{$self->{_retry_buffer}} > $self->{retry_buffer_size};
  1         4  
107 1         5 $looping = 0;
108             }
109             elsif ($retry_plan eq 'discard') {
110 3         4 pop(@{$self->{_retry_buffer}});
  3         141  
111 3         28 $looping = 0;
112             }
113             }
114             }
115             }
116              
117             sub DESTROY {
118 1     1   2535 my $self = shift;
119 1 50       14 $self->{transport}->close() if $self->{transport};
120             }
121              
122             1;
123              
124              
125             =head1 NAME
126              
127             Log::Dispatch::Scribe - Logging via Facebook's Scribe server software
128              
129             =head1 SYNOPSIS
130              
131             use Log::Dispatch::Scribe;
132              
133             my $log = Log::Dispatch::Scribe->new(
134             name => 'scribe',
135             min_level => 'info',
136             host => 'localhost',
137             port => 1463,
138             default_category => 'test',
139             retry_plan_a => 'buffer',
140             retry_plan_b => 'die',
141             );
142              
143             $log->log(level => 'emergency', message => 'something BAD happened');
144             $log->log(category => 'system', level => 'emergency', message => 'something BAD happened');
145              
146             # Or, via Log::Log4perl (using YAML style configuration in this example):
147              
148             log4perl.rootLogger: INFO, Scribe
149             log4perl.appender.Scribe: Log::Dispatch::Scribe
150             log4perl.appender.Scribe.host: localhost
151             log4perl.appender.Scribe.port: 1465
152             log4perl.appender.Scribe.category: system
153             log4perl.appender.Scribe.layout: Log::Log4perl::Layout::PatternLayout
154             log4perl.appender.Scribe.layout.ConversionPattern: "[%d] [%p] %m%n"
155              
156             use Log::Log4perl;
157             Log::Log4perl->init('log4perlconfig.yml'); # initialise using config file
158              
159             $log = Log::Log4perl->get_logger('example.usage');
160             $log->info("..."); # Log an info message via Log::Log4perl
161             $log->log($INFO, "..."); # alternative syntax
162              
163             =head1 DESCRIPTION
164              
165             This module provides a L style interface to Scribe, and
166             is also fully compatible with L.
167              
168             Scribe is a server for aggregating log data streamed in real time from
169             a large number of servers. It is designed to be scalable, extensible
170             without client-side modification, and robust to failure of the network
171             or any specific machine. Scribe was developed at Facebook and released
172             as open source.
173              
174             =head2 Installing Scribe and Thrift Perl Modules
175              
176             Scribe, and the related Thrift Perl modules, are available from the
177             respective source distributions (as of this writing, the modules are not
178             available on CPAN). When compiling Scribe, ensure that the namespace
179             is set to 'namespace perl Scribe.Thrift' in the scribe.thrift file.
180             Further information is available here:
181             L.
182              
183             =head2 Scribe Categories
184              
185             A Scribe category is an identifier that determines how Scribe handles
186             the message. Scribe configuration files define logging behaviour
187             per-category (or by category prefix, or by a default behaviour if no
188             matching category is found).
189              
190             L also uses logger 'categories' which can be used to
191             filter messages. Log4perl categories will typically be more
192             fine-grained that Scribe categories, but could also conceivably have a
193             1:1 mapping depending on system design.
194              
195             C has several ways of specifying categories
196             to handle these situations. 'category' and 'default_category' values
197             may be passed to the constructor, and 'category' and 'log4p_category'
198             values may be passed to the log_message method. These are handled as follows:
199              
200             =over 4
201              
202             =item * 'category' passed to the constructor overrides all other values and will always be used if defined. This essentially fixes the Scribe category for this logger instance.
203              
204             =item * 'category' passed to log_message() will be used otherwise, if defined.
205              
206             =item * 'log4p_category' passed to log_message() will be used otherwise, if defined. Log4perl sets this parameter from the logger category.
207              
208             =item * 'default_category' passed to the constructor is used where no other category parameters have been set. If no 'default_category' is given, it defaults to 'none'.
209              
210             =back
211              
212             =head2 Scribe Server and Error Handling
213              
214             A Scribe server is expected to be listening for log messages on a given host and port number.
215              
216             The standard behaviour of most Log::Dispatch::* loggers is to die on
217             error, such as when a file cannot be written. It is feasible that the
218             Scribe server might be restarted from time to time resulting in
219             temporary connection failures, and it would not be very satisfactory
220             if one's Perl application should die just because of a temporary
221             outage of the Scribe server. Log::Dispatch::Scribe offers several
222             options for retrying delivery of log messages.
223              
224             The retry behaviour is set through 'retry_plan_a' and 'retry_plan_b'
225             parameters. Plan A is tried first, and if that fails, then Plan B.
226             There is no Plan C. The 'retry_plan_*' parameters may have any of the
227             following values:
228              
229             =over 4
230              
231             =item * die
232              
233             Die immediately. Plan B becomes irrelevant if this is the setting for Plan A.
234              
235             =item * wait_forever
236              
237             The Perl application blocks, waiting forever in a loop to reconnect to
238             the Scribe server, retrying after a specified 'retry_delay'. Plan B
239             becomes irrelevant if this is the setting for Plan A.
240              
241             =item * wait_count
242              
243             The Perl application blocks, waiting for 'retry_delay' seconds, up to
244             'retry_count' times, then move on to the next plan if possible,
245             otherwise die. (Note that the count is not doubled if both Plan A and
246             B are 'wait_count').
247              
248             =item * discard
249              
250             Discard the current message and return immediately, allowing the Perl
251             application to continue.
252              
253             =item * buffer
254              
255             Buffer messages up to the given 'retry_buffer_size' (a count of number
256             of messages, not bytes), then move on to the next plan if the buffer
257             fills. This allows the Perl application to continue at least until
258             the buffer fills.
259              
260             =back
261              
262             The default settings are:
263              
264             retry_plan_a => 'buffer',
265             retry_buffer_size => 1000,
266             retry_plan_b => 'discard',
267              
268             in which case the first 1000 messages will be kept, then
269             subsequent messages discarded until the Scribe service recovers. The
270             first 1000 messages will then be flushed to Scribe as soon as it
271             recovers.
272              
273              
274             =head1 METHODS
275              
276             =over 4
277              
278             =item new
279              
280             $log = Log::Dispatch::Scribe->new(%params);
281              
282             This method takes a hash of parameters. The following options are valid:
283              
284             =over 4
285              
286             =item * name, min_level, max_level, callbacks
287              
288             Same as various Log::Dispatch::* classes.
289              
290             =item * host, port
291              
292             The host and port number of the Scribe server.
293              
294             =item * category, default_category
295              
296             See above under L.
297              
298             =item * retry_plan_a, retry_plan_b
299              
300             See above under L.
301              
302             =item * retry_buffer_size
303              
304             Maximum number of messages to hold in a memory buffer if Scribe
305             becomes unavailable and a retry plan is set to 'buffer'. See above
306             under L. Defaults to 1000.
307              
308             =item * retry_delay
309              
310             For the 'wait_forever' and 'wait_count' retry plans, the time interval
311             (in seconds) between attempts to reconnect. See above under L
312             Server and Error Handling>. Defaults to 10 seconds.
313              
314             =item * retry_count
315              
316             For the 'wait_count' retry plans, the number of times to retry before
317             giving up. See above under L. Defaults to 100.
318              
319             =back
320              
321             =item log
322              
323             $log->log( level => $level, message => $message, category => $category )
324              
325             As for L, but also supports passing in a 'category'
326             parameter to specify the Scribe category, and 'log4p_category'. See
327             above under L.
328              
329             =back
330              
331             =head1 SEE ALSO
332              
333             =over 4
334              
335             =item * L
336              
337             =item * L
338              
339             =item * L
340              
341             =item * L
342              
343             =item * L, L
344              
345             =back
346              
347             =head1 AUTHOR
348              
349             Jon Schutz, C<< >>, L
350              
351             =head1 BUGS
352              
353             Please report any bugs or feature requests to C, or through the web interface at L. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
354              
355              
356             =head1 SUPPORT
357              
358             You can find documentation for this module with the perldoc command.
359              
360             perldoc Log::Dispatch::Scribe
361              
362              
363             You can also look for information at:
364              
365             =over 4
366              
367             =item * RT: CPAN's request tracker
368              
369             L
370              
371             =item * AnnoCPAN: Annotated CPAN documentation
372              
373             L
374              
375             =item * CPAN Ratings
376              
377             L
378              
379             =item * Search CPAN
380              
381             L
382              
383             =back
384              
385              
386             =head1 COPYRIGHT & LICENSE
387              
388             Copyright 2009 Jon Schutz, all rights reserved.
389              
390             This program is free software; you can redistribute it and/or modify it
391             under the same terms as Perl itself.
392              
393              
394             =cut
395              
396             1; # End of Log::Dispatch::Scribe