Log::H...amples(3pm)
๐ Log::Handler::Examples ๐
okay bestie, gather round ๐ซถ this is the User Contributed Perl Documentation for Log::Handler::Examples โ the 3pm one, iykyk. It's not deep lore, it's literally just examples. Every single one, no skips, no cuts. Let's go ๐
๐ CREATE LOGGER ๐
Quite simple โจ
The bare minimum, the little starter pack:
use Log::Handler;
my $log = Log::Handler->new();
$log->add( screen => \%options );
Application wide logger ๐
Bestie, make ONE logger for your whole app like this:
my $log = Log::Handler->create_logger("myapp");
$log->add( screen => \%options );
And once you've created it, you can pull that same application logger into every module of your project. Iconic behaviour, truly:
package MyApp;
use Log::Handler;
my $log = Log::Handler->create_logger("myapp");
$log->add( screen => \%options );
package MyApp::Admin;
use Log::Handler;
my $log = Log::Handler->get_logger("myapp");
$log->info("message");
๐ซ ADD OUTPUTS ๐ซ
So you can just stack outputs like it's nothing:
use Log::Handler;
my $log = Log::Handler->new();
$log->add( dbi => \%options );
$log->add( email => \%options );
$log->add( file => \%options );
$log->add( forward => \%options );
$log->add( screen => \%options );
$log->add( socket => \%options );
And fr fr, this is the exact same thing as writing out the full package names:
$log->add( "Log::Handler::Output::DBI" => \%options );
$log->add( "Log::Handler::Output::Email" => \%options );
$log->add( "Log::Handler::Output::File" => \%options );
$log->add( "Log::Handler::Output::Forward" => \%options );
$log->add( "Log::Handler::Output::Screen" => \%options );
$log->add( "Log::Handler::Output::Socket" => \%options );
๐ RELOAD THE LOGGER ๐
Quite simple
use Log::Handler;
my $log = Log::Handler->new();
$log->config(config => "logger.conf");
$log->reload(config => "logger.conf");
Reload on HUP ๐จ
Catch that HUP signal and reload the config โ main character energy:
use Log::Handler;
my $log = Log::Handler->new();
$log->config(config => "logger.conf");
$SIG{HUP} = sub {
unless ($log->reload(config => "logger.conf")) {
warn "unable to reload configuration";
warn $log->errstr;
}
};
Validate first โ
And bestie, you can do a whole configuration check before you reload. So valid. So safe:
$log->validate(config => "logger.conf")
or warn $log->errstr;
๐๏ธ LOG VIA DBI ๐๏ธ
use Log::Handler;
my $log = Log::Handler->new();
$log->add(
dbi => {
database => "database",
driver => "mysql",
host => "127.0.0.1",
port => 3306,
user => "user",
password => "password",
table => "messages",
columns => [ qw/level ctime cdate pid hostname caller progname mtime message/ ],
values => [ qw/%level %time %date %pid %hostname %caller %progname %mtime %message/ ],
maxlevel => "error",
minlevel => "emergency"
newline => 0,
message_pattern => "%L %T %D %P %H %C %S %t %m",
}
);
$log->error("log an error");
Or you can do it with dbname instead:
$log->add(
dbi => {
dbname => "database",
driver => "Pg",
host => "127.0.0.1",
port => 5432,
user => "user",
password => "password",
table => "messages",
columns => [ qw/level ctime cdate pid hostname caller progname mtime message/ ],
values => [ qw/%level %time %date %pid %hostname %caller %progname %mtime %message/ ],
maxlevel => "error",
minlevel => "emergency"
newline => 0,
message_pattern => "%L %T %D %P %H %C %S %t %m",
}
);
Orrrrr with data_source ๐ซถ:
$log->add(
dbi => {
data_source => "dbi:SQLite:dbname=database.sqlite",
table => "messages",
columns => [ qw/level ctime cdate pid hostname caller progname mtime message/ ],
values => [ qw/%level %time %date %pid %hostname %caller %progname %mtime %message/ ],
maxlevel => "error",
minlevel => "emergency"
newline => 0,
message_pattern => "%L %T %D %P %H %C %S %t %m",
}
);
๐ง LOG VIA EMAIL ๐ง
Sliding straight into someone's inbox:
use Log::Handler;
my $log = Log::Handler->new();
$log->add(
email => {
host => "mx.bar.example",
hello => "EHLO my.domain.example",
timeout => 30,
from => "bar@foo.example",
to => "foo@bar.example",
subject => "your subject",
buffer => 0,
maxlevel => "emergency",
minlevel => "emergency",
message_pattern => '%L',
}
);
$log->emergency("log an emergency issue");
๐ฎ LOG VIA SENDMAIL ๐ฎ
Same vibe, but through sendmail:
use Log::Handler;
my $log = Log::Handler->new();
$log->add(
sendmail => {
from => "bar@foo.example",
to => "foo@bar.example",
subject => "your subject",
maxlevel => "error",
minlevel => "error",
message_pattern => '%L',
}
);
$log->emergency("message");
๐ LOG VIA FILE ๐
Writing it all down, journaling era:
use Log::Handler;
my $log = Log::Handler->new();
$log->add(
file => {
filename => "file1.log",
maxlevel => 7,
minlevel => 0
}
);
$log->error("log an error");
๐ค LOG VIA FORWARD ๐ค
Send it off to your own little callback, no cap:
use Log::Handler;
my $log = Log::Handler->new();
$log->add(
forward => {
forward_to => \&my_func,
message_pattern => [ qw/%L %T %P %H %C %S %t/ ],
message_layout => "%m",
maxlevel => "info",
}
);
$log->info("log a information");
sub my_func {
my $params = shift;
print Dumper($params);
}
๐ฅ๏ธ LOG VIA SCREEN ๐ฅ๏ธ
Straight to the terminal, right in front of you:
use Log::Handler;
my $log = Log::Handler->new();
$log->add(
screen => {
log_to => "STDERR",
maxlevel => "info",
}
);
$log->info("log to the screen");
๐ LOG VIA SOCKET ๐
Sending messages over the wire, periodt:
use Log::Handler;
my $log = Log::Handler->new();
$log->add(
socket => {
peeraddr => "127.0.0.1",
peerport => 44444,
maxlevel => "info",
die_on_errors => 0,
}
);
while ( 1 ) {
$log->info("test")
or warn "unable to send message: ", $log->errstr;
sleep 1;
}
๐ง SIMPLE SOCKET SERVER (TCP) ๐ง
And here's the other side of that conversation โ a whole lil server bestie:
use strict;
use warnings;
use IO::Socket::INET;
use Log::Handler::Output::File;
my $sock = IO::Socket::INET->new(
LocalAddr => "127.0.0.1",
LocalPort => 44444,
Listen => 2,
) or die $!;
my $file = Log::Handler::Output::File->new(
filename => "file.log",
fileopen => 1,
reopen => 1,
);
while ( 1 ) {
$file->log(message => "waiting for next connection\n");
while (my $request = $sock->accept) {
my $ipaddr = sprintf("%-15s", $request->peerhost);
while (my $message = <$request>) {
$file->log(message => "$ipaddr - $message");
}
}
}
๐ DIFFERENT OUTPUTS ๐
Okay so this is where it gets good โ different outputs catching different things at different levels:
use Log::Handler;
my $log = Log::Handler->new();
$log->add(
file => {
filename => "common.log",
maxlevel => 6,
minlevel => 5,
}
);
$log->add(
file => {
filename => "error.log",
maxlevel => 4,
minlevel => 0,
}
);
$log->add(
email => {
host => "mx.bar.example",
hello => "EHLO my.domain.example",
timeout => 120,
from => "bar@foo.example",
to => "foo@bar.example",
subject => "your subject",
buffer => 0,
maxlevel => 0,
}
);
# log to common.log
$log->info("this is a info message");
# log to error.log
$log->warning("this is a warning");
# log to error.log and to foo@bar.example
$log->emergency("this is a emergency message");
๐ฏ FILTER MESSAGES ๐ฏ
Filtering by content, using match1, match2, match3 and a condition:
my $log = Log::Handler->new();
$log->add(
screen => {
maxlevel => 6,
filter_message => {
match1 => "foo",
match2 => "bar",
match3 => "baz",
condition => "(match1 && match2) && !match3"
}
}
);
$log->info("foo");
$log->info("foo bar");
$log->info("foo baz");
๐ต๏ธ FILTER CALLER ๐ต๏ธ
This one shows you how it's possible to debug messages only from a special namespace. So specific. So demure:
my $log = Log::Handler->new();
$log->add(
file => {
filename => "file1.log",
maxlevel => "warning",
}
);
$log->add(
screen => {
maxlevel => "debug",
message_layout => "message from %p - %m",
filter_caller => qr/^Foo::Bar\z/,
}
);
$log->warning("a warning here");
package Foo::Bar;
$log->info("an info here");
1;
๐ฆ ANOTHER FILTER ๐ฆ
And here's filter_message / filter_caller in all their flavours โ string, regexp, code ref, or a whole condition situation:
filter_message => "as string"
filter_message => qr/as regexp/
filter_message => sub { shift->{message} =~ /as code ref/ }
# or with conditions
filter_message => {
match1 => "as string",
match2 => qr/as regexp/,
condition => "match1 || match2",
}
filter_caller => "as string"
filter_caller => qr/as regexp/
๐ CONFIG ๐
okay so you can ALSO hand this whole thing a config file and let it do the work for you, which is giving main character energy honestly. three ways, pick your fighter:
my $log = Log::Handler->new( config => "logger.conf" );
# or
$log->add( config => "logger.conf" );
# or
$log->config( config => "logger.conf" );
like, new, or add, or config โ all of them eat a config key and all of them come out the same on the other side. ๐
๐ธ Example with Config::General ๐ธ
Config::General is the vibe here, bestie. so first up, the script:
use Log::Handler;
my $log = Log::Handler->new();
$log->config( config => "logger.conf" );
and then the config file itself, logger.conf:
<file>
alias = common
filename = example.log
maxlevel = info
minlevel = warn
</file>
<file>
alias = error
filename = example-error.log
maxlevel = warn
minlevel = emergency
</file>
<file>
alias = debug
filename = example-debug.log
maxlevel = debug
minlevel = debug
</file>
<screen>
log_to = STDERR
dump = 1
maxlevel = debug
minlevel = debug
</screen>
look at that. four outputs, each one with its own alias, its own filename (or log_to for the screen one), its own maxlevel and its own minlevel. no cap, it's literally just the option names you already know, written down in a file instead of typed into Perl. iconic. โจ
๐ฅ CHECK FOR ACTIVE LEVELS ๐ฅ
okay this one is so useful and it's lowkey underrated. sometimes you wanna know if a level is even on before you do a bunch of expensive work.
use Log::Handler;
use Data::Dumper;
my $log = Log::Handler->new();
$log->add(
file => {
filename => "file1.log",
maxlevel => 4,
}
);
my %hash = (foo => 1, bar => 2);
so we got a file output chugging along with maxlevel set to 4. and we got a hash, and bestie, you know how it is โ you wanna dump that hash, but not in any case. not always. not for free.
if ( $log->is_debug ) {
my $dump = Dumper(\%hash);
$log->debug($dump);
}
the way that $log->is_debug justโฆ checks it for you ๐ซถ. this would dump the hash only if the level debug is active. no wasted Dumper calls, no wasted cycles, no sadness. periodt.
๐ AUTHOR ๐
Jonny Schulz <jschulz.cpan(at)bloonix.de>. king behavior, honestly. ๐
and that's the whole page, bestie!! this was Log::H...amples(3pm) from perl v5.36.0, dated 2022-08-28. go forth and log responsibly (or don't, I'm a manpage not a cop). mwah ๐๐ธโจ
๐ฆ where this page comes from
- ubuntu noble: liblog-handler-perl 0.90-2 ยท Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: liblog-handler-perl 0.90-2 ยท Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: liblog-handler-perl 0.90-2 ยท Artistic-1.0-Perl OR GPL-1.0-or-later
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms