File Coverage

blib/lib/Log/Any/Adapter/Capture.pm
Criterion Covered Total %
statement 49 56 87.5
branch 20 28 71.4
condition 13 20 65.0
subroutine 14 15 93.3
pod 0 1 0.0
total 96 120 80.0


line stmt bran cond sub pod time code
1 1     1   17 use 5.008001;
  1         3  
2 1     1   5 use strict;
  1         2  
  1         20  
3 1     1   4 use warnings;
  1         3  
  1         42  
4              
5             package Log::Any::Adapter::Capture;
6              
7             # ABSTRACT: Adapter for capturing log messages into an arrayref
8             our $VERSION = '1.715';
9              
10 1     1   6 use Log::Any::Adapter::Util ();
  1         3  
  1         28  
11              
12 1     1   6 use Log::Any::Adapter::Base;
  1         3  
  1         502  
13             our @ISA = qw/Log::Any::Adapter::Base/;
14              
15             # Subclass for optional structured logging
16             @Log::Any::Adapter::Capture::_Structured::ISA = ( __PACKAGE__ );
17              
18             sub init {
19 15     15 0 28 my ($self) = @_;
20              
21             # Handle 'text' and 'structured' aliases
22 15 100       32 if ( defined $self->{text} ) {
23 3         6 $self->{format} = 'text';
24 3         7 $self->{to} = delete $self->{text};
25             }
26 15 100       25 if ( defined $self->{structured} ) {
27 3         5 $self->{format} = 'structured';
28 3         7 $self->{to} = delete $self->{structured};
29             }
30              
31 15         19 my $to = $self->{to};
32 15 50 66     67 unless ( $to and ref $to eq 'CODE' || ref $to eq 'ARRAY' ) {
      33        
33 0         0 require Carp;
34 0         0 Carp::croak( "Capture destination 'to' must be an arrayref or coderef" );
35             }
36              
37 15   100     38 my $format = $self->{format} || 'messages';
38 15 100       36 if ( $format eq 'text' ) {
    100          
    50          
39             $self->{_callback} = # only pass the message text argument
40 0     0   0 ref $to eq 'CODE' ? sub { $to->($_[2]) }
41 3 50   2   13 : sub { push @$to, $_[2] };
  2         7  
42             }
43             elsif ( $format eq 'messages' ) {
44 6 100   3   20 $self->{_callback} = ref $to eq 'CODE' ? $to : sub { push @$to, [ @_ ] };
  3         11  
45             }
46             elsif ( $format eq 'structured' ) {
47 6 50   4   25 $self->{_callback} = ref $to eq 'CODE' ? $to : sub { push @$to, [ @_ ] };
  4         14  
48             # Structured logging is determined by whether or not the package
49             # contains a method of that name. If structured logging were enabled,
50             # the proxy would always call ->structured rather than its default
51             # behavior of flattening to a string, even for the case where the user
52             # of this module wanted strings. So, enable/disable of structured
53             # capture requires changing the class of this object.
54             # This line is written in a way to make subclassing possible.
55 6 50       26 bless $self, ref($self).'::_Structured' unless $self->can('structured');
56             }
57             else {
58 0         0 require Carp;
59 0         0 Carp::croak( "Unknown capture format '$format' (expected 'text', 'messages', or 'structured')" );
60             }
61              
62 15 100 66     53 if ( defined $self->{log_level} && $self->{log_level} =~ /\D/ ) {
63 3         10 my $numeric_level = Log::Any::Adapter::Util::numeric_level( $self->{log_level} );
64 3 50       7 if ( !defined($numeric_level) ) {
65 0         0 require Carp;
66 0         0 Carp::carp( "Invalid log level '$self->{log_level}'. Will capture all messages." );
67             }
68 3         8 $self->{log_level} = $numeric_level;
69             }
70             }
71              
72             # Each logging method simply passes its arguments (minus $self) to the _callback
73             # Logging can be skipped if a log_level is in effect.
74             foreach my $method ( Log::Any::Adapter::Util::logging_methods() ) {
75 1     1   7 no strict 'refs';
  1         3  
  1         138  
76             my $method_level = Log::Any::Adapter::Util::numeric_level($method);
77             *{$method} = sub {
78 7     7   14 my ( $self, $text ) = @_;
79 7 50 66     20 return if defined $self->{log_level} and $method_level > $self->{log_level};
80 7         17 $self->{_callback}->( $method, $self->{category}, $text );
81             };
82             }
83              
84             # Detection methods return true unless a log_level is in effect
85             foreach my $method ( Log::Any::Adapter::Util::detection_methods() ) {
86 1     1   6 no strict 'refs';
  1         2  
  1         192  
87             my $base = substr( $method, 3 );
88             my $method_level = Log::Any::Adapter::Util::numeric_level($base);
89             *{$method} = sub {
90 12   100 12   63 return !defined $_[0]{log_level} || !!( $method_level <= $_[0]{log_level} );
91             };
92             }
93              
94             # A separate package is required for handling the ->structured Adapter API.
95             # See notes in init()
96             sub Log::Any::Adapter::Capture::_Structured::structured {
97 4     4   10 my ( $self, $method, $category, @parts ) = @_;
98             return if defined $self->{log_level}
99 4 50 33     12 and Log::Any::Adapter::Util::numeric_level($method) > $self->{log_level};
100 4         17 $self->{_callback}->( $method, $category, @parts );
101             };
102              
103             1;
104              
105             __END__