File Coverage

blib/lib/LWP/ConsoleLogger.pm
Criterion Covered Total %
statement 231 264 87.5
branch 82 104 78.8
condition 34 48 70.8
subroutine 40 46 86.9
pod 2 3 66.6
total 389 465 83.6


line stmt bran cond sub pod time code
1              
2             use Moo;
3 7     7   33157 use MooX::StrictConstructor;
  7         70966  
  7         33  
4 7     7   11443  
  7         81189  
  7         28  
5             use 5.006;
6 7     7   140677  
  7         23  
7             our $VERSION = '1.000000';
8              
9             use Data::Printer { end_separator => 1, hash_separator => ' => ' };
10 7     7   2924 use DateTime ();
  7         152479  
  7         62  
11 7     7   8692 use HTML::Restrict ();
  7         3168985  
  7         277  
12 7     7   3631 use HTTP::Body ();
  7         688288  
  7         315  
13 7     7   3278 use HTTP::CookieMonster ();
  7         172801  
  7         193  
14 7     7   2782 use JSON::MaybeXS qw( decode_json );
  7         161167  
  7         222  
15 7     7   3160 use List::AllUtils qw( any apply none );
  7         34997  
  7         399  
16 7     7   3833 use Log::Dispatch ();
  7         67505  
  7         639  
17 7     7   3299 use Parse::MIME qw( parse_mime_type );
  7         1277747  
  7         234  
18 7     7   3073 use Ref::Util qw( is_blessed_ref );
  7         7311  
  7         581  
19 7     7   57 use Term::Size::Any ();
  7         15  
  7         424  
20 7     7   2783 use Text::SimpleTable::AutoWidth 0.09 ();
  7         1507  
  7         170  
21 7     7   2635 use Try::Tiny qw( catch try );
  7         43418  
  7         205  
22 7     7   51 use Types::Common::Numeric qw( PositiveInt );
  7         15  
  7         459  
23 7     7   2656 use Types::Standard qw( ArrayRef Bool CodeRef InstanceOf );
  7         78929  
  7         60  
24 7     7   3157 use URI::QueryParam qw();
  7         74  
  7         42  
25 7     7   9161 use XML::Simple qw( XMLin );
  7         4716  
  7         192  
26 7     7   4545  
  7         55189  
  7         59  
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 79220 }
32 23         462  
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   205 : [];
145             }
146 19 50       310  
147             my $self = shift;
148             return $ENV{LWPCL_REDACT_PARAMS}
149             ? [ split m{,}, $ENV{LWPCL_REDACT_PARAMS} ]
150             : [];
151 2     2   51 }
152              
153 2 50       47 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 42937 $uri_without_query->query(undef);
165 27         78  
166 27         71 $self->_debug( $req->method . q{ } . $uri_without_query . "\n" );
167             }
168 27 100       845  
169 25         319 if ( $req->method eq 'GET' ) {
170 25         571 $self->_log_params( $req, 'GET' );
171             }
172 25         507 else {
173             $self->_log_params( $req, $_ ) for ( 'GET', $req->method );
174             }
175 27 100       1343801  
176 22         401 $self->_log_headers( 'request (before sending)', $req->headers );
177              
178             # This request might have a body.
179 5         106 return unless $req->content;
180              
181             $self->_log_content($req);
182 27         1906 $self->_log_text($req);
183             return;
184             }
185 27 100       20130  
186             my $self = shift;
187 5         91 my $res = shift;
188 5         1713 my $ua = shift;
189 5         1991  
190             $self->_log_headers( 'request (after sending)', $res->request->headers );
191              
192             if ( $self->dump_status ) {
193 22     22 1 25841 $self->_debug( '==> ' . $res->status_line . "\n" );
194 22         46 }
195 22         48 if ( $self->dump_title && $ua->can('title') && $ua->title ) {
196             $self->_debug( 'Title: ' . $ua->title . "\n" );
197 22         75 }
198              
199 22 100       16780 $self->_log_headers( 'response', $res->headers );
200 20         287 $self->_log_cookies( 'response', $ua->cookie_jar, $res->request->uri );
201              
202 22 100 100     5488 $self->_log_content($res);
      100        
203 1         532 $self->_log_text($res);
204             return;
205             }
206 22         1428  
207 22         20486 my ( $self, $type, $headers ) = @_;
208              
209 22         163 return if !$self->dump_headers;
210 22         27272  
211 22         4328 unless ( $self->pretty ) {
212             $self->_debug( $headers->as_string );
213             return;
214             }
215 71     71   949  
216             my $t = Text::SimpleTable::AutoWidth->new();
217 71 100       1574 $t->captions( [ ucfirst $type . ' Header', 'Value' ] );
218              
219 65 100       1577 foreach my $name ( sort $headers->header_field_names ) {
220 3         205 my $val
221 3         1548 = ( any { $name eq $_ } @{ $self->headers_to_redact } )
222             ? '[REDACTED]'
223             : $headers->header($name);
224 62         1403 $t->row( $name, $val );
225 62         7176 }
226              
227 62         241 $self->_draw($t);
228             }
229 174 50   0   3311  
  0         0  
  174         3069  
