summaryrefslogtreecommitdiffstats
path: root/Log/Log4perl/Layout
diff options
context:
space:
mode:
Diffstat (limited to 'Log/Log4perl/Layout')
-rw-r--r--Log/Log4perl/Layout/Mozilla.pm124
1 files changed, 124 insertions, 0 deletions
diff --git a/Log/Log4perl/Layout/Mozilla.pm b/Log/Log4perl/Layout/Mozilla.pm
new file mode 100644
index 000000000..67a070c54
--- /dev/null
+++ b/Log/Log4perl/Layout/Mozilla.pm
@@ -0,0 +1,124 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Log::Log4perl::Layout::Mozilla;
+use 5.10.1;
+use Moo;
+use Sys::Hostname;
+use JSON::MaybeXS ();
+use English qw(-no_match_vars $PID);
+
+use constant LOGGING_FORMAT_VERSION => 2.0;
+
+extends 'Log::Log4perl::Layout';
+
+has 'name' => (
+ is => 'ro',
+ default => 'Bugzilla',
+);
+
+has 'max_json_length' => (
+ is => 'ro',
+ isa => sub { die "must be at least 1024\n" if $_[0] < 1024 },
+ default => 4096,
+);
+
+sub BUILDARGS {
+ my ($class, $params) = @_;
+
+ delete $params->{value};
+ foreach my $key (keys %$params) {
+ if (ref $params->{$key} eq 'HASH') {
+ $params->{$key} = $params->{$key}{value};
+ }
+ }
+ return $params;
+}
+
+sub render {
+ my ( $self, $msg, $category, $priority, $caller_level ) = @_;
+
+ state $HOSTNAME = hostname();
+ state $JSON = JSON::MaybeXS->new(
+ indent => 0, # to prevent newlines (and save space)
+ ascii => 1, # to avoid encoding issues downstream
+ allow_unknown => 1, # encode null on bad value (instead of exception)
+ convert_blessed => 1, # call TO_JSON on blessed ref, if it exists
+ allow_blessed => 1, # encode null on blessed ref that can't be converted
+ );
+
+ my $mdc = Log::Log4perl::MDC->get_context;
+ my %out = (
+ EnvVersion => LOGGING_FORMAT_VERSION,
+ Hostname => $HOSTNAME,
+ Logger => $self->name,
+ Pid => $PID,
+ Severity => $Log::Log4perl::Level::SYSLOG{$priority},
+ Timestamp => time() * 1e9,
+ Type => $category,
+ Fields => { msg => $msg, %$mdc },
+ );
+
+ my $json_text = $JSON->encode(\%out) . "\n";
+ if (length($json_text) > $self->max_json_length) {
+ my $scary_msg = sprintf( "DANGER! LOG MESSAGE TOO BIG %d > %d", length($json_text), $self->max_json_length );
+ $out{Fields} = { remote_ip => $mdc->{remote_ip}, msg => $scary_msg };
+ $out{Severity} = 1; # alert
+ $json_text = $JSON->encode(\%out) . "\n";
+ }
+
+ return $json_text;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Log::Log4perl::Layout::Mozilla - Implement the mozilla-services json log format
+
+=head1 SYNOPSIS
+
+Example configuration:
+
+ log4perl.appender.Example.layout = Log::Log4perl::Layout::Mozilla
+ log4perl.appender.Example.layout.max_json_length = 16384
+ log4perl.appender.Example.layout.name = Bugzilla
+
+=head1 DESCRIPTION
+
+This class implements a C<Log::Log4perl> layout format
+that implements the recommend json format using in Mozilla services.
+L<https://wiki.mozilla.org/Firefox/Services/Logging#MozLog_JSON_schema>.
+
+The JSON hash is ASCII encoded, with no newlines or other whitespace, and is
+suitable for output, via Log::Log4perl appenders, to files and syslog etc.
+
+Contextual data in the L<Log::Log4perl::MDC> hash will be put into the Fields
+hash.
+
+=head1 LAYOUT CONFIGURATION
+
+=head2 name
+
+Data source, server that is doing the logging, e.g. "Sync-1_5".
+
+Use the server's name, and avoid implementation details. "FxaAuthWebserver", not "NginxLogs".
+
+=head2 max_json_length
+
+Set the maximum JSON length in bytes. The default is 4096,
+and it cannot be smaller than 1024.
+
+ log4perl.appender.Example.layout.max_json_length = 16384
+
+This is useful where some downstream system has a limit on the maximum size of
+a message.
+
+If the message is larger than this limit, the message will be replaced
+with a scary message at a severity level of ALERT. \ No newline at end of file