File Coverage

blib/lib/Mojolicious/Plugin/Logf.pm
Criterion Covered Total %
statement 32 32 100.0
branch 16 16 100.0
condition 3 4 75.0
subroutine 9 9 100.0
pod 3 3 100.0
total 63 64 98.4


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::Logf;
2 4     4   44672 use Mojo::Base 'Mojolicious::Plugin';
  4         7  
  4         53  
3 4     4   2698 use Data::Dumper ();
  4         14430  
  4         81  
4 4     4   22 use overload ();
  4         4  
  4         118  
5 4   100 4   14 use constant UNDEF => $ENV{MOJO_LOGF_UNDEF} || '__UNDEF__';
  4         5  
  4         1678  
6              
7             our $VERSION = '0.09';
8              
9             sub logf {
10 5     5 1 14 my ($self, $c, $level, $format, @args) = @_;
11 5         15 my $log = $c->app->log;
12 5 100       41 $log->$level(@args ? sprintf $format, $self->flatten(@args) : $format)
    100          
13             if $log->is_level($level);
14 5         262 $c;
15             }
16              
17             sub flatten {
18 6     6 1 412 my $self = shift;
19 6 100       11 my @args = map { ref $_ eq 'CODE' ? $_->() : $_ } @_;
  11         38  
20              
21 6         228 local $Data::Dumper::Indent = 0;
22 6   50     32 local $Data::Dumper::Maxdepth = $Data::Dumper::Maxdepth || 2;
23 6         9 local $Data::Dumper::Sortkeys = 1;
24 6         9 local $Data::Dumper::Terse = 1;
25              
26 6         13 for (@args) {
27 11 100       1224 $_
    100          
    100          
28             = !defined($_) ? UNDEF
29             : overload::Method($_, q("")) ? "$_"
30             : ref($_) ? Data::Dumper::Dumper($_)
31             : $_;
32             }
33              
34 6         1811 return @args;
35             }
36              
37             sub register {
38 2     2 1 57 my ($self, $app, $config) = @_;
39              
40 2 100   6   18 $app->helper(logf => sub { @_ == 1 ? $self : logf($self, @_) });
  6         40669  
41 2 100       60 $app->log->format(\&_rfc3339) if $config->{rfc3339};
42             }
43              
44             sub _rfc3339 {
45 1     1   56 my ($s, $m, $h, $day, $month, $year) = gmtime(shift);
46 1         12 sprintf '[%04d-%02d-%02dT%02d:%02d:%02dZ] [%s] %s', $year + 1900, $month + 1, $day, $h,
47             $m, $s, shift(@_), join "\n", @_, '';
48             }
49              
50             1;
51              
52             =encoding utf8
53              
54             =head1 NAME
55              
56             Mojolicious::Plugin::Logf - Plugin for logging datastructures using sprintf
57              
58             =head1 VERSION
59              
60             0.09
61              
62             =head1 DESCRIPTION
63              
64             L is a plugin which will log complex datastructures
65             and avoid "unitialized" warnings. This plugin use L or whatever
66             L is set to, to do the actual logging.
67              
68             =head1 SYNOPSIS
69              
70             use Mojolicious::Lite;
71             plugin logf => {rfc3339 => 1};
72              
73             get "/" => sub {
74             my $c = shift;
75             $c->logf(info => 'request: %s', $self->req->params->to_hash);
76             $c->render(text => "cool!");
77             };
78              
79             Setting C to "1" will make the log look like this:
80              
81             [2016-02-19T13:05:37Z] [info] Some log message
82              
83             =head1 COPY/PASTE CODE
84              
85             If you think it's a waste to depend on this module, you can copy paste the
86             code below to get the same functionality as the L helper:
87              
88             helper logf => sub {
89             my ($c, $level, $format) = (shift, shift, shift);
90             my $log = $c->app->log;
91             return $c unless $log->is_level($level);
92             my @args = map { ref $_ eq 'CODE' ? $_->() : $_ } @_;
93             local $Data::Dumper::Indent = 0;
94             local $Data::Dumper::Maxdepth = $Data::Dumper::Maxdepth || 2;
95             local $Data::Dumper::Sortkeys = 1;
96             local $Data::Dumper::Terse = 1;
97             for (@args) {
98             $_
99             = !defined($_) ? "__UNDEF__"
100             : overload::Method($_, q("")) ? "$_"
101             : ref($_) ? Data::Dumper::Dumper($_)
102             : $_;
103             }
104             $log->$level(sprintf $format, @args);
105             return $c;
106             };
107              
108             Note: The code above is generated and tested from the original source code,
109             but it will more difficult to get updates and bug fixes.
110              
111             =head1 HELPERS
112              
113             =head2 logf
114              
115             $self = $c->logf;
116             $c = $c->logf($level => $format, @args);
117              
118             Logs a string formatted by the usual C conventions of the C library
119             function C. C<$level> need to be a valid L level.
120             C<@args> will be converted using L.
121              
122             Calling this method without any arguments will return C<$self>
123             (an instance of this plugin), allowing you to call L:
124              
125             @args_as_strings = $c->logf->flatten(@args);
126              
127             =head1 METHODS
128              
129             =head2 flatten
130              
131             @args_as_strings = $self->flatten(@args);
132              
133             Used to convert input C<@args> using these rules:
134              
135             =over 4
136              
137             =item * Scalar
138              
139             No rule applied.
140              
141             =item * Code ref
142              
143             A code ref will be called, and the list of return values will be flattened.
144             The code below will not calculate the request params, unless the log level
145             is "debug":
146              
147             $c->logf(debug => 'request: %s', sub {$c->req->params->to_hash});
148              
149             =item * Object with string overloading
150              
151             Will be coverted to a string using the string overloading function.
152              
153             =item * Data structure or object
154              
155             Will be serialized using L with these settings:
156              
157             $Data::Dumper::Indent = 0;
158             $Data::Dumper::Maxdepth = $Data::Dumper::Maxdepth || 2;
159             $Data::Dumper::Sortkeys = 1;
160             $Data::Dumper::Terse = 1;
161              
162             NOTE! These settings might change, but will always do its best to
163             serialize the object into one line. C<$Data::Dumper::Maxdepth> is
164             used to avoid dumping large nested objects. Set this variable
165             if you need deeper logging. Example:
166              
167             local $Data::Dumper::Maxdepth = 1000;
168             $c->logf(info => 'Deep structure: %s', $some_object);
169              
170             =item * Undefined value
171              
172             Will be logged as "__UNDEF__". This value can be changed by setting
173             the global environment variable C before loading this
174             plugin.
175              
176             =back
177              
178             =head2 register
179              
180             Will register the L helper in the application
181              
182             =head1 COPYRIGHT AND LICENSE
183              
184             Copyright (C) 2014, Jan Henning Thorsen
185              
186             This program is free software, you can redistribute it and/or modify it under
187             the terms of the Artistic License version 2.0.
188              
189             =head1 AUTHOR
190              
191             Jan Henning Thorsen - C
192              
193             =cut