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