File Coverage

blib/lib/LWP/ConsoleLogger.pm
Criterion Covered Total %
statement 237 270 87.7
branch 82 104 78.8
condition 34 48 70.8
subroutine 42 48 87.5
pod 2 3 66.6
total 397 473 83.9


line stmt bran cond sub pod time code
1 7     7   29379 use strict;
  7         13  
  7         178  
2 7     7   30 use warnings;
  7         13  
  7         135  
3              
4 7     7   118 use 5.006;
  7         18  
5              
6             our $VERSION = '0.000044';
7             use Data::Printer { end_separator => 1, hash_separator => ' => ' };
8 7     7   2897 use DateTime ();
  7         152423  
  7         50  
9 7     7   8515 use HTML::Restrict ();
  7         3172136  
  7         264  
10 7     7   3453 use HTTP::Body ();
  7         782225  
  7         309  
11 7     7   3201 use HTTP::CookieMonster ();
  7         169929  
  7         165  
12 7     7   2746 use JSON::MaybeXS qw( decode_json );
  7         157794  
  7         213  
13 7     7   2907 use List::AllUtils qw( any apply none );
  7         34637  
  7         383  
14 7     7   3816 use Log::Dispatch ();
  7         65273  
  7         643  
15 7     7   3171 use Moo;
  7         1270918  
  7         268  
16 7     7   54 use MooX::StrictConstructor;
  7         16  
  7         79  
17 7     7   5619 use Parse::MIME qw( parse_mime_type );
  7         55180  
  7         35  
18 7     7   23596 use Ref::Util qw( is_blessed_ref );
  7         7169  
  7         580  
19 7     7   65 use Term::Size::Any ();
  7         96  
  7         468  
20 7     7   2730 use Text::SimpleTable::AutoWidth 0.09 ();
  7         1578  
  7         159  
21 7     7   2750 use Try::Tiny qw( catch try );
  7         39933  
  7         196  
22 7     7   45 use Types::Common::Numeric qw( PositiveInt );
  7         111  
  7         494  
23 7     7   2696 use Types::Standard qw( ArrayRef Bool CodeRef InstanceOf );
  7         78690  
  7         66  
24 7     7   2777 use URI::QueryParam qw();
  7         13  
  7         47  
25 7     7   8888 use XML::Simple qw( XMLin );
  7         4289  
  7         170  
26 7     7   4804  
  7         54255  
  7         56  
