File Coverage

blib/lib/Debug/Easy.pm
Criterion Covered Total %
statement 179 241 74.2
branch 63 106 59.4
condition 9 24 37.5
subroutine 14 25 56.0
pod 12 12 100.0
total 277 408 67.8


line stmt bran cond sub pod time code
1             package Debug::Easy 2.27;
2              
3 1     1   149309 use strict;
  1         2  
  1         99  
4             # use warnings;
5             use constant {
6 1         117 TRUE => 1,
7             FALSE => 0,
8 1     1   7 };
  1         2  
9              
10 1     1   7 use Config;
  1         2  
  1         61  
11 1     1   7 use Term::ANSIColor;
  1         1  
  1         89  
12 1     1   6 use Time::HiRes qw(time);
  1         2  
  1         9  
13 1     1   75 use File::Basename qw(fileparse);
  1         2  
  1         84  
14              
15 1     1   742 use Data::Dumper; # Included in Perl
  1         11263  
  1         121  
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   8 use if ($Config{'useithreads'}), 'threads';
  1         2  
  1         105  
23              
24             BEGIN {
25 1     1   6 require Exporter;
26              
27             # Inherit from Exporter to export functions and variables
28 1         20 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         4839 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 or ERROR = Error
68             WARN or WARNING = Warning
69             NOTICE or ATTENTION = Notice
70             INFO or INFORMATION = 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->ATTENTION( ['Notice message']);
90              
91             $debug->INFO( ['Information and VERBOSE mode message']);
92             $debug->INFORMATION(['Information and VERBOSE mode message']);
93              
94             $debug->DEBUG( ['Level 1 Debug message']);
95             $debug->DEBUGMAX( ['Level 2 (terse) Debug message']);
96              
97             my @messages = (
98             'First Message',
99             'Second Message',
100             "Third Message First Line\nThird Message Second Line",
101             \%hash_reference
102             );
103              
104             $debug->INFO([\@messages]);
105              
106             =head1 DESCRIPTION
107              
108             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 [...]
109              
110             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 [...]
111              
112             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.
113              
114             Generally all you need are the defaults and you are ready to go.
115              
116             =head1 B
117              
118             =head2 B<@Levels>
119              
120             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.
121              
122             =cut
123              
124             sub DESTROY { # We spit out one last message before we die, the total execute time.
125 6     6   3113 my $self = shift;
126             my $bench = (! $self->{'COLOR'})
127             ? sprintf('%06.2f', (time - $self->{'MASTERSTART'}))
128 6 50       119 : colored(['bright_cyan'], sprintf('%06.2f', (time - $self->{'MASTERSTART'})));
129 6         344 my $name = $SCRIPTNAME;
130 6 50       44 $name .= ' [child]' if ($PARENT ne $$);
131 6 50       24 unless ($self->{'COLOR'}) {
132 0         0 $self->DEBUG(["$bench ---- $name complete ----"]);
133             } else {
134 6         40 $self->DEBUG([$bench . ' ' . colored(['black on_white'], "---- $name complete ----")]);
135             }
136             } ## end sub DESTROY
137              
138             =head1 B
139              
140             =head2 B
141              
142             * The parameter names are case insensitive as of Version 0.04.
143              
144             =over 4
145              
146             =item B [level]
147              
148             This adjusts the global log level of the Debug object. It requires a string.
149              
150             =back
151              
152             =over 8
153              
154             B (default)
155              
156             This level shows only error messages and all other messages are not shown.
157              
158             B
159              
160             This level shows error and warning messages. All other messages are not shown.
161              
162             B
163              
164             This level shows error, warning, and notice messages. All other messages are not shown.
165              
166             B
167              
168             This level shows error, warning, notice, and information messages. Only debug level messages are not shown.
169              
170             B
171              
172             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.
173              
174             B
175              
176             This level shows error, warning, notice, information, and level 1 debugging messages. Level 2 Debug messages are not shown.
177              
178             B
179              
180             This level shows all messages up to level 2 debugging messages.
181              
182             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 [...]
183              
184             =back
185              
186             =over 4
187              
188             =item B [boolean] (Not case sensitive)
189              
190             B<0>, B, or B (Off)
191              
192             This turns off colored output. Everything is plain text only.
193              
194             B<1>, B, or B (On - Default)
195              
196             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' [...]
197              
198             =back
199              
200             =over 4
201              
202             =item B [pattern]
203              
204             This is global
205              
206             A string that is parsed into the output prefix.
207              
208             DEFAULT: '%Date% %Time% %Benchmark% %Loglevel%[%Subroutine%][%Lastline%] '
209              
210             %Date% = Date (Uses format of "DateStamp" below)
211             %Time% = Time (Uses format of "TimeStamp" below)
212             %Epoch% = Epoch (Unix epoch)
213             %Benchmark% = Benchmark - The time it took between the last benchmark display
214             of this loglevel. If in an INFO level message, it benchmarks
215             the time until the next INFO level message. The same rule is
216             true for all loglevels.
217             %Loglevel% = Log Level
218             %Lines% = Line Numbers of all nested calls
219             %Module% = Module and subroutine of call (can be a lot of stuff!)
220             %Subroutine% = Just the last subroutine
221             %Lastline% = Just the last line number
222             %PID% = Process ID
223             %date% = Just Date (typically used internally only, use %Date%)
224             %time% = Just time (typically used internally only, use %Time%)
225             %epoch% = Unix epoch (typically used internally only, use %Epoch%)
226             %Filename% = Script Filename (parsed $0)
227             %Fork% = Running in parent or child?
228             P = Parent
229             C = Child
230             %Thread% = Running in Parent or Thread
231             P = Parent
232             T## = Thread # = Thread ID
233              
234             =item B<[loglevel]-Prefix> [pattern]
235              
236             You can define a prefix for a specific log level.
237              
238             ERR-Prefix
239             WARN-Prefix
240             NOTICE-Prefix
241             INFO-Prefix
242             DEBUG-Prefix
243             DEBUGMAX-Prefix
244              
245             If one of these are not defined, then the global value is used.
246              
247             =item B [pattern]
248              
249             (See Log::Fast for specifics on these)
250              
251             I suggest you just use Prefix above, but here it is anyway.
252              
253             Make this an empty string to turn it off, otherwise:
254              
255             =back
256              
257             =over 8
258              
259             B<%T>
260              
261             Formats the timestamp as HH:MM:SS. This is the default for the timestamp.
262              
263             B<%S>
264              
265             Formats the timestamp as seconds.milliseconds. Normally not needed, as the benchmark is more helpful.
266              
267             B<%T %S>
268              
269             Combines both of the above. Normally this is just too much, but here if you really want it.
270              
271             =back
272              
273             =over 4
274              
275             =item B [pattern]
276              
277             I suggest you just use Prefix above, but here it is anyway.
278              
279             Make this an empty string to turn it off, otherwise:
280              
281             =back
282              
283             =over 8
284              
285             B<%D>
286              
287             Formats the datestamp as YYYY-MM-DD. It is the default, and the only option.
288              
289             =back
290              
291             =over 4
292              
293             =item B
294              
295             File handle to write log messages.
296              
297             =item B
298              
299             Contains a hash reference describing the various colored debug level labels
300              
301             The default definition (using Term::ANSIColor) is as follows:
302              
303             =back
304              
305             =over 8
306              
307             'ANSILevel' => {
308             'ERR' => colored(['white on_red'], '[ ERROR ]'),
309             'WARN' => colored(['black on_yellow'], '[WARNING ]'),
310             'NOTICE' => colored(['yellow'], '[ NOTICE ]'),
311             'INFO' => colored(['black on_white'], '[ INFO ]'),
312             'DEBUG' => colored(['bold green'], '[ DEBUG ]'),
313             'DEBUGMAX' => colored(['bold black on_green'], '[DEBUGMAX]'),
314             }
315              
316             =back
317              
318             =cut
319              
320             sub new {
321 6     6 1 240547 my $class = shift;
322 6         265 my ($filename, $dir, $suffix) = fileparse($0);
323 6         21 my $tm = time;
324 6         54 my $self = {
325             'LogLevel' => 'ERR', # Default is errors only
326             'Type' => 'fh', # Default is a filehandle
327             'Path' => '/var/log', # Default path should type be unix
328             'FileHandle' => \*STDERR, # Default filehandle is STDERR
329             'MasterStart' => $tm,
330             'ANY_LastStamp' => $tm, # Initialize main benchmark
331             'ERR_LastStamp' => $tm, # Initialize the ERR benchmark
332             'WARN_LastStamp' => $tm, # Initialize the WARN benchmark
333             'INFO_LastStamp' => $tm, # Initialize the INFO benchmark
334             'NOTICE_LastStamp' => $tm, # Initialize the NOTICE benchmark
335             'DEBUG_LastStamp' => $tm, # Initialize the DEBUG benchmark
336             'DEBUGMAX_LastStamp' => $tm, # Initialize the DEBUGMAX benchmark
337             'Color' => TRUE, # Default to colorized output
338             'DateStamp' => colored(['yellow'], '%date%'),
339             'TimeStamp' => colored(['yellow'], '%time%'),
340             'Epoch' => colored(['cyan'], '%epoch%'),
341             'Padding' => -20, # Default padding is 20 spaces
342             'Lines-Padding' => -2,
343             'Subroutine-Padding' => 0,
344             'Line-Padding' => 0,
345             'PARENT' => $$,
346             'Prefix' => '%Date% %Time% %Benchmark% %Loglevel%[%Subroutine%][%Lastline%] ',
347             'DEBUGMAX-Prefix' => '%Date% %Time% %Benchmark% %Loglevel%[%Module%][%Lines%] ',
348             'Filename' => '[' . colored(['magenta'], $filename) . ']',
349             'ANSILevel' => {
350             'ERR' => colored(['white on_red'], '[ ERROR ]'),
351             'WARN' => colored(['black on_yellow'], '[WARNING ]'),
352             'NOTICE' => colored(['yellow'], '[ NOTICE ]'),
353             'INFO' => colored(['black on_white'], '[ INFO ]'),
354             'DEBUG' => colored(['bold green'], '[ DEBUG ]'),
355             'DEBUGMAX' => colored(['bold black on_green'], '[DEBUGMAX]'),
356             },
357             };
358              
359             # This pretty much makes all hash keys uppercase
360 6         1946 my @Keys = (keys %{$self}); # Hash is redefined on the fly, so get the list before
  6         33  
361 6         16 foreach my $Key (@Keys) {
362 150         153 my $upper = uc($Key);
363 150 100       211 if ($Key ne $upper) {
    50          
364 144         228 $self->{$upper} = $self->{$Key};
365              
366             # This fixes a documentation error for past versions
367 144 100       209 if ($upper eq 'LOGLEVEL') {
368 6 50       18 $self->{$upper} = 'ERR' if ($self->{$upper} =~ /^ERROR$/i);
369 6         18 $self->{$upper} = uc($self->{$upper}); # Make loglevels case insensitive
370             }
371 144         238 delete($self->{$Key}); # Get rid of the bad key
372             } elsif ($Key eq 'LOGLEVEL') { # Make loglevels case insensitive
373 0         0 $self->{$upper} = uc($self->{$upper});
374             }
375             } ## end foreach my $Key (@Keys)
376             { # This makes sure the user overrides actually override
377 6         10 my %params = (@_);
  6         24  
378 6         15 foreach my $Key (keys %params) {
379 18         22 my $new_name = $Key;
380 18         29 $new_name =~ s/ATTENTION/NOTICE/i;
381 18         52 $self->{ uc($new_name) } = $params{$Key};
382             }
383             }
384              
385             # Cache numeric log level value for quick comparisons
386 6         22 $self->{'LOGLEVEL_VALUE'} = $LevelLogic{ $self->{'LOGLEVEL'} };
387              
388             # Cache thread support check for hot path
389 6 50 33     124 my $use_threads = ($Config{'useithreads'} && eval { require threads; 1 }) ? 1 : 0;
390 6         19 $self->{'USE_THREADS'} = $use_threads;
391              
392             # This instructs the ANSIColor library to turn off coloring,
393             # if the Color attribute is set to zero.
394 6 50       19 unless ($self->{'COLOR'}) {
395             # If COLOR is FALSE, then clear color data from ANSILEVEL, as these were
396             # defined before color was turned off.
397 0         0 $self->{'ANSILEVEL'} = {
398             'ERR' => '[ ERROR ]',
399             'WARN' => '[WARNING ]',
400             'NOTICE' => '[ NOTICE ]',
401             'INFO' => '[ INFO ]',
402             'DEBUG' => '[ DEBUG ]',
403             'DEBUGMAX' => '[DEBUGMAX]',
404             };
405 0         0 $self->{'DATESTAMP'} = '%date%';
406 0         0 $self->{'TIMESTAMP'} = '%time%';
407 0         0 $self->{'EPOCH'} = '%epoch%';
408 0         0 $self->{'FILENAME'} = '[' . $filename . ']'; # Ensure filename without color
409             }
410              
411 6         18 foreach my $lvl (@Levels) {
412 42 100 66     145 $self->{"$lvl-PREFIX"} = $self->{'PREFIX'} unless (exists($self->{"$lvl-PREFIX"}) && defined($self->{"$lvl-PREFIX"}));
413             }
414              
415             # Precompute static prefix templates per level to minimize per-line substitutions.
416             # We will leave dynamic tokens (%date%, %time%, %epoch%, %Benchmark%) for runtime.
417 6         14 $self->{'_PREFIX_TEMPLATES'} = {};
418 6         14 foreach my $lvl (@Levels) {
419 42         55 my $tmpl = $self->{"$lvl-PREFIX"} . ''; # copy
420 42 50       116 my $forked = ($PARENT ne $$) ? 'C' : 'P';
421 42         39 my $threaded = 'PT-';
422 42 50       62 if ($self->{'USE_THREADS'}) {
423 0 0       0 my $tid = threads->can('tid') ? threads->tid() : 0;
424 0 0 0     0 $threaded = ($tid && $tid > 0) ? sprintf('T%02d', $tid) : 'PT-';
425             }
426              
427             # Static substitutions
428 42         103 $tmpl =~ s/\%PID\%/$$/gi;
429 42         199 $tmpl =~ s/\%Loglevel\%/$self->{'ANSILEVEL'}->{$lvl}/gi;
430 42         87 $tmpl =~ s/\%Filename\%/$self->{'FILENAME'}/gi;
431 42         80 $tmpl =~ s/\%Fork\%/$forked/gi;
432 42         77 $tmpl =~ s/\%Thread\%/$threaded/gi;
433              
434             # Leave dynamic tokens for runtime:
435             # %Lines%, %Lastline%, %Subroutine%, %Module% (caller-dependent)
436             # %Date%, %Time%, %Epoch% (colorized stamp placeholders)
437             # %date%, %time%, %epoch% (raw values)
438             # %Benchmark%
439              
440 42         89 $self->{'_PREFIX_TEMPLATES'}->{$lvl} = $tmpl;
441             }
442              
443 6         13 my $fh = $self->{'FILEHANDLE'};
444              
445             # Signal the script has started (and logger initialized)
446 6         13 my $name = $SCRIPTNAME;
447 6 50       21 $name .= ' [child]' if ($PARENT ne $$);
448 6 50       36 my $string = (! $self->{'COLOR'}) ? "----- $name begin -----" : colored(['black on_white'], "----- $name begin -----");
449 6 100       314 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/);
450              
451 6         15 bless($self, $class);
452 6         44 return ($self);
453             } ## end sub new
454              
455             =head2 debug
456              
457             NOTE: This is a legacy method for backwards compatibility. Please use the direct methods instead.
458              
459             The parameters must be passed in the order given
460              
461             =over 4
462              
463             =item B
464              
465             The log level with which this message is to be triggered
466              
467             =item B
468              
469             A string or a reference to a list of strings to output line by line.
470              
471             =back
472              
473             =cut
474              
475             sub debug {
476 114     114 1 61210 my $self = shift;
477 114         179 my $level = uc(shift);
478 114         153 my $msgs = shift;
479              
480 114 50       800 if ($level !~ /ERR.*|WARN.*|NOTICE|INFO.*|DEBUG/i) { # Compatibility with older versions.
481 0         0 $level = uc($msgs); # It tosses the legacy __LINE__ argument
482 0         0 $msgs = shift;
483             }
484 114         332 $level =~ s/(OR|ING|RMATION)$//; # Strip off the excess
485              
486             # A much quicker bypass when the log level is below what is needed
487             # This minimizes the execution overhead for log levels not active.
488 114 100       427 return if ($self->{'LOGLEVEL_VALUE'} < $LevelLogic{$level});
489              
490 65         70 my @messages;
491 65 100 66     321 if (ref($msgs) eq 'SCALAR' || ref($msgs) eq '') {
    50          
492 42         71 push(@messages, $msgs);
493             } elsif (ref($msgs) eq 'ARRAY') {
494 23         126 @messages = @{$msgs};
  23         63  
495             } else {
496 0         0 push(@messages, _send_to_dumper($msgs));
497             }
498 65         207 my ($sname, $cline, $nested, $subroutine, $thisBench, $thisBench2, $sline, $short) = ('', '', '', '', '', '', '', '');
499              
500             # Figure out the proper caller tree and line number ladder
501             # But only if it's part of the effective level prefix, else don't waste time.
502 65   33     172 my $effective_prefix = $self->{ $level . '-PREFIX' } || $self->{'PREFIX'};
503 65 50       351 if ($effective_prefix =~ /\%(Subroutine|Module|Lines|Lastline)\%/i) { # %P = Subroutine, %l = Line number(s)
504 65         84 my $package = '';
505 65         69 my $count = 1;
506 65         61 my $nest = 0;
507 65         176 while (my @array = caller($count)) {
508 6 100       15 if ($array[3] !~ /Debug::Easy/) {
509 2         4 $package = $array[0];
510 2         4 my $subroutine = $array[3];
511 2         21 $subroutine =~ s/^$package\:\://;
512 2         12 $sname =~ s/$subroutine//;
513 2 50       6 if ($sname eq '') {
514 2 50       5 $sname = ($subroutine ne '') ? $subroutine : $package;
515 2         4 $cline = $array[2];
516             } else {
517 0         0 $sname = $subroutine . '::' . $sname;
518 0         0 $cline = $array[2] . '/' . $cline;
519             }
520 2 50       6 if ($count == 2) {
521 0         0 $short = $array[3];
522 0         0 $sline = $array[2];
523             }
524 2         4 $nest++;
525             } ## end if ($array[3] !~ /Debug::Easy/)
526 6         24 $count++;
527             } ## end while (my @array = caller...)
528 65 100       99 if ($package ne '') {
529 2         4 $sname = $package . '::' . $sname;
530 2 50       6 $nested = ' ' x $nest if ($nest);
531             } else {
532 63         74 my @array = caller(1);
533 63         82 $cline = $array[2];
534 63 50 33     135 if (!defined($cline) || $cline eq '') {
535 63         257 @array = caller(0);
536 63         141 $cline = $array[2];
537             }
538 63         70 $sname = 'main';
539 63         73 $sline = $cline;
540 63         126 $short = $sname;
541             } ## end else [ if ($package ne '') ]
542 65 50       105 $subroutine = ($sname ne '') ? $sname : 'main';
543 65 50       159 $self->{'PADDING'} = 0 - length($subroutine) if (length($subroutine) > abs($self->{'PADDING'}));
544 65 50       127 $self->{'LINES-PADDING'} = 0 - length($cline) if (length($cline) > abs($self->{'LINES-PADDING'}));
545 65 100       129 $self->{'SUBROUTINE-PADDING'} = 0 - length($short) if (length($short) > abs($self->{'SUBROUTINE-PADDING'}));
546 65 100       130 $self->{'LINE-PADDING'} = 0 - length($sline) if (length($sline) > abs($self->{'LINE-PADDING'}));
547 65         205 $cline = sprintf('%' . $self->{'LINES-PADDING'} . 's', $cline);
548 65 50       328 $subroutine = (! $self->{'COLOR'}) ? sprintf('%' . $self->{'PADDING'} . 's', $subroutine) : colored(['bold cyan'], sprintf('%' . $self->{'PADDING'} . 's', $subroutine));
549 65         2746 $sline = sprintf('%' . $self->{'LINE-PADDING'} . 's', $sline);
550 65 50       196 $short = (! $self->{'COLOR'}) ? sprintf('%' . $self->{'SUBROUTINE-PADDING'} . 's', $short) : colored(['bold cyan'], sprintf('%' . $self->{'SUBROUTINE-PADDING'} . 's', $short));
551             } ## end if ($effective_prefix ...)
552              
553             # Figure out the benchmarks, but only if it is in the prefix
554 65 50       2377 if ($effective_prefix =~ /\%Benchmark\%/i) {
555             # For multiline output, only output the bench data on the first line. Use padded spaces for the rest.
556 65         431 $thisBench = sprintf('%7s', sprintf(' %.02f', time - $self->{'ANY_LASTSTAMP'}));
557 65         120 $thisBench2 = ' ' x length($thisBench);
558             } ## end if ($effective_prefix ...)
559 65         92 my $first = TRUE; # Set the first line flag.
560              
561             # Buffer lines to reduce syscalls for multi-line messages
562 65         73 my $buffer = '';
563              
564 65         88 foreach my $msg (@messages) { # Loop through each line of output and format accordingly.
565 107 50       192 if (ref($msg) ne '') {
566 0         0 $msg = _send_to_dumper($msg);
567             }
568 107 100       192 if ($msg =~ /\n/s) { # If the line contains newlines, then it too must be split into multiple lines.
569 21         71 my @message = split(/\n/, $msg);
570 21         35 foreach my $line (@message) { # Loop through the split lines and format accordingly.
571 42         90 $buffer .= $self->_format_line($level, $nested, $line, $first, $thisBench, $thisBench2, $subroutine, $cline, $sline, $short);
572 42         57 $buffer .= "\n";
573 42         102 $first = FALSE; # Clear the first line flag.
574             }
575             } else { # This line does not contain newlines. Treat it as a single line.
576 86         184 $buffer .= $self->_format_line($level, $nested, $msg, $first, $thisBench, $thisBench2, $subroutine, $cline, $sline, $short);
577 86         120 $buffer .= "\n";
578             }
579 107         185 $first = FALSE; # Clear the first line flag.
580             } ## end foreach my $msg (@messages)
581              
582 65         113 my $fh = $self->{'FILEHANDLE'};
583 65 50 66     197 if ($level eq 'INFO' && $self->{'LOGLEVEL'} eq 'VERBOSE') { # Trap verbose flag and temporarily drop the prefix.
    100          
584             # For verbose, we need to print messages without prefixes.
585             # Extract lines and print only message contents.
586 0         0 foreach my $msg (@messages) {
587 0 0       0 if (ref($msg) ne '') {
588 0         0 $msg = _send_to_dumper($msg);
589             }
590 0 0       0 if ($msg =~ /\n/s) {
591 0         0 my @message = split(/\n/, $msg);
592 0         0 foreach my $line (@message) {
593 0         0 print $fh "$line\n";
594             }
595             } else {
596 0         0 print $fh "$msg\n";
597             }
598             }
599             } elsif ($level eq 'DEBUGMAX') { # Special version of DEBUG. Extremely verbose debugging and quite noisy
600 3 50       10 if ($self->{'LOGLEVEL'} eq 'DEBUGMAX') {
601 3         16 print $fh $buffer;
602             }
603             } else {
604 62         364 print $fh $buffer;
605             }
606              
607 65         114 $self->{'ANY_LASTSTAMP'} = time;
608 65         325 $self->{ $level . '_LASTSTAMP' } = time;
609             } ## end sub debug
610              
611             sub _send_to_dumper {
612 0     0   0 my $msg = shift;
613             # Set up dumper variables for friendly output that doesn't interfere with the script Dumpor variables (if applicable).
614 0         0 local $Data::Dumper::Terse = TRUE;
615 0         0 local $Data::Dumper::Indent = TRUE;
616 0         0 local $Data::Dumper::Useqq = TRUE;
617 0         0 local $Data::Dumper::Deparse = TRUE;
618 0         0 local $Data::Dumper::Quotekeys = TRUE;
619 0         0 local $Data::Dumper::Trailingcomma = TRUE;
620 0         0 local $Data::Dumper::Sortkeys = TRUE;
621 0         0 local $Data::Dumper::Purity = TRUE;
622 0         0 return(Dumper($msg));
623             }
624              
625             # Internal: format a single line for logging (without printing)
626             sub _format_line {
627 128     128   316 my ($self, $level, $padding, $msg, $first, $thisBench, $thisBench2, $subroutine, $cline, $sline, $shortsub) = @_;
628              
629             # Build prefix based on precomputed template and runtime substitutions
630 128         215 my $tmpl = $self->{'_PREFIX_TEMPLATES'}->{$level};
631 128 50       216 $tmpl = $self->{"$level-PREFIX"} . '' unless defined $tmpl; # Fallback safety
632              
633             # Clone template since we mutate
634 128         148 my $prefix = $tmpl . '';
635              
636             # Apply caller-derived fields only if present in the effective level prefix
637 128 100       393 if ($prefix =~ /\%Lines\%/i) { $prefix =~ s/\%Lines\%/$cline/gi; }
  6         27  
638 128 100       386 if ($prefix =~ /\%Lastline\%/i) { $prefix =~ s/\%Lastline\%/$sline/gi; }
  122         424  
639 128 100       386 if ($prefix =~ /\%Subroutine\%/i){ $prefix =~ s/\%Subroutine\%/$shortsub/gi; }
  122         310  
640 128 100       304 if ($prefix =~ /\%Module\%/i) { $prefix =~ s/\%Module\%/$subroutine/gi; }
  6         23  
641              
642 128         2096 my ($sec,$min,$hour,$mday,$mon,$year) = localtime();
643 128         481 my $Date = sprintf('%02d/%02d/%04d', $mday, ($mon + 1), (1900 + $year));
644 128         236 my $Time = sprintf('%02d:%02d:%02d', $hour, $min, $sec);
645 128         193 my $epoch = time;
646              
647             # Apply dynamic tokens
648 128 100       176 if ($first) {
649 65         262 $prefix =~ s/\%Benchmark\%/$thisBench/gi;
650             } else {
651 63         195 $prefix =~ s/\%Benchmark\%/$thisBench2/gi;
652             }
653 128         586 $prefix =~ s/\%Date\%/$self->{'DATESTAMP'}/gi;
654 128         358 $prefix =~ s/\%Time\%/$self->{'TIMESTAMP'}/gi;
655 128         203 $prefix =~ s/\%Epoch\%/$self->{'EPOCH'}/gi;
656 128         337 $prefix =~ s/\%date\%/$Date/gi;
657 128         336 $prefix =~ s/\%time\%/$Time/gi;
658 128         150 $prefix =~ s/\%epoch\%/$epoch/gi;
659              
660 128         440 return "$prefix$padding$msg";
661             }
662              
663             sub _send_to_logger { # Legacy path: retained for backward compatibility but routed via _format_line
664 0     0   0 my ($self, $level, $padding, $msg, $first, $thisBench, $thisBench2, $subroutine, $cline, $sline, $shortsub) = @_;
665              
666 0         0 my $fh = $self->{'FILEHANDLE'};
667 0 0 0     0 if ($level eq 'INFO' && $self->{'LOGLEVEL'} eq 'VERBOSE') { # Trap verbose flag and temporarily drop the prefix.
    0          
668 0         0 print $fh "$msg\n";
669             } elsif ($level eq 'DEBUGMAX') { # Special version of DEBUG. Extremely verbose debugging and quite noisy
670 0 0       0 if ($self->{'LOGLEVEL'} eq 'DEBUGMAX') {
671 0         0 my $line = $self->_format_line($level, $padding, $msg, $first, $thisBench, $thisBench2, $subroutine, $cline, $sline, $shortsub);
672 0         0 print $fh "$line\n";
673             }
674             } else {
675 0         0 my $line = $self->_format_line($level, $padding, $msg, $first, $thisBench, $thisBench2, $subroutine, $cline, $sline, $shortsub);
676 0         0 print $fh "$line\n";
677             }
678             } ## end sub _send_to_logger
679              
680             =head2 B or B
681              
682             Sends ERROR level debugging output to the log. Errors are always shown.
683              
684             =over 4
685              
686             =item B
687              
688             Either a single string or a reference to a list of strings
689              
690             =back
691             =cut
692              
693             sub ERR {
694 0     0 1 0 my $self = shift;
695 0         0 $self->debug('ERR', @_);
696             }
697              
698             sub ERROR {
699 0     0 1 0 my $self = shift;
700 0         0 $self->debug('ERR', @_);
701             }
702              
703             =head2 B or B
704              
705             If the log level is WARN or above, then these warnings are logged.
706              
707             =over 4
708              
709             =item B
710              
711             Either a single string or a reference to a list of strings
712              
713             =back
714             =cut
715              
716             sub WARN {
717 0     0 1 0 my $self = shift;
718 0         0 $self->debug('WARN', @_);
719             }
720              
721             sub WARNING {
722 0     0 1 0 my $self = shift;
723 0         0 $self->debug('WARN', @_);
724             }
725              
726             =head2 B or B
727              
728             If the loglevel is NOTICE or above, then these notices are logged.
729              
730             =over 4
731              
732             =item B
733              
734             Either a single string or a reference to a list of strings
735              
736             =back
737             =cut
738              
739             sub NOTICE {
740 0     0 1 0 my $self = shift;
741 0         0 $self->debug('NOTICE', @_);
742             }
743              
744             sub ATTENTION {
745 0     0 1 0 my $self = shift;
746 0         0 $self->debug('NOTICE', @_);
747             }
748              
749             =head2 B or B
750              
751             If the loglevel is INFO (or VERBOSE) or above, then these information messages are displayed.
752              
753             =over 4
754              
755             =item B
756              
757             Either a single string or a reference to a list of strings
758              
759             =back
760             =cut
761              
762             sub INFO {
763 0     0 1 0 my $self = shift;
764 0         0 $self->debug('INFO', @_);
765             }
766              
767             sub INFORMATION {
768 0     0 1 0 my $self = shift;
769 0         0 $self->debug('INFO', @_);
770             }
771              
772             =head2 B
773              
774             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 [...]
775              
776             =over 4
777              
778             =item B
779              
780             Either a single string or a reference to a list of strings
781              
782             =back
783             =cut
784              
785             sub DEBUG {
786 6     6 1 266 my $self = shift;
787 6         33 $self->debug('DEBUG', @_);
788             }
789              
790             =head2 B
791              
792             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 [...]
793              
794             =over 4
795              
796             =item B
797              
798             Either a single string or a reference to a list of strings
799              
800             =back
801             =cut
802              
803             sub DEBUGMAX {
804 0     0 1   my $self = shift;
805 0           $self->debug('DEBUGMAX', @_);
806             }
807              
808             1;
809              
810             =head1 B
811              
812             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 [...]
813              
814             Ways around this are to separately create a Debug::Easy object in each fork or thread, and have them log to separate files.
815              
816             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.
817              
818             =head1 B
819              
820             To install this module, run the following commands:
821              
822             perl Makefile.PL
823             make
824             make test
825             [sudo] make install
826              
827             =head1 AUTHOR
828              
829             Richard Kelsch
830              
831             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
832              
833             =head1 B
834              
835             Version 2.26
836              
837             =head1 B
838              
839             You can find documentation for this module with the perldoc command.
840              
841             C
842              
843             or if you have "man" installed, then
844              
845             C
846              
847             You can also look for information at: L
848              
849             =head1 B
850              
851             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, [...]
852              
853             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.
854              
855             =head1 B
856              
857             Copyright 2013-2026 Richard Kelsch.
858              
859             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:
860              
861             L
862              
863             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, [...]
864              
865             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 [...]
866              
867             This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder.
868              
869             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 [...]
870              
871             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 [...]
872              
873             =head1 B
874              
875             Perl modules available on github - L
876              
877             And available on CPAN:
878              
879             * Debug::Easy - L
880             * Graphics::Framebuffer - L
881             * Term::ANSIEncode - L
882              
883             =cut