File Coverage

blib/lib/Mojolicious/Plugin/ChromeLogger.pm
Criterion Covered Total %
statement 56 58 96.5
branch 5 10 50.0
condition 3 6 50.0
subroutine 8 8 100.0
pod 1 1 100.0
total 73 83 87.9


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::ChromeLogger;
2              
3 1     1   1043 use Mojo::Base 'Mojolicious::Plugin';
  1         2  
  1         6  
4 1     1   180 use Mojo::ByteStream qw/b/;
  1         2  
  1         55  
5 1     1   12 use Mojo::JSON qw/encode_json/;
  1         2  
  1         771  
6              
7             our $VERSION = 0.06;
8              
9             has logs => sub { return [] };
10              
11             my %types_map = (
12             'debug' => '',
13             'info' => 'info',
14             'warn' => 'warn',
15             'error' => 'error',
16             'fatal' => 'error',
17             );
18              
19             sub register {
20 1     1 1 50 my ( $self, $app, $opts ) = @_;
21              
22 1   50     8 $opts->{show_session} //= 1;
23 1   50     5 $opts->{show_stash} //= 1;
24 1   50     5 $opts->{show_config} //= 0;
25              
26             # We do use monkey patch instead of inheriting Mojo::Log to be compatible with Log::Any::Adapter::Mojo
27 1         3 $self->_monkey_patch_logger();
28              
29             $app->hook(
30             after_dispatch => sub {
31 1     1   82 my ($c) = @_;
32 1         27 my $logs = $self->logs;
33              
34             # Leave static content untouched
35 1 50       11 return if $c->stash('mojo.static');
36              
37             # Do not allow if not development mode
38 1 50       40 return if $c->app->mode ne 'development';
39              
40 1         55 my $data = {
41             version => $VERSION,
42             columns => [ 'log', 'backtrace', 'type' ],
43             rows => []
44             };
45              
46 1         3 my $rows = $data->{rows};
47              
48             # Start main group
49 1         7 my $main_group = 'Mojolicious: ' . $c->req->method . ' ' . $c->req->url->path->to_string;
50 1         301 push @$rows, [[ $main_group ], undef, 'groupCollapsed'];
51              
52             # Add session
53 1 50       7 if ( $opts->{show_session} ) {
54 1         5 push @$rows, [[ { '___class_name' => 'Session', %{$c->session} }], undef, ''];
  1         10  
55             }
56              
57             # Add config
58 1 50       418 if ( $opts->{show_config} ) {
59 0         0 push @$rows, [[ { '___class_name' => 'Config', %{$c->config} }], undef, ''];
  0         0  
60             }
61              
62             # Add stash
63 1 50       6 if ( $opts->{show_stash} ) {
64 1         2 my %clean_stash = map { $_ => $c->stash($_) } grep { $_ !~ /^(?:mojo\.|config$)/ } keys %{ $c->stash };
  1         3  
  8         25  
  1         4  
65 1         15 push @$rows, [[ { '___class_name' => 'Stash', %clean_stash }], undef, ''];
66             }
67              
68             # Logs: fatal, info, debug, error
69 1         4 push @$rows, [[ 'logs' ], undef, 'group'];
70              
71 1         2 foreach my $msg (@$logs) {
72 9         25 push @$rows, [ $msg->[1], $msg->[2], $types_map{ $msg->[0] } ];
73             }
74              
75 1         3 push @$rows, [[ 'logs' ], undef, 'groupEnd'];
76              
77             # End main group
78 1         4 push @$rows, [[ $main_group ], undef, 'groupEnd'];
79              
80 1         6 my $json = encode_json($data);
81 1         850 my $final_data = b($json)->b64_encode('');
82 1         72 $c->res->headers->add( 'X-ChromeLogger-Data' => $final_data );
83              
84 1         125 $self->logs( [] );
85             }
86 1         14 );
87             }
88              
89             sub _monkey_patch_logger {
90 1     1   2 my ($self) = @_;
91              
92 1     1   5 no strict 'refs';
  1         2  
  1         226  
93 1         1 my $stash = \%{"Mojo::Log::"};
  1         5  
94              
95 1         3 foreach my $level (qw/debug info warn error fatal/) {
96 5         19 my $orig = delete $stash->{$level};
97              
98 5         22 *{"Mojo::Log::$level"} = sub {
99 9     9   36724 my ($package, $filename, $line) = caller;
100 9         12 push @{ $self->logs }, [ $level, [ $_[-1] ], "at $filename:$line" ];
  9         230  
101 9         85 $orig->(@_);
102 5         34 };
103             }
104             }
105              
106             1;
107              
108             =head1 NAME
109              
110             Mojolicious::Plugin::ChromeLogger - Pushes Mojolicious logs, stash, session, config to Google Chrome console
111              
112             =head1 DESCRIPTION
113              
114             L pushes Mojolicious log messages, stash, session and config to Google Chrome console. Works with all types of responses(including JSON).
115             To view logs in Google Chrome you should install ChromeLogger extenstion. Logging works only in development mode.
116              
117             See details here http://craig.is/writing/chrome-logger
118              
119             =head1 USAGE
120              
121             use Mojolicious::Lite;
122              
123             plugin 'ChromeLogger';
124             # or with options - plugin 'ChromeLogger' => {show_config => 1};
125              
126             get '/' => sub {
127             my $self = shift;
128              
129             app->log->debug('Some debug here');
130             app->log->info('Some info here');
131             app->log->warn('Some warn here');
132             app->log->error('Some error here');
133             app->log->fatal('Some fatal here');
134              
135             $self->render( text => 'Open Google Chrome console' );
136             };
137              
138             app->start;
139              
140             =head1 CONFIG
141              
142             =head2 C
143              
144             push config to ChromeLogger (default 0)
145              
146             By default we do not show config. It is usually static and can contain confidential data.
147              
148             =head2 C
149              
150             push stash to ChromeLogger (default 1)
151              
152             =head2 C
153              
154             push session to ChromeLogger (default 1)
155              
156             =head1 SEE ALSO
157              
158             L
159              
160             =head1 DEVELOPMENT
161              
162             L
163              
164             =head1 CREDITS
165              
166             Inspired by L
167              
168             =head1 AUTHORS
169              
170             Viktor Turskyi koorchik@cpan.org
171              
172             =cut