Log::H...amples(3pm)

๐Ÿ“ฆ
version 0.90 ยท 5 versions
, in ubuntu noble, ubuntu resolute, ubuntu stonking

๐Ÿ’– 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