1#**************************************************************
2#
3#  Licensed to the Apache Software Foundation (ASF) under one
4#  or more contributor license agreements.  See the NOTICE file
5#  distributed with this work for additional information
6#  regarding copyright ownership.  The ASF licenses this file
7#  to you under the Apache License, Version 2.0 (the
8#  "License"); you may not use this file except in compliance
9#  with the License.  You may obtain a copy of the License at
10#
11#    http://www.apache.org/licenses/LICENSE-2.0
12#
13#  Unless required by applicable law or agreed to in writing,
14#  software distributed under the License is distributed on an
15#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16#  KIND, either express or implied.  See the License for the
17#  specific language governing permissions and limitations
18#  under the License.
19#
20#**************************************************************
21
22
23
24package installer::logger;
25
26use installer::files;
27use installer::globals;
28use Time::HiRes qw(gettimeofday tv_interval);
29use English;
30use IO::Handle;
31use strict;
32
33my $StartTime = undef;
34
35sub Die ($)
36{
37    my ($message) = @_;
38    print "Stack Trace:\n";
39    my $i = 1;
40    while ((my @call_details = (caller($i++))))
41    {
42        printf("%s:%s in function %s\n", $call_details[1], $call_details[2], $call_details[3]);
43    }
44
45    die $message;
46}
47
48
49=head1 NAME
50
51    installer::logger
52
53    Logging for the installer modules.
54
55=cut
56
57=head1 DESCRIPTION
58
59    This module is in a transition state from a set of loosly connected functions to a single class.
60
61    There are three globaly available logger objects:
62
63=over
64
65=item $Lang
66
67    is language specific and writes messages to a log file.
68
69=cut
70
71=item $Glob
72
73    is independent of the current language.  Its messages are prepended to each $Lang logger.
74
75=cut
76
77=item $Info
78
79    is for output to the console.
80
81=cut
82
83=back
84
85=cut
86
87
88our $Global = installer::logger->new("glob",
89    'is_save_lines' => 1,
90    'is_print_to_console' => 0,
91    'is_show_relative_time' => 1);
92our $Lang = installer::logger->new("lang",
93    'is_print_to_console' => 0,
94    'is_show_relative_time' => 1,
95    'is_show_log_id' => 1
96    );
97our $Info = installer::logger->new("info",
98    'is_show_relative_time' => 0,
99    'is_show_process_id' => 0,
100    'is_show_log_id' => 0
101    );
102
103=head2 new($class, $id, @arguments)
104
105    Create a new instance of the logger class.
106    @arguments lets you override default values.
107
108=cut
109
110sub new ($$@)
111{
112    my ($class, $id, @arguments) = @_;
113
114    my $self = {
115        'id' => $id,
116        'filename' => "",
117        # When set then lines are printed to this file.
118        'file' => undef,
119        # When true then lines are printed to the console.
120        'is_print_to_console' => 1,
121        'is_save_lines' => 0,
122        # A container of printed lines.  Lines are added only when 'is_save_lines' is true.
123        'lines' => [],
124        # Another logger to which all prints are forwarded.
125        'forward' => [],
126        # A filter function that for example can recoginze build errors.
127        'filter' => undef,
128        # Show relative time
129        'is_show_relative_time' => 0,
130        # Show log id (mostly for debugging the logger)
131        'is_show_log_id' => 0,
132        # Show the process id, useful on the console when doing a multiprocessor build.
133        'is_show_process_id' => 0
134    };
135    while (scalar @arguments >= 2)
136    {
137        my $key = shift @arguments;
138        my $value = shift @arguments;
139        $self->{$key} = $value;
140    }
141
142    bless($self, $class);
143
144    return $self;
145}
146
147
148
149=head2 printf($self, $message, @arguments)
150
151    Identical in syntax and semantics to the usual perl (s)printf.
152
153=cut
154sub printf ($$@)
155{
156    my ($self, $format, @arguments) = @_;
157
158    $self->print(sprintf($format, @arguments), 0);
159}
160
161
162
163
164=head2 print ($self, $message, [optional] $force)
165
166    Print the given message.
167    If the optional $force parameter is given and it evaluates to true then the message
168    is printed even when the golbal $installer::globals::quiet is true.
169
170=cut
171sub print ($$;$)
172{
173    my ($self, $message, $force) = @_;
174
175    Die "newline at start of line" if ($message =~ /^\n.+/);
176
177    $force = 0 unless defined $force;
178
179    my $relative_time = tv_interval($StartTime, [gettimeofday()]);
180    foreach my $target ($self, @{$self->{'forward'}})
181    {
182        $target->process_line(
183            $relative_time,
184            $self->{'id'},
185            $PID,
186            $message,
187            $force);
188    }
189}
190
191
192
193
194=head2 process_line ($self, $relative_time, $log_id, $pid, $message, $force)
195
196    Internal function that decides whether to
197    a) write to a log file
198    b) print to the console
199    c) store in an array for later use
200    the preformatted message.
201
202=cut
203sub process_line ($$$$$$)
204{
205    my ($self, $relative_time, $log_id, $pid, $message, $force) = @_;
206
207    # Apply the line filter.
208    if (defined $self->{'filter'})
209    {
210        $message = &{$self->{'filter'}}($relative_time, $log_id, $pid, $message);
211    }
212
213    # Format the line.
214    my $line = "";
215    if ($self->{'is_show_relative_time'})
216    {
217        $line .= sprintf("%12.6f : ", $relative_time);
218    }
219    if ($self->{'is_show_log_id'})
220    {
221        $line .= $log_id . " : ";
222    }
223    if ($self->{'is_show_process_id'})
224    {
225        $line .= $pid . " : ";
226    }
227    $line .= $message;
228
229    # Print the line to a file or to the console or store it for later use.
230    my $fid = $self->{'file'};
231    if (defined $fid)
232    {
233        print $fid ($line);
234    }
235    if (($force || ! $installer::globals::quiet)
236        && $self->{'is_print_to_console'})
237    {
238        print($line);
239    }
240    if ($self->{'is_save_lines'})
241    {
242        push @{$self->{'lines'}}, [$relative_time, $log_id, $pid, $message, $force];
243    }
244}
245
246
247
248
249=head2 set_filename (Self, $filename)
250
251    When the name of a writable file is given then all future messages will go to that file.
252    Output to the console is turned off.
253    This method is typically used to tie the language dependent $Lang logger to different log files.
254
255=cut
256sub set_filename ($$)
257{
258    my ($self, $filename) = @_;
259
260    $filename = "" unless defined $filename;
261    if ($self->{'filename'} ne $filename)
262    {
263        if (defined $self->{'file'})
264        {
265            $self->{'is_print_to_console'} = 1;
266            close $self->{'file'};
267            $self->{'file'} = undef;
268        }
269
270        $self->{'filename'} = $filename;
271
272        if ($filename ne "")
273        {
274            open $self->{'file'}, ">", $self->{'filename'}
275            || Die "can not open log file ".$self->{'filename'}." for writing";
276            $self->{'is_print_to_console'} = 0;
277
278            # Make all writes synchronous so that we don't loose any messages on an
279            # 'abrupt' end.
280            my $handle = select $self->{'file'};
281            $| = 1;
282            select $handle;
283        }
284    }
285}
286
287
288
289
290=head2 set_filter ($self, $filter)
291
292    Sets $filter (a function reference) as line filter.  It is applied to each line.
293    The filter can extract information from the given message and modify it before it is printed.
294
295=cut
296sub set_filter ($$)
297{
298    my ($self, $filter) = @_;
299    $self->{'filter'} = $filter;
300}
301
302
303
304
305=head2 add_timestamp ($self, $message)
306
307    Print the given message together with the current (absolute) time.
308
309=cut
310sub add_timestamp ($$)
311{
312	my ($self, $message) = @_;
313
314	my $timestring = get_time_string();
315    $self->printf("%s\t%s", $message, $timestring);
316}
317
318
319
320=head2 copy_lines_from ($self, $other)
321
322    Copy saved lines from another logger object.
323
324=cut
325sub copy_lines_from ($$)
326{
327    my ($self, $other) = @_;
328
329    my $is_print_to_console = $self->{'is_print_to_console'};
330    my $is_save_lines = $self->{'is_save_lines'};
331    my $fid = $self->{'file'};
332
333    foreach my $line (@{$other->{'lines'}})
334    {
335        $self->process_line(@$line);
336    }
337}
338
339
340
341
342=head2 set_forward ($self, $other)
343
344    Set a forwarding target.  All future messages are forwarded (copied) to $other.
345    A typical use is to tie $Info to $Lang so that all messages sent to $Info are
346    printed to the console AND written to the log file.
347
348=cut
349sub set_forward ($$)
350{
351    my ($self, $other) = @_;
352
353    # At the moment at most one forward target is allowed.
354    if (defined $other)
355    {
356        $self->{'forward'} = [$other];
357    }
358    else
359    {
360        $self->{'forward'} = [];
361    }
362}
363
364
365
366
367####################################################
368# Including header files into the logfile
369####################################################
370
371sub include_header_into_logfile
372{
373	my ($message) = @_;
374
375	$Lang->print("\n");
376	$Lang->print(get_time_string());
377	$Lang->print("######################################################\n");
378	$Lang->print($message."\n");
379	$Lang->print("######################################################\n");
380}
381
382####################################################
383# Including header files into the logfile
384####################################################
385
386sub include_header_into_globallogfile
387{
388	my ($message) = @_;
389
390	$Global->print("\n");
391	$Global->print(get_time_string());
392	$Global->print("######################################################\n");
393	$Global->print($message."\n");
394	$Global->print("######################################################\n");
395}
396
397####################################################
398# Write timestamp into log file
399####################################################
400
401sub include_timestamp_into_logfile
402{
403    Die "deprected";
404	my ($message) = @_;
405
406	my $infoline;
407	my $timestring = get_time_string();
408    $Lang->printf("%s\t%s", $message, $timestring);
409}
410
411####################################################
412# Writing all variables content into the log file
413####################################################
414
415sub log_hashref
416{
417	my ($hashref) = @_;
418
419    $Global->print("\n");
420    $Global->print("Logging variable settings:\n");
421
422	my $itemkey;
423
424	foreach $itemkey ( keys %{$hashref} )
425	{
426		my $line = "";
427		my $itemvalue = "";
428		if ( $hashref->{$itemkey} ) { $itemvalue = $hashref->{$itemkey}; }
429        $Global->printf("%s=%s\n", $itemkey, $itemvalue);
430	}
431
432    $Global->print("\n");
433}
434
435#########################################################
436# Including global logging info into global log array
437#########################################################
438
439sub globallog
440{
441	my ($message) = @_;
442
443	my $infoline;
444
445    $Global->print("\n");
446    $Global->print(get_time_string());
447    $Global->print("################################################################\n");
448    $Global->print($message."\n");
449    $Global->print("################################################################\n");
450}
451
452###############################################################
453# For each product (new language) a new log file is created.
454# Therefore the global logging has to be saved in this file.
455###############################################################
456
457sub copy_globalinfo_into_logfile
458{
459	for ( my $i = 0; $i <= $#installer::globals::globallogfileinfo; $i++ )
460	{
461		push(@installer::globals::logfileinfo, $installer::globals::globallogfileinfo[$i]);
462	}
463}
464
465###############################################################
466# For each product (new language) a new log file is created.
467# Therefore the global logging has to be saved in this file.
468###############################################################
469
470sub debuginfo
471{
472	my  ( $message ) = @_;
473
474	$message = $message . "\n";
475	push(@installer::globals::functioncalls, $message);
476}
477
478###############################################################
479# Saving the debug information.
480###############################################################
481
482sub savedebug
483{
484	my ( $outputdir ) = @_;
485
486	installer::files::save_file($outputdir . $installer::globals::debugfilename, \@installer::globals::functioncalls);
487	print_message( "... writing debug file " . $outputdir . $installer::globals::debugfilename . "\n" );
488}
489
490###############################################################
491# Starting the time
492###############################################################
493
494sub starttime
495{
496	$installer::globals::starttime = time();
497	$StartTime = [gettimeofday()];
498
499    my $localtime = localtime();
500}
501
502###############################################################
503# Convert time string
504###############################################################
505
506sub convert_timestring
507{
508	my ($secondstring) = @_;
509
510	my $timestring = "";
511
512	if ( $secondstring < 60 )	 # less than a minute
513	{
514		if ( $secondstring < 10 ) { $secondstring = "0" . $secondstring; }
515		$timestring = "00\:$secondstring min\.";
516	}
517	elsif ( $secondstring < 3600 )
518	{
519		my $minutes = $secondstring / 60;
520		my $seconds = $secondstring % 60;
521		if ( $minutes =~ /(\d*)\.\d*/ ) { $minutes = $1; }
522		if ( $minutes < 10 ) { $minutes = "0" . $minutes; }
523		if ( $seconds < 10 ) { $seconds = "0" . $seconds; }
524		$timestring = "$minutes\:$seconds min\.";
525	}
526	else	# more than one hour
527	{
528		my $hours = $secondstring / 3600;
529		my $secondstring = $secondstring % 3600;
530		my $minutes = $secondstring / 60;
531		my $seconds = $secondstring % 60;
532		if ( $hours =~ /(\d*)\.\d*/ ) { $hours = $1; }
533		if ( $minutes =~ /(\d*)\.\d*/ ) { $minutes = $1; }
534		if ( $hours < 10 ) { $hours = "0" . $hours; }
535		if ( $minutes < 10 ) { $minutes = "0" . $minutes; }
536		if ( $seconds < 10 ) { $seconds = "0" . $seconds; }
537		$timestring = "$hours\:$minutes\:$seconds hours";
538	}
539
540	return $timestring;
541}
542
543###############################################################
544# Returning time string for logging
545###############################################################
546
547sub get_time_string
548{
549	my $currenttime = time();
550	$currenttime = $currenttime - $installer::globals::starttime;
551	$currenttime = convert_timestring($currenttime);
552	$currenttime = localtime() . " \(" . $currenttime . "\)\n";
553	return $currenttime;
554}
555
556###############################################################
557# Returning the age of a file (in seconds)
558###############################################################
559
560sub get_file_age
561{
562	my ( $filename ) = @_;
563
564	my $filetime = (stat($filename))[9];
565	my $timediff = time() - $filetime;
566	return $timediff;
567}
568
569###############################################################
570# Stopping the time
571###############################################################
572
573sub stoptime
574{
575    my $localtime = localtime();
576    $Info->printf("stopping log at %s\n", $localtime);
577}
578
579###############################################################
580# Set date string, format: yymmdd
581###############################################################
582
583sub set_installation_date
584{
585	my $datestring = "";
586
587	my @timearray = localtime(time);
588
589	my $day = $timearray[3];
590	my $month = $timearray[4] + 1;
591	my $year = $timearray[5] - 100;
592
593	if ( $year < 10 ) { $year = "0" . $year; }
594	if ( $month < 10 ) { $month = "0" . $month; }
595	if ( $day < 10 ) { $day = "0" . $day; }
596
597	$datestring = $year . $month . $day;
598
599	return $datestring;
600}
601
602###############################################################
603# Console output: messages
604###############################################################
605
606sub print_message
607{
608    Die "print_message is deprecated";
609
610    my $message = shift;
611    chomp $message;
612    my $force = shift || 0;
613    print "$message\n" if ( $force || ! $installer::globals::quiet );
614    return;
615}
616
617sub print_message_without_newline
618{
619    my $message = shift;
620    chomp $message;
621    print "$message" if ( ! $installer::globals::quiet );
622    return;
623}
624
625###############################################################
626# Console output: warnings
627###############################################################
628
629sub print_warning
630{
631    my $message = shift;
632    chomp $message;
633    print STDERR "WARNING: $message";
634    return;
635}
636
637###############################################################
638# Console output: errors
639###############################################################
640
641sub print_error
642{
643    my $message = shift;
644    chomp $message;
645    print STDERR "\n";
646    print STDERR "**************************************************\n";
647    print STDERR "ERROR: $message";
648    print STDERR "\n";
649    print STDERR "**************************************************\n";
650    return;
651}
652
6531;
654