#!/usr/bin/perl -w use strict; [...] # use whatever else use threads; use threads::shared; use Thread::Queue; [...] sub logger; our ($logs, $logger); $logs = Thread::Queue->new; $logger = threads->new(\&logger, "/path/to/logfile"); our $DEBUG : shared = 1; # care for signals: $SIG{INT} = $SIG{QUIT} = $SIG{CONT} = $SIG{STOP} = $SIG{TSTP} = $SIG{CHLD} = $SIG{TRAP} = $SIG{USR1} = $SIG{TTIN} = $SIG{TTOU} = $SIG{VTALRM} = $SIG{ALRM} = \&sig_ignore; $SIG{PIPE} = \&sig_pipe; $SIG{BUS} = $SIG{ILL} = $SIG{SEGV} = $SIG{FPE} = 'DEFAULT'; $SIG{HUP} = $SIG{ABRT} = $SIG{TERM} = $SIG{KILL} = \&sig_handler; $SIG{__WARN__} = $SIG{__DIE__} = sub { $logs->enqueue(shift) }; # now install all the other threads you want to run. # everything they have to do in case something goes wrong, # will be to simply warn() or die() - roughly told. # hence the logger finction is quite simple: sub logger { my $logfile = shift; open LOG, ">>$logfile" || die $!; while (my $msg = $logs->dequeue) { my $line = "[".localtime(time)."] $msg"; chomp $line; print LOG "$line\n"; flush LOG if $DEBUG; } close LOG; } [...] sub sig_ignore { return unless @_; $logs->enqueue("Silently ignoring signal ".shift); } sub sig_pipe { return unless @_; my $warn = "Caught signal ".shift().", "; # append more information to the warning message # if necessary $logs->enqueue($warn); } # the sig_handler() function has to carefully wait for all # threads to terminate properly and then simply exit - # which means again that there is only one process # to care for: sub sig_handler { return unless @_; $logs->enqueue(undef); $logger->join; [...] exit 0; }