← Index
NYTProf Performance Profile   « line view »
For ./view
  Run on Fri Jul 31 18:42:36 2015
Reported on Fri Jul 31 18:48:15 2015

Filename/var/www/foswikidev/core/lib/Foswiki/Logger/PlainFile.pm
StatementsExecuted 110 statements in 2.24ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
1111.20ms1.38msFoswiki::Logger::PlainFile::::BEGIN@36 Foswiki::Logger::PlainFile::BEGIN@36
111371µs448µsFoswiki::Logger::PlainFile::::BEGIN@38 Foswiki::Logger::PlainFile::BEGIN@38
111282µs362µsFoswiki::Logger::PlainFile::::BEGIN@37 Foswiki::Logger::PlainFile::BEGIN@37
111118µs496µsFoswiki::Logger::PlainFile::::log Foswiki::Logger::PlainFile::log
11136µs133µsFoswiki::Logger::PlainFile::::_rotate Foswiki::Logger::PlainFile::_rotate
22131µs31µsFoswiki::Logger::PlainFile::::_time2month Foswiki::Logger::PlainFile::_time2month
11118µs46µsFoswiki::Logger::PlainFile::EventIterator::::BEGIN@5Foswiki::Logger::PlainFile::EventIterator::BEGIN@5
11114µs14µsFoswiki::Logger::PlainFile::::_lock Foswiki::Logger::PlainFile::_lock
11113µs26µsFoswiki::Logger::PlainFile::EventIterator::::BEGIN@3Foswiki::Logger::PlainFile::EventIterator::BEGIN@3
11112µs26µsFoswiki::Logger::PlainFile::::_getLogsForLevel Foswiki::Logger::PlainFile::_getLogsForLevel
11111µs11µsFoswiki::Logger::PlainFile::::BEGIN@39 Foswiki::Logger::PlainFile::BEGIN@39
11111µs16µsFoswiki::Logger::PlainFile::::BEGIN@62 Foswiki::Logger::PlainFile::BEGIN@62
11110µs119µsFoswiki::Logger::PlainFile::EventIterator::::BEGIN@7Foswiki::Logger::PlainFile::EventIterator::BEGIN@7
11110µs14µsFoswiki::Logger::PlainFile::EventIterator::::BEGIN@4Foswiki::Logger::PlainFile::EventIterator::BEGIN@4
1119µs33µsFoswiki::Logger::PlainFile::::BEGIN@33 Foswiki::Logger::PlainFile::BEGIN@33
1119µs38µsFoswiki::Logger::PlainFile::::BEGIN@64 Foswiki::Logger::PlainFile::BEGIN@64
1119µs16µsFoswiki::Logger::PlainFile::::BEGIN@32 Foswiki::Logger::PlainFile::BEGIN@32
1119µs21µsFoswiki::Logger::PlainFile::::BEGIN@31 Foswiki::Logger::PlainFile::BEGIN@31
1119µs9µsFoswiki::Logger::PlainFile::::new Foswiki::Logger::PlainFile::new
1118µs91µsFoswiki::Logger::PlainFile::::BEGIN@40 Foswiki::Logger::PlainFile::BEGIN@40
1115µs5µsFoswiki::Logger::PlainFile::::BEGIN@35 Foswiki::Logger::PlainFile::BEGIN@35
1115µs5µsFoswiki::Logger::PlainFile::EventIterator::::BEGIN@9Foswiki::Logger::PlainFile::EventIterator::BEGIN@9
1114µs4µsFoswiki::Logger::PlainFile::::_time Foswiki::Logger::PlainFile::_time
1113µs3µsFoswiki::Logger::PlainFile::::_stat Foswiki::Logger::PlainFile::_stat
0000s0sFoswiki::Logger::PlainFile::EventIterator::::DESTROYFoswiki::Logger::PlainFile::EventIterator::DESTROY
0000s0sFoswiki::Logger::PlainFile::::eachEventSince Foswiki::Logger::PlainFile::eachEventSince
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# See bottom of file for license and copyright information
2package Foswiki::Logger::PlainFile::EventIterator;
3230µs239µs
# spent 26µs (13+13) within Foswiki::Logger::PlainFile::EventIterator::BEGIN@3 which was called: # once (13µs+13µs) by Foswiki::logger at line 3
use strict;
# spent 26µs making 1 call to Foswiki::Logger::PlainFile::EventIterator::BEGIN@3 # spent 13µs making 1 call to strict::import
4224µs218µs
# spent 14µs (10+4) within Foswiki::Logger::PlainFile::EventIterator::BEGIN@4 which was called: # once (10µs+4µs) by Foswiki::logger at line 4
use warnings;
# spent 14µs making 1 call to Foswiki::Logger::PlainFile::EventIterator::BEGIN@4 # spent 4µs making 1 call to warnings::import
5230µs274µs
# spent 46µs (18+28) within Foswiki::Logger::PlainFile::EventIterator::BEGIN@5 which was called: # once (18µs+28µs) by Foswiki::logger at line 5
use Assert;
# spent 46µs making 1 call to Foswiki::Logger::PlainFile::EventIterator::BEGIN@5 # spent 28µs making 1 call to Exporter::import
6
7254µs2227µs
# spent 119µs (10+108) within Foswiki::Logger::PlainFile::EventIterator::BEGIN@7 which was called: # once (10µs+108µs) by Foswiki::logger at line 7
use Fcntl qw(:flock);
# spent 119µs making 1 call to Foswiki::Logger::PlainFile::EventIterator::BEGIN@7 # spent 108µs making 1 call to Exporter::import
8
9
# spent 5µs within Foswiki::Logger::PlainFile::EventIterator::BEGIN@9 which was called: # once (5µs+0s) by Foswiki::logger at line 14
BEGIN {
1014µs if ( $Foswiki::cfg{UseLocale} ) {
11 require locale;
12 import locale();
13 }
14188µs15µs}
15
16# Internal class for Logfile iterators.
17# So we don't break encapsulation of file handles. Open / Close in same file.
1819µsour @ISA = qw/Foswiki::Iterator::EventIterator/;
19
20# # Object destruction
21# # Release locks and file
22sub DESTROY {
23 my $this = shift;
24 flock( $this->{handle}, LOCK_UN )
25 if ( defined $this->{logLocked} );
26 close( delete $this->{handle} ) if ( defined $this->{handle} );
27}
28
29package Foswiki::Logger::PlainFile;
30
31226µs234µs
# spent 21µs (9+12) within Foswiki::Logger::PlainFile::BEGIN@31 which was called: # once (9µs+12µs) by Foswiki::logger at line 31
use strict;
# spent 21µs making 1 call to Foswiki::Logger::PlainFile::BEGIN@31 # spent 12µs making 1 call to strict::import
32223µs222µs
# spent 16µs (9+7) within Foswiki::Logger::PlainFile::BEGIN@32 which was called: # once (9µs+7µs) by Foswiki::logger at line 32
use warnings;
# spent 16µs making 1 call to Foswiki::Logger::PlainFile::BEGIN@32 # spent 7µs making 1 call to warnings::import
33227µs256µs
# spent 33µs (9+23) within Foswiki::Logger::PlainFile::BEGIN@33 which was called: # once (9µs+23µs) by Foswiki::logger at line 33
use Assert;
# spent 33µs making 1 call to Foswiki::Logger::PlainFile::BEGIN@33 # spent 24µs making 1 call to Exporter::import
34
35221µs15µs
# spent 5µs within Foswiki::Logger::PlainFile::BEGIN@35 which was called: # once (5µs+0s) by Foswiki::logger at line 35
use Foswiki::Logger ();
# spent 5µs making 1 call to Foswiki::Logger::PlainFile::BEGIN@35
36291µs11.38ms
# spent 1.38ms (1.20+178µs) within Foswiki::Logger::PlainFile::BEGIN@36 which was called: # once (1.20ms+178µs) by Foswiki::logger at line 36
use Foswiki::Iterator::EventIterator ();
# spent 1.38ms making 1 call to Foswiki::Logger::PlainFile::BEGIN@36
37283µs1362µs
# spent 362µs (282+80) within Foswiki::Logger::PlainFile::BEGIN@37 which was called: # once (282µs+80µs) by Foswiki::logger at line 37
use Foswiki::Iterator::AggregateEventIterator ();
# spent 362µs making 1 call to Foswiki::Logger::PlainFile::BEGIN@37
38287µs1448µs
# spent 448µs (371+77) within Foswiki::Logger::PlainFile::BEGIN@38 which was called: # once (371µs+77µs) by Foswiki::logger at line 38
use Foswiki::Iterator::MergeEventIterator ();
# spent 448µs making 1 call to Foswiki::Logger::PlainFile::BEGIN@38
39230µs111µs
# spent 11µs within Foswiki::Logger::PlainFile::BEGIN@39 which was called: # once (11µs+0s) by Foswiki::logger at line 39
use Foswiki::Configure::Load;
# spent 11µs making 1 call to Foswiki::Logger::PlainFile::BEGIN@39
40254µs2174µs
# spent 91µs (8+83) within Foswiki::Logger::PlainFile::BEGIN@40 which was called: # once (8µs+83µs) by Foswiki::logger at line 40
use Fcntl qw(:flock);
# spent 91µs making 1 call to Foswiki::Logger::PlainFile::BEGIN@40 # spent 83µs making 1 call to Exporter::import
4115µsour @ISA = ('Foswiki::Logger');
42
43=begin TML
44
45---+ package Foswiki::Logger::PlainFile
46
47Plain file implementation of the Foswiki Logger interface. Mostly
48compatible with TWiki (and Foswiki 1.0.0) log files, except that dates
49are recorded using ISO format, and include the time, and it dies when
50a log can't be written (rather than printing a warning).
51
52This logger implementation maps groups of levels to a single logfile, viz.
53 * =debug= messages are output to $Foswiki::cfg{Log}{Dir}/debug.log
54 * =info= messages are output to $Foswiki::cfg{Log}{Dir}/events.log
55 * =warning=, =error=, =critical=, =alert=, =emergency= messages are
56 output to $Foswiki::cfg{Log}{Dir}/error.log.
57 * =error=, =critical=, =alert=, and =emergency= messages are also
58 written to standard error (the webserver log file, usually)
59
60=cut
61
62228µs220µs
# spent 16µs (11+5) within Foswiki::Logger::PlainFile::BEGIN@62 which was called: # once (11µs+5µs) by Foswiki::logger at line 62
use Foswiki::Time qw(-nofoswiki);
# spent 16µs making 1 call to Foswiki::Logger::PlainFile::BEGIN@62 # spent 5µs making 1 call to Foswiki::Time::import
63
6421.29ms268µs
# spent 38µs (9+29) within Foswiki::Logger::PlainFile::BEGIN@64 which was called: # once (9µs+29µs) by Foswiki::logger at line 64
use constant TRACE => 0;
# spent 38µs making 1 call to Foswiki::Logger::PlainFile::BEGIN@64 # spent 29µs making 1 call to constant::import
65
66# Map from a log level to the root of a log file name
6715µsour %LEVEL2LOG = (
68 debug => 'debug',
69 info => 'events',
70 notice => 'configure',
71 warning => 'error',
72 error => 'error',
73 critical => 'error',
74 alert => 'error',
75 emergency => 'error'
76);
77
7812µsour %nextCheckDue = (
79 configure => 0,
80 debug => 0,
81 events => 0,
82 error => 0,
83);
84
85# Symbols used so we can override during unit testing
861200nsour $dontRotate = 0;
8716µs
# spent 4µs within Foswiki::Logger::PlainFile::_time which was called: # once (4µs+0s) by Foswiki::Logger::PlainFile::log at line 117
sub _time { time() }
8816µs
# spent 3µs within Foswiki::Logger::PlainFile::_stat which was called: # once (3µs+0s) by Foswiki::Logger::PlainFile::_rotate at line 319
sub _stat { stat(@_); }
89
90
# spent 9µs within Foswiki::Logger::PlainFile::new which was called: # once (9µs+0s) by Foswiki::logger at line 2385 of /var/www/foswikidev/core/lib/Foswiki.pm
sub new {
911600ns my $class = shift;
92112µs return bless( { acceptsHash => 1 }, $class );
93}
94
95=begin TML
96
97---++ ObjectMethod log($level, @fields)
98
99See Foswiki::Logger for the interface.
100
101=cut
102
103
# spent 496µs (118+378) within Foswiki::Logger::PlainFile::log which was called: # once (118µs+378µs) by Foswiki::UI::View::view at line 256 of /var/www/foswikidev/core/lib/Foswiki/UI/View.pm
sub log {
1041300ns my $this = shift;
1051100ns my $level;
1061200ns my @fields;
107
108 # Native interface: Convert the hash back to list format
1091900ns if ( ref( $_[0] ) eq 'HASH' ) {
11017µs186µs ( $level, @fields ) = Foswiki::Logger::getOldCall(@_);
# spent 86µs making 1 call to Foswiki::Logger::getOldCall
1111400ns return unless defined $level;
112 }
113 else {
114 ( $level, @fields ) = @_;
115 }
116
11711µs14µs my $now = _time();
# spent 4µs making 1 call to Foswiki::Logger::PlainFile::_time
11812µs126µs my @logs = _getLogsForLevel( [$level] );
# spent 26µs making 1 call to Foswiki::Logger::PlainFile::_getLogsForLevel
1191500ns my $log = shift @logs;
12012µs1133µs _rotate( $LEVEL2LOG{$level}, $log, $now );
# spent 133µs making 1 call to Foswiki::Logger::PlainFile::_rotate
12113µs185µs my $time = Foswiki::Time::formatTime( $now, 'iso', 'servertime' );
# spent 85µs making 1 call to Foswiki::Time::formatTime
122
123 # Unfortunate compatibility requirement; need the level, but the old
124 # logfile format doesn't allow us to add fields. Since we are changing
125 # the date format anyway, the least pain is to concatenate the level
126 # to the date; Foswiki::Time::ParseTime can handle it, and it looks
127 # OK too.
12812µs unshift( @fields, "$time $level" );
12961µs my $message =
13076µs '| ' . join( ' | ', map { s/\|/&vbar;/g; $_ } @fields ) . ' |';
131
1321100ns my $file;
1331300ns my $mode = '>>';
134
135118µs if ( open( $file, $mode, $log ) ) {
136113µs326µs binmode $file, ":encoding(utf-8)";
# spent 17µs making 1 call to Encode::find_encoding # spent 9µs making 1 call to Encode::Encoding::renew # spent 700ns making 1 call to Encode::Encoding::needs_lines
13711µs114µs _lock($file);
# spent 14µs making 1 call to Foswiki::Logger::PlainFile::_lock
13814µs print $file "$message\n";
139137µs13µs close($file);
# spent 3µs making 1 call to Encode::utf8::encode_xs
140 }
141 elsif ( $Foswiki::cfg{isVALID} ) {
142
143 # Only whine if there is a known good configuration (in which case
144 # $Foswiki::cfg{Log}{Dir} will be set sensibly)
145 if ( !-w $log ) {
146 die
147"ERROR: Could not open logfile $log for write. Your admin should 'configure' now and fix the errors!\n";
148 }
149
150 # die to force the admin to get permissions correct
151 die 'ERROR: Could not write ' . $message . ' to ' . "$log: $!\n";
152 }
15318µs if ( $level =~ m/^(error|critical|alert|emergency)$/ ) {
154 print STDERR "$message\n";
155 }
156}
157
158
# spent 14µs within Foswiki::Logger::PlainFile::_lock which was called: # once (14µs+0s) by Foswiki::Logger::PlainFile::log at line 137
sub _lock { # borrowed from Log::Dispatch::FileRotate, Thanks!
1591400ns my $fh = shift;
160210µs eval { flock( $fh, LOCK_EX ) }; # Ignore lock errors, not all platforms support flock
161 # Make sure we are at the EOF
16212µs seek( $fh, 0, 2 );
16314µs return 1;
164}
165
166=begin TML
167
168---++ ObjectMethod eachEventSince($time, \@levels, $version) -> $iterator
169 * =$time= - a time in the past
170 * =\@levels= - log levels to return events for. Individual level or array reference.
171 * =$version= - Version 1 of API returns a hash instead of an array.
172
173See Foswiki::Logger for the interface.
174
175=cut
176
177sub eachEventSince {
178 my ( $this, $time, $level, $version ) = @_;
179
180 $level = ref $level ? $level : [$level];
181
182 my @log4level = _getLogsForLevel($level);
183
184 # Find the year-month for the current time
185 my $now = _time();
186 my $nowLogYear = Foswiki::Time::formatTime( $now, '$year', 'servertime' );
187 my $nowLogMonth = Foswiki::Time::formatTime( $now, '$mo', 'servertime' );
188
189 # Find the year-month for the first time in the range
190 my $logYear = Foswiki::Time::formatTime( $time, '$year', 'servertime' );
191 my $logMonth = Foswiki::Time::formatTime( $time, '$mo', 'servertime' );
192
193 print STDERR "Scanning $logYear:$logMonth thru $nowLogYear:$nowLogMonth\n"
194 if (TRACE);
195
196 # Convert the requested level into a regular expression for the scan
197 my $reqLevel = join( '|', @$level );
198 $reqLevel = "(?:$reqLevel)";
199
200 my @mergeIterators;
201
202 foreach my $log (@log4level) {
203
204 # Get the names of all the logfiles in the time range
205 my @logs;
206 while ( !( $logMonth == $nowLogMonth && $logYear == $nowLogYear ) ) {
207 my $logfile = $log;
208 my $logTime = $logYear . sprintf( "%02d", $logMonth );
209
210 $logfile =~ s/\.log$/.$logTime/g;
211 push( @logs, $logfile );
212 $logMonth++;
213 if ( $logMonth == 13 ) {
214 $logMonth = 1;
215 $logYear++;
216 }
217 }
218
219 # Finally the current log
220 push( @logs, $log );
221
222 my @iterators;
223 foreach my $logfile (@logs) {
224 next unless -r $logfile;
225
226 my $fh;
227 if ( open( $fh, '<:encoding(utf-8)', $logfile ) ) {
228 my $logIt =
229 new Foswiki::Logger::PlainFile::EventIterator( $fh, $time,
230 $reqLevel, $version, $logfile );
231 $logIt->{logLocked} =
232 eval { flock( $fh, LOCK_SH ) }; # No error in case on non-flockable FS; eval in case flock not supported.
233 # print STDERR " pushed iterator for $reqLevel \n";
234 push( @iterators, $logIt );
235 }
236 else {
237
238 # Would be nice to report this, but it's chicken and egg and
239 # besides, empty logfiles can happen.
240 print STDERR "Failed to open $logfile: $!" if (TRACE);
241 }
242 }
243
244 push @mergeIterators,
245 new Foswiki::Iterator::AggregateEventIterator( \@iterators );
246 }
247
248 if (TRACE) {
249 require Data::Dumper;
250 print STDERR "Merge built for \@mergeIterators "
251 . Data::Dumper::Dumper( \@mergeIterators );
252 }
253
254 return new Foswiki::Iterator::MergeEventIterator( \@mergeIterators );
255}
256
257# Get the name of the log for a given reporting level
258
# spent 26µs (12+14) within Foswiki::Logger::PlainFile::_getLogsForLevel which was called: # once (12µs+14µs) by Foswiki::Logger::PlainFile::log at line 118
sub _getLogsForLevel {
2591300ns my $level = shift;
2601200ns my %logs;
261
2621700ns foreach my $lvl (@$level) {
263 ASSERT( defined $LEVEL2LOG{$lvl} ) if DEBUG;
26412µs my $log = $Foswiki::cfg{Log}{Dir} . '/' . $LEVEL2LOG{$lvl} . '.log';
265
266 # SMELL: Expand should not be needed, except if bin/configure tries
267 # to log to locations relative to $Foswiki::cfg{WorkingDir}, DataDir, etc.
268 # Windows seemed to be the most difficult to fix - this was the only thing
269 # that I could find that worked all the time.
27012µs114µs Foswiki::Configure::Load::expandValue($log);
# spent 14µs making 1 call to Foswiki::Configure::Load::expandValue
27112µs $logs{$log} = 1;
272 }
273
27415µs return ( keys %logs );
275}
276
277
# spent 31µs within Foswiki::Logger::PlainFile::_time2month which was called 2 times, avg 16µs/call: # once (21µs+0s) by Foswiki::Logger::PlainFile::_rotate at line 297 # once (10µs+0s) by Foswiki::Logger::PlainFile::_rotate at line 320
sub _time2month {
2782500ns my $time = shift;
2792300ns return undef unless ( defined $time );
280223µs my @t = localtime($time);
28121µs $t[5] += 1900;
282212µs return sprintf( '%0.4d%0.2d', $t[5], $t[4] + 1 );
283}
284
285# See if the log needs to be rotated. If the log was last modified
286# last month, we need to rotate it.
287
# spent 133µs (36+97) within Foswiki::Logger::PlainFile::_rotate which was called: # once (36µs+97µs) by Foswiki::Logger::PlainFile::log at line 120
sub _rotate {
2881700ns my ( $level, $log, $now ) = @_;
289
2901100ns return if $dontRotate;
2911100ns return unless $level;
292
293 # Don't bother checking if we have checked in this process already
2941400ns return if ( $now < $nextCheckDue{$level} );
295
296 # Work out the current month
29711µs121µs my $curMonth = _time2month($now);
# spent 21µs making 1 call to Foswiki::Logger::PlainFile::_time2month
298 print STDERR "Current MONTH = $curMonth\n" if (TRACE);
299
300 # After this check, don't check again for a month.
30113µs $curMonth =~ m/(\d{4})(\d{2})/;
30213µs my ( $y, $m ) = ( $1, $2 + 1 );
3031700ns if ( $m > 12 ) {
304 $m = '01';
305 $y++;
306 }
307 else {
30811µs $m = sprintf( '%0.2d', $m );
309 }
31013µs163µs $nextCheckDue{$level} = Foswiki::Time::parseTime("$y-$m-01");
# spent 63µs making 1 call to Foswiki::Time::parseTime
311 print STDERR "Next log check due $nextCheckDue{$level} for $level\n"
312 if (TRACE);
313
314 # If there's no existing log, there's nothing to rotate
31515µs return unless -e $log;
316
317 # Check when the log was last modified. If it was in the previous
318 # month, if may need to be rotated.
31913µs13µs my @stat = _stat($log);
# spent 3µs making 1 call to Foswiki::Logger::PlainFile::_stat
32012µs110µs my $modMonth = _time2month( $stat[9] );
# spent 10µs making 1 call to Foswiki::Logger::PlainFile::_time2month
321 print STDERR "compare $modMonth, $curMonth\n" if (TRACE);
32214µs return if ( $modMonth == $curMonth );
323
324 my $lockfile;
325 unless ( open( $lockfile, '>', $log . 'LOCK' ) ) {
326 print STDERR "ERROR: PlainFile Logger could not open $log.LOCK: $! \n";
327 return;
328 }
329 flock( $lockfile, LOCK_EX );
330
331 my $newname = $log;
332 $newname =~ s/log$/$modMonth/;
333 print STDERR "Renaming from $log to $newname \n" if (TRACE);
334
335 unless ( -e $newname ) {
336 open( my $lf, '>>', $log );
337 _lock($lf);
338 rename $log, $newname;
339 close($lf);
340 unlink $log . 'LOCK';
341 }
342 else {
343 print STDERR "ROTATE SKIPPED - prior log ($log) exists\n";
344 unlink $log . 'LOCK';
345 }
346
347}
348
34916µs1;
350__END__