27             my $json_regex = qr{vnd.*\+json};
28              
29             my $self = shift;
30             $Text::SimpleTable::AutoWidth::WIDTH_LIMIT = $self->term_width();
31 23     23 0 77958 }
32 23         485  
33             has content_pre_filter => (
34             is => 'rw',
35             isa => CodeRef,
36             );
37              
38             has dump_content => (
39             is => 'rw',
40             isa => Bool,
41             default => 0,
42             );
43              
44             has dump_cookies => (
45             is => 'rw',
46             isa => Bool,
47             default => 0,
48             );
49              
50             has dump_headers => (
51             is => 'rw',
52             isa => Bool,
53             default => 1,
54             );
55              
56             has dump_params => (
57             is => 'rw',
58             isa => Bool,
59             default => 1,
60             );
61              
62             has dump_status => (
63             is => 'rw',
64             isa => Bool,
65             default => 1,
66             );
67              
68             has dump_text => (
69             is => 'rw',
70             isa => Bool,
71             default => 1,
72             );
73              
74             has dump_title => (
75             is => 'rw',
76             isa => Bool,
77             default => 1,
78             );
79              
80             has dump_uri => (
81             is => 'rw',
82             isa => Bool,
83             default => 1,
84             );
85              
86             has headers_to_redact => (
87             is => 'rw',
88             isa => ArrayRef,
89             lazy => 1,
90             builder => '_build_headers_to_redact',
91             );
92              
93             has html_restrict => (
94             is => 'rw',
95             isa => InstanceOf ['HTML::Restrict'],
96             lazy => 1,
97             default => sub { HTML::Restrict->new },
98             );
99              
100             has logger => (
101             is => 'rw',
102             isa => InstanceOf ['Log::Dispatch'],
103             lazy => 1,
104             handles => { _debug => 'debug' },
105             default => sub {
106             return Log::Dispatch->new(
107             outputs => [
108             [ 'Screen', min_level => 'debug', newline => 1, utf8 => 1, ],
109             ],
110             );
111             },
112             );
113              
114             has params_to_redact => (
115             is => 'rw',
116             isa => ArrayRef,
117             lazy => 1,
118             builder => '_build_params_to_redact',
119             );
120              
121             has pretty => (
122             is => 'rw',
123             isa => Bool,
124             default => 1,
125             );
126              
127             has term_width => (
128             is => 'rw',
129             isa => PositiveInt,
130             required => 0,
131             lazy => 1,
132             trigger => \&_term_set,
133             builder => '_build_term_width',
134             );
135              
136             has text_pre_filter => (
137             is => 'rw',
138             isa => CodeRef,
139             );
140              
141             my $self = shift;
142             return $ENV{LWPCL_REDACT_HEADERS}
143             ? [ split m{,}, $ENV{LWPCL_REDACT_HEADERS} ]
144 19     19   228 : [];
145             }
146 19 50       314  
147             my $self = shift;
148             return $ENV{LWPCL_REDACT_PARAMS}
149             ? [ split m{,}, $ENV{LWPCL_REDACT_PARAMS} ]
150             : [];
151 2     2   91 }
152              
153 2 50       48 my $self = shift;
154             my $width = shift;
155             $Text::SimpleTable::AutoWidth::WIDTH_LIMIT = $width;
156             }
157              
158 0     0   0 my $self = shift;
159 0         0 my $req = shift;
160 0         0 my $ua = shift;
161              
162             if ( $self->dump_uri ) {
163             my $uri_without_query = $req->uri->clone;
164 27     27 1 39499 $uri_without_query->query(undef);
165 27         66  
166 27         63 $self->_debug( $req->method . q{ } . $uri_without_query . "\n" );
167             }
168 27 100       923  
169 25         301 if ( $req->method eq 'GET' ) {
170 25         607 $self->_log_params( $req, 'GET' );
171             }
172 25         551 else {
173             $self->_log_params( $req, $_ ) for ( 'GET', $req->method );
174             }
175 27 100       1336513  
176 22         423 $self->_log_headers( 'request (before sending)', $req->headers );
177              
178             # This request might have a body.
179 5         108 return unless $req->content;
180              
181             $self->_log_content($req);
182 27         1778 $self->_log_text($req);
183             return;
184             }
185 27 100       19620  
186             my $self = shift;
187 5         101 my $res = shift;
188 5         1864 my $ua = shift;
189 5         2358  
190             $self->_log_headers( 'request (after sending)', $res->request->headers );
191              
192             if ( $self->dump_status ) {
193 22     22 1 25002 $self->_debug( '==> ' . $res->status_line . "\n" );
194 22         57 }
195 22         44 if ( $self->dump_title && $ua->can('title') && $ua->title ) {
196             $self->_debug( 'Title: ' . $ua->title . "\n" );
197 22         205 }
198              
199 22 100       15352 $self->_log_headers( 'response', $res->headers );
200 20         280 $self->_log_cookies( 'response', $ua->cookie_jar, $res->request->uri );
201              
202 22 100 100     4333 $self->_log_content($res);
      100        
203 1         479 $self->_log_text($res);
204             return;
205             }
206 22         1122  
207 22         18889 my ( $self, $type, $headers ) = @_;
208              
209 22         192 return if !$self->dump_headers;
210 22         25624  
211 22         4592 unless ( $self->pretty ) {
212             $self->_debug( $headers->as_string );
213             return;
214             }
215 71     71   870  
216             my $t = Text::SimpleTable::AutoWidth->new();
217 71 100       1512 $t->captions( [ ucfirst $type . ' Header', 'Value' ] );
218              
219 65 100       1506 foreach my $name ( sort $headers->header_field_names ) {
220 3         33 my $val
221 3         699 = ( any { $name eq $_ } @{ $self->headers_to_redact } )
222             ? '[REDACTED]'
223             : $headers->header($name);
224 62         1395 $t->row( $name, $val );
225 62         7195 }
226              
227 62         253 $self->_draw($t);
228             }
229 174 50   0   3122  
  0         0  
  174         2952  
