File Coverage

blib/lib/Log/Syslog/DangaSocket.pm
Criterion Covered Total %
statement 18 52 34.6
branch 0 10 0.0
condition 0 3 0.0
subroutine 6 12 50.0
pod 1 4 25.0
total 25 81 30.8


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Log::Syslog::DangaSocket - Danga::Socket wrapper around a syslog sending socket
4             (TCP, UDP, or UNIX).
5              
6             =head1 SYNOPSIS
7              
8             my $logger = Log::Syslog::DangaSocket->new(
9             $proto, # 'udp', 'tcp', or 'unix'
10             $dest_host, # destination hostname or filename
11             $dest_port, # destination port (ignored for unix socket)
12             $sender_host, # sender hostname (informational only)
13             $sender_name, # sender application name (informational only)
14             $facility, # syslog facility number
15             $severity, # syslog severity number
16             $reconnect # whether to reconnect on error
17             );
18              
19             Danga::Socket->AddTimer(5, sub { $logger->send("5 seconds elapsed") });
20              
21             Danga::Socket->EventLoop;
22              
23             =head1 DESCRIPTION
24              
25             This module constructs and asynchronously sends syslog packets to a syslogd
26             listening on a TCP or UDP port, or a UNIX socket. Calls to
27             C<$logger-Esend()> are guaranteed to never block; though naturally, this
28             only works in the context of a running Danga::Socket event loop.
29              
30             UDP support is present primarily for completeness; an implementation like
31             L will provide non-blocking behavior with less overhead.
32             Only in the unlikely case of the local socket buffer being full will this
33             module benefit you by buffering the failed write and retrying it when possible,
34             instead of silently dropping the message. But you should really be using TCP
35             or a domain socket if you care about reliability.
36              
37             Trailing newlines are added automatically to log messages.
38              
39             =head2 ERROR HANDLING
40              
41             If a fatal occur occurs during sending (e.g. the connection is remotely closed
42             or reset), Log::Syslog::DangaSocket will attempt to automatically reconnect if
43             $reconnect is true. Any pending writes from the closed connection will be
44             retried in the new one.
45              
46             =head1 SEE ALSO
47              
48             L
49              
50             L
51              
52             L
53              
54             =head1 AUTHOR
55              
56             Adam Thomason, Eathomason@sixapart.comE
57              
58             =head1 COPYRIGHT AND LICENSE
59              
60             Copyright (C) 2009 by Six Apart, Ecpan@sixapart.comE
61              
62             This library is free software; you can redistribute it and/or modify
63             it under the same terms as Perl itself, either Perl version 5.8.6 or,
64             at your option, any later version of Perl 5 you may have available.
65              
66             =cut
67              
68             package Log::Syslog::DangaSocket;
69              
70 2     2   104272 use strict;
  2         5  
  2         102  
71 2     2   11 use warnings;
  2         5  
  2         127  
72              
73             our $VERSION = '1.06';
74              
75             our $CONNECT_TIMEOUT = 1;
76              
77 2     2   1397 use Log::Syslog::DangaSocket::Socket;
  2         8  
  2         70  
78 2     2   16 use POSIX 'strftime';
  2         3  
  2         21  
79              
80 2     2   163 use base 'fields';
  2         4  
  2         182  
81              
82             use fields (
83             # ->new params
84 2         9 'send_host', # where log message originated
85             'name', # application-defined logger name
86             'facility', # syslog facility constant
87             'severity', # syslog severity constant
88             'reconnect', # whether to attempt reconnect on error
89              
90             # state vars
91             'sock', # Log::Syslog::DangaSocket::Socket object
92             'last_time', # last epoch time when a prefix was generated
93             'prefix', # stringified time changes only once per second, so cache it and rest of prefix
94 2     2   10 );
  2         2  
95              
96             sub new {
97 0     0 1   my $ref = shift;
98 0   0       my $class = ref $ref || $ref;
99              
100 0           my $proto = shift;
101 0           my $host = shift;
102 0           my $port = shift;
103              
104 0           my Log::Syslog::DangaSocket $self = fields::new($class);
105              
106 0           ( $self->{send_host},
107             $self->{name},
108             $self->{facility},
109             $self->{severity},
110             $self->{reconnect} ) = @_;
111              
112 0           my $connecter;
113             $connecter = sub {
114 0     0     my $unsent = shift;
115 0 0         $self->{sock} = Log::Syslog::DangaSocket::Socket->new(
116             $proto, $host, $port, $connecter, $unsent,
117             ($self->{reconnect} ? $connecter : ()),
118             );
119 0           };
120 0           $connecter->();
121              
122 0           for (qw/ send_host name facility severity /) {
123 0 0         die "missing parameter $_" unless $self->{$_};
124             }
125              
126 0           $self->_update_prefix(time);
127              
128 0           return $self;
129             }
130              
131             sub facility {
132 0     0 0   my $self = shift;
133 0 0         if (@_) {
134 0           $self->{facility} = shift;
135 0           $self->_update_prefix(time);
136             }
137 0           return $self->{facility};
138             }
139              
140             sub severity {
141 0     0 0   my $self = shift;
142 0 0         if (@_) {
143 0           $self->{severity} = shift;
144 0           $self->_update_prefix(time);
145             }
146 0           return $self->{severity};
147             }
148              
149             sub _update_prefix {
150 0     0     my Log::Syslog::DangaSocket $self = shift;
151              
152             # based on http://www.faqs.org/rfcs/rfc3164.html
153 0           my $time_str = strftime('%b %d %H:%M:%S', localtime($self->{last_time} = shift));
154              
155 0           my $priority = ($self->{facility} << 3) | $self->{severity}; # RFC3164/4.1.1 PRI Part
156              
157 0           $self->{prefix} = "<$priority>$time_str $self->{send_host} $self->{name}\[$$]: ";
158             }
159              
160             sub send {
161 0     0 0   my Log::Syslog::DangaSocket $self = shift;
162              
163             # update the log-line prefix only if the time has changed
164 0           my $time = time;
165 0 0         $self->_update_prefix($time) if $time != $self->{last_time};
166              
167 0           $self->{sock}->write_buffered(\join '', $self->{prefix}, $_[0], "\n");
168             }
169              
170             1;