← Index
NYTProf Performance Profile   « block view • line view • sub view »
For /usr/local/src/github.com/foswiki/core/bin/view
  Run on Sun Dec 4 17:17:59 2011
Reported on Sun Dec 4 17:27:16 2011

Filename/usr/local/src/github.com/foswiki/core/lib/Foswiki/Logger/PlainFile.pm
StatementsExecuted 81 statements in 3.64ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
111179µs3.92msFoswiki::Logger::PlainFile::::log Foswiki::Logger::PlainFile::log
11185µs3.23msFoswiki::Logger::PlainFile::::_rotate Foswiki::Logger::PlainFile::_rotate
22146µs46µsFoswiki::Logger::PlainFile::::_time2month Foswiki::Logger::PlainFile::_time2month
11144µs59µsFoswiki::Logger::PlainFile::::BEGIN@4 Foswiki::Logger::PlainFile::BEGIN@4
11139µs52µsFoswiki::Logger::PlainFile::::BEGIN@6 Foswiki::Logger::PlainFile::BEGIN@6
11132µs32µsFoswiki::Logger::PlainFile::::BEGIN@10 Foswiki::Logger::PlainFile::BEGIN@10
11132µs67µsFoswiki::Logger::PlainFile::::_getLogForLevel Foswiki::Logger::PlainFile::_getLogForLevel
11129µs29µsFoswiki::Logger::PlainFile::::CORE:close Foswiki::Logger::PlainFile::CORE:close (opcode)
11129µs65µsFoswiki::Logger::PlainFile::::BEGIN@5 Foswiki::Logger::PlainFile::BEGIN@5
11127µs98µsFoswiki::Logger::PlainFile::::BEGIN@7 Foswiki::Logger::PlainFile::BEGIN@7
11124µs24µsFoswiki::Logger::PlainFile::::CORE:open Foswiki::Logger::PlainFile::CORE:open (opcode)
11123µs23µsFoswiki::Logger::PlainFile::::new Foswiki::Logger::PlainFile::new
11113µs13µsFoswiki::Logger::PlainFile::::BEGIN@33 Foswiki::Logger::PlainFile::BEGIN@33
11112µs12µsFoswiki::Logger::PlainFile::::CORE:print Foswiki::Logger::PlainFile::CORE:print (opcode)
33112µs12µsFoswiki::Logger::PlainFile::::CORE:match Foswiki::Logger::PlainFile::CORE:match (opcode)
11112µs12µsFoswiki::Logger::PlainFile::::BEGIN@9 Foswiki::Logger::PlainFile::BEGIN@9
11111µs11µsFoswiki::Logger::PlainFile::::BEGIN@32 Foswiki::Logger::PlainFile::BEGIN@32
11111µs17µsFoswiki::Logger::PlainFile::::_stat Foswiki::Logger::PlainFile::_stat
61110µs10µsFoswiki::Logger::PlainFile::::CORE:subst Foswiki::Logger::PlainFile::CORE:subst (opcode)
1118µs8µsFoswiki::Logger::PlainFile::::CORE:ftis Foswiki::Logger::PlainFile::CORE:ftis (opcode)
1116µs6µsFoswiki::Logger::PlainFile::::CORE:stat Foswiki::Logger::PlainFile::CORE:stat (opcode)
1115µs5µsFoswiki::Logger::PlainFile::::_time Foswiki::Logger::PlainFile::_time
0000s0sFoswiki::Logger::PlainFile::EventIterator::::hasNextFoswiki::Logger::PlainFile::EventIterator::hasNext
0000s0sFoswiki::Logger::PlainFile::EventIterator::::newFoswiki::Logger::PlainFile::EventIterator::new
0000s0sFoswiki::Logger::PlainFile::EventIterator::::nextFoswiki::Logger::PlainFile::EventIterator::next
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;
3
4270µs274µs
# spent 59µs (44+15) within Foswiki::Logger::PlainFile::BEGIN@4 which was called: # once (44µs+15µs) by Foswiki::logger at line 4
use strict;
# spent 59µs making 1 call to Foswiki::Logger::PlainFile::BEGIN@4 # spent 15µs making 1 call to strict::import
5261µs2101µs
# spent 65µs (29+36) within Foswiki::Logger::PlainFile::BEGIN@5 which was called: # once (29µs+36µs) by Foswiki::logger at line 5
use warnings;
# spent 65µs making 1 call to Foswiki::Logger::PlainFile::BEGIN@5 # spent 36µs making 1 call to warnings::import
6267µs266µs
# spent 52µs (39+13) within Foswiki::Logger::PlainFile::BEGIN@6 which was called: # once (39µs+13µs) by Foswiki::logger at line 6
use utf8;
# spent 52µs making 1 call to Foswiki::Logger::PlainFile::BEGIN@6 # spent 13µs making 1 call to utf8::import
7258µs2170µs
# spent 98µs (27+71) within Foswiki::Logger::PlainFile::BEGIN@7 which was called: # once (27µs+71µs) by Foswiki::logger at line 7
use Assert;
# spent 98µs making 1 call to Foswiki::Logger::PlainFile::BEGIN@7 # spent 71µs making 1 call to Assert::import
8
9244µs112µs
# spent 12µs within Foswiki::Logger::PlainFile::BEGIN@9 which was called: # once (12µs+0s) by Foswiki::logger at line 9
use Foswiki::Logger ();
# spent 12µs making 1 call to Foswiki::Logger::PlainFile::BEGIN@9
102117µs132µs
# spent 32µs within Foswiki::Logger::PlainFile::BEGIN@10 which was called: # once (32µs+0s) by Foswiki::logger at line 10
use Foswiki::Configure::Load;
# spent 32µs making 1 call to Foswiki::Logger::PlainFile::BEGIN@10
11116µsour @ISA = ('Foswiki::Logger');
12
13=begin TML
14
15---+ package Foswiki::Logger::PlainFile
16
17Plain file implementation of the Foswiki Logger interface. Mostly
18compatible with TWiki (and Foswiki 1.0.0) log files, except that dates
19are recorded using ISO format, and include the time, and it dies when
20a log can't be written (rather than printing a warning).
21
22This logger implementation maps groups of levels to a single logfile, viz.
23 * =debug= messages are output to $Foswiki::cfg{Log}{Dir}/debug.log
24 * =info= messages are output to $Foswiki::cfg{Log}{Dir}/events.log
25 * =warning=, =error=, =critical=, =alert=, =emergency= messages are
26 output to $Foswiki::cfg{Log}{Dir}/error.log.
27 * =error=, =critical=, =alert=, and =emergency= messages are also
28 written to standard error (the webserver log file, usually)
29
30=cut
31
32243µs111µs
# spent 11µs within Foswiki::Logger::PlainFile::BEGIN@32 which was called: # once (11µs+0s) by Foswiki::logger at line 32
use Foswiki::Time ();
# spent 11µs making 1 call to Foswiki::Logger::PlainFile::BEGIN@32
3322.53ms113µs
# spent 13µs within Foswiki::Logger::PlainFile::BEGIN@33 which was called: # once (13µs+0s) by Foswiki::logger at line 33
use Foswiki::ListIterator ();
# spent 13µs making 1 call to Foswiki::Logger::PlainFile::BEGIN@33
34
35# Map from a log level to the root of a log file name
36112µsour %LEVEL2LOG = (
37 debug => 'debug',
38 info => 'events',
39 warning => 'error',
40 error => 'error',
41 critical => 'error',
42 alert => 'error',
43 emergency => 'error'
44);
45
4612µsour $nextCheckDue = 0;
47
48# Symbols used so we can override during unit testing
4911µsour $dontRotate = 0;
5018µs
# spent 5µs within Foswiki::Logger::PlainFile::_time which was called: # once (5µs+0s) by Foswiki::Logger::PlainFile::log at line 70
sub _time { time() }
51120µs16µs
# spent 17µs (11+6) within Foswiki::Logger::PlainFile::_stat which was called: # once (11µs+6µs) by Foswiki::Logger::PlainFile::_rotate at line 280
sub _stat { stat(@_); }
# spent 6µs making 1 call to Foswiki::Logger::PlainFile::CORE:stat
52
53
# spent 23µs within Foswiki::Logger::PlainFile::new which was called: # once (23µs+0s) by Foswiki::logger at line 1998 of /usr/local/src/github.com/foswiki/core/lib/Foswiki.pm
sub new {
5412µs my $class = shift;
55124µs return bless( {}, $class );
56}
57
58=begin TML
59
60---++ ObjectMethod log($level, @fields)
61
62See Foswiki::Logger for the interface.
63
64=cut
65
66
# spent 3.92ms (179µs+3.74) within Foswiki::Logger::PlainFile::log which was called: # once (179µs+3.74ms) by Foswiki::logEvent at line 2194 of /usr/local/src/github.com/foswiki/core/lib/Foswiki.pm
sub log {
6715µs my ( $this, $level, @fields ) = @_;
68
6915µs167µs my $log = _getLogForLevel($level);
# spent 67µs making 1 call to Foswiki::Logger::PlainFile::_getLogForLevel
7016µs15µs my $now = _time();
# spent 5µs making 1 call to Foswiki::Logger::PlainFile::_time
7115µs13.23ms _rotate( $log, $now );
# spent 3.23ms making 1 call to Foswiki::Logger::PlainFile::_rotate
7219µs1364µs my $time = Foswiki::Time::formatTime( $now, 'iso', 'gmtime' );
# spent 364µs making 1 call to Foswiki::Time::formatTime
73
74 # Unfortunate compatibility requirement; need the level, but the old
75 # logfile format doesn't allow us to add fields. Since we are changing
76 # the date format anyway, the least pain is to concatenate the level
77 # to the date; Foswiki::Time::ParseTime can handle it, and it looks
78 # OK too.
7913µs unshift( @fields, "$time $level" );
80640µs610µs my $message =
# spent 10µs making 6 calls to Foswiki::Logger::PlainFile::CORE:subst, avg 2µs/call
81731µs '| ' . join( ' | ', map { s/\|/&vbar;/g; $_ } @fields ) . ' |';
82
8311µs my $file;
8412µs my $mode = '>>';
85
86 # Item10764, SMELL UNICODE: actually, perhaps we should open the stream this
87 # way for any encoding, not just utf8. Babar says: check what Catalyst does.
88120µs25µs if ( $Foswiki::cfg{Site}{CharSet}
# spent 3µs making 1 call to utf8::is_utf8 # spent 2µs making 1 call to Foswiki::Logger::PlainFile::CORE:match
89 && $Foswiki::cfg{Site}{CharSet} =~ /^utf-?8$/ )
90 {
91 $mode .= ":encoding($Foswiki::cfg{Site}{CharSet})";
92 }
93 elsif ( utf8::is_utf8($message) ) {
94 require Encode;
95 $message = Encode::encode( $Foswiki::cfg{Site}{CharSet}, $message, 0 );
96 }
97141µs124µs if ( open( $file, $mode, $log ) ) {
# spent 24µs making 1 call to Foswiki::Logger::PlainFile::CORE:open
98127µs112µs print $file "$message\n";
# spent 12µs making 1 call to Foswiki::Logger::PlainFile::CORE:print
99139µs129µs close($file);
# spent 29µs making 1 call to Foswiki::Logger::PlainFile::CORE:close
100 }
101 else {
102 if ( !-w $log ) {
103 die
104"ERROR: Could not open logfile $log for write. Your admin should 'configure' now and fix the errors!\n";
105 }
106
107 # die to force the admin to get permissions correct
108 die 'ERROR: Could not write ' . $message . ' to ' . "$log: $!\n";
109 }
110118µs11µs if ( $level =~ /^(error|critical|alert|emergency)$/ ) {
# spent 1µs making 1 call to Foswiki::Logger::PlainFile::CORE:match
111 print STDERR "$message\n";
112 }
113}
114
115{
116
117 # Private subclass of LineIterator that splits events into fields
11813µs package Foswiki::Logger::PlainFile::EventIterator;
1191111µs require Foswiki::LineIterator;
12018µs @Foswiki::Logger::PlainFile::EventIterator::ISA = ('Foswiki::LineIterator');
121
122 sub new {
123 my ( $class, $fh, $threshold, $level ) = @_;
124 my $this = $class->SUPER::new($fh);
125 $this->{_threshold} = $threshold;
126 $this->{_level} = $level;
127 return $this;
128 }
129
130 sub hasNext {
131 my $this = shift;
132 return 1 if defined $this->{_nextEvent};
133 while ( $this->SUPER::hasNext() ) {
134 my @line = split( /\s*\|\s*/, $this->SUPER::next() );
135 shift @line; # skip the leading empty cell
136 if (
137 $line[0] =~ s/\s+$this->{_level}\s*$// # test the level
138 # accept a plain 'old' format date with no level only if reading info (statistics)
139 || $line[0] =~ /^\d{1,2} [a-z]{3} \d{4}/i
140 && $this->{_level} eq 'info'
141 )
142 {
143 $line[0] = Foswiki::Time::parseTime( $line[0] );
144 if ( $line[0] >= $this->{_threshold} ) { # test the time
145 $this->{_nextEvent} = \@line;
146 return 1;
147 }
148 }
149 }
150 return 0;
151 }
152
153 sub next {
154 my $this = shift;
155 my $data = $this->{_nextEvent};
156 undef $this->{_nextEvent};
157 return $data;
158 }
159}
160
161=begin TML
162
163---++ StaticMethod eachEventSince($time, $level) -> $iterator
164
165See Foswiki::Logger for the interface.
166
167This logger implementation maps groups of levels to a single logfile, viz.
168 * =info= messages are output together.
169 * =warning=, =error=, =critical=, =alert=, =emergency= messages are
170 output together.
171This method cannot
172
173=cut
174
175sub eachEventSince {
176 my ( $this, $time, $level ) = @_;
177 my $log = _getLogForLevel($level);
178
179 # Find the year-month for the current time
180 my $now = _time();
181 my $nowLogYear = Foswiki::Time::formatTime( $now, '$year', 'servertime' );
182 my $nowLogMonth = Foswiki::Time::formatTime( $now, '$mo', 'servertime' );
183
184 # Find the year-month for the first time in the range
185 my $logYear = Foswiki::Time::formatTime( $time, '$year', 'servertime' );
186 my $logMonth = Foswiki::Time::formatTime( $time, '$mo', 'servertime' );
187
188 # Get the names of all the logfiles in the time range
189 my @logs;
190 while ( !( $logMonth == $nowLogMonth && $logYear == $nowLogYear ) ) {
191 my $logfile = $log;
192 my $logTime = $logYear . sprintf( "%02d", $logMonth );
193 $logfile =~ s/\.log$/.$logTime/g;
194 push( @logs, $logfile );
195 $logMonth++;
196 if ( $logMonth == 13 ) {
197 $logMonth = 1;
198 $logYear++;
199 }
200 }
201
202 # Finally the current log
203 push( @logs, $log );
204
205 my @iterators;
206 foreach my $logfile (@logs) {
207 next unless -r $logfile;
208 my $fh;
209 if ( open( $fh, '<', $logfile ) ) {
210 push(
211 @iterators,
212 new Foswiki::Logger::PlainFile::EventIterator(
213 $fh, $time, $level
214 )
215 );
216 }
217 else {
218
219 # Would be nice to report this, but it's chicken and egg and
220 # besides, empty logfiles can happen.
221 #print STDERR "Failed to open $logfile: $!";
222 }
223 }
224 return new Foswiki::ListIterator( \@iterators ) if scalar(@iterators) == 0;
225 return $iterators[0] if scalar(@iterators) == 1;
226 return new Foswiki::AggregateIterator( \@iterators );
227}
228
229# Get the name of the log for a given reporting level
230
# spent 67µs (32+35) within Foswiki::Logger::PlainFile::_getLogForLevel which was called: # once (32µs+35µs) by Foswiki::Logger::PlainFile::log at line 69
sub _getLogForLevel {
23112µs my $level = shift;
23215µs14µs ASSERT( defined $LEVEL2LOG{$level} ) if DEBUG;
# spent 4µs making 1 call to Assert::ASSERTS_OFF
23315µs my $log = $Foswiki::cfg{Log}{Dir} . '/' . $LEVEL2LOG{$level} . '.log';
234
235 # SMELL: Expand should not be needed, except if bin/configure tries
236 # to log to locations relative to $Foswiki::cfg{WorkingDir}, DataDir, etc.
237 # Windows seemed to be the most difficult to fix - this was the only thing
238 # that I could find that worked all the time.
239110µs132µs Foswiki::Configure::Load::expandValue($log);
# spent 32µs making 1 call to Foswiki::Configure::Load::expandValue
24018µs return $log;
241}
242
243
# spent 46µs within Foswiki::Logger::PlainFile::_time2month which was called 2 times, avg 23µs/call: # once (31µs+0s) by Foswiki::Logger::PlainFile::_rotate at line 261 # once (15µs+0s) by Foswiki::Logger::PlainFile::_rotate at line 281
sub _time2month {
24423µs my $time = shift;
245224µs my @t = gmtime($time);
24625µs $t[5] += 1900;
247221µs return sprintf( '%0.4d%0.2d', $t[5], $t[4] + 1 );
248}
249
250# See if the log needs to be rotated. If the log was last modified
251# last month, we need to rotate it.
252
# spent 3.23ms (85µs+3.14) within Foswiki::Logger::PlainFile::_rotate which was called: # once (85µs+3.14ms) by Foswiki::Logger::PlainFile::log at line 71
sub _rotate {
25312µs my ( $log, $now ) = @_;
254
25511µs return if $dontRotate;
256
257 # Don't bother checking if we have checked in this process already
25812µs return if ( $now < $nextCheckDue );
259
260 # Work out the current month
26115µs131µs my $curMonth = _time2month($now);
# spent 31µs making 1 call to Foswiki::Logger::PlainFile::_time2month
262
263 # After this check, don't check again for a month.
264118µs18µs $curMonth =~ /(\d{4})(\d{2})/;
# spent 8µs making 1 call to Foswiki::Logger::PlainFile::CORE:match
265111µs my ( $y, $m ) = ( $1, $2 + 1 );
26613µs if ( $m > 12 ) {
26712µs $m = '01';
26812µs $y++;
269 }
270 else {
271 $m = sprintf( '%0.2d', $m );
272 }
27319µs13.06ms $nextCheckDue = Foswiki::Time::parseTime("$y-$m-01");
# spent 3.06ms making 1 call to Foswiki::Time::parseTime
274
275 # If there's no existing log, there's nothing to rotate
276117µs18µs return unless -e $log;
# spent 8µs making 1 call to Foswiki::Logger::PlainFile::CORE:ftis
277
278 # Check when the log was last modified. If it was in the previous
279 # month, if may need to be rotated.
28017µs117µs my @stat = _stat($log);
# spent 17µs making 1 call to Foswiki::Logger::PlainFile::_stat
28116µs115µs my $modMonth = _time2month( $stat[9] );
# spent 15µs making 1 call to Foswiki::Logger::PlainFile::_time2month
28219µs return if ( $modMonth == $curMonth );
283
284 # The log was last modified in a month that was not the current month.
285 # Rotate older entries out into month-by-month logfiles.
286
287 #print STDERR ">> Checking $log entries\n";
288
289 # Open the current log
290 my $lf;
291 return unless open( $lf, '<', $log );
292
293 # Analyse the log and partition the lines into month groups
294 my %months;
295
296 local $/ = "\n";
297 my $line;
298 while ( $line = <$lf> ) {
299 my @event = split( /\s*\|\s*/, $line );
300 last unless $event[1];
301 my $eventTime = Foswiki::Time::parseTime( $event[1] );
302
303 if ( !$eventTime ) {
304
305 #print STDERR ">> Bad time in log: $line\n";
306 close($lf);
307 return;
308 }
309
310 my $eventMonth = _time2month($eventTime);
311
312 if ( $eventMonth < $curMonth ) {
313 push( @{ $months{$eventMonth} }, $line );
314 }
315 else {
316
317 # Reached the start of log entries for this month
318 last;
319 }
320 }
321
322 if ( !scalar( keys %months ) ) {
323
324 # no old months, we're done. The modify time on the current
325 # log will be touched by the next write, so we won't attempt
326 # to rotate again until next month (or $forceRotate is set).
327 #print STDERR ">> No old months\n";
328 close($lf);
329 return;
330 }
331
332 # Sook up the rest of the current log
333 $line ||= '';
334 $/ = undef;
335 my $curLog = $line . <$lf>;
336 close($lf);
337
338 foreach my $month ( keys %months ) {
339 my $bf;
340 my $backup = $log;
341 $backup =~ s/log$/$month/;
342 if ( -e $backup || !open( $bf, '>', $backup ) ) {
343
344 #print STDERR ">> Could not create $backup\n";
345 return;
346 }
347 print $bf join( '', @{ $months{$month} } );
348 close($bf);
349 }
350
351 # Finally rewrite the shortened current log
352 return unless open( $lf, '>', $log );
353 print $lf $curLog;
354 close($lf);
355}
356
357110µs1;
358__END__
 