230             my ( $self, $req, $type ) = @_;
231              
232 174         7364 return if !$self->dump_params;
233              
234             my %params;
235 62         829 my $uri = $req->uri;
236              
237             if ( $type eq 'GET' ) {
238             my @params = $uri->query_param;
239 32     32   192 return unless @params;
240              
241 32 100       804 $params{$_} = [ $uri->query_param($_) ] for @params;
242             }
243 29         341  
244 29         125 elsif ( $req->header('Content-Length') ) {
245             my $content_type = $req->header('Content-Type');
246 29 100       291 my $body = HTTP::Body->new(
    100          
247 25         720 $content_type,
248 25 50       1065 $req->header('Content-Length')
249             );
250 0         0 $body->add( $req->content );
251             %params = %{ $body->param };
252              
253             unless ( keys %params ) {
254 3         241 {
255 3         161 my $t = Text::SimpleTable::AutoWidth->new;
256             $t->captions( [ $type . ' Raw Body' ] );
257             $t->row( $req->content );
258             $self->_draw($t);
259 3         1187 }
260 3         2177  
  3         27  
261             {
262 3 100       77 my $t = Text::SimpleTable::AutoWidth->new;
263             $t->captions( [ $type . ' Parsed Body' ] );
264 1         34 $self->_parse_body( $req->content, $content_type, $t );
265 1         2495 $self->_draw($t);
266 1         6 }
267 1         54 }
268             }
269              
270             my $t = Text::SimpleTable::AutoWidth->new();
271 1         3 $t->captions( [ 'Key', 'Value' ] );
  1         777  
  1         20  
272 1         18 foreach my $name ( sort keys %params ) {
273 1         4 my @values
274 1         18 = ( any { $name eq $_ } @{ $self->params_to_redact } )
275             ? '[REDACTED]'
276             : ref $params{$name} ? @{ $params{$name} }
277             : $params{$name};
278              
279 4         1070 $t->row( $name, $_ ) for sort @values;
280 4         2555 }
281 4         40  
282             $self->_draw( $t, "$type Params:\n" );
283 0     0   0 }
  4         97  
284              
285 0         0 my $self = shift;
286 4 50       77 my $type = shift;
    50          
