File Coverage

blib/lib/Plack/Middleware/AccessLog/Structured.pm
Criterion Covered Total %
statement 55 56 98.2
branch 12 12 100.0
condition 9 14 64.2
subroutine 13 13 100.0
pod 2 2 100.0
total 91 97 93.8


line stmt bran cond sub pod time code
1             package Plack::Middleware::AccessLog::Structured;
2             $Plack::Middleware::AccessLog::Structured::VERSION = '0.002000';
3 1     1   137412 use parent qw(Plack::Middleware);
  1         11  
  1         6  
4              
5             # ABSTRACT: Access log middleware which creates structured log messages
6              
7 1     1   13808 use strict;
  1         3  
  1         20  
8 1     1   6 use warnings;
  1         3  
  1         23  
9              
10 1     1   5 use Carp;
  1         2  
  1         52  
11 1     1   6 use MRO::Compat;
  1         3  
  1         25  
12 1     1   487 use Time::Moment;
  1         1484  
  1         43  
13 1     1   7 use Plack::Util::Accessor qw(logger callback extra_field);
  1         4  
  1         6  
14 1     1   639 use Net::Domain qw(hostname hostfqdn);
  1         9138  
  1         69  
15 1     1   709 use JSON;
  1         9117  
  1         6  
16              
17              
18             sub new {
19 7     7 1 35289 my ($class, $arg_ref) = @_;
20              
21 7         46 my $self = $class->next::method($arg_ref);
22 7 100 100     246 if (defined $self->callback() && ref $self->callback() ne 'CODE') {
23 1         36 croak("Passed 'callback' parameter must be a code reference");
24             }
25 6 100 100     112 if (defined $self->extra_field() && ref $self->extra_field() ne 'HASH') {
26 1         22 croak("Passed 'extra_field' parameter must be a hash reference");
27             }
28              
29 5         45 return $self;
30             }
31              
32              
33              
34             sub call {
35 5     5 1 26815 my ($self, $env) = @_;
36              
37 5         49 my $t_before = Time::Moment->now_utc();
38 5         25 my $res = $self->app->($env);
39              
40             return $self->response_cb($res, sub {
41 5     5   1000847 my ($cb_res) = @_;
42              
43 5         38 my $t_after = Time::Moment->now_utc();
44 5         28 my $h = Plack::Util::headers($cb_res->[1]);
45 5         230 my $content_type = $h->get('Content-Type');
46             my $log_entry = {
47             class => ref($self),
48             # Request data
49             remote_addr => $env->{REMOTE_ADDR},
50             request_method => _safe($env->{REQUEST_METHOD}),
51             request_uri => _safe($env->{REQUEST_URI}),
52             server_protocol => $env->{SERVER_PROTOCOL},
53             http_user_agent => _safe($env->{HTTP_USER_AGENT}),
54             http_host => $env->{HTTP_HOST} || $env->{SERVER_NAME},
55             http_referer => $env->{HTTP_REFERER},
56             remote_user => $env->{REMOTE_USER},
57             # Server information
58 5 100 33     192 pid => $$,
      33        
59             hostfqdn => hostfqdn(),
60             hostname => hostname(),
61             # Response data
62             response_status => $cb_res->[0],
63             content_length => Plack::Util::content_length($cb_res->[2]) || $h->get('Content-Length'),
64             content_type => defined $content_type ? "$content_type" : undef,
65             # Timing
66             request_duration => ( $t_before->delta_microseconds($t_after) / 1000 ),
67             date => $t_before->strftime('%FT%T%3fZ'),
68             epochtime => $t_before->strftime('%s.%3N'),
69             };
70              
71 5 100       8453 if ($self->extra_field()) {
72 1         8 for my $env_field (keys %{$self->extra_field}) {
  1         4  
73             $log_entry->{$self->extra_field()->{$env_field}}
74 1         30 = $env->{$env_field};
75             }
76             }
77              
78 5 100       48 if ($self->callback()) {
79 1         9 $log_entry = $self->callback()->($env, $log_entry);
80             }
81              
82 5   50     44 my $logger = $self->logger() || sub { $env->{'psgi.errors'}->print($_[0] . "\n") };
83 5         143 $logger->(encode_json($log_entry));
84              
85 5         119 return;
86 5         105 });
87             }
88              
89              
90             # Taken from Plack::Middleware::AccessLog
91             sub _safe {
92 15     15   48 my ($string) = @_;
93 15 100       51 $string =~ s/([^[:print:]])/"\\x" . unpack("H*", $1)/xeg
  0         0  
94             if defined $string;
95 15         129 return $string;
96             }
97              
98              
99              
100             1;
101              
102             __END__