File Coverage

lib/DBI/Log.pm
Criterion Covered Total %
statement 80 116 68.9
branch 16 26 61.5
condition 0 3 0.0
subroutine 11 16 68.7
pod 0 2 0.0
total 107 163 65.6


line stmt bran cond sub pod time code
1             package DBI::Log;
2              
3 1     1   90368 use 5.006;
  1         4  
4 1     1   5 no strict;
  1         3  
  1         21  
5 1     1   5 no warnings;
  1         3  
  1         33  
6 1     1   5 use DBI;
  1         1  
  1         32  
7 1     1   562 use Time::HiRes;
  1         1407  
  1         5  
8              
9             our $VERSION = "0.09";
10             our %opts = (
11             file => $file,
12             trace => 0,
13             timing => 0,
14             fh => undef,
15             exclude => undef,
16             );
17              
18             my $orig_execute = \&DBI::st::execute;
19             *DBI::st::execute = sub {
20 3     3   14804 my ($sth, @args) = @_;
21 3         28 my $log = pre_query("execute", $sth->{Database}, $sth->{Statement}, \@args);
22 3         28245 my $retval = $orig_execute->($sth, @args);
23 3         26 post_query($log);
24 3         13 return $retval;
25             };
26              
27             my $orig_selectall_arrayref = \&DBI::db::selectall_arrayref;
28             *DBI::db::selectall_arrayref = sub {
29 0     0   0 my ($dbh, $query, $yup, @args) = @_;
30 0         0 my $log = pre_query("selectall_arrayref", $dbh, $query, \@args);
31 0         0 my $retval = $orig_selectall_arrayref->($dbh, $query, $yup, @args);
32 0         0 post_query($log);
33 0         0 return $retval;
34             };
35              
36             my $orig_selectcol_arrayref = \&DBI::db::selectcol_arrayref;
37             *DBI::db::selectcol_arrayref = sub {
38 1     1   8 my ($dbh, $query, $yup, @args) = @_;
39 1         4 my $log = pre_query("selectcol_arrayref", $dbh, $query, \@args);
40 1         33 my $retval = $orig_selectcol_arrayref->($dbh, $query, $yup, @args);
41 1         81 post_query($log);
42 1         3 return $retval;
43             };
44              
45             my $orig_selectall_hashref = \&DBI::db::selectall_hashref;
46             *DBI::db::selectall_hashref = sub {
47 0     0   0 my ($dbh, $query, $yup, @args) = @_;
48 0         0 my $log = pre_query("selectall_hashref", $dbh, $query, \@args);
49 0         0 my $retval = $orig_selectall_hashref->($dbh, $query, $yup, @args);
50 0         0 post_query($log);
51 0         0 return $retval;
52             };
53              
54             my $orig_selectrow_arrayref = \&DBI::db::selectrow_arrayref;
55             *DBI::db::selectrow_arrayref = sub {
56 0     0   0 my ($dbh, $query, $yup, @args) = @_;
57 0         0 my $log = pre_query("selectrow_arrayref", $dbh, $query, \@args);
58 0         0 my $retval = $orig_selectrow_arrayref->($dbh, $query, $yup, @args);
59 0         0 post_query($log);
60 0         0 return $retval;
61             };
62              
63             my $orig_selectrow_array = \&DBI::db::selectrow_array;
64             *DBI::db::selectrow_array = sub {
65 0     0   0 my ($dbh, $query, $yup, @args) = @_;
66 0         0 my $log = pre_query("selectrow_array", $dbh, $query, \@args);
67 0         0 my $retval = $orig_selectrow_array->($dbh, $query, $yup, @args);
68 0         0 post_query($log);
69 0         0 return $retval;
70             };
71              
72             my $orig_selectrow_hashref = \&DBI::db::selectrow_hashref;
73             *DBI::db::selectrow_hashref = sub {
74 0     0   0 my ($dbh, $query, $yup, @args) = @_;
75 0         0 my $log = pre_query("selectrow_hashref", $dbh, $query, \@args);
76 0         0 my $retval = $orig_selectrow_hashref->($dbh, $query, $yup, @args);
77 0         0 post_query($log);
78 0         0 return $retval;
79             };
80              
81             my $orig_do = \&DBI::db::do;
82             *DBI::db::do = sub {
83 2     2   20 my ($dbh, $query, $yup, @args) = @_;
84 2         6 my $log = pre_query("do", $dbh, $query, \@args);
85 2         17 my $retval = $orig_do->($dbh, $query, $yup, @args);
86 1         41 post_query($log);
87 1         5 return $retval;
88             };
89              
90              
91             sub import {
92 1     1   10 my ($package, %args) = @_;
93 1         3 for my $key (keys %args) {
94 1         3 $opts{$key} = $args{$key};
95             }
96 1 50       4 if (!$opts{file}) {
97 0         0 $opts{fh} = \*STDERR;
98             }
99             else {
100 1         2 my $file2 = $opts{file};
101 1 50       5 if ($file2 =~ m{^~/}) {
102 0   0     0 my $home = $ENV{HOME} || (getpwuid($<))[7];
103 0         0 $file2 =~ s{^~/}{$home/};
104             }
105 1 50       1765 open $opts{fh}, ">>", $file2 or die "Can't open $opts{file}: $!";
106             }
107             }
108              
109             sub pre_query {
110 6     6 0 41 my ($name, $dbh, $query, $args) = @_;
111 6         12 my $log = {};
112 6         10 my $mcount = 0;
113              
114             # Some DBI functions are composed of other DBI functions, so make sure we
115             # are only logging the top level one. For example $dbh->do() will call
116             # $dbh->execute() internally, so we need to make sure a DBI::Log function
117             # logs the $dbh->do() and not the internal $dbh->execute(). If multiple
118             # functions were called, we return and flag this log entry to be skipped in
119             # the post_query() part.
120 6         69 for (my $i = 0; my @caller = caller($i); $i++) {
121 15         29 my ($package, $file, $line, $sub) = @caller;
122 15 100       45 if ($package eq "DBI::Log") {
123 8         8 $mcount++;
124 8 100       53 if ($mcount > 1) {
125 2         9 $log->{skip} = 1;
126 2         98 return $log;
127             }
128             }
129             }
130 4         5 my @callers;
131 4         19 for (my $i = 0; my @caller = caller($i); $i++) {
132 9         54 push @callers, \@caller;
133             }
134              
135             # Order the call stack based on the highest level calls first, then the
136             # lower level calls. Once you reach a package that is excluded, do not show
137             # any more lines in the stack trace. By default, it will exclude anything
138             # past the DBI::Log package, but if user provides an exclude option, it will
139             # stop there.
140 4         4 my @filtered_callers;
141 4         8 CALLER: for my $caller (reverse @callers) {
142 9         13 my ($package, $file, $line, $sub) = @$caller;
143 9 100       15 if ($package eq "DBI::Log") {
144 4         8 last CALLER;
145             }
146 5 50       9 if ($opts{exclude}) {
147 0         0 for my $item (@{$opts{exclude}}) {
  0         0  
148 0 0       0 if ($package =~ /^$item(::|$)/) {
149 0         0 last CALLER;
150             }
151             }
152             }
153 5         9 push @filtered_callers, $caller;
154              
155             }
156 4 50       11 if (!$opts{trace}) {
157 4         19 @filtered_callers = ($filtered_callers[-1]);
158             }
159              
160 4         5 my $stack = "";
161 4         8 for my $caller (@filtered_callers) {
162 4         9 my ($package, $file, $line, $sub) = @$caller;
163 4         4 my $short_sub = $sub;
164 4         27 $short_sub =~ s/.*:://;
165 4 50       13 $short_sub = $name if $sub eq "DBI::Log::__ANON__";
166 4         16 $stack .= "-- $short_sub $file $line\n";
167             }
168              
169 4 50       8 if ($dbh) {
170 4         5 my $i = 0;
171 4         28 $query =~ s{\?}{$dbh->quote($args->[$i++])}eg;
  4         55  
172             }
173 4         60 $query =~ s/^\s*\n|\s*$//g;
174 4         145 $info = "-- " . scalar(localtime()) . "\n";
175 4         10 print {$opts{fh}} "$info$stack$query\n";
  4         30  
176 4         22 $log->{time1} = Time::HiRes::time();
177 4         21 return $log;
178             }
179              
180             sub post_query {
181 5     5 0 15 my ($log) = @_;
182 5 100       42 return if $log->{skip};
183 3 50       10 if ($opts{timing}) {
184 0         0 $log->{time2} = Time::HiRes::time();
185 0         0 my $diff = sprintf '%.3f', $log->{time2} - $log->{time1};
186 0         0 print {$opts{fh}} "-- ${diff}s\n";
  0         0  
187             }
188 3         4 print {$opts{fh}} "\n";
  3         11  
189             }
190              
191             1;
192              
193             __END__