File Coverage

blib/lib/Debug/Easy.pm
Criterion Covered Total %
statement 156 212 73.5
branch 52 92 56.5
condition 6 15 40.0
subroutine 14 24 58.3
pod 12 12 100.0
total 240 355 67.6


line stmt bran cond sub pod time code
1             package Debug::Easy 2.24;
2              
3 1     1   160011 use strict;
  1         2  
  1         73  
4             # use warnings;
5             use constant {
6 1         98 TRUE => 1,
7             FALSE => 0,
8 1     1   7 };
  1         1  
9              
10 1     1   6 use Config;
  1         4  
  1         64  
11 1     1   7 use Term::ANSIColor;
  1         3  
  1         84  
12 1     1   8 use Time::HiRes qw(time);
  1         2  
  1         9  
13 1     1   155 use File::Basename qw(fileparse);
  1         2  
  1         83  
14              
15 1     1   717 use Data::Dumper; # Included in Perl
  1         11463  
  1         136  
16             eval { # Data::Dumper::Simple is preferred. Try to load it without dying.
17             require Data::Dumper::Simple;
18             Data::Dumper::Simple->import();
19             1;
20             };
21              
22 1     1   13 use if ($Config{'useithreads'}), 'threads';
  1         2  
  1         107  
23              
24             BEGIN {
25 1     1   8 require Exporter;
26              
27             # Inherit from Exporter to export functions and variables
28 1         22 our @ISA = qw(Exporter);
29              
30             # Functions and variables which are exported by default
31 1         3 our @EXPORT = qw();
32              
33             # Functions and variables which can be optionally exported
34 1         4594 our @EXPORT_OK = qw(fileparse @Levels);
35             } ## end BEGIN
36              
37             # This can be optionally exported for whatever
38             our @Levels = qw( ERR WARN NOTICE INFO VERBOSE DEBUG DEBUGMAX );
39              
40             # For quick level checks to speed up execution
41             our %LevelLogic;
42             for (my $count = 0; $count < scalar(@Levels); $count++) {
43             $LevelLogic{ $Levels[$count] } = $count;
44             }
45              
46             our $PARENT = $$; # This needs to be defined at the very beginning before new
47             our ($SCRIPTNAME, $SCRIPTPATH, $suffix) = fileparse($0);
48             # our @months = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
49             # our @days = qw(Sun Mon Tue Wed Thu Fri Sat Sun);
50              
51             =encoding utf8
52              
53             =head1 NAME
54              
55             Debug::Easy - A Handy Debugging Module With Colorized Output and Formatting
56              
57             =head1 SYNOPSIS
58              
59             use Debug::Easy;
60              
61             my $debug = Debug::Easy->new( 'LogLevel' => 'DEBUG', 'Color' => 1 );
62              
63             'LogLevel' is the maximum level to report, and ignore the rest. The method names correspond to their loglevels, when outputting a specific message. This identifies to the module what type of message [...]
64              
65             The following is a list, in order of level, of the logging methods:
66              
67             ERR = Error
68             WARN = Warning
69             NOTICE = Notice
70             INFO = Information
71             VERBOSE = Special version of INFO that does not output any
72             Logging headings. Very useful for verbose modes in your
73             scripts.
74             DEBUG = Level 1 Debugging messages
75             DEBUGMAX = Level 2 Debugging messages (typically more terse like dumping
76             variables)
77              
78             The parameter is either a string or a reference to an array of strings to output as multiple lines.
79              
80             Each string can contain newlines, which will also be split into a separate line and formatted accordingly.
81              
82             $debug->ERR( ['Error message']);
83             $debug->ERROR( ['Error message']);
84              
85             $debug->WARN( ['Warning message']);
86             $debug->WARNING( ['Warning message']);
87              
88             $debug->NOTICE( ['Notice message']);
89             $debug->INFO( ['Information and VERBOSE mode message']);
90             $debug->INFORMATION(['Information and VERBOSE mode message']);
91              
92             $debug->DEBUG( ['Level 1 Debug message']);
93             $debug->DEBUGMAX( ['Level 2 (terse) Debug message']);
94              
95             my @messages = (
96             'First Message',
97             'Second Message',
98             "Third Message First Line\nThird Message Second Line",
99             \%hash_reference
100             );
101              
102             $debug->INFO([\@messages]);
103              
104             =head1 DESCRIPTION
105              
106             This module makes it easy to add debugging features to your code, Without having to re-invent the wheel. It uses STDERR and ANSI color formatted text output, as well as indented and multiline text fo [...]
107              
108             Benchmarking is automatic, to make it easy to spot bottlenecks in code. It automatically stamps from where it was called, and makes debug coding so much easier, without having to include the location [...]
109              
110             It also allows multiple output levels from errors only, to warnings, to notices, to verbose information, to full on debug output. All of this fully controllable by the coder.
111              
112             Generally all you need are the defaults and you are ready to go.
113              
114             =head1 B
115              
116             =head2 B<@Levels>
117              
118             A simple list of all the acceptable debug levels to pass as "LogLevel" in the {new} method. Not normally needed for coding, more for reference. Only exported if requested.
119              
120             =cut
121              
122             sub DESTROY { # We spit out one last message before we die, the total execute time.
123 6     6   6332 my $self = shift;
124             my $bench = (! $self->{'COLOR'})
125             ? sprintf('%06.2f', (time - $self->{'MASTERSTART'}))
126 6 50       214 : colored(['bright_cyan'], sprintf('%06.2f', (time - $self->{'MASTERSTART'})));
127 6         390 my $name = $SCRIPTNAME;
128 6 50       63 $name .= ' [child]' if ($PARENT ne $$);
129 6 50       29 unless ($self->{'COLOR'}) {
130 0         0 $self->DEBUG(["$bench ---- $name complete ----"]);
131             } else {
132 6         50 $self->DEBUG([$bench . ' ' . colored(['black on_white'], "---- $name complete ----")]);
133             }
134             } ## end sub DESTROY
135              
136             =head1 B
137              
138             =head2 B
139              
140             * The parameter names are case insensitive as of Version 0.04.
141              
142             =over 4
143              
144             =item B [level]
145              
146             This adjusts the global log level of the Debug object. It requires a string.
147              
148             =back
149              
150             =over 8
151              
152             B (default)
153              
154             This level shows only error messages and all other messages are not shown.
155              
156             B
157              
158             This level shows error and warning messages. All other messages are not shown.
159              
160             B
161              
162             This level shows error, warning, and notice messages. All other messages are not shown.
163              
164             B
165              
166             This level shows error, warning, notice, and information messages. Only debug level messages are not shown.
167              
168             B
169              
170             This level can be used as a way to do "Verbose" output for your scripts. It ouputs INFO level messages without logging headers and on STDOUT instead of STDERR.
171              
172             B
173              
174             This level shows error, warning, notice, information, and level 1 debugging messages. Level 2 Debug messages are not shown.
175              
176             B
177              
178             This level shows all messages up to level 2 debugging messages.
179              
180             NOTE: It has been asked "Why have two debugging levels?" Well, I have had many times where I would like to see what a script is doing without it showing what I consider garbage overhead it may gene [...]
181              
182             =back
183              
184             =over 4
185              
186             =item B [boolean] (Not case sensitive)
187              
188             B<0>, B, or B (Off)
189              
190             This turns off colored output. Everything is plain text only.
191              
192             B<1>, B, or B (On - Default)
193              
194             This turns on colored output. This makes it easier to spot all of the different types of messages throughout a sea of debug output. You can read the output with "less", and see color, by using it' [...]
195              
196             =back
197              
198             =over 4
199              
200             =item B [pattern]
201              
202             This is global
203              
204             A string that is parsed into the output prefix.
205              
206             DEFAULT: '%Date% %Time% %Benchmark% %Loglevel%[%Subroutine%][%Lastline%] '
207              
208             %Date% = Date (Uses format of "DateStamp" below)
209             %Time% = Time (Uses format of "TimeStamp" below)
210             %Epoch% = Epoch (Unix epoch)
211             %Benchmark% = Benchmark - The time it took between the last benchmark display
212             of this loglevel. If in an INFO level message, it benchmarks
213             the time until the next INFO level message. The same rule is
214             true for all loglevels.
215             %Loglevel% = Log Level
216             %Lines% = Line Numbers of all nested calls
217             %Module% = Module and subroutine of call (can be a lot of stuff!)
218             %Subroutine% = Just the last subroutine
219             %Lastline% = Just the last line number
220             %PID% = Process ID
221             %date% = Just Date (typically used internally only, use %Date%)
222             %time% = Just time (typically used internally only, use %Time%)
223             %epoch% = Unix epoch (typically used internally only, use %Epoch%)
224             %Filename% = Script Filename (parsed $0)
225             %Fork% = Running in parent or child?
226             P = Parent
227             C = Child
228             %Thread% = Running in Parent or Thread
229             P = Parent
230             T## = Thread # = Thread ID
231              
232             =item B<[loglevel]-Prefix> [pattern]
233              
234             You can define a prefix for a specific log level.
235              
236             ERR-Prefix
237             WARN-Prefix
238             NOTICE-Prefix
239             INFO-Prefix
240             DEBUG-Prefix
241             DEBUGMAX-Prefix
242              
243             If one of these are not defined, then the global value is used.
244              
245             =item B [pattern]
246              
247             (See Log::Fast for specifics on these)
248              
249             I suggest you just use Prefix above, but here it is anyway.
250              
251             Make this an empty string to turn it off, otherwise:
252              
253             =back
254              
255             =over 8
256              
257             B<%T>
258              
259             Formats the timestamp as HH:MM:SS. This is the default for the timestamp.
260              
261             B<%S>
262              
263             Formats the timestamp as seconds.milliseconds. Normally not needed, as the benchmark is more helpful.
264              
265             B<%T %S>
266              
267             Combines both of the above. Normally this is just too much, but here if you really want it.
268              
269             =back
270              
271             =over 4
272              
273             =item B [pattern]
274              
275             I suggest you just use Prefix above, but here it is anyway.
276              
277             Make this an empty string to turn it off, otherwise:
278              
279             =back
280              
281             =over 8
282              
283             B<%D>
284              
285             Formats the datestamp as YYYY-MM-DD. It is the default, and the only option.
286              
287             =back
288              
289             =over 4
290              
291             =item B
292              
293             File handle to write log messages.
294              
295             =item B
296              
297             Contains a hash reference describing the various colored debug level labels
298              
299             The default definition (using Term::ANSIColor) is as follows:
300              
301             =back
302              
303             =over 8
304              
305             'ANSILevel' => {
306             'ERR' => colored(['white on_red'], '[ ERROR ]'),
307             'WARN' => colored(['black on_yellow'], '[WARNING ]'),
308             'NOTICE' => colored(['yellow'], '[ NOTICE ]'),
309             'INFO' => colored(['black on_white'], '[ INFO ]'),
310             'DEBUG' => colored(['bold green'], '[ DEBUG ]'),
311             'DEBUGMAX' => colored(['bold black on_green'], '[DEBUGMAX]'),
312             }
313              
314             =back
315              
316             =cut
317              
318             sub new {
319 6     6 1 203926 my $class = shift;
320 6         276 my ($filename, $dir, $suffix) = fileparse($0);
321 6         33 my $tm = time;
322 6         66 my $self = { # The keys are set to upper-case later in the initialization
323             'LOGLEVEL' => 'ERR', # Default is errors only
324             'TYPE' => 'fh', # Default is a filehandle
325             'PATH' => '/var/log', # Default path should type be unix
326             'FILEHANDLE' => \*STDERR, # Default filehandle is STDERR
327             'MASTERSTART' => $tm,
328             'ANY_LASTSTAMP' => $tm, # Initialize main benchmark
329             'ERR_LASTSTAMP' => $tm, # Initialize the ERR benchmark
330             'WARN_LASTSTAMP' => $tm, # Initialize the WARN benchmark
331             'INFO_LASTSTAMP' => $tm, # Initialize the INFO benchmark
332             'NOTICE_LASTSTAMP' => $tm, # Initialize the NOTICE benchmark
333             'DEBUG_LASTSTAMP' => $tm, # Initialize the DEBUG benchmark
334             'DEBUGMAX_LASTSTAMP' => $tm, # Initialize the DEBUGMAX benchmark
335             'COLOR' => TRUE, # Default to colorized output
336             'DATESTAMP' => colored(['yellow'], '%date%'),
337             'TIMESTAMP' => colored(['yellow'], '%time%'),
338             'EPOCH' => colored(['cyan'], '%epoch%'),
339             'PADDING' => -20, # Default padding is 20 spaces
340             'LINES-PADDING' => -2,
341             'SUBROUTINE-PADDING' => 0,
342             'LINE-PADDING' => 0,
343             'PARENT' => $$,
344             'GLOBAL-PREFIX' => '%Date% %Time% %Benchmark% %Loglevel%[%Subroutine%][%Lastline%] ',
345             'DEBUGMAX-PREFIX' => '%Date% %Time% %Benchmark% %Loglevel%[%Module%][%Lines%] ',
346             'FILENAME' => '[' . colored(['magenta'], $filename) . ']',
347             'ANSILEVEL' => {
348             'ERR' => colored(['white on_red'], '[ ERROR ]'),
349             'WARN' => colored(['black on_yellow'], '[WARNING ]'),
350             'NOTICE' => colored(['yellow'], '[ NOTICE ]'),
351             'INFO' => colored(['black on_white'], '[ INFO ]'),
352             'DEBUG' => colored(['bold green'], '[ DEBUG ]'),
353             'DEBUGMAX' => colored(['bold black on_green'], '[DEBUGMAX]'),
354             },
355             };
356              
357             # This pretty much makes all hash keys uppercase
358 6         3359 my @Keys = (keys %{$self}); # Hash is redefined on the fly, so get the list before
  6         46  
359 6         29 foreach my $Key (@Keys) {
360 150         307 my $upper = uc($Key);
361 150 50       517 if ($Key ne $upper) {
    100          
362 0         0 $self->{$upper} = $self->{$Key};
363              
364             # This fixes a documentation error for past versions
365 0 0       0 if ($upper eq 'LOGLEVEL') {
366 0 0       0 $self->{$upper} = 'ERR' if ($self->{$upper} =~ /^ERROR$/i);
367 0         0 $self->{$upper} = uc($self->{$upper}); # Make loglevels case insensitive
368             }
369 0         0 delete($self->{$Key}); # Get rid of the bad key
370             } elsif ($Key eq 'LOGLEVEL') { # Make loglevels case insensitive
371 6         26 $self->{$upper} = uc($self->{$upper});
372             }
373             } ## end foreach my $Key (@Keys)
374             { # This makes sure the user overrides actually override
375 6         14 my %params = (@_);
  6         39  
376 6         23 foreach my $Key (keys %params) {
377 18         89 $self->{ uc($Key) } = $params{$Key};
378             }
379             }
380              
381             # Cache numeric log level value for quick comparisons
382 6         34 $self->{'LOGLEVEL_VALUE'} = $LevelLogic{ $self->{'LOGLEVEL'} };
383              
384             # Cache thread support check for hot path
385 6 50       124 $self->{'USE_THREADS'} = ($Config{'useithreads'}) ? 1 : 0;
386              
387             # This instructs the ANSIColor library to turn off coloring,
388             # if the Color attribute is set to zero.
389 6 50       34 unless ($self->{'COLOR'}) {
390             # If COLOR is FALSE, then clear color data from ANSILEVEL, as these were
391             # defined before color was turned off.
392 0         0 $self->{'ANSILEVEL'} = {
393             'ERR' => '[ ERROR ]',
394             'WARN' => '[WARNING ]',
395             'NOTICE' => '[ NOTICE ]',
396             'INFO' => '[ INFO ]',
397             'DEBUG' => '[ DEBUG ]',
398             'DEBUGMAX' => '[DEBUGMAX]',
399             };
400 0         0 $self->{'DATESTAMP'} = '%date%';
401 0         0 $self->{'TIMESTAMP'} = '%time%';
402 0         0 $self->{'EPOCH'} = '%epoch%';
403 0         0 $self->{'FILENAME'} = '[' . $filename . ']'; # Ensure filename without color
404             }
405              
406 6         21 foreach my $lvl (@Levels) { # Set any undefined prefix to the global prefix
407 42 100 66     213 $self->{"$lvl-PREFIX"} = $self->{'GLOBAL-PREFIX'} unless (exists($self->{"$lvl-PREFIX"}) && defined($self->{"$lvl-PREFIX"}));
408             }
409              
410             # Precompute static prefix templates per level to minimize per-line substitutions.
411             # We will leave dynamic tokens (%date%, %time%, %epoch%, %Benchmark%) for runtime.
412 6         22 $self->{'_PREFIX_TEMPLATES'} = {};
413 6         17 foreach my $lvl (@Levels) {
414 42         145 my $tmpl = $self->{"$lvl-PREFIX"} . ''; # copy
415 42 50       205 my $forked = ($PARENT ne $$) ? 'C' : 'P';
416 42         73 my $threaded = 'PT-';
417 42 50       106 if ($self->{'USE_THREADS'}) {
418 0 0       0 my $tid = threads->can('tid') ? threads->tid() : 0;
419 0 0 0     0 $threaded = ($tid && $tid > 0) ? sprintf('T%02d', $tid) : 'PT-';
420             }
421              
422             my %mp = (
423             'PID' => $$,
424             'Loglevel' => $self->{'ANSILEVEL'}->{$lvl},
425 42         290 'Filename' => $self->{'FILENAME'},
426             'Fork' => $forked,
427             'Thread' => $threaded,
428             );
429              
430             # Static substitutions
431 42         285 $tmpl =~ s/\%(PID|Loglevel|Filename|Fork|Thread)\%/$mp{$1}/gei;
  42         305  
432              
433 42         219 $self->{'_PREFIX_TEMPLATES'}->{$lvl} = $tmpl;
434             }
435              
436 6         16 my $fh = $self->{'FILEHANDLE'};
437              
438             # Signal the script has started (and logger initialized)
439 6         19 my $name = $SCRIPTNAME;
440 6 50       41 $name .= ' [child]' if ($PARENT ne $$);
441 6 50       66 my $string = (! $self->{'COLOR'}) ? "----- $name begin -----" : colored(['black on_white'], "----- $name begin -----");
442 6 100       513 print $fh sprintf(' %.02f%s %s%s', 0, $self->{'ANSILEVEL'}->{'DEBUG'}, $string, " (To View in 'less', use it's '-r' switch)"), "\n" if ($self->{'LOGLEVEL'} !~ /ERR/);
443              
444 6         23 bless($self, $class);
445 6         57 return ($self);
446             } ## end sub new
447              
448             =head2 debug
449              
450             NOTE: This is a legacy method for backwards compatibility. Please use the direct methods instead.
451              
452             The parameters must be passed in the order given
453              
454             =over 4
455              
456             =item B
457              
458             The log level with which this message is to be triggered
459              
460             =item B
461              
462             A string or a reference to a list of strings to output line by line.
463              
464             =back
465              
466             =cut
467              
468             sub debug {
469 114     114 1 110389 my $self = shift;
470 114         298 my $level = uc(shift);
471 114         236 my $msgs = shift;
472              
473 114 50       1015 if ($level !~ /ERR.*|WARN.*|NOTICE|INFO.*|DEBUG/i) { # Compatibility with older versions.
474 0         0 $level = uc($msgs); # It tosses the legacy __LINE__ argument
475 0         0 $msgs = shift;
476             }
477 114         499 $level =~ s/(OR|ING|RMATION)$//; # Strip off the excess
478              
479             # A much quicker bypass when the log level is below what is needed
480             # This minimizes the execution overhead for log levels not active.
481 114 100       632 return if ($self->{'LOGLEVEL_VALUE'} < $LevelLogic{$level});
482              
483 65         110 my @messages;
484 65 50       298 if (ref($msgs) =~ /HASH|CODE|FORMAT|IO/) {
    100          
485 0         0 push(@messages, _send_to_Dumper($msgs));
486             } elsif (ref($msgs) eq 'ARRAY') {
487 23         44 @messages = @{$msgs};
  23         80  
488             } else {
489 42         96 push(@messages, $msgs);
490             }
491 65         245 my ($sname, $cline, $nested, $subroutine, $thisBench, $sline, $short) = ('', '', '', '', '', '', '');
492             # Figure out the proper caller tree and line number ladder
493             # But only if it's part of the effective level prefix, else don't waste time.
494             # The effective level prefix can be different for each call to debug. It cannot be cached.
495 65   33     233 my $effective_prefix = $self->{ $level . '-PREFIX' } || $self->{'GLOBAL-PREFIX'};
496 65 50       550 if ($effective_prefix =~ /\%(Subroutine|Module|Lines|Lastline)\%/i) { # %P = Subroutine, %l = Line number(s)
497 65         140 my $package = '';
498 65         102 my $count = 1;
499 65         114 my $nest = 0;
500 65         274 while (my @array = caller($count)) {
501 6 100       22 if ($array[3] !~ /Debug::Easy/) {
502 2         5 $package = $array[0];
503 2         6 my $subroutine = $array[3];
504 2         29 $subroutine =~ s/^$package\:\://;
505 2         16 $sname =~ s/$subroutine//;
506 2 50       10 if ($sname eq '') {
507 2 50       9 $sname = ($subroutine ne '') ? $subroutine : $package;
508 2         6 $cline = $array[2];
509             } else {
510 0         0 $sname = $subroutine . '::' . $sname;
511 0         0 $cline = $array[2] . '/' . $cline;
512             }
513 2 50       7 if ($count == 2) {
514 0         0 $short = $array[3];
515 0         0 $sline = $array[2];
516             }
517 2         5 $nest++;
518             } ## end if ($array[3] !~ /Debug::Easy/)
519 6         36 $count++;
520             } ## end while (my @array = caller...)
521 65 100       162 if ($package ne '') {
522 2         6 $sname = $package . '::' . $sname;
523 2 50       8 $nested = ' ' x $nest if ($nest);
524             } else {
525 63         124 my @array = caller(1);
526 63         122 $cline = $array[2];
527 63 50 33     240 if (!defined($cline) || $cline eq '') {
528 63         445 @array = caller(0);
529 63         159 $cline = $array[2];
530             }
531 63         110 $sname = 'main';
532 63         139 $sline = $cline;
533 63         195 $short = $sname;
534             } ## end else [ if ($package ne '') ]
535 65 50       160 $subroutine = ($sname ne '') ? $sname : 'main';
536 65 50       233 $self->{'PADDING'} = 0 - length($subroutine) if (length($subroutine) > abs($self->{'PADDING'}));
537 65 50       228 $self->{'LINES-PADDING'} = 0 - length($cline) if (length($cline) > abs($self->{'LINES-PADDING'}));
538 65 100       197 $self->{'SUBROUTINE-PADDING'} = 0 - length($short) if (length($short) > abs($self->{'SUBROUTINE-PADDING'}));
539 65 100       165 $self->{'LINE-PADDING'} = 0 - length($sline) if (length($sline) > abs($self->{'LINE-PADDING'}));
540 65         272 $cline = sprintf('%' . $self->{'LINES-PADDING'} . 's', $cline);
541 65 50       485 $subroutine = (! $self->{'COLOR'}) ? sprintf('%' . $self->{'PADDING'} . 's', $subroutine) : colored(['bold cyan'], sprintf('%' . $self->{'PADDING'} . 's', $subroutine));
542 65         4412 $sline = sprintf('%' . $self->{'LINE-PADDING'} . 's', $sline);
543 65 50       379 $short = (! $self->{'COLOR'}) ? sprintf('%' . $self->{'SUBROUTINE-PADDING'} . 's', $short) : colored(['bold cyan'], sprintf('%' . $self->{'SUBROUTINE-PADDING'} . 's', $short));
544             } ## end if ($effective_prefix ...)
545              
546             # Figure out the benchmarks, but only if it is in the prefix
547 65 50       4262 if ($effective_prefix =~ /\%Benchmark\%/i) {
548             # For multiline output, only output the bench data on the first line. Use padded spaces for the rest.
549 65         686 $thisBench = sprintf('%7s', sprintf(' %.02f', time - $self->{'ANY_LASTSTAMP'}));
550             } ## end if ($effective_prefix ...)
551 65         121 my $first = TRUE; # Set the first line flag.
552              
553             # Buffer lines to reduce syscalls for multi-line messages
554 65         116 my $buffer = '';
555              
556 65         163 my $fh = $self->{'FILEHANDLE'};
557 65 50 66     291 if ($level eq 'INFO' && $self->{'LOGLEVEL'} eq 'VERBOSE') {
558             # For verbose, we need to print messages without prefixes.
559             # Extract lines and print only message contents.
560 0         0 foreach my $msg (@messages) {
561 0 0       0 if (ref($msg) ne '') {
562 0         0 $msg = _send_to_Dumper($msg);
563             }
564 0 0       0 if ($msg =~ /\n/s) {
565 0         0 my @message = split(/\n/, $msg);
566 0         0 foreach my $line (@message) {
567 0         0 print $fh "$line\n";
568             }
569             } else {
570 0         0 print $fh "$msg\n";
571             }
572             }
573             } else {
574 65         1969 my ($sec,$min,$hour,$mday,$mon,$year) = localtime();
575 65         363 my $Date = sprintf('%02d/%02d/%04d', $mday, ($mon + 1), (1900 + $year));
576 65         226 my $Time = sprintf('%02d:%02d:%02d', $hour, $min, $sec);
577 65         200 my $epoch = time;
578              
579 65         212 foreach my $msg (@messages) { # Loop through each line of output and format accordingly.
580 107 50       328 if (ref($msg) =~ /HASH|ARRAY|CODE|FORMAT|IO/) {
581 0         0 $msg = _send_to_Dumper($msg);
582             }
583 107 100       297 if ($msg =~ /\n/s) { # If the line contains newlines, then it too must be split into multiple lines.
584 21         78 my @message = split(/\n/, $msg);
585 21         45 foreach my $line (@message) { # Loop through the split lines and format accordingly.
586 42         172 $buffer .= $self->_format_line($level, $nested, $line, $first, $thisBench, $subroutine, $cline, $sline, $short, $Date, $Time, $epoch);
587 42         88 $buffer .= "\n";
588 42         136 $first = FALSE; # Clear the first line flag.
589             }
590             } else { # This line does not contain newlines. Treat it as a single line.
591 86         332 $buffer .= $self->_format_line($level, $nested, $msg, $first, $thisBench, $subroutine, $cline, $sline, $short);
592 86         195 $buffer .= "\n";
593             }
594 107         314 $first = FALSE; # Clear the first line flag.
595             } ## end foreach my $msg (@messages)
596 65 100       168 if ($level eq 'DEBUGMAX') { # Special version of DEBUG. Extremely verbose debugging and quite noisy
597 3 50       10 if ($self->{'LOGLEVEL'} eq 'DEBUGMAX') {
598 3         19 print $fh $buffer;
599             }
600             } else {
601 62         425 print $fh $buffer;
602             }
603             }
604              
605 65         250 $self->{'ANY_LASTSTAMP'} = time;
606 65         526 $self->{ $level . '_LASTSTAMP' } = time;
607             } ## end sub debug
608              
609             sub _send_to_Dumper {
610 0     0   0 local $Data::Dumper::Terse = TRUE;
611 0         0 local $Data::Dumper::Indent = TRUE;
612 0         0 local $Data::Dumper::Useqq = TRUE;
613 0         0 local $Data::Dumper::Deparse = TRUE;
614 0         0 local $Data::Dumper::Quotekeys = TRUE;
615 0         0 local $Data::Dumper::Trailingcomma = TRUE;
616 0         0 local $Data::Dumper::Sortkeys = TRUE;
617 0         0 local $Data::Dumper::Purity = TRUE;
618 0         0 return(Dumper(@_));
619             }
620              
621             # Internal: format a single line for logging (without printing)
622             sub _format_line {
623 128     128   489 my ($self, $level, $padding, $msg, $first, $thisBench, $subroutine, $cline, $sline, $shortsub, $Date, $Time, $epoch) = @_;
624              
625             # Build prefix based on precomputed template and runtime substitutions
626 128         374 my $tmpl = $self->{'_PREFIX_TEMPLATES'}->{$level};
627 128 50       310 $tmpl = $self->{"$level-PREFIX"} . '' unless defined $tmpl; # Fallback safety
628              
629             # Clone template since we mutate
630 128         241 my $prefix = "$tmpl";
631              
632             my %mp = ( # Create a temporary index
633             'Benchmark' => ($first) ? $thisBench : ' ' x length($thisBench),
634             'Lines' => $cline,
635             'Lastline' => $cline,
636             'Subroutine' => $shortsub,
637             'Module' => $subroutine,
638             'Date' => $self->{'DATESTAMP'},
639             'Time' => $self->{'TIMESTAMP'},
640 128 100       1255 'Epoch' => $self->{'EPOCH'},
641             'date' => $Date,
642             'time' => $Time,
643             'epoch' => $epoch,
644             );
645 128         809 $prefix =~ s/\%(Lines|Lastline|Subroutine|Module|Date|Time|Epoch|date|time|epoch)\%/$mp{$1}/ge;
  512         2263  
646              
647 128         957 return "$prefix$padding$msg";
648             }
649              
650             =head2 B or B
651              
652             Sends ERROR level debugging output to the log. Errors are always shown.
653              
654             =over 4
655              
656             =item B
657              
658             Either a single string or a reference to a list of strings
659              
660             =back
661             =cut
662              
663             sub ERR {
664 0     0 1 0 my $self = shift;
665 0         0 $self->debug('ERR', @_);
666             }
667              
668             sub ERROR {
669 0     0 1 0 my $self = shift;
670 0         0 $self->debug('ERR', @_);
671             }
672              
673             =head2 B or B
674              
675             If the log level is WARN or above, then these warnings are logged.
676              
677             =over 4
678              
679             =item B
680              
681             Either a single string or a reference to a list of strings
682              
683             =back
684             =cut
685              
686             sub WARN {
687 0     0 1 0 my $self = shift;
688 0         0 $self->debug('WARN', @_);
689             }
690              
691             sub WARNING {
692 0     0 1 0 my $self = shift;
693 0         0 $self->debug('WARN', @_);
694             }
695              
696             =head2 B or B
697              
698             If the loglevel is NOTICE or above, then these notices are logged.
699              
700             =over 4
701              
702             =item B
703              
704             Either a single string or a reference to a list of strings
705              
706             =back
707             =cut
708              
709             sub NOTICE {
710 0     0 1 0 my $self = shift;
711 0         0 $self->debug('NOTICE', @_);
712             }
713              
714             sub ATTENTION {
715 0     0 1 0 my $self = shift;
716 0         0 $self->debug('NOTICE', @_);
717             }
718              
719             =head2 B or B
720              
721             If the loglevel is INFO (or VERBOSE) or above, then these information messages are displayed.
722              
723             =over 4
724              
725             =item B
726              
727             Either a single string or a reference to a list of strings
728              
729             =back
730             =cut
731              
732             sub INFO {
733 0     0 1 0 my $self = shift;
734 0         0 $self->debug('INFO', @_);
735             }
736              
737             sub INFORMATION {
738 0     0 1 0 my $self = shift;
739 0         0 $self->debug('INFO', @_);
740             }
741              
742             =head2 B
743              
744             If the Loglevel is DEBUG or above, then basic debugging messages are logged. DEBUG is intended for basic program flow messages for easy tracing. Best not to place variable contents in these messages [...]
745              
746             =over 4
747              
748             =item B
749              
750             Either a single string or a reference to a list of strings
751              
752             =back
753             =cut
754              
755             sub DEBUG {
756 6     6 1 419 my $self = shift;
757 6         23 $self->debug('DEBUG', @_);
758             }
759              
760             =head2 B
761              
762             If the loglevel is DEBUGMAX, then all messages are shown, and terse debugging messages as well. Typically DEBUGMAX is used for variable dumps and detailed data output for heavy tracing. This is a ve [...]
763              
764             =over 4
765              
766             =item B
767              
768             Either a single string or a reference to a list of strings
769              
770             =back
771             =cut
772              
773             sub DEBUGMAX {
774 0     0 1   my $self = shift;
775 0           $self->debug('DEBUGMAX', @_);
776             }
777              
778             1;
779              
780             =head1 B
781              
782             Since it is possible to duplicate the object in a fork or thread, the output formatting may be mismatched between forks and threads due to the automatic padding adjustment of the subroutine name field [...]
783              
784             Ways around this are to separately create a Debug::Easy object in each fork or thread, and have them log to separate files.
785              
786             The "less" pager is the best for viewing log files generated by this module. It's switch "-r" allows you to see them in all their colorful glory.
787              
788             =head1 B
789              
790             To install this module, run the following commands:
791              
792             perl Makefile.PL
793             make
794             make test
795             [sudo] make install
796              
797             =head1 AUTHOR
798              
799             Richard Kelsch
800              
801             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
802              
803             =head1 B
804              
805             Version 2.19
806              
807             =head1 B
808              
809             You can find documentation for this module with the perldoc command.
810              
811             C
812              
813             or if you have "man" installed, then
814              
815             C
816              
817             You can also look for information at: L
818              
819             =head1 B
820              
821             I coded this module because it filled a gap when I was working for a major chip manufacturing company (which I coded at home on my own time). It gave the necessary output the other coders asked for, [...]
822              
823             If you have any features you wish added, or functionality improved or changed, then I welcome them, and will very likely incorporate them sooner than you think.
824              
825             =head1 B
826              
827             Copyright 2013-2025 Richard Kelsch.
828              
829             This program is free software; you can redistribute it and/or modify it under the terms of the the Artistic License (2.0). You may obtain a copy of the full license at:
830              
831             L
832              
833             Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, [...]
834              
835             If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of t [...]
836              
837             This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder.
838              
839             This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent cla [...]
840              
841             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A [...]
842              
843             =head1 B
844              
845             Perl modules available on github - L
846              
847             And available on CPAN:
848              
849             * BBS::Universal
850             * Debug::Easy
851             * Graphics::Framebuffer
852             * Term::ANSIEncode
853             * BBS::Universal - A Perl based Internet BBS server
854              
855             =cut
856