klassa has asked for the wisdom of the Perl Monks concerning the following question:

To a system of about a hundred perl scripts (that get executed by thousands of people a day, across the company), I'd like to add some basic logging (well, more like monitoring). I'd like to track things like the name of the script, who ran it, its execution time, and the nature of any unhandled exceptions that it threw, every time a script runs.

Now, I already have a repository for the data, and a way to get it there (we have a departmental tool with an XML interface for receiving the data; it also has a web UI, for reporting against collected data). I also have the collection details worked out, in the form of a script template into which I could wrap the contents of each script. It's the obvious approach, and looks like this:

#!/usr/bin/perl -w use strict; use Monitor; eval { Monitor::register($0, @ARGV); ### EXISTING CODE GOES HERE }; if ($@) { my $exc = $@; Monitor::exception($exc); die $exc }

Inside the Monitor module, a BEGIN block would track the start time, and an END block would track the elapsed time and handle the task of posting all of the data to the monitor application. Pretty straightforward stuff. What I don't like, though, is that this template code has to go into every source file... This amount of copy/paste just seems wrong (and lacking in elegance).

A colleague suggested that we should shoot for a way to get it down to a single-line addition to every file: the inclusion of a use Monitor line. He reasoned that we should be able to come up with a way to trap unhandled exceptions, without actually putting the eval everywhere. Basically, the Monitor module would do something to set things up, and then reach back into main:: (e.g.), from its END block, to figure out whether any unhandled exceptions occurred.

Alas, despite playing with this for a quite some time, we couldn't get it worked out. For one thing, $@ only applies after an eval. For another, there doesn't seem to be a foolproof way to even get to $@, given that other modules could have END blocks that fire first, each of which could corrupt it.

So, I come to the monks for guidance...

Replies are listed 'Best First'.
Re: logging, to include unhandled exceptions
by ikegami (Patriarch) on Aug 31, 2008 at 01:42 UTC
    use Monitor;
    plus
    package Monitor; BEGIN { ... } END { ... } $SIG{__DIE__} = sub { die $@ if $^S; ... }; $SIG{__WARN__} = sub { ... }; 1;

    References:

    • perlmod (for BEGIN and END)
    • %SIG (for $SIG{__DIE__} and $SIG{__WARN__})
    • $^S

      Thanks... I'd considered __DIE__, but was a little put off by the warning in perlvar:

      Having to even think about the $^S variable in your exception handlers is simply wrong. $SIG{__DIE__} as currently implemented invites grievous and difficult to track down errors. Avoid it and use an "END{}" or CORE::GLOBAL::die override instead.

      I'm also not quite sure I understand the use of $^S, in your example. According to the man page, your code would propagate the die -- but only if executing an eval. Why not always do it?

      Anyway, in light of the warning in perlvar, what "difficult to track down" errors am I apt to run into, here? I mean, is there a way to categorize them in some way?

      Thanks!

        According to the man page, your code would propagate the die -- but only if executing an eval. Why not always do it?

        Not quite. I propagated the die without further action if executing an eval.

        I left up to you what to do when outside of any eval, likely something followed by die or exit.

        I don't really see the difference between using $SIG{__DIE__} and overriding CORE::GLOBAL::die, so you might as well use the latter.

        BEGIN { *CORE::GLOBAL::die = sub { CORE::die(@_) if $^S; ... }; } BEGIN { *CORE::GLOBAL::warn = sub { ... }; }

        We still need to check $^S, so I don't understand the warning perlvar gives.

        Anyway, in light of the warning in perlvar, what "difficult to track down" errors am I apt to run into, here?

        I don't know.