In Perl 5, $0 is the magic superglobal which contains the name of the program being executed. This is the name you see in the output of ps or in the top utility.

Some clever programs provide several symlinks to the main program and examine $0 to enable or disable certain behaviors. This is an easy way to hide the details of execution from users while making those behavior mnemonic.

I usually don't write those kinds of programs, but this past year I've written several batch processing programs which have several interdependent states. For example, one program runs from cron regularly to run through a pipeline of behaviors. Data moves through that pipeline; it's basically one big state machine.

The core of the program is a pipeline manager which runs the appropriate processing stages in order, such that on every invocation, the program moves data through at least one stage and potentially every stage. It doesn't have to move everything through the pipeline all in one invocation, but it does have to make progress on every invocation.

For various uninteresting optimization and locking reasons, I made this program a single execution unit. (I do use asynchronous IO for things like network access, but that's because the program is largely IO bound.) The program also has copious logging of the stage traversal, split between one log which tracks stage transitions and timings and stage-specific log files which have more details on the progress of those stages.

Until a few minutes ago, the easiest way to see the program's current stage was to tail the top-level log file. While running some live tests on a new feature, I found myself with free time and the desire not to switch back and forth to a tail -f screen again, so I checked the documentation for $0 again.

I knew that on certain platforms (GNU/Linux, which makes my life easier) you can actually write to it. If you do this, you can control what appears in the output of ps and top .

Every stage runs from a closure (shades of Plack):

my $sub = sub { my ($self, $config) = @_; my $log = $self->get_fh_for_step( $config, lc $app ); # show app stage in ps output local $0 = $app; my $app = $module->new( logger => $log, map { $_ => $config->{General}{$_} } @keys, ); $app->run; $log->log( sprintf $message, $app->count ) if $app->count; };

A loop in the pipeline manager creates a new closure over the name of the module which implements the stage to create a new object for the stage, set up the logger, provide the appropriate configuration, and run the stage. The emboldened code shows the change I made.

Right now, my top window shows that the image processing stage has just given way to the report writing stage—and now the program has exited. In a couple of minutes, everything will start again.

Writing this entry took longer than implementing this feature. Five minutes of experimenting has improved the visibility and monitoring of this program immensely. Maybe it'll help you.