diff --git a/perl-xCAT/xCAT/MsgUtils.pm b/perl-xCAT/xCAT/MsgUtils.pm index 22cade65f..3c849046d 100644 --- a/perl-xCAT/xCAT/MsgUtils.pm +++ b/perl-xCAT/xCAT/MsgUtils.pm @@ -5,6 +5,7 @@ package xCAT::MsgUtils; use strict; use Sys::Syslog qw (:DEFAULT setlogsock); + #use locale; use Socket; use File::Path; @@ -47,13 +48,7 @@ This program module file, supports the xcat messaging and logging =head3 message - Display a msg STDOUT,STDERR or return to callback function. - If callback routine is provide, the message will be returned to the callback - routine. - - If callback routime is not provide, the message is displayed to STDOUT or - STDERR. - + Display a msg STDOUT,STDERR,log a msg and/or return to callback function. Arguments: The arguments of the message() function are: @@ -68,6 +63,14 @@ This program module file, supports the xcat messaging and logging If address of the callback is not provide, then the message will be displayed to STDERR or STDOUT or added to SYSLOG. See flags below. + + If logging (L) is requested, the message structure + must be a simple string. The routine will convert + it to the appropriate callback structure, if a callback + is provided. + Note for logging xCAT:MsgUtils->start_logging and + xCAT:MstUtils->stop_logging must be used to + open and close the log. For compatibility with existing code, the message routine will move the data into the appropriate callback structure, if required. @@ -106,7 +109,18 @@ This program module file, supports the xcat messaging and logging routine and only use the I flag for the message. If V flag is detected, it will be changed to an I flag. W - warning this is displayed/logged by daemon/Client.pm. - + L - Log error to xCAT Log on the local machine. + Routine must have setup log by calling + MsgUtils->start_log routine which returns + $::LOG_FILE_HANDLE. Log is closed with + MsgUtils->stop_log routine. Note can be combined with + other flags: + LS - Log to xCAT Log and Syslog + LSE/LSI - Log to xCAT Log and Syslog and display + if this option is used the message must be a simple + string. The message routine will format for callback + based on the (D,E,I,W) flag. + Here's the meaning of the 1st character, if no callback specified: @@ -118,9 +132,19 @@ This program module file, supports the xcat messaging and logging SE logs message to syslog and is sent to STDERR. V - verbose. This flag is not valid, the calling routine should check for verbose mode before calling the message + routine and only use the I flag for the message. If V flag is detected, it will be changed to an I flag. W - warning goes to STDOUT. + L - log goes to /var/log/xcat/ + ( see MsgUtils->start_log) + Routine must have setup log by calling + MsgUtils->start_log routine which returns + $::LOG_FILE_HANDLE. Log is closed with + MsgUtils->stop_log routine. Note can be combined with + other flags: + LS - Log to xCAT Log and Syslog + LSE/LSI - Log to xCAT Log and Syslog and display Returns: none @@ -131,11 +155,19 @@ This program module file, supports the xcat messaging and logging Example: Use with no callback + # Message to STDOUT + xCAT::MsgUtils->message('I', "Operation $value1 succeeded\n"); + # Message to STDERR xCAT::MsgUtils->message('E', "Operation $value1 failed\n"); + # Message to Syslog xCAT::MsgUtils->message('S', "Host $host not responding\n"); - xCAT::MsgUtils->message('SI', "Host $host not responding\n"); + # Message to Log and Syslog + xCAT::MsgUtils->message('LS', "Host $host not responding\n"); + # Message to Log + xCAT::MsgUtils->message('L', "Host $host not responding\n"); Use with callback + # Message to callback my %rsp; $rsp->{data}->[0] = "Job did not run. \n"; xCAT::MsgUtils->message("D", $rsp, $::CALLBACK); @@ -159,20 +191,35 @@ This program module file, supports the xcat messaging and logging xCAT::MsgUtils->message("S", $rsp, $::CALLBACK); + # Message to Syslog and callback my %rsp; $rsp->{error}->[0] = "Host not responding\n"; xCAT::MsgUtils->message("SE", $rsp, $::CALLBACK); + # Message to Syslog and callback my %rsp; $rsp->{info}->[0] = "Host not responding\n"; xCAT::MsgUtils->message("SI", $rsp, $::CALLBACK); + # Message to Log, Syslog and callback + my $msg; + $msg = "Host not responding\n"; + xCAT::MsgUtils->message("LSI", $msg, $::CALLBACK); + + # Message to Log and callback + my $msg; + $msg = "Host not responding\n"; + xCAT::MsgUtils->message("LI", $msg, $::CALLBACK); + + + + Comments: Returns: - none + 1 for internal error ( invalid input to the routine) @@ -190,8 +237,8 @@ sub message my $call_back = shift; # optional my $exitcode = shift; # optional - # should be I, D, E, S, W - # or S(I, D, E, S, W) + # should be I, D, E, S, W , L + # or S(I, D, E, S, W, L) my $stdouterrf = \*STDOUT; my $stdouterrd = ''; @@ -201,6 +248,43 @@ sub message $stdouterrd = '1>&2'; } + # check for logging + my $logging = 0; + if ($sev =~ /[L]/) + { + + # no log opened, we have an error + if (!defined($::LOG_FILE_HANDLE)) + { + if ($call_back) + { + + # build callback structure + my $newrsp; + my $sevkey = 'error'; + my $err = + "Logging requested without setting up log by calling xCAT:MsgUtils->start_logging.\n"; + push @{$newrsp->{$sevkey}}, $err; + push @{$newrsp->{errorcode}}, "1"; + $call_back->($newrsp); # send message to daemon/Client.pm + return 1; + } + else + { + print + "Logging requested without setting up log by calling xCAT:MsgUtils->start_logging.\n"; + return 1; + } + } + + else + { + + $logging = 1; + + } + } + if ($sev eq 'V') { # verbose should have been handled in calling routine $sev = "I"; @@ -217,6 +301,10 @@ sub message # should not just be check the 0th element. 2) a cmd may have both error # text and data text. 3) this message() function should just take in a plain # string and put it in the correct place based on the severity. + + # + # if a callback routine is provided + # if ($call_back) { # callback routine provided my $sevkey; @@ -233,66 +321,281 @@ sub message } else { - print - "Internal Error: Invalid severity passed to MsgUtils::message().\n"; + + # build callback structure + my $newrsp; + my $sevkey = 'error'; + my $err = + "Invalid or no severity code passed to MsgUtils::message().\n"; + push @{$newrsp->{$sevkey}}, $err; + push @{$newrsp->{errorcode}}, "1"; + $call_back->($newrsp); # send message to daemon/Client.pm return 1; } - if ($sevkey ne 'data') + # check if logging to xCAT log, must be handled + # separately because message data is a simple string + # + if (!$logging) { - if (!defined($rsp->{$sevkey}) || !scalar(@{$rsp->{$sevkey}})) - { # did not pass the text in in the severity-specific field - if (defined($rsp->{data}) && scalar(@{$rsp->{data}})) - { - push @{$rsp->{$sevkey}}, - @{$rsp->{data} - }; # assume they passed in the text in the data field instead - @{$rsp->{data}} = (); # clear out the data field + if ($sevkey ne 'data') + { + if (!defined($rsp->{$sevkey}) || !scalar(@{$rsp->{$sevkey}})) + { # did not pass the text in in the severity-specific field + # so fix it + if (defined($rsp->{data}) && scalar(@{$rsp->{data}})) + { + push @{$rsp->{$sevkey}}, @{$rsp->{data}}; + + # assume they passed + # in the text in the data field instead + @{$rsp->{data}} = (); # clear out the data field + } } } - } - if (!defined($rsp->{$sevkey}) || !scalar(@{$rsp->{$sevkey}})) - { - return; - } # if still nothing in the array, there is nothing to print out - if ($sev ne 'S') - { # if sev is anything but only-syslog, print the msg - if ($exitcode) { push @{$rsp->{errorcode}}, $exitcode; } + # if still nothing in the array, there is nothing to print out + if (!defined($rsp->{$sevkey}) || !scalar(@{$rsp->{$sevkey}})) + { + return; + } + + if ($exitcode) + { + push @{$rsp->{errorcode}}, $exitcode; + } $call_back->($rsp); # send message to daemon/Client.pm @{$rsp->{$sevkey}} = (); # clear out the rsp structure in case they use it again @{$rsp->{data}} = (); @{$rsp->{errorcode}} = (); } + else # logging + { - # No need to support syslog writing when they specified a callback + # write to log + print $::LOG_FILE_HANDLE $rsp; + + # build callback structure + my $newrsp; + push @{$newrsp->{$sevkey}}, $rsp; + if ($exitcode) + { + push @{$newrsp->{errorcode}}, $exitcode; + } + $call_back->($newrsp); # send message to daemon/Client.pm + + } } - else # no callback provided + else # no callback provided { - if ($sev ne 'S') # it is not syslog only - { + if ($logging) + { # print to local xcat log + print $::LOG_FILE_HANDLE $rsp; + } + else + { # print to stdout + print $stdouterrf $rsp; # print the message } - if ($sev =~ /S/) - { + } - # If they want this msg to also go to syslog, do that now - eval { - openlog("xCAT", '', 'local4'); - setlogsock(["tcp","unix","stream"]); - syslog("err", $rsp); - closelog(); - }; - my $errstr = $@; - if ($errstr) { - print $stdouterrf "Unable to log $rsp to syslog because of $errstr\n"; - } + # is syslog requested + if ($sev =~ /S/) + { + + # If they want this msg to also go to syslog, do that now + eval { + openlog("xCAT", '', 'local4'); + setlogsock(["tcp", "unix", "stream"]); + syslog("err", $rsp); + closelog(); + }; + my $errstr = $@; + if ($errstr) + { + print $stdouterrf + "Unable to log $rsp to syslog because of $errstr\n"; } } return; } +#-------------------------------------------------------------------------------- + +=head2 xCAT Logging Routines + To use xCAT Logging follow the following sample + + my $rc=xCAT::MsgUtils->start_logging("mylogname"); # create/open log + . + . + . + # Message to Log and callback + my $msg; + $msg = "Host not responding\n"; + xCAT::MsgUtils->message("LI", $msg, $::CALLBACK); + . + . + # Message to Log + my $msg; + $msg = "Host not responding\n"; + xCAT::MsgUtils->message("L", $msg); + . + + my $rc=xCAT::MsgUtils->stop_logging(); # close log + + +=cut + +#-------------------------------------------------------------------------------- + +=head3 start_logging + + Start logging messages to a logfile. Return the log file handle so it + can be used for updates and to close the file when done logging + using stop_logging. + + Arguments: + $logfilename ( just name, path is by default /var/log/xcat) + Returns: + $::LOG_FILE_HANDLE + Globals: + $::LOG_FILE_HANDLE + Error: + $::NOK + Example: + xCAT:Utils->start_logging("logname"); + +=cut + +#-------------------------------------------------------------------------------- + +sub start_logging +{ + my ($class, $logfilename) = @_; + my ($cmd, $rc); + my $xCATLogDir = "/var/log/xcat/"; + + my $logfile = $xCATLogDir; + $logfile .= $logfilename; + xCAT::MsgUtils->backup_logfile($logfile); + + # create the log directory if it's not already there + if (!-d $xCATLogDir) + { + $cmd = "mkdir -m 644 -p $xCATLogDir"; + $rc = system("$cmd"); + if ($rc >> 8) + { + xCAT::MsgUtils->message('SE', "Error running $cmd.\n"); + return ($::NOK); + } + } + + # open the log file + unless (open(LOGFILE, ">>$logfile")) + { + + # Cannot open file + xCAT::MsgUtils->message('SE', "Error opening $logfile.\n"); + return ($::NOK); + } + + $::LOG_FILE_HANDLE = \*LOGFILE; + $::LOG_FILE_NAME = $logfile; + + # Print the program name and date to the top of the logfile + my $sdate = `/bin/date`; + chomp $sdate; + my $program = $0; + xCAT::MsgUtils->message('L', "$program:logging started $sdate.\n"); + + return ($::LOG_FILE_HANDLE); +} + +#-------------------------------------------------------------------------------- + +=head3 stop_logging + + Turn off message logging. Routine expects to have a file handle + passed in via the global $::LOG_FILE_HANDLE. + + Arguments: + + Returns: + $::OK + Globals: + $::LOG_FILE_HANDLE + Error: + none + Example: + MsgUtils->stop_logging(); + Comments: + closes the logfile and undefines $::LOG_FILE_HANDLE + even on error. + +=cut + +#-------------------------------------------------------------------------------- + +sub stop_logging +{ + my ($class) = @_; + if (defined($::LOG_FILE_HANDLE)) + { + + # Print the date at the bottom of the logfile + my $sdate = `/bin/date`; + chomp $sdate; + my $program = $0; + xCAT::MsgUtils->message('L', "$program:logging stopped $sdate.\n"); + + close($::LOG_FILE_HANDLE); + undef $::LOG_FILE_HANDLE; + } + return $::OK; +} + +#-------------------------------------------------------------------------------- + +=head3 backup_logfile + + Backup the current logfile. Move logfile to logfile.1. + Shift all other logfiles + (logfile.[1-3]) up one number. The original logfile.4 is removed as in a FIFO. + + Arguments: + $logfile ( full path) + Returns: + $::OK + Error: + undefined + Example: + xCAT::MsgUtils->backup_logfile($logfile); + +=cut + +#-------------------------------------------------------------------------------- + +sub backup_logfile +{ + my ($class, $logfile) = @_; + + my ($logfile1) = $logfile . ".1"; + my ($logfile2) = $logfile . ".2"; + my ($logfile3) = $logfile . ".3"; + my ($logfile4) = $logfile . ".4"; + + if (-f $logfile) + { + rename($logfile3, $logfile4) if (-f $logfile3); + rename($logfile2, $logfile3) if (-f $logfile2); + rename($logfile1, $logfile2) if (-f $logfile1); + rename($logfile, $logfile1); + } + return $::OK; +} + 1;