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