package FU::Log 0.1; use v5.36; use Exporter 'import'; use POSIX 'strftime'; our @EXPORT_OK = ('log_write'); my $dest = [\*STDERR]; my $capture_warn = 0; my $fmt = \&default_fmt; our $in_log = 0; sub default_fmt($msg, @extra) { my $pre = ''; $msg =~ s/^\s+//; $msg =~ s/\s+$//; if ($msg =~ /\n/) { $msg =~ s/(^|\n)/\n# /g; $msg .= "\n"; $pre = "\n"; } else { $msg = " # $msg"; } sprintf "%s%sZ%s%s\n", $pre, strftime('%Y-%m-%d %H:%M:%S', gmtime), join('', map " $_", @extra), $msg } sub log_write($msg) { local $SIG{__WARN__} = undef if $capture_warn; my $line = (!$in_log && eval { local $in_log = 1; $fmt->($msg) }) || default_fmt($msg); utf8::encode($line); for my $out (@$dest) { if (ref $out eq 'GLOB') { print $out $line; } elsif (open my $F, '>>', $out) { flock $F, 2; seek $F, 0, 2; print $F $line; flock $F, 4; close $F; } } } sub capture_warn($enabled) { $capture_warn = !!$enabled; $SIG{__WARN__} = $enabled ? sub { log_write($_) for @_ } : undef; } sub set_fmt :prototype(&) ($f) { $fmt = $f || \&default_fmt } sub set_file($path) { $dest = !defined $path ? [\*STDERR] : [ $path ne '-' && -t STDERR ? \*STDERR : (), $path eq '-' ? \*STDOUT : $path ]; } 1; __END__ =head1 NAME FU::Log - Extremely Basic Process-Wide Logging Infrastructure =head1 SYNOPSIS use FU::Log 'log_write'; FU::Log::capture_warn(1); FU::Log::set_file('/var/log/mylog.log'); =head1 DESCRIPTION This module doesn't do a whole lot. Its main purpose is to have a centrally-configured logging facility so that modules can log stuff and an application can configure where those logs should end up. There's no log levels or filtering; the I to log question is better answered with separate configuration options per module. There's no OO-style interface either; the entire point of this module is that it only handles process-global logging. This module mainly exists for users of the L framework. =head2 Configuration =over =item FU::Log::set_file($path) Set the path to write logs to. If no path is configured or if C<$path> is C, logs are written to C. If C<$path> is C<->, logs are written to C. When writing to file, logs are still replicated to C if that is a TTY. =item FU::Log::capture_warn($enabled) Whether to capture Perl C messages. =item FU::Log::set_fmt($sub) Subroutine to call to format the log messages. Is given a log message as Unicode string as first argument and should return a formatted Unicode string. The given message may include newlines, it is up to the formatting function to decide how to log that. This function is not called when inside C, the default log format is then used instead. This is to avoid recursion. =back =head2 Exportable function =over =item log_write($msg) Write a message to the log. =back =head1 COPYRIGHT MIT. =head1 AUTHOR Yorhel