File Coverage

blib/lib/Medusa.pm
Criterion Covered Total %
statement 111 118 94.0
branch 33 56 58.9
condition 4 9 44.4
subroutine 16 16 100.0
pod 0 1 0.0
total 164 200 82.0


line stmt bran cond sub pod time code
1             package Medusa;
2              
3 8     8   925624 use 5.008003;
  8         26  
4 8     8   36 use strict;
  8         13  
  8         264  
5 8     8   61 use warnings;
  8         14  
  8         472  
6 8     8   36 use Time::HiRes qw/time/;
  8         18  
  8         77  
7 8     8   580 use B;
  8         10  
  8         194  
8 8     8   4729 use Data::Dumper;
  8         68950  
  8         709  
9 8     8   3716 use POSIX qw(strftime);
  8         74252  
  8         47  
10 8     8   15837 use Data::GUID;
  8         150420  
  8         51  
11              
12             our %LOG;
13              
14             our $VERSION = '0.05';
15              
16             BEGIN {
17             %LOG = (
18             LOGGER => 'Medusa::Logger',
19             LOG_LEVEL => 'debug',
20             LOG_FILE => 'audit.log',
21             LOG_INIT => sub {
22 2         8 (my $module = $LOG{LOGGER}) =~ s/::/\//g;
23 2         758 require $module . '.pm';
24             $LOG{LOGGER}->new(
25             file => $LOG{LOG_FILE},
26 2         13 );
27             },
28             TIME => 'gmtime',
29             TIME_FORMAT => 'default', # example '%Y%m%dT%H:%M:%S.%ms',
30             LOG => undef,
31             LOG_FUNCTIONS => {
32             error => 'error',
33             info => 'info',
34             debug => 'debug',
35             },
36             QUOTE => '†',
37             OPTIONS => {
38             date => 1,
39             guid => 1,
40             level => 1,
41             elapsed_call => 1,
42             caller => 1,
43             },
44             FORMAT_MESSAGE => sub {
45 15         152790 my %params = @_;
46 15         53 my $log_message = $params{message};
47 15   33     52 my $log_meth = $params{level} || $LOG{LOG_FUNCTIONS}{$LOG{LOG_LEVEL}};
48 15         38 my $options = $LOG{OPTIONS};
49             my $time = ! $options->{date}
50             ? 0
51             : $LOG{TIME_FORMAT} eq 'default'
52             ? $LOG{TIME} eq 'gmtime'
53             ? gmtime
54             : localtime
55 15 50       164 : do {
    50          
    50          
56 0 0       0 my @now = $LOG{TIME} eq 'gmtime'
57             ? gmtime
58             : localtime;
59 0         0 my ($format, $ms) = $LOG{TIME_FORMAT};
60 0 0       0 if ($format =~ s/\.\%ms$//) {
61 0         0 my $time = Time::HiRes::time;
62 0         0 $time =~ m/(\.\d+)/;
63 0         0 $ms = $1;
64             }
65 0         0 strftime($format, @now) . "$ms";
66             };
67 15         31 my $sprintf = "";
68 15         23 my @sprintf_params;
69 15 50       61 if ($time) {
70 15         28 $sprintf .= "%s ";
71 15         31 push @sprintf_params, $time;
72             }
73 15 100 66     86 if ($options->{guid} && $params{guid}) {
74 8         16 $sprintf .= "%s ";
75 8         13 push @sprintf_params, $params{guid};
76             }
77 15 50       67 if ($options->{level}) {
78 15         24 $sprintf .= "%s";
79 15         45 push @sprintf_params, uc $log_meth;
80             }
81 15         65 $sprintf =~ s/\s$//;
82 15         107 my $message = sprintf($sprintf, @sprintf_params);
83 15         97 for my $key (sort keys %params) {
84 79 100       301 next if $key =~ m/^prefix|level|guid$/;
85 42 100       107 if (ref $params{$key}) {
86 14         32 my $ref = ref $params{$key};
87 14 50       35 my $len = $ref eq 'ARRAY' ? scalar @{$params{$key} || []} - 1 : 1;
  14 50       51  
88 14         40 for my $i (0 .. $len) {
89 20 50       139 my $data = Dumper($ref eq 'HASH' ? $params{$key} : $params{params}->[$i]);
90 20         1456 $data =~ s/\$VAR1\s=\s//;
91 20 100       164 $data =~ s/(\s+)(['"][^"]+['"])*/defined $2 ? $2 : ""/gem;
  25         124  
92 20         76 $data =~ s/;$//;
93             $message = sprintf("%s %s%s=%s%s%s",
94             $message,
95             $params{prefix},
96             $i,
97             $LOG{QUOTE},
98             $data,
99             $LOG{QUOTE},
100 20         138 );
101             }
102             } else {
103 28         130 $message = sprintf("%s %s=%s%s%s", $message, $key, $LOG{QUOTE}, $params{$key}, $LOG{QUOTE});
104             }
105             }
106 15         131 return $message;
107             }
108 8     8   8836 );
109             }
110              
111             sub import {
112 10     10   171933 my ($pkg, @import) = @_;
113 10 100       46 if (scalar @import % 2) {
114 1         9 die "odd number of params passed in import";
115             }
116 9         45 my $caller = caller();
117             {
118 8     8   73 no strict 'refs';
  8         12  
  8         2120  
  9         27  
119 9         13 push @{"${caller}::ISA"}, $pkg;
  9         134  
120             }
121 9         86187 while (@import) {
122 5         9 my ($key, $val) = (shift @import, shift @import);
123 5         1646 $LOG{$key} = $val;
124             }
125             }
126              
127             sub MODIFY_CODE_ATTRIBUTES {
128 7     7   9131 my ($class,$code,@attrs) = @_;
129            
130 7 100       27 unless (ref $LOG{LOG}) {
131 2         7 $LOG{LOG} = $LOG{LOG_INIT}->();
132             }
133            
134 7 50       16 my ($att) = grep { $_ =~ m/Audit/ && $_ } @attrs;
  7         56  
135 7 50       20 if ($att) {
136 7         17 $att =~ m/Audit(?:\((.*)\))/;
137 7         38 $att = $1;
138 7         43 my $meta = B::svref_2object($code);
139 7         47 my $meth = $meta->GV->NAME;
140 7         101 my $caller = caller(1);
141 8     8   47 no strict 'refs';
  8         16  
  8         304  
142 8     8   35 no warnings 'redefine';
  8         11  
  8         3695  
143 7         35 *{"${caller}::$meth"} = sub {
144 5     5   688235 my $options = $LOG{OPTIONS};
145 5         17 my ($n, $caller) = (0, "");
146 5 50       27 if ($options->{caller}) {
147 5         55 while (my @l = (caller($n))) {
148 5 50       18 $caller .= "->" if $caller;
149 5         19 $caller = sprintf "%s%s:%s", $caller, $l[0], $l[2];
150 5         28 $n++;
151             }
152             }
153 5 50       67 my $guid = !$options->{guid} ? 0 : Data::GUID->new->as_string;
154 5         2401 my ($now, $after) = (0, 0);
155 5 50       50 log_message(
156             ($caller ? ( caller => $caller ) : ()),
157             guid => $guid,
158             message => sprintf(
159             "subroutine %s called with args:",
160             $meth
161             ),
162             params => [@_],
163             prefix => 'arg'
164             );
165 5 50       77 $now = time if $options->{elapsed_call};
166 5         48 my @out = $code->(@_);
167 5 50       250384 $after = time if $options->{elapsed_call};
168             log_message(
169             ($caller ? ( caller => $caller ) : ()),
170             guid => $guid,
171             message => sprintf(
172             "subroutine %s returned:",
173             $meth
174             ),
175             params => [@out],
176 5 50       60 ($options->{elapsed_call} ? (elapsed_call => $after == $now ? 0 : $after - $now) : ()),
    50          
    50          
177             prefix => 'return'
178             );
179 5 100       59 return wantarray ? @out : shift @out;
180 7         60 };
181 7         31 return;
182             }
183              
184             }
185              
186             sub log_message {
187 12     12 0 104955 my (%params) = @_;
188 12   33     112 my $log_meth = $params{level} || $LOG{LOG_FUNCTIONS}{$LOG{LOG_LEVEL}};
189             $LOG{LOG}->$log_meth(
190 12         65 $LOG{FORMAT_MESSAGE}->(
191             %params,
192             level => $log_meth
193             )
194             );
195             }
196              
197             1;
198              
199             __END__