in reply to execution log

If you mean logging from within a perl script, I wrote the package below for operations logging. It could (should?) be objectified (so you can have more than one logstream per application instance). If need be, you could also integrate it with an external logging service, e.g., syslog (*nix) or the Windows Event Service. However, there are undoubtedly extant perl modules which offer this.

Again, this code is intended for logging operations and is probably not appropriate for debug tracing. For the latter purpose, I've written a separate package with more suitable usage and formatting conventions (indents (someday!), trace levels (ditto), no timestamps, etc.). As well, there must be other debug tracing packages out there.

Regards,
Michael

use Time::Local; # ============================================== # # global state-of-the-module variables # # ============================================== # my $_logstarted=0; my $LOG; undef $LOG; my @_logstack=(); # ============================================== # # log 'stack' management # # ---------------------------------------------- # # Enables inclusion in logged messages of the # # name of the subroutine logging a particular # # message for diagnostics and debugging. # # ---------------------------------------------- # # usage: # # logstack('push', <caller>) # # logstack('pop') # # logstack('clear') # # logstack() # # ---------------------------------------------- # # logstack('push', $caller), logstack('pop') # # and logstack() return the callstack as a # # string of colon-separated fields; # # logstack('clear') returns numeric 1. # # ---------------------------------------------- # # To work correctly, requires subroutines # # to call logstack('push', $callername) on # # entry and logstack('pop') on return # # ---------------------------------------------- # # EXPORTED # # ============================================== # # examples: # 16 Apr 2006 16:05:50 myfiles: process: catalog: found 35 file(s) # 02 Feb 2006 10:48:02 myfiles: process: archive: error moving todaysf +iles.html: No such file or directory sub logstack { return if !$_logstarted; my $op=@_ ? shift : ""; @_logstack=(), return 1 if ($op eq 'clear'); push @_logstack, @_ if $op eq 'push'; pop @_logstack if $op eq 'pop'; return join (': ', @_logstack); } # ============================================== # # logstart -- initializes log service # # ---------------------------------------------- # # usage: # # logstart( # # directory => $logdirectory, # # filename => $logfilename, # # caller => $callername, # # path => $logpath, # # error => \$errmsg) # # # # The logfile must be specified with either # # <logpath> or [<directory>, <filename>], # # with <logpath> used if both are passed. # # # # The logfile is the only required parameter. # # ---------------------------------------------- # # Returns numeric 1 or 0, respectively, # # on success or failure # # # # On error or failure, logstart() returns # # an error message via the optional error # # parameter. # # ---------------------------------------------- # # EXPORTED # # ============================================== # sub logstart { return if $_logstarted || (@_<2); my $logdir=""; my $logfile=""; my $msg=""; undef $msg; my $caller=""; my $logpath=""; my $error; undef $error; my %argh=(); # collect and validate function args while (@_) { my $key=shift; my $value=shift; $argh{$key}=$value; } $caller=$argh{caller} if exists($argh{caller}); $msg=$argh{message} if exists($argh{message}); $error=$argh{error} if exists($argh{error}); if (exists $argh{path}) { $logpath=$argh{path}; } elsif ((exists $argh{directory}) && (exists $argh{filename})) { $argh{directory}=~s/\\$//; $logdir=$argh{directory}; $logfile=$argh{filename}; $logpath=$logdir."\\".$logfile; } else { $$error="missing logfile directory and filename" if $error; return 0; } # create log subdirectory if necessary if (!(-e $logdir) && !(mkdir $logdir)) { $$error="couldn't create log directory ".$logdir.": ".$! if $error; return 0; } # open logfile if (!(open($LOG, "+>>:utf8", $logpath))) { $$error="error opening logfile ".$logpath.": ".$! if $error; return 0; } $LOG->autoflush(1); print $LOG "\n"; $_logstarted=1; logstack('clear'); logstack('push', $caller) if $caller; logstack('push', 'logstart'); logmsg($msg) if $msg; logstack('pop'); logstack('pop') if $caller; return 1; } # ============================================== # # logstop -- shuts down log service # # ---------------------------------------------- # # usage: # # logstop() # # logstop($lastwords) # # # # The optional parameter $lastwords is a # # message to log before closing the logfile # # ---------------------------------------------- # # EXPORTED # # ============================================== # sub logstop { return if !$_logstarted; my $msg=shift; $msg and logmsg($msg); close $LOG; undef $LOG; $_logstarted=0; logstack('clear'); return; } # ============================================== # # logstr -- composes formatted string of form # # <date & time><TAB><callstack><TAB><msg> where # # date & time is of form # # <dd mmm yyyy> <MM>:<hh>:<ss> # # ---------------------------------------------- # # usage: # # logstr() # # logstr($msg) # # ---------------------------------------------- # # logstr() with no parameters returns # # a formatted timestamp only # # ---------------------------------------------- # # EXPORTED # # ============================================== # sub logstr { my $msg=shift; $msg=$msg?$msg:""; my ($sec, $min, $hour, $day, $month, $year, $weekday, $yday, $dst)=localtime(time); $year+=1900; my $timestamp=sprintf("%02d", $day)." ".$mmm[$month]." ". $year." ".sprintf("%02d", $hour).":".sprintf("%02d", $min). ":".sprintf("%02d", $sec); $msg=$timestamp."\t".join (': ', @_logstack).": ".$msg; return $msg } # ============================================== # # logmsg -- writes formatted string of form # # <date & time><TAB><callstack><TAB><msg> # # to the logfile specified when the log # # service was started with logstart() # # # # The content of <callstack> is # # managed using logstack() # # ---------------------------------------------- # # usage: # # logmsg($msg) # # ---------------------------------------------- # # Does nothing if $msg is missing or the log # # service wasn't previously started with # # logstart() # # ---------------------------------------------- # # EXPORTED # # ============================================== # sub logmsg { return if !$_logstarted || !(defined $LOG) || !@_; my $msg=logstr(shift()); print $LOG $msg."\n"; # print $msg."\n"; # optionally print duplicate message to STDOUT return $msg; }

