File Coverage

blib/lib/Log/Any/Manager.pm
Criterion Covered Total %
statement 84 85 98.8
branch 21 24 87.5
condition 2 3 66.6
subroutine 15 15 100.0
pod 0 4 0.0
total 122 131 93.1


line stmt bran cond sub pod time code
1             package Log::Any::Manager;
2             {
3             $Log::Any::Manager::VERSION = '0.11';
4             }
5 5     5   30 use strict;
  5         12  
  5         700  
6 5     5   27 use warnings;
  5         12  
  5         146  
7 5     5   674 use Carp qw(croak);
  5         12  
  5         619  
8 5     5   8387 use Devel::GlobalDestruction;
  5         13787  
  5         35  
9 5     5   2236 use Log::Any::Adapter::Util qw(require_dynamic);
  5         13  
  5         285  
10 5     5   4748 use Guard;
  5         3539  
  5         4848  
11              
12             sub new {
13 5     5 0 9 my $class = shift;
14 5         12 my $self = { entries => [] };
15 5         16 bless $self, $class;
16              
17             # Create the initial Null entry (this is always present)
18             #
19 5         17 $self->set('Null');
20 5         20 my $null_entry = $self->{entries}->[0];
21              
22             # Start our category cache with any null adapters already returned from raw Log::Any
23             #
24 2         7 $self->{category_cache} = {
25             map {
26 5         16 (
27             $_ => {
28             adapter => $Log::Any::NullAdapters{$_},
29             entry => $null_entry
30             }
31             )
32             } keys(%Log::Any::NullAdapters)
33             };
34              
35 5         22 return $self;
36             }
37              
38             sub get_logger {
39 11     11 0 22 my ( $self, $category ) = @_;
40              
41             # Create a new adapter for this category if it is not already in cache
42             #
43 11         27 my $category_cache = $self->{category_cache};
44 11 100       40 if ( !defined( $category_cache->{$category} ) ) {
45 5         25 my $entry = $self->_choose_entry_for_category($category);
46 5         21 my $adapter = $self->_new_adapter_for_entry( $entry, $category );
47 5         31 $category_cache->{$category} = { entry => $entry, adapter => $adapter };
48             }
49 11         62 return $category_cache->{$category}->{adapter};
50             }
51              
52             sub _choose_entry_for_category {
53 31     31   45 my ( $self, $category ) = @_;
54              
55 31         45 foreach my $entry ( @{ $self->{entries} } ) {
  31         69  
56 40 100       214 if ( $category =~ $entry->{pattern} ) {
57 31         71 return $entry;
58             }
59             }
60 0         0 die "no entries matched '$category' - should not get here!";
61             }
62              
63             sub _new_adapter_for_entry {
64 17     17   27 my ( $self, $entry, $category ) = @_;
65              
66 17         134 return $entry->{adapter_class}
67 17         27 ->new( @{ $entry->{adapter_params} }, category => $category );
68             }
69              
70             sub set {
71 16     16 0 24 my $self = shift;
72 16         25 my $options;
73 16 100       63 if ( ref( $_[0] ) eq 'HASH' ) {
74 3         5 $options = shift(@_);
75             }
76 16         37 my ( $adapter_name, @adapter_params ) = @_;
77              
78 16 100 66     341 croak "expected adapter name"
79             unless defined($adapter_name) && $adapter_name =~ /\S/;
80              
81 15         30 my $pattern = $options->{category};
82 15 100       53 if ( !defined($pattern) ) {
    100          
83 12         60 $pattern = qr/.*/;
84             }
85             elsif ( !ref($pattern) ) {
86 1         25 $pattern = qr/^\Q$pattern\E$/;
87             }
88              
89 15         28 $adapter_name =~ s/^Log:://; # Log::Dispatch -> Dispatch, etc.
90 15 100       65 my $adapter_class = (
91             substr( $adapter_name, 0, 1 ) eq '+'
92             ? substr( $adapter_name, 1 )
93             : "Log::Any::Adapter::$adapter_name"
94             );
95 15         71 require_dynamic($adapter_class);
96              
97 13         76 my $entry = $self->_new_entry( $pattern, $adapter_class, \@adapter_params );
98 13         23 unshift( @{ $self->{entries} }, $entry );
  13         61  
99              
100 13         39 $self->_reselect_matching_adapters($pattern);
101              
102 13 100       43 if ( my $lex_ref = $options->{lexically} ) {
103             $$lex_ref =
104 1 50   1   9 Guard::guard { $self->remove($entry) if !in_global_destruction };
  1         624  
105             }
106              
107 13         103 return $entry;
108             }
109              
110             sub remove {
111 3     3 0 18 my ( $self, $entry ) = @_;
112              
113 3         8 my $pattern = $entry->{pattern};
114 3         4 my $size = scalar( @{ $self->{entries} } );
  3         8  
115 3 50       18 die "cannot remove bottom entry"
116             if $entry eq $self->{entries}->[ $size - 1 ];
117 3         5 $self->{entries} = [ grep { $_ ne $entry } @{ $self->{entries} } ];
  7         29  
  3         8  
118 3         12 $self->_reselect_matching_adapters($pattern);
119             }
120              
121             sub _new_entry {
122 13     13   30 my ( $self, $pattern, $adapter_class, $adapter_params ) = @_;
123              
124             return {
125 13         64 pattern => $pattern,
126             adapter_class => $adapter_class,
127             adapter_params => $adapter_params,
128             };
129             }
130              
131             sub _reselect_matching_adapters {
132 16     16   25 my ( $self, $pattern ) = @_;
133              
134 16 50       471 return if in_global_destruction;
135              
136             # Reselect adapter for each category matching $pattern
137             #
138 16         111 while ( my ( $category, $category_info ) =
  42         190  
139             each( %{ $self->{category_cache} } ) )
140             {
141 26         63 my $new_entry = $self->_choose_entry_for_category($category);
142 26 100       106 if ( $new_entry ne $category_info->{entry} ) {
143 12         33 my $new_adapter =
144             $self->_new_adapter_for_entry( $new_entry, $category );
145 12         71 %{ $category_info->{adapter} } = %$new_adapter;
  12         83  
146 12         35 bless( $category_info->{adapter}, ref($new_adapter) );
147 12         79 $category_info->{entry} = $new_entry;
148             }
149             }
150             }
151              
152             1;