287             my $jar = shift;
288 4         162 my $uri = shift;
289              
290             return if !$self->dump_cookies || !$jar || !is_blessed_ref($jar);
291 4         62  
292             if ( $jar->isa('HTTP::Cookies') ) {
293             my $monster = HTTP::CookieMonster->new($jar);
294             my @cookies = $monster->all_cookies;
295 22     22   687  
296 22         62 my @methods = (
297 22         42 'key', 'val', 'path', 'domain',
298 22         36 'path_spec', 'secure', 'expires'
299             );
300 22 100 100     531  
      66        
301             foreach my $cookie (@cookies) {
302 18 100       602  
    100          
303 16         472 my $t = Text::SimpleTable::AutoWidth->new;
304 16         11634 $t->captions( [ 'Key', 'Value' ] );
305              
306 16         601 foreach my $method (@methods) {
307             my $val = $cookie->$method;
308             if ($val) {
309             $val = DateTime->from_epoch( epoch => $val )
310             if $method eq 'expires';
311 16         109 $t->row( $method, $val );
312             }
313 0         0 }
314 0         0  
315             $self->_draw( $t, ucfirst $type . " Cookie:\n" );
316 0         0 }
317 0         0 }
318 0 0       0 elsif ( $jar->isa('HTTP::CookieJar') ) {
319 0 0       0 my @cookies = $jar->cookies_for($uri);
320             for my $cookie (@cookies) {
321 0         0  
322             my $t = Text::SimpleTable::AutoWidth->new;
323             $t->captions( [ 'Key', 'Value' ] );
324              
325 0         0 for my $key ( sort keys %{$cookie} ) {
326             my $val = $cookie->{$key};
327             if ( $val && $key =~ m{expires|_time} ) {
328             $val = DateTime->from_epoch( epoch => $val );
329 1         8 }
330 1         74 $t->row( $key, $val );
331             }
332 0         0  
333 0         0 $self->_draw(
334             $t,
335 0         0 sprintf( '%s Cookie (%s)', ucfirst($type), $cookie->{name} )
  0         0  
336 0         0 );
337 0 0 0     0 }
338 0         0 }
339             }
340 0         0  
341             my $self = shift;
342             my $r = shift;
343              
344             my $content
345             = $r->can('decoded_content') ? $r->decoded_content : $r->content;
346 0         0 return unless $content;
347              
348             my $content_type = $r->header('Content-Type');
349             return $content unless $content_type;
350              
351             my ( $type, $subtype ) = apply { lc $_ } parse_mime_type($content_type);
352 48     48   95 if (
353 48         96 ( $type ne 'text' )
354             && ( none { $_ eq $subtype }
355 48 50       182 ( 'javascript', 'html', 'json', 'xml', 'x-www-form-urlencoded', )
356             )
357 48 100       31889 && $subtype !~ m{$json_regex}
358             ) {
359 46         153 $content = $self->_redaction_message($content_type);
360 46 50       1806 }
361             elsif ( $self->content_pre_filter ) {
362 46     138   477 $content = $self->content_pre_filter->( $content, $content_type );
  138         1818  
363 46 100 100     1061 }
    100 66        