keywords: log;logging;event;events;syslog;event logging;execution log;execution logging;event log; audit trail;log service;logging service;event service;

Replies are listed 'Best First'.
Re^2: execution log
by yburge (Acolyte) on May 08, 2006 at 21:09 UTC
    Thank you, mscudder! BTW...the expected use is simply for logging the execution of a working script, not for debugging, anyway. I had nearly given up on finding an event log, and will gladly use your package. Now...excuse the Perl-newbie dumb question - since this is a package, how do I use it in a script? Do I code a "use" statement, or perform an "exec" or "system"?
      The simplest option -- if you don't care about reusability or maintainability -- is simply to paste the code into the file containing your script.

      To create a (reusable) module, create a file in the format below, i.e. starting with the section labeled module interface, followed by the actual module code, and concluding with the numeral 1 (which should be the very last character in the file). Name the module file using the extension .pm, e.g., logmsg.pm.

      Locate the .pm file in one of the module directories searched by the perl interpreter:

      1. Same directory as the script.
      2. One of the standard perl extension directories for your platform and perl distribution.
      3. Your own perl extension directory, in which case you'll have to add the library to the interpreter's search path using one of the perl -I command line option, @INC array, PERL5LIB environmental variable, lib pragma, or FindBin module.

      To import the module into (make it accessible from within) your script, include the statement use Logmsg. The module name must be exactly as it appears in the corresponding package statement in the .pm file, including capitalization. By convention, at least the first letter of the module name should be upper case.

      Good luck!

      Best regards,
      Michael

      # ============================================== # # module interface # # ============================================== # package Logmsg; # for example -- name as you think best use strict; use Exporter(); our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS); # set the version for version checking $VERSION = 1.0; @ISA = qw(Exporter); @EXPORT = qw( # autoexported symbols &logstart &logmsg &logstr &logstop &logstack ); %EXPORT_TAGS = (); # symbol groupings @EXPORT_OK = (); # symbols exported on request # ============================================== # # module code # # ============================================== # # ============================================== # # at end of file # # ============================================== # 1;

      References

      Programming with Perl Modules: Chapter 1, Sebastopol, CA:O'Reilly Media, Inc.

      Perlmod - Perl Modules

      Perlmodlib - Constructing New Perl Modules and Finding Existing Ones

      Chapter 10, "Packages" in Wall, Larry, Tom Christiansen, and Jon Orwant, Programming Perl, Third Edition, Sebastopol, CA:O'Reilly Media, Inc., 2000, ISBN 0596000278.

      Chapter 11, "Modules" in Programming Perl.

      Chapter 12, "Packages, Libraries, and Modules," in Christiansen, Tom and Nathan Torkington, Perl Cookbook, Second Edition, Sebastopol, CA:O'Reilly Media, Inc., 2003, ISBN 0596003137.

        Wow - thank you SO much, mscudder! I'm working on a project to convert our DOS .bat scripts to Perl, while learning both script languages, and I spend a lot of time in the state of confusion. I appreciate your clear writing style, while still managing to avoid talking down to me.