File Coverage

blib/lib/Tie/Syslog.pm
Criterion Covered Total %
statement 95 112 84.8
branch 27 52 51.9
condition 1 3 33.3
subroutine 18 22 81.8
pod 1 1 100.0
total 142 190 74.7


line stmt bran cond sub pod time code
1             package Tie::Syslog;
2              
3             $Tie::Syslog::VERSION = '2.04.03';
4              
5 3     3   66619 use 5.006;
  3         12  
  3         107  
6 3     3   15 use strict;
  3         7  
  3         94  
7 3     3   14 use warnings;
  3         14  
  3         85  
8 3     3   16 use Carp qw/carp croak confess/;
  3         5  
  3         235  
9 3     3   3716 use Sys::Syslog qw/:standard :macros/;
  3         72880  
  3         1073  
10              
11             # ------------------------------------------------------------------------------
12             # Define all default handle-tying subs, so that they can be autoloaded if
13             # necessary.
14 3         18 use subs qw(
15             TIEHANDLE
16             WRITE
17             PRINT
18             PRINTF
19             READ
20             READLINE
21             GETC
22             CLOSE
23             OPEN
24             BINMODE
25             EOF
26             TELL
27             SEEK
28             UNTIE
29             FILENO
30 3     3   2906 );
  3         66  
31              
32             # --- 'Public' Globals - DEFAULTS ----------------------------------------------
33             $Tie::Syslog::ident = (split '/', $0)[-1];
34             $Tie::Syslog::logopt = 'pid,ndelay';
35              
36             # --- 'Private' Globals --------------------------------------------------------
37             my @mandatory_opts = ('facility', 'priority');
38             # Since calling openlog() twice for the same facility makes it croak, we will
39             # keep a list of already-open-facility-connections. This will also be useful to
40             # know if we must call closelog().
41             my %open_connections;
42              
43              
44             # ------------------------------------------------------------------------------
45             # 'Private' functions
46             # ------------------------------------------------------------------------------
47              
48             ########################
49             # sub _parse_config(@) #
50             ########################
51             # This sub is responsible of setting up the configuration for the subsequent
52             # openlog() and syslog() calls. It returns a reference to a hash that contains
53             # configuration parameters for our Tie::Syslog object.
54             sub _parse_config(@) {
55              
56 4     4   6 my $params;
57              
58 4 100       12 if (ref($_[0]) eq 'HASH') {
59             # New-style configuration
60             # Copy values so we don't risk changing an existing reference.
61             # NOTE: this configuration has no defaults defined. 'priority' and
62             # 'facility' must be explicitly set.
63 2         8 $params = {
64 2         2 %{ shift() },
65             };
66             } else {
67             # Old-style configuration, parameters are:
68             # 'facility.loglevel', 'identity', 'logopt', 'setlogsock options'
69             # Old-style config provided local0.error as default, in case nothing
70             # else was specified. We keep this defaults only for old-style config.
71 2 100       11 my ($facility, $priority) =
72             @_ ? split '\.', shift : ('LOG_LOCAL0', 'LOG_ERR');
73 2 50       8 $Tie::Syslog::ident = shift if @_;
74 2 50       8 $Tie::Syslog::logopt = shift if @_;
75             # There can still be one option: socket type for setlogsock. Since we
76             # do not call setlogsock (according to Sys::Syslog rules), we
77             # may (safely?) ignore this option.
78 2         10 $params = {
79             facility => $facility,
80             priority => $priority,
81             };
82             }
83              
84             # Normalize names
85 4         13 for ('facility', 'priority') {
86 8 100       24 next unless $params->{ $_ };
87 6         16 $params->{ $_ } = uc( $params->{ $_ } );
88 6         17 $params->{ $_ } =~ s/EMERGENCY/EMERG/;
89 6         12 $params->{ $_ } =~ s/ERROR/ERR/;
90 6         27 $params->{ $_ } =~ s/CRITICAL/CRIT/;
91 6 100       31 $params->{ $_ } = 'LOG_' . $params->{ $_ }
92             unless $params->{ $_ } =~ /^LOG_/;
93             }
94              
95 4         11 return $params;
96             }
97              
98             # ------------------------------------------------------------------------------
99             # Accessors/mutators
100             # ------------------------------------------------------------------------------
101              
102             # Because writing $self->facility() is better than writing $self->{'facility'}
103             for my $opt (@mandatory_opts) {
104 3     3   1673 no strict 'refs';
  3         27  
  3         3119  
105             *$opt = sub {
106 27     27   38 my $self = shift;
107 27 50       57 $self->{$opt} = shift if @_;
108 27         112 return $self->{$opt};
109             };
110             }
111              
112             # ------------------------------------------------------------------------------
113             # Handle tying methods - see 'perldoc perltie' and 'perldoc Tie::Handle'
114             # ------------------------------------------------------------------------------
115              
116             # This is the method called by the 'tie' function.
117             # It returns a hash reference blessed to Tie::Syslog package.
118             sub TIEHANDLE {
119 4     4   1403 my $self;
120              
121             # Set log mask as permissive as possible - masking will be done at syslog
122             # level
123 4         7 eval {
124 4         57 setlogmask(
125             LOG_MASK(LOG_EMERG)|
126             LOG_MASK(LOG_ALERT)|
127             LOG_MASK(LOG_CRIT)|
128             LOG_MASK(LOG_ERR)|
129             LOG_MASK(LOG_WARNING)|
130             LOG_MASK(LOG_NOTICE)|
131             LOG_MASK(LOG_INFO)|
132             LOG_MASK(LOG_DEBUG)
133             );
134             };
135 4 50       31 carp "Call to setlogmask() failed: $@"
136             if $@;
137              
138             # See if we were called as an instance method, or as a class method.
139             # In the first case, we provide a copy-constructor that takes the invocant
140             # as a prototype and uses the same configuration.
141 4 50       16 if (my $pkg = ref($_[0])) {
142             # Use a copy-constructor, providing support for
143             # single-parameter-override via the @_
144 0         0 my $prototype = shift;
145 0         0 my $other_parameters = _parse_config @_;
146 0         0 $self = bless {
147             %$prototype,
148             %$other_parameters,
149             }, $pkg;
150             } else {
151             # Called as a class method
152 4         8 $pkg = shift;
153            
154 4         14 my $parameters = _parse_config @_;
155              
156 4         11 $self = bless $parameters, $pkg;
157             }
158              
159             # Check for all mandatory values
160 4         12 for (@mandatory_opts) {
161 7 100       224 croak "You must provide value for '$_' option"
162             unless $self->{$_};
163             }
164              
165             # Now openlog() if needed, by calling our own open()
166 3         11 $self->OPEN();
167              
168             # Finally return
169 3         26 return $self;
170             }
171              
172             sub OPEN {
173             # Ignore any parameter passed, since we just call openlog() with parameters
174             # got from initialization
175 5     5   946 my $self = shift;
176 5         35 my $f = $self->facility;
177 5         9 eval {
178 5 50       21 openlog($Tie::Syslog::ident, $Tie::Syslog::logopt, $self->facility)
179             unless $open_connections{ $f };
180             };
181 5 50       890 croak "openlog() failed with errors: $@"
182             if $@;
183 5         12 $open_connections{ $f } = 1;
184 5         22 return $self->{'is_open'} = 1;
185             }
186              
187             # Usually, we should have just one connection to syslog. It may happen, though,
188             # that multiple connections have been established, if multiple facilities have
189             # been used (but please NOTE that this is AGAINST Sys::Syslog rules).
190             # In the latter case, closelog() will just close the last connection, which may
191             # be completely unrelated to the handle we're closing here. In case of multiple
192             # connections, just skip closelog().
193             sub CLOSE {
194 9     9   963 my $self = shift;
195 9 100       27 return 1 unless $self->{'is_open'};
196 5         11 $self->{'is_open'} = 0;
197 5 50       19 unless (scalar(keys(%open_connections)) > 1) {
198 5         9 eval {
199 5         19 closelog();
200             };
201 5 50       124 croak "Call to closelog() failed with errors: $@"
202             if $@;
203 5         14 delete $open_connections{ $self->facility };
204             }
205 5         18 return 1;
206             }
207              
208             sub PRINT {
209 5     5   2176 my $self = shift;
210 5 50       18 carp "Cannot PRINT to a closed filehandle!" unless $self->{'is_open'};
211 5         10 eval { syslog $self->facility."|".$self->priority, "@_" };
  5         14  
212 5 50       1158 croak "PRINT failed with errors: $@"
213             if $@;
214             }
215              
216             sub PRINTF {
217 1     1   13 my $self = shift;
218 1 50       4 carp "Cannot PRINTF to a closed filehandle!" unless $self->{'is_open'};
219 1         4 my $format = shift;
220 1         3 eval { syslog $self->facility."|".$self->priority, $format, @_ };
  1         3  
221 1 50       138 croak "PRINTF failed with errors: $@"
222             if $@;
223             }
224              
225             # Provide a fallback method for write
226             sub WRITE {
227 1     1   12 my $self = shift;
228 1         2 my $string = shift;
229 1   33     3 my $length = shift || length $string;
230 1         3 my $offset = shift; # Ignored
231              
232 1         3 $self->PRINT(substr($string, 0, $length));
233              
234 1         9 return $length;
235             }
236              
237             # This peeks a little into Sys:Syslog internals, so it might break sooner or
238             # later. Expect this to happen.
239             # fileno() of socket if available
240             # -1 if we have an open handle
241             # undef if we're not connected
242             # When Sys::Syslog uses 'native' connection, *Sys::Syslog::SYSLOG is not
243             # defined, and $Sys::Syslog::connected is a lexical, so it's not accessible
244             # by us. In other words, we have to try and guess.
245             sub FILENO {
246 0     0   0 my $self = shift;
247 0         0 my $fd = fileno(*Sys::Syslog::SYSLOG);
248 0 0       0 return defined($fd) ? $fd
    0          
249             : $self->{'is_open'} ? -1 : undef;
250             }
251              
252             sub DESTROY {
253 7     7   42 my $self = shift;
254 7 50       20 return 1 unless $self;
255 7         18 $self->CLOSE();
256 7         42 undef $self;
257             }
258              
259             sub UNTIE {
260 3     3   1385 my $self = shift;
261 3 50       12 return 1 unless $self;
262 3         11 $self->DESTROY;
263             }
264              
265              
266             # ------------------------------------------------------------------------------
267             # Provide a graceful fallback for not(-yet?)-implemented methods
268             # ------------------------------------------------------------------------------
269             sub AUTOLOAD {
270 0     0     my $self = shift;
271 0           my $name = (split '::', our $AUTOLOAD)[-1];
272 0 0         return if $name eq 'DESTROY';
273              
274 0           my $err = "$name operation not (yet?) supported";
275              
276             # See if errors are fatals
277 0 0         my $errors_are_fatal = ref($self) ? $self->{'errors_are_fatal'} : 1;
278 0 0         confess $err if $errors_are_fatal;
279              
280             # Install a handler for this operation if errors are nonfatal
281             {
282 3     3   21 no strict 'refs';
  3         11  
  3         283  
  0            
283             *$name = sub {
284 0     0     print "$name operation not (yet?) supported";
285             }
286 0           }
287              
288 0           $self->$name;
289             }
290              
291              
292             # ------------------------------------------------------------------------------
293             # Compatibility with Tie::Syslog v1.x
294             # ------------------------------------------------------------------------------
295             # die() and warn() print to STDERR
296             sub ExtendedSTDERR {
297 0     0 1   return 1;
298             }
299              
300             'End of Tie::Syslog'
301             __END__