364              
365 48     48   458 return $content;
366             }
367              
368             my $self = shift;
369             my $r = shift;
370 2         8  
371             return unless $self->dump_content;
372              
373 2         36 my $content = $self->_get_content($r);
374              
375             return unless $content;
376 46         1493  
377             unless ( $self->pretty ) {
378             $self->_debug("Content\n\n$content\n\n");
379             return;
380 27     27   55 }
381 27         47  
382             my $t = Text::SimpleTable::AutoWidth->new();
383 27 100       543 $t->captions( ['Content'] );
384              
385 24         301 $t->row($content);
386             $self->_draw($t);
387 24 100       70 }
388              
389 23 100       440 my $self = shift;
390 1         22 my $r = shift; # HTTP::Request or HTTP::Response
391 1         140  
392             return unless $self->dump_text;
393             my $content = $self->_get_content($r);
394 22         474 return unless $content;
395 22         434  
396             my $content_type = $r->header('Content-Type');
397 22         90  
398 22         227 # If a pre_filter converts HTML to text, for example, we don't want to
399             # reprocess the text as HTML.
400              
401             if ( $self->text_pre_filter && $r->isa('HTTP::Response') ) {
402 27     27   66 ( $content, my $type )
403 27         44 = $self->text_pre_filter->( $content, $content_type, $r->base );
404             $content_type = $type if $type;
405 27 100       639 }
406 24         260  
407 24 100       82 return unless $content;
408              
409 23         66 unless ( $self->pretty ) {
410             $self->_debug("Text\n\n$content\n\n");
411             return;
412             }
413              
414 23 100 100     1438 my $t = Text::SimpleTable::AutoWidth->new();
415 15         411 $t->captions( ['Text'] );
416              
417 15 100       34040 $self->_parse_body( $content, $content_type, $t );
418              
419             $self->_draw($t);
420 23 100       202 }
421              
422 14 100       263 my $self = shift;
423 1         22 my $content = shift;
424 1         152 my $content_type = shift;
425             my $t = shift;
426              
427 13         289 # Is this maybe JSON?
428 13         294 try {
429             decode_json($content);
430 13         89  
431             # If we get this far, it's valid JSON.
432 13         146 $content_type = 'application/json';
433             };
434              
435             # nothing to do here
436 14     14   59 unless ($content_type) {
437 14         30 $t->row($content);
438 14         23 return;
439 14         33 }
440              
441             my ( $type, $subtype ) = apply { lc $_ } parse_mime_type($content_type);
442             unless ($subtype) {
443 14     14   622 $t->row($content);
444             return;
445             }
446 4         12  
447 14         146 if ( $subtype eq 'html' ) {
448             $content = $self->html_restrict->process($content);
449             $content =~ s{\s+}{ }g;
450 14 50       226 $content =~ s{\n{2,}}{\n\n}g;
451 0         0  
452 0         0 return if !$content;
453             }
454             elsif ( $subtype eq 'xml' ) {
455 14     42   126 try {
  42         491  
456 14 50       64 my $pretty = XMLin( $content, KeepRoot => 1 );
457 0         0 $content = np( $pretty, return_value => 'dump' );
458 0         0 }
459             catch { $t->row("Error parsing XML: $_") };
460             }
461 14 100 66     379 elsif ( $subtype eq 'json' || $subtype =~ m{$json_regex} ) {
    50 66        
    100 100        
    100 66        
    100 66        
    100 66        
462 4         73 try {
463 4         12423 $content = decode_json($content);
464 4         9 $content = np( $content, return_value => 'dump' );
465             }
466 4 50       9 catch { $t->row("Error parsing JSON: $_") };
467             }
468             elsif ( $type && $type eq 'application' && $subtype eq 'javascript' ) {
469              
470 0     0   0 # clean it up a bit, and print some of it
471 0         0 $content =~ s{^\s*}{}mg;
472             if ( length $content > 253 ) {
473 0     0   0 $content = substr( $content, 0, 252 ) . '...';
  0         0  
474             }
475             }
476             elsif ($type
477 4     4   165 && $type eq 'application'
478 4         46 && $subtype eq 'x-www-form-urlencoded' ) {
479              
480 4     0   55 # Pretend we have query params.
  0         0  
481             my $uri = URI->new( '?' . $content );
482             $content = np( $uri->query_form_hash );
483             }
484             elsif ( !$type || $type ne 'text' ) {
485 2         45  
486 2 50       17 # Avoid things like dumping gzipped content to the screen
487 2         7 $content = $self->_redaction_message($content_type);
488             }
489              
490             $content =~ s{^\\ }{}; # don't prefix HashRef with Data::Printer's slash
491             $t->row($content);
492             }
493              
494             my $self = shift;
495 1         15 my $content_type = shift;
496 1         96 return sprintf '[ REDACTED by %s. Do not know how to display %s. ]',
497             __PACKAGE__, $content_type;
498             }
499              
500             my ($self) = @_;
501 1         14  
502             # cargo culted from Plack::Middleware::DebugLogging
503             my $width = eval '
504 14         26168 my ($columns, $rows) = Term::Size::Any::chars;
505 14         57 return $columns;
506             ';
507              
508             if ($@) {
509 3     3   11 $width = $ENV{COLUMNS}
510 3         7 if exists( $ENV{COLUMNS} )
511 3         16 && $ENV{COLUMNS} =~ m/^\d+$/;
512             }
513              
514             $width = 80 unless ( $width && $width >= 80 );
515             return $width;
516 23     23   264 }
517              
518             my $self = shift;
519 23         1730 my $t = shift;
520             my $preamble = shift;
521              
522             return if !$t->rows;
523             $self->_debug($preamble) if $preamble;
524 23 50       156 $self->_debug( $t->draw );
525             }
526              
527 23 50 33     125 1;
528              
529             =pod
530 23 50 33     87  
531 23         461 =encoding UTF-8
532              
533             =head1 NAME
534              
535 103     103   234 LWP::ConsoleLogger - LWP tracing and debugging
536 103         186  
537 103         157 =head1 VERSION
538              
539 103 100       282 version 0.000044
540 101 100       228  
541 101         631 =head1 SYNOPSIS
542              
543             The simplest way to get started is by adding L<LWP::ConsoleLogger::Everywhere>
544             to your code and then just watching your output.
545              
546             use LWP::ConsoleLogger::Everywhere ();
547              
548             If you need more control, look at L<LWP::ConsoleLogger::Easy>.
549              
550             use LWP::ConsoleLogger::Easy qw( debug_ua );
551             use WWW::Mechanize;
552              
553             my $mech = WWW::Mechanize->new; # or LWP::UserAgent->new() etc
554             my $console_logger = debug_ua( $mech );
555             $mech->get( 'https://metacpan.org' );
556              
557             # now watch the console for debugging output
558             # turn off header dumps
559             $console_logger->dump_headers( 0 );
560              
561             $mech->get( $some_other_url );
562              
563             To get down to the lowest level, use LWP::ConsoleLogger directly.
564              
565             my $ua = LWP::UserAgent->new( cookie_jar => {} );
566             my $console_logger = LWP::ConsoleLogger->new(
567             dump_content => 1,
568             dump_text => 1,
569             content_pre_filter => sub {
570             my $content = shift;
571             my $content_type = shift;
572              
573             # mangle content here
574             # ...
575              
576             return $content;
577             },
578             );
579              
580             $ua->default_header(
581             'Accept-Encoding' => scalar HTTP::Message::decodable() );
582              
583             $ua->add_handler( 'response_done',
584             sub { $console_logger->response_callback( @_ ) } );
585             $ua->add_handler( 'request_send',
586             sub { $console_logger->request_callback( @_ ) } );
587              
588             # now watch debugging output to your screen
589             $ua->get( 'http://nytimes.com/' );
590              
591             Sample output might look like this.
592              
593             GET http://www.nytimes.com/2014/04/24/technology/fcc-new-net-neutrality-rules.html
594              
595             GET params:
596             .-----+-------.
597             | Key | Value |
598             +-----+-------+
599             | _r | 1 |
600             | hp | |
601             '-----+-------'
602              
603             .-----------------+--------------------------------.
604             | Request Header | Value |
605             +-----------------+--------------------------------+
606             | Accept-Encoding | gzip |
607             | Cookie2 | $Version="1" |
608             | Referer | http://www.nytimes.com?foo=bar |
609             | User-Agent | WWW-Mechanize/1.73 |
610             '-----------------+--------------------------------'
611              
612             ==> 200 OK
613              
614             Title: The New York Times - Breaking News, World News & Multimedia
615              
616             .--------------------------+-------------------------------.
617             | Response Header | Value |
618             +--------------------------+-------------------------------+
619             | Accept-Ranges | bytes |
620             | Age | 176 |
621             | Cache-Control | no-cache |
622             | Channels | NytNow |
623             | Client-Date | Fri, 30 May 2014 22:37:42 GMT |
624             | Client-Peer | 170.149.172.130:80 |
625             | Client-Response-Num | 1 |
626             | Client-Transfer-Encoding | chunked |
627             | Connection | keep-alive |
628             | Content-Encoding | gzip |
629             | Content-Type | text/html; charset=utf-8 |
630             | Date | Fri, 30 May 2014 22:37:41 GMT |
631             | NtCoent-Length | 65951 |
632             | Server | Apache |
633             | Via | 1.1 varnish |
634             | X-Cache | HIT |
635             | X-Varnish | 1142859770 1142854917 |
636             '--------------------------+-------------------------------'
637              
638             .--------------------------+-------------------------------.
639             | Text |
640             +--------------------------+-------------------------------+
641             | F.C.C., in a Shift, Backs Fast Lanes for Web Traffic... |
642             '--------------------------+-------------------------------'
643              
644             =head1 DESCRIPTION
645              
646             BETA BETA BETA. This is currently an experiment. Things could change. Please
647             adjust accordingly.
648              
649             It can be hard (or at least tedious) to debug mechanize scripts. LWP::Debug is
650             deprecated. It suggests you write your own debugging handlers, set up a proxy
651             or install Wireshark. Those are all workable solutions, but this module exists
652             to save you some of that work. The guts of this module are stolen from
653             L<Plack::Middleware::DebugLogging>, which in turn stole most of its internals
654             from L<Catalyst>. If you're new to LWP::ConsoleLogger, I suggest getting
655             started with the L<LWP::ConsoleLogger::Easy> wrapper. This will get you up and
656             running in minutes. If you need to tweak the settings that
657             L<LWP::ConsoleLogger::Easy> chooses for you (or if you just want to be fancy),
658             please read on.
659              
660             Since this is a debugging library, I've left as much mutable state as possible,
661             so that you can easily toggle output on and off and otherwise adjust how you
662             deal with the output.
663              
664             =head1 CONSTRUCTOR
665              
666             =head2 new()
667              
668             The following arguments can be passed to new(), although none are required.
669             They can also be called as methods on an instantiated object. I'll list them
670             here and discuss them in detail below.
671              
672             =over 4
673              
674             =item * C<< dump_content => 0|1 >>
675              
676             =item * C<< dump_cookies => 0|1 >>
677              
678             =item * C<< dump_headers => 0|1 >>
679              
680             =item * C<< dump_params => 0|1 >>
681              
682             =item * C<< dump_status => 0|1 >>
683              
684             =item * C<< dump_text => 0|1 >>
685              
686             =item * C<< dump_title => 0|1 >>
687              
688             =item * C<< dump_text => 0|1 >>
689              
690             =item * C<< dump_uri => 0|1 >>
691              
692             =item * C<< content_pre_filter => sub { ... } >>
693              
694             =item * C<< headers_to_redact => ['Authentication', 'Foo'] >>
695              
696             =item * C<< params_to_redact => ['token', 'password'] >>
697              
698             =item * C<< text_pre_filter => sub { ... } >>
699              
700             =item * C<< html_restrict => HTML::Restrict->new( ... ) >>
701              
702             =item * C<< logger => Log::Dispatch->new( ... ) >>
703              
704             =item * C<< pretty => 0|1 >>
705              
706             =item * C<< term_width => $integer >>
707              
708             =back
709              
710             =head1 SUBROUTINES/METHODS
711              
712             =head2 dump_content( 0|1 )
713              
714             Boolean value. If true, the actual content of your response (HTML, JSON, etc)
715             will be dumped to your screen. Defaults to false.
716              
717             =head2 dump_cookies( 0|1 )
718              
719             Boolean value. If true, the content of your cookies will be dumped to your
720             screen. Defaults to false.
721              
722             =head2 dump_headers( 0|1 )
723              
724             Boolean value. If true, both request and response headers will be dumped to
725             your screen. Defaults to true.
726              
727             Headers are dumped in alphabetical order.
728              
729             =head2 dump_params( 0|1 )
730              
731             Boolean value. If true, both GET and POST params will be dumped to your screen.
732             Defaults to true.
733              
734             Params are dumped in alphabetical order.
735              
736             =head2 dump_status( 0|1 )
737              
738             Boolean value. If true, dumps the HTTP response code for each page being
739             visited. Defaults to true.
740              
741             =head2 dump_text( 0|1 )
742              
743             Boolean value. If true, dumps the text of your page after both the
744             content_pre_filter and text_pre_filters have been applied. Defaults to true.
745              
746             =head2 dump_title( 0|1 )
747              
748             Boolean value. If true, dumps the titles of HTML pages if your UserAgent has
749             a C<title> method and if it returns something useful. Defaults to true.
750              
751             =head2 dump_uri( 0|1 )
752              
753             Boolean value. If true, dumps the URI of each page being visited. Defaults to
754             true.
755              
756             =head2 pretty ( 0|1 )
757              
758             Boolean value. If disabled, request headers, response headers, content and text
759             sections will be dumped without using tables. Handy for copy/pasting JSON etc
760             for faking responses later. Defaults to true.
761              
762             =head2 content_pre_filter( sub { ... } )
763              
764             Subroutine reference. This allows you to manipulate content before it is
765             dumped. A common use case might be stripping headers and footers away from
766             HTML content to make it easier to detect changes in the body of the page.
767              
768             $easy_logger->content_pre_filter(
769             sub {
770             my $content = shift;
771             my $content_type = shift; # the value of the Content-Type header
772             if ( $content_type =~ m{html}i
773             && $content =~ m{<!--\scontent\s-->(.*)<!--\sfooter}msx ) {
774             return $1;
775             }
776             return $content;
777             }
778             );
779              
780             Try to make sure that your content mangling doesn't return broken HTML as that
781             may not play well with L<HTML::Restrict>.
782              
783             =head2 request_callback
784              
785             Use this handler to set up console logging on your requests.
786              
787             my $ua = LWP::UserAgent->new;
788             $ua->add_handler(
789             'request_send',
790             sub { $console_logger->request_callback(@_) }
791             );
792              
793             This is done for you by default if you set up your logging via
794             L<LWP::ConsoleLogger::Easy>.
795              
796             =head2 response_callback
797              
798             Use this handler to set up console logging on your responses.
799              
800             my $ua = LWP::UserAgent->new;
801             $ua->add_handler(
802             'response_done',
803             sub { $console_logger->response_callback(@_) }
804             );
805              
806             This is done for you by default if you set up your logging via
807             L<LWP::ConsoleLogger::Easy>.
808              
809             =head2 text_pre_filter( sub { ... } )
810              
811             Subroutine reference. This allows you to manipulate text before it is dumped.
812             A common use case might be stripping away duplicate whitespace and/or newlines
813             in order to improve formatting. Keep in mind that the C<content_pre_filter>
814             will have been applied to the content which is passed to the text_pre_filter.
815             The idea is that you can strip away an HTML you don't care about in the
816             content_pre_filter phase and then process the remainder of the content in the
817             text_pre_filter.
818              
819             $easy_logger->text_pre_filter(
820             sub {
821             my $content = shift;
822             my $content_type = shift; # the value of the Content-Type header
823             my $base_url = shift;
824              
825             # do something with the content
826             # ...
827              
828             return ( $content, $new_content_type );
829             }
830             );
831              
832             If your C<text_pre_filter()> converts from HTML to plain text, be sure to
833             return the new content type (text/plain) when you exit the sub. If you do not
834             do this, HTML formatting will then be applied to your plain text as is
835             explained below.
836              
837             If this is HTML content, L<HTML::Restrict> will be applied after the
838             text_pre_filter has been run. LWP::ConsoleLogger will then strip away some
839             whitespace and newlines from processed HTML in its own opinionated way, in
840             order to present you with more readable text.
841              
842             =head2 html_restrict( HTML::Restrict->new( ... ) )
843              
844             If the content_type indicates HTML then HTML::Restrict will be used to strip
845             tags from your content in the text rendering process. You may pass your own
846             HTML::Restrict object, if you like. This would be helpful in situations where
847             you still do want to have some tags in your text.
848              
849             =head2 logger( Log::Dispatch->new( ... ) )
850              
851             By default all data will be dumped to your console (as the name of this module
852             implies) using Log::Dispatch. However, you may use your own Log::Dispatch
853             module in order to facilitate logging to files or any other output which
854             Log::Dispatch supports.
855              
856             =head2 term_width( $integer )
857              
858             By default this module will try to find the maximum width of your terminal and
859             use all available space when displaying tabular data. You may use this
860             parameter to constrain the tables to an arbitrary width.
861              
862             =head1 CAVEATS
863              
864             Aside from the BETA warnings, I should say that I've written this to suit my
865             needs and there are a lot of things I haven't considered. For example, I'm
866             mostly assuming that the content will be text, HTML, JSON or XML.
867              
868             The test suite is not very robust either. If you'd like to contribute to this
869             module and you can't find an appropriate test, do add something to the example
870             folder (either a new script or alter an existing one), so that I can see what
871             your patch does.
872              
873             =for Pod::Coverage BUILD
874              
875             =head1 AUTHOR
876              
877             Olaf Alders <olaf@wundercounter.com>
878              
879             =head1 COPYRIGHT AND LICENSE
880              
881             This software is Copyright (c) 2014 by MaxMind, Inc.
882              
883             This is free software, licensed under:
884              
885             The Artistic License 2.0 (GPL Compatible)
886              
887             =cut
888              
889              
890             # ABSTRACT: LWP tracing and debugging
891