# spent 29µs within Foswiki::Logger::PlainFile::CORE:close which was called: # once (29µs+0s) by Foswiki::Logger::PlainFile::log at line 99
sub Foswiki::Logger::PlainFile::CORE:close; # opcode
# spent 8µs within Foswiki::Logger::PlainFile::CORE:ftis which was called: # once (8µs+0s) by Foswiki::Logger::PlainFile::_rotate at line 276
sub Foswiki::Logger::PlainFile::CORE:ftis; # opcode
# spent 12µs within Foswiki::Logger::PlainFile::CORE:match which was called 3 times, avg 4µs/call: # once (8µs+0s) by Foswiki::Logger::PlainFile::_rotate at line 264 # once (2µs+0s) by Foswiki::Logger::PlainFile::log at line 88 # once (1µs+0s) by Foswiki::Logger::PlainFile::log at line 110
sub Foswiki::Logger::PlainFile::CORE:match; # opcode
# spent 24µs within Foswiki::Logger::PlainFile::CORE:open which was called: # once (24µs+0s) by Foswiki::Logger::PlainFile::log at line 97
sub Foswiki::Logger::PlainFile::CORE:open; # opcode
# spent 12µs within Foswiki::Logger::PlainFile::CORE:print which was called: # once (12µs+0s) by Foswiki::Logger::PlainFile::log at line 98
sub Foswiki::Logger::PlainFile::CORE:print; # opcode
# spent 6µs within Foswiki::Logger::PlainFile::CORE:stat which was called: # once (6µs+0s) by Foswiki::Logger::PlainFile::_stat at line 51
sub Foswiki::Logger::PlainFile::CORE:stat; # opcode
# spent 10µs within Foswiki::Logger::PlainFile::CORE:subst which was called 6 times, avg 2µs/call: # 6 times (10µs+0s) by Foswiki::Logger::PlainFile::log at line 80, avg 2µs/call
sub Foswiki::Logger::PlainFile::CORE:subst; # opcode