230             my ( $self, $req, $type ) = @_;
231              
232 174         7693 return if !$self->dump_params;
233              
234             my %params;
235 62         820 my $uri = $req->uri;
236              
237             if ( $type eq 'GET' ) {
238             my @params = $uri->query_param;
239 32     32   226 return unless @params;
240              
241 32 100       755 $params{$_} = [ $uri->query_param($_) ] for @params;
242             }
243 29         325  
244 29         121 elsif ( $req->header('Content-Length') ) {
245             my $content_type = $req->header('Content-Type');
246 29 100       257 my $body = HTTP::Body->new(
    100          
247 25         315 $content_type,
248 25 50       891 $req->header('Content-Length')
249             );
250 0         0 $body->add( $req->content );
251             %params = %{ $body->param };
252              
253             unless ( keys %params ) {
254 3         212 {
255 3         132 my $t = Text::SimpleTable::AutoWidth->new;
256             $t->captions( [ $type . ' Raw Body' ] );
257             $t->row( $req->content );
258             $self->_draw($t);
259 3         1170 }
260 3         2118  
  3         208  
261             {
262 3 100       301 my $t = Text::SimpleTable::AutoWidth->new;
263             $t->captions( [ $type . ' Parsed Body' ] );
264 1         39 $self->_parse_body( $req->content, $content_type, $t );
265 1         2559 $self->_draw($t);
266 1         12 }
267 1         68 }
268             }
269              
270             my $t = Text::SimpleTable::AutoWidth->new();
271 1         2 $t->captions( [ 'Key', 'Value' ] );
  1         877  
  1         24  
272 1         19 foreach my $name ( sort keys %params ) {
273 1         6 my @values
274 1         15 = ( any { $name eq $_ } @{ $self->params_to_redact } )
275             ? '[REDACTED]'
276             : ref $params{$name} ? @{ $params{$name} }
277             : $params{$name};
278              
279 4         991 $t->row( $name, $_ ) for sort @values;
280 4         2345 }
281 4         34  
282             $self->_draw( $t, "$type Params:\n" );
283 0     0   0 }
  4         104  
284              
285 0         0 my $self = shift;
286 4 50       76 my $type = shift;
    50          
287             my $jar = shift;
288 4         175 my $uri = shift;
289              
290             return if !$self->dump_cookies || !$jar || !is_blessed_ref($jar);
291 4         65  
292             if ( $jar->isa('HTTP::Cookies') ) {
293             my $monster = HTTP::CookieMonster->new($jar);
294             my @cookies = $monster->all_cookies;
295 22     22   839  
296 22         49 my @methods = (
297 22         74 'key', 'val', 'path', 'domain',
298 22         47 'path_spec', 'secure', 'expires'
299             );
300 22 100 100     576  
      66        
301             foreach my $cookie (@cookies) {
302 18 100       755  
    100          
303 16         588 my $t = Text::SimpleTable::AutoWidth->new;
304 16         13190 $t->captions( [ 'Key', 'Value' ] );
305              
306 16         683 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         153 $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         7 }
330 1         79 $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   87 if (
353 48         81 ( $type ne 'text' )
354             && (
355 48 50       193 none { $_ eq $subtype } (
356             'javascript', 'html', 'json', 'xml', 'soap+xml',
357 48 100       31282 'x-www-form-urlencoded',
358             )
359 46         136 )
360 46 50       1891 && $subtype !~ m{$json_regex}
361             ) {
362 46     138   426 $content = $self->_redaction_message($content_type);
  138         2163  
363 46 100 100     1022 }
    100 66        
364             elsif ( $self->content_pre_filter ) {
365             $content = $self->content_pre_filter->( $content, $content_type );
366 52     52   498 }
367              
368             return $content;
369             }
370              
371             my $self = shift;
372             my $r = shift;
373 2         17  
374             return unless $self->dump_content;
375              
376 2         41 my $content = $self->_get_content($r);
377              
378             return unless $content;
379 46         1532  
380             unless ( $self->pretty ) {
381             $self->_debug("Content\n\n$content\n\n");
382             return;
383 27     27   67 }
384 27         45  
385             my $t = Text::SimpleTable::AutoWidth->new();
386 27 100       545 $t->captions( ['Content'] );
387              
388 24         386 $t->row($content);
389             $self->_draw($t);
390 24 100       70 }
391              
392 23 100       457 my $self = shift;
393 1         21 my $r = shift; # HTTP::Request or HTTP::Response
394 1         224  
395             return unless $self->dump_text;
396             my $content = $self->_get_content($r);
397 22         470 return unless $content;
398 22         463  
399             my $content_type = $r->header('Content-Type');
400 22         87  
401 22         232 # If a pre_filter converts HTML to text, for example, we don't want to
402             # reprocess the text as HTML.
403              
404             if ( $self->text_pre_filter && $r->isa('HTTP::Response') ) {
405 27     27   80 ( $content, my $type )
406 27         40 = $self->text_pre_filter->( $content, $content_type, $r->base );
407             $content_type = $type if $type;
408 27 100       619 }
409 24         239  
410 24 100       64 return unless $content;
411              
412 23         62 unless ( $self->pretty ) {
413             $self->_debug("Text\n\n$content\n\n");
414             return;
415             }
416              
417 23 100 100     1467 my $t = Text::SimpleTable::AutoWidth->new();
418 15         434 $t->captions( ['Text'] );
419              
420 15 100       32802 $self->_parse_body( $content, $content_type, $t );
421              
422             $self->_draw($t);
423 23 100       234 }
424              
425 14 100       238 my $self = shift;
426 1         19 my $content = shift;
427 1         221 my $content_type = shift;
428             my $t = shift;
429              
430 13         268 # Is this maybe JSON?
431 13         269 try {
432             decode_json($content);
433 13         78  
434             # If we get this far, it's valid JSON.
435 13         137 $content_type = 'application/json';
436             };
437              
438             # nothing to do here
439 14     14   63 unless ($content_type) {
440 14         29 $t->row($content);
441 14         26 return;
442 14         21 }
443              
444             my ( $type, $subtype ) = apply { lc $_ } parse_mime_type($content_type);
445             unless ($subtype) {
446 14     14   652 $t->row($content);
447             return;
448             }
449 4         11  
450 14         113 if ( $subtype eq 'html' ) {
451             $content = $self->html_restrict->process($content);
452             $content =~ s{\s+}{ }g;
453 14 50       225 $content =~ s{\n{2,}}{\n\n}g;
454 0         0  
455 0         0 return if !$content;
456             }
457             elsif ( $subtype eq 'xml' ) {
458 14     42   133 try {
  42         488  
459 14 50       74 my $pretty = XMLin( $content, KeepRoot => 1 );
460 0         0 $content = np( $pretty, return_value => 'dump' );
461 0         0 }
462             catch { $t->row("Error parsing XML: $_") };
463             }
464 14 100 66     357 elsif ( $subtype eq 'json' || $subtype =~ m{$json_regex} ) {
    50 66        
    100 100        
    100 66        
    100 66        
    100 66        
465 4         82 try {
466 4         13075 $content = decode_json($content);
467 4         13 $content = np( $content, return_value => 'dump' );
468             }
469 4 50       14 catch { $t->row("Error parsing JSON: $_") };
470             }
471             elsif ( $type && $type eq 'application' && $subtype eq 'javascript' ) {
472              
473 0     0   0 # clean it up a bit, and print some of it
474 0         0 $content =~ s{^\s*}{}mg;
475             if ( length $content > 253 ) {
476 0     0   0 $content = substr( $content, 0, 252 ) . '...';
  0         0  
477             }
478             }
479             elsif ($type
480 4     4   146 && $type eq 'application'
481 4         31 && $subtype eq 'x-www-form-urlencoded' ) {
482              
483 4     0   44 # Pretend we have query params.
  0         0  
484             my $uri = URI->new( '?' . $content );
485             $content = np( $uri->query_form_hash );
486             }
487             elsif ( !$type || $type ne 'text' ) {
488 2         33  
489 2 50       7 # Avoid things like dumping gzipped content to the screen
490 2         22 $content = $self->_redaction_message($content_type);
491             }
492              
493             $content =~ s{^\\ }{}; # don't prefix HashRef with Data::Printer's slash
494             $t->row($content);
495             }
496              
497             my $self = shift;
498 1         14 my $content_type = shift;
499 1         84 return sprintf '[ REDACTED by %s. Do not know how to display %s. ]',
500             __PACKAGE__, $content_type;
501             }
502              
503             my ($self) = @_;
504 1         3  
505             # cargo culted from Plack::Middleware::DebugLogging
506             my $width = eval '
507 14         25901 my ($columns, $rows) = Term::Size::Any::chars;
508 14         55 return $columns;
509             ';
510              
511             if ($@) {
512 3     3   8 $width = $ENV{COLUMNS}
513 3         10 if exists( $ENV{COLUMNS} )
514 3         17 && $ENV{COLUMNS} =~ m/^\d+$/;
515             }
516              
517             $width = 80 unless ( $width && $width >= 80 );
518             return $width;
519 23     23   279 }
520              
521             my $self = shift;
522 23         1392 my $t = shift;
523             my $preamble = shift;
524              
525             return if !$t->rows;
526             $self->_debug($preamble) if $preamble;
527 23 50       150 $self->_debug( $t->draw );
528             }
529              
530 23 50 33     124 1;
531              
532             =pod
533 23 50 33     85  
534 23         440 =encoding UTF-8
535              
536             =head1 NAME
537              
538 103     103   169 LWP::ConsoleLogger - LWP tracing and debugging
539 103         163  
540 103         206 =head1 VERSION
541              
542 103 100       297 version 1.000000
543 101 100       291  
544 101         711 =head1 SYNOPSIS
545              
546             The simplest way to get started is by adding L<LWP::ConsoleLogger::Everywhere>
547             to your code and then just watching your output.
548              
549             use LWP::ConsoleLogger::Everywhere ();
550              
551             If you need more control, look at L<LWP::ConsoleLogger::Easy>.
552              
553             use LWP::ConsoleLogger::Easy qw( debug_ua );
554             use WWW::Mechanize;
555              
556             my $mech = WWW::Mechanize->new; # or LWP::UserAgent->new() etc
557             my $console_logger = debug_ua( $mech );
558             $mech->get( 'https://metacpan.org' );
559              
560             # now watch the console for debugging output
561             # turn off header dumps
562             $console_logger->dump_headers( 0 );
563              
564             $mech->get( $some_other_url );
565              
566             To get down to the lowest level, use LWP::ConsoleLogger directly.
567              
568             my $ua = LWP::UserAgent->new( cookie_jar => {} );
569             my $console_logger = LWP::ConsoleLogger->new(
570             dump_content => 1,
571             dump_text => 1,
572             content_pre_filter => sub {
573             my $content = shift;
574             my $content_type = shift;
575              
576             # mangle content here
577             # ...
578              
579             return $content;
580             },
581             );
582              
583             $ua->default_header(
584             'Accept-Encoding' => scalar HTTP::Message::decodable() );
585              
586             $ua->add_handler( 'response_done',
587             sub { $console_logger->response_callback( @_ ) } );
588             $ua->add_handler( 'request_send',
589             sub { $console_logger->request_callback( @_ ) } );
590              
591             # now watch debugging output to your screen
592             $ua->get( 'http://nytimes.com/' );
593              
594             Sample output might look like this.
595              
596             GET http://www.nytimes.com/2014/04/24/technology/fcc-new-net-neutrality-rules.html
597              
598             GET params:
599             .-----+-------.
600             | Key | Value |
601             +-----+-------+
602             | _r | 1 |
603             | hp | |
604             '-----+-------'
605              
606             .-----------------+--------------------------------.
607             | Request Header | Value |
608             +-----------------+--------------------------------+
609             | Accept-Encoding | gzip |
610             | Cookie2 | $Version="1" |
611             | Referer | http://www.nytimes.com?foo=bar |
612             | User-Agent | WWW-Mechanize/1.73 |
613             '-----------------+--------------------------------'
614              
615             ==> 200 OK
616              
617             Title: The New York Times - Breaking News, World News & Multimedia
618              
619             .--------------------------+-------------------------------.
620             | Response Header | Value |
621             +--------------------------+-------------------------------+
622             | Accept-Ranges | bytes |
623             | Age | 176 |
624             | Cache-Control | no-cache |
625             | Channels | NytNow |
626             | Client-Date | Fri, 30 May 2014 22:37:42 GMT |
627             | Client-Peer | 170.149.172.130:80 |
628             | Client-Response-Num | 1 |
629             | Client-Transfer-Encoding | chunked |
630             | Connection | keep-alive |
631             | Content-Encoding | gzip |
632             | Content-Type | text/html; charset=utf-8 |
633             | Date | Fri, 30 May 2014 22:37:41 GMT |
634             | NtCoent-Length | 65951 |
635             | Server | Apache |
636             | Via | 1.1 varnish |
637             | X-Cache | HIT |
638             | X-Varnish | 1142859770 1142854917 |
639             '--------------------------+-------------------------------'
640              
641             .--------------------------+-------------------------------.
642             | Text |
643             +--------------------------+-------------------------------+
644             | F.C.C., in a Shift, Backs Fast Lanes for Web Traffic... |
645             '--------------------------+-------------------------------'
646              
647             =head1 DESCRIPTION
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             I've written this to suit my needs and there are a lot of things I haven't
865             considered. For example, I'm mostly assuming that the content will be text,
866             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