1######################################################################
2# Synchronized.pm -- 2003, 2007 Mike Schilli <m@perlmeister.com>
3######################################################################
4# Special appender employing a locking strategy to synchronize
5# access.
6######################################################################
7
8###########################################
9package Log::Log4perl::Appender::Synchronized;
10###########################################
11
12use strict;
13use warnings;
14use Log::Log4perl::Util::Semaphore;
15
16our @ISA = qw(Log::Log4perl::Appender);
17
18our $CVSVERSION   = '$Revision: 1.12 $';
19our ($VERSION)    = ($CVSVERSION =~ /(\d+\.\d+)/);
20
21###########################################
22sub new {
23###########################################
24    my($class, %options) = @_;
25
26    my $self = {
27        appender=> undef,
28        key     => '_l4p',
29        level   => 0,
30        %options,
31    };
32
33    my @values = ();
34    for my $param (qw(uid gid mode destroy key)) {
35        push @values, $param, $self->{$param} if defined $self->{$param};
36    }
37
38    $self->{sem} = Log::Log4perl::Util::Semaphore->new(
39        @values
40    );
41
42        # Pass back the appender to be synchronized as a dependency
43        # to the configuration file parser
44    push @{$options{l4p_depends_on}}, $self->{appender};
45
46        # Run our post_init method in the configurator after
47        # all appenders have been defined to make sure the
48        # appender we're synchronizing really exists
49    push @{$options{l4p_post_config_subs}}, sub { $self->post_init() };
50
51    bless $self, $class;
52}
53
54###########################################
55sub log {
56###########################################
57    my($self, %params) = @_;
58
59    $self->{sem}->semlock();
60
61    # Relay that to the SUPER class which needs to render the
62    # message according to the appender's layout, first.
63    $Log::Log4perl::caller_depth +=2;
64    $self->{app}->SUPER::log(\%params,
65                             $params{log4p_category},
66                             $params{log4p_level});
67    $Log::Log4perl::caller_depth -=2;
68
69    $self->{sem}->semunlock();
70}
71
72###########################################
73sub post_init {
74###########################################
75    my($self) = @_;
76
77    if(! exists $self->{appender}) {
78       die "No appender defined for " . __PACKAGE__;
79    }
80
81    my $appenders = Log::Log4perl->appenders();
82    my $appender = Log::Log4perl->appenders()->{$self->{appender}};
83
84    if(! defined $appender) {
85       die "Appender $self->{appender} not defined (yet) when " .
86           __PACKAGE__ . " needed it";
87    }
88
89    $self->{app} = $appender;
90}
91
921;
93
94__END__
95
96=head1 NAME
97
98    Log::Log4perl::Appender::Synchronized - Synchronizing other appenders
99
100=head1 SYNOPSIS
101
102    use Log::Log4perl qw(:easy);
103
104    my $conf = qq(
105    log4perl.category                   = WARN, Syncer
106
107        # File appender (unsynchronized)
108    log4perl.appender.Logfile           = Log::Log4perl::Appender::File
109    log4perl.appender.Logfile.autoflush = 1
110    log4perl.appender.Logfile.filename  = test.log
111    log4perl.appender.Logfile.mode      = truncate
112    log4perl.appender.Logfile.layout    = SimpleLayout
113
114        # Synchronizing appender, using the file appender above
115    log4perl.appender.Syncer            = Log::Log4perl::Appender::Synchronized
116    log4perl.appender.Syncer.appender   = Logfile
117);
118
119    Log::Log4perl->init(\$conf);
120    WARN("This message is guaranteed to be complete.");
121
122=head1 DESCRIPTION
123
124If multiple processes are using the same C<Log::Log4perl> appender
125without synchronization, overwrites might happen. A typical scenario
126for this would be a process spawning children, each of which inherits
127the parent's Log::Log4perl configuration.
128
129In most cases, you won't need an external synchronisation tool like
130Log::Log4perl::Appender::Synchronized at all. Log4perl's file appender,
131Log::Log4perl::Appender::File, for example, provides the C<syswrite>
132mechanism for making sure that even long log lines won't interleave.
133Short log lines won't interleave anyway, because the operating system
134makes sure the line gets written before a task switch occurs.
135
136In cases where you need additional synchronization, however, you can use
137C<Log::Log4perl::Appender::Synchronized> as a gateway between your
138loggers and your appenders. An appender itself,
139C<Log::Log4perl::Appender::Synchronized> just takes two additional
140arguments:
141
142=over 4
143
144=item C<appender>
145
146Specifies the name of the appender it synchronizes access to. The
147appender specified must be defined somewhere in the configuration file,
148not necessarily before the definition of
149C<Log::Log4perl::Appender::Synchronized>.
150
151=item C<key>
152
153This optional argument specifies the key for the semaphore that
154C<Log::Log4perl::Appender::Synchronized> uses internally to ensure
155atomic operations. It defaults to C<_l4p>. If you define more than
156one C<Log::Log4perl::Appender::Synchronized> appender, it is
157important to specify different keys for them, as otherwise every
158new C<Log::Log4perl::Appender::Synchronized> appender will nuke
159previously defined semaphores. The maximum key length is four
160characters, longer keys will be truncated to 4 characters --
161C<mylongkey1> and C<mylongkey2> are interpreted to be the same:
162C<mylo> (thanks to David Viner E<lt>dviner@yahoo-inc.comE<gt> for
163pointing this out).
164
165=back
166
167C<Log::Log4perl::Appender::Synchronized> uses Log::Log4perl::Util::Semaphore
168internally to perform locking with semaphores provided by the
169operating system used.
170
171=head2 Performance tips
172
173The C<Log::Log4perl::Appender::Synchronized> serializes access to a
174protected resource globally, slowing down actions otherwise performed in
175parallel.
176
177Unless specified otherwise, all instances of
178C<Log::Log4perl::Appender::Synchronized> objects in the system will
179use the same global IPC key C<_l4p>.
180
181To control access to different appender instances, it often makes sense
182to define different keys for different synchronizing appenders. In this
183way, Log::Log4perl serializes access to each appender instance separately:
184
185    log4perl.category                   = WARN, Syncer1, Syncer2
186
187        # File appender 1 (unsynchronized)
188    log4perl.appender.Logfile1           = Log::Log4perl::Appender::File
189    log4perl.appender.Logfile1.filename  = test1.log
190    log4perl.appender.Logfile1.layout    = SimpleLayout
191
192        # File appender 2 (unsynchronized)
193    log4perl.appender.Logfile2           = Log::Log4perl::Appender::File
194    log4perl.appender.Logfile2.filename  = test2.log
195    log4perl.appender.Logfile2.layout    = SimpleLayout
196
197        # Synchronizing appender, using the file appender above
198    log4perl.appender.Syncer1            = Log::Log4perl::Appender::Synchronized
199    log4perl.appender.Syncer1.appender   = Logfile1
200    log4perl.appender.Syncer1.key        = l4p1
201
202        # Synchronizing appender, using the file appender above
203    log4perl.appender.Syncer2            = Log::Log4perl::Appender::Synchronized
204    log4perl.appender.Syncer2.appender   = Logfile2
205    log4perl.appender.Syncer2.key        = l4p2
206
207Without the C<.key = l4p1> and C<.key = l4p2> lines, both Synchronized
208appenders would be using the default C<_l4p> key, causing unnecessary
209serialization of output written to different files.
210
211=head2 Advanced configuration
212
213To configure the underlying Log::Log4perl::Util::Semaphore module in
214a different way than with the default settings provided by
215Log::Log4perl::Appender::Synchronized, use the following parameters:
216
217    log4perl.appender.Syncer1.destroy  = 1
218    log4perl.appender.Syncer1.mode     = sub { 0775 }
219    log4perl.appender.Syncer1.uid      = hugo
220    log4perl.appender.Syncer1.gid      = 100
221
222Valid options are
223C<destroy> (Remove the semaphore on exit),
224C<mode> (permissions on the semaphore),
225C<uid> (uid or user name the semaphore is owned by),
226and
227C<gid> (group id the semaphore is owned by),
228
229Note that C<mode> is usually given in octal and therefore needs to be
230specified as a perl sub {}, unless you want to calculate what 0755 means
231in decimal.
232
233Changing ownership or group settings for a semaphore will obviously only
234work if the current user ID owns the semaphore already or if the current
235user is C<root>. The C<destroy> option causes the current process to
236destroy the semaphore on exit. Spawned children of the process won't
237inherit this behavior.
238
239=head2 Semaphore user and group IDs with mod_perl
240
241Setting user and group IDs is especially important when the Synchronized
242appender is used with mod_perl. If Log4perl gets initialized by a startup
243handler, which runs as root, and not as the user who will later use
244the semaphore, the settings for uid, gid, and mode can help establish
245matching semaphore ownership and access rights.
246
247=head1 DEVELOPMENT NOTES
248
249C<Log::Log4perl::Appender::Synchronized> is a I<composite> appender.
250Unlike other appenders, it doesn't log any messages, it just
251passes them on to its attached sub-appender.
252For this reason, it doesn't need a layout (contrary to regular appenders).
253If it defines none, messages are passed on unaltered.
254
255Custom filters are also applied to the composite appender only.
256They are I<not> applied to the sub-appender. Same applies to appender
257thresholds. This behaviour might change in the future.
258
259=head1 LICENSE
260
261Copyright 2002-2012 by Mike Schilli E<lt>m@perlmeister.comE<gt>
262and Kevin Goess E<lt>cpan@goess.orgE<gt>.
263
264This library is free software; you can redistribute it and/or modify
265it under the same terms as Perl itself.
266
267=head1 AUTHOR
268
269Please contribute patches to the project on Github:
270
271    http://github.com/mschilli/log4perl
272
273Send bug reports or requests for enhancements to the authors via our
274
275MAILING LIST (questions, bug reports, suggestions/patches):
276log4perl-devel@lists.sourceforge.net
277
278Authors (please contact them via the list above, not directly):
279Mike Schilli <m@perlmeister.com>,
280Kevin Goess <cpan@goess.org>
281
282Contributors (in alphabetical order):
283Ateeq Altaf, Cory Bennett, Jens Berthold, Jeremy Bopp, Hutton
284Davidson, Chris R. Donnelly, Matisse Enzer, Hugh Esco, Anthony
285Foiani, James FitzGibbon, Carl Franks, Dennis Gregorovic, Andy
286Grundman, Paul Harrington, David Hull, Robert Jacobson, Jason Kohles,
287Jeff Macdonald, Markus Peter, Brett Rann, Peter Rabbitson, Erik
288Selberg, Aaron Straup Cope, Lars Thegler, David Viner, Mac Yang.
289
290