New module: Debug_Messages

Austin Schutz tex at off.org
Sat Dec 2 23:31:24 CST 2000


Who: me
Where: http://off.org/debugm.tgz
When: now
What: New debugging module

	This module is one of my favorite pieces of my own code, which
I haven't released to the public until now for fear that everyone would
tear it apart and tell me what a dork I am for releasing it because there
are similar tools, etc. etc. etc. Anyway, I figured by letting a few people
try it on this small list that I could get some feedback before going further
with it.

	There's a few different ways of using this module, so if the first
bit doesn't seem interesting to you, please keep reading.

	Debug_Messages (better name suggestions are welcome) is a debugging
and error reporting swiss army knife. It allows you to:

	-debug variables and entire perl object structures quickly and
		easily.
	-trace the execution of your code, in part or in full.
	-do more effective and concise error handling. 
	-pinpoint how your code is executed. 
	-provide debugging facilities to the users of your code.
	-do all of the above in any of syslog, stderr, or
		stdout in html format.

	Ok, did you get all that?
	
	There's a longer explanation of what the module is/does below.

Why:

	Because there is no existing tool that is as complete in its
utility, and because it provides a clean and consistent error reporting
device. It's very different (and better, IMO) than anything you might
think is similar, e.g. Carp.

	Please let me know what you think.

	Austin

perldoc Debug_Messages:


Debug_Messages(User Contributed Perl DocumentaDebug_Messages(3pm)


       DESCRIPTION

       A module for concise error reporting, process tracing,
       variable debugging.  Multiple destinations are easy, so
       you can send output to a web page, syslog, or STDERR
       without extra code. Debugging code is very easy to use and
       may be left in the final version of scripts without
       creating a lot of overhead.

       You should really look at the docs on each sub to have an
       idea what can be done with this module.

       use Debug_Messages;

       use Debug_Messages ':standard';

               :standard includes everything that can be imported.

               The following can be imported from Debug_Messages:

               debug()
               trace()
               marker()
               vprint()
               perror()
               eappend()
               eprint()
               eclear()

       The following special variables affect the behavior of
       Debug_Messages:

       debug(@messages)


               debug is handy for displaying non-error messages.
       debug prints a marker, followed by separate indented lines for each
       message, e.g.:

       $Debug = 2;

       sub test {

         debug "hello";

       }

       produces:

       debug 2: ./test.pl line 8, package main: main::test():
           hello

               Note that debug in this case would print _nothing_ if $Debug
       were less than 2 or undefined. See the next example for clarification.



2/Dec/2000             perl 5.005, patch 03                     1





Debug_Messages(User Contributed Perl DocumentaDebug_Messages(3pm)


               One of the handy things this allows you to do is create a trace
       of the execution of your program, e.g.:

       use Debug_Messages ':standard';

       $Debug = 4;

       sub a {
         debug;
         b(); }

       sub b {
         debug;
         c(); }

       sub c {
         debug;
         print "hi there\n"; }

       a();

       produces:

       debug 2: test.pl line 8, package main: main::a() debug 3:
       test.pl line 13, package main: main::b() debug 4: test.pl
       line 18, package main: main::c() hi there

               The number after 'debug' refers to the recursive 'depth' of the
       call. If you set $Debug = a smaller number, you will only see lines
       that are of lower depth. Here's the output with Debug set to 3:

       debug 2: test.pl line 8, package main: main::a() debug 3:
       test.pl line 13, package main: main::b() hi there

               Notice that the variable set is $main::Debug. Debugging level
       is set per package, so you can debug or not debug each package. See
       the Special Variables section for more information.


       trace($depth)


               Will return an array representing a subroutine stack trace. Ex.:

       sub a { b(); }

       sub b { c(); }

       sub c { print( map( " ".$_."\n", trace())); }

       a;

       produces:




2/Dec/2000             perl 5.005, patch 03                     2





Debug_Messages(User Contributed Perl DocumentaDebug_Messages(3pm)


        ./test.pl line 5, package main: main::c()
        ./test.pl line 4, package main: main::b()
        ./test.pl line 3, package main: main::a()
        ./test.pl line 6, package main: (not in a sub)

               trace is mostly used like:

       debug( trace );

               which would output:

       debug 4: ./test.pl line 7, package main: main::c():
           ./test.pl line 7, package main: main::c()
           ./test.pl line 6, package main: main::b()
           ./test.pl line 5, package main: main::a()
           ./test.pl line 8, package main: (not in a sub)

       marker($starting_depth,$maximum_depth)


               Like trace, but by default only returns the first line.
       Typically you would just call marker();. It will create a trace of
       calls starting with whatever the starting depth is. This is handy if
       you have a sub that wants to report a marker of its parent. Ex:

       sub test {
         report(); } sub report {
         print "Marker from test:".marker(1); }

               marker(), which is the first line printed by debug(),
       is a string, or array of strings, containing the following:

       $filename line $line_number, package $package $I<sub>(
       @sub_args )

       e.g.

       "./test.pl line 5, package main: main::a()"

       where $filename is the file containing the marker() line,
       $line_number is the line in $filename where marker() was
       called, $package is the package in scope where marker()
       was called, $sub is the name of the sub being executed
       where marker was called, and @sub_args are the arguments
       which were passed to $sub when it was called.

       vprint(\variable, 'variable name',
       $recursive_display_depth);









2/Dec/2000             perl 5.005, patch 03                     3





Debug_Messages(User Contributed Perl DocumentaDebug_Messages(3pm)


               Print a marker, followed by the name and contents of variable.
       Both the name of the variable and $recursive_display_depth are optional.
       vprint needs to be passed B<_a_reference_to_> the variable in question,
       indicated as \variable above. 'variable' can be a variable of any type,
       or a reference to a variable of any type. vprint knows how to deal with
       SCALARs, ARRAYs, HASHes, GLOBs, and CODE refs. I wasn't sure to to
       represent a generic variable reference, so I chose \variable. Sorry
       for the confusion.

               'variable name' is optional. It's not trivial to have vprint
       be able to tell what the name of a variable is with only its reference
       to go by, so you have to pass it if you want the name printed in vprint's
       output. Otherwise vprint will print '(no name passed)'.

               $recursive_display_depth refers to how deep within the variable /
       object you want to go displaying the contents of references. Default is
       20, which is pretty deep.


       perror(@messages)


               Like debug but will always print, not just when $Debug is set.


       eappend(@messages)


               Set error messages for the current location. This is extremely
       handy for giving detailed information about why something failed. Ex.:

       sub get_filehandle {
         local(*FILE);
         my($filename) = shift;
         unless( open(FILE, $filename)) {
           eappend( "couldn't open file", $!);
           return undef;
         }
         return \*FILE; }

       sub process_file {
         my($filename) = shift;
         my($file);
         unless( defined($file = get_filehandle($filename))) {
           eappend( "Couldn't get filehandle" );
           return undef;
         }
        ...  }

       unless(defined(process_file($my_file))) {
         eappend( "Couldn't process $my_file");
         eprint();
         exit; }




2/Dec/2000             perl 5.005, patch 03                     4





Debug_Messages(User Contributed Perl DocumentaDebug_Messages(3pm)


               This is extremely handy method for a few reasons.

               First, the error isn't necessarily printed by any of the subs that get
       called. This is important if you are writing a module where someone else may
       not want you crapping your error messages to the screen. It's up to them to
       do it. eappend() merely stores the errors.

               Second, errors are extremely verbose and printed (by eprint)
       in a very easy to read fashion pinpointing the exact location and nature of the
       problem.

               Third, calling subs get to decide whether or not an error is
       catastrophic or ignorable.

               Fourth, Debug_Messages can print messages to several locations
       (or none!) depending on the whim of the user. Maybe error messages should
       be printed to syslog, or maybe they should be totally ignored. Or maybe
       the errors should be in html because the user has a web application.

               Fifth, there are probably other reasons I can't think of right now.


       eprint()


               print errors to all configured places, then clear errors. See the
       special variables section for details. Returns the errors as a string
       as would be printed to STDERR if
       $Debug_Messages::Debug_Styles->{stderr} is set.


       eclear()


               clears $Debug_Messages::Errors; Useful if you have caught and
       ignored an error, or dealt with the error manually.


       close_log()


               close syslog handle. Useful if you want to change the syslog
       facility. Close the log, which will automatically be reopened the next time a
       message is printed. See special variables for syslog vars.


       Special Variables


               $Debug_Messages::Errors is an array reference containing
       the stack of the last error messages and their "depth". More on this in
       the 'Internals' section.





2/Dec/2000             perl 5.005, patch 03                     5





Debug_Messages(User Contributed Perl DocumentaDebug_Messages(3pm)


               $Debug_Messages::Debug_Styles is a hash ref containing the different
       output styles currently selected, e.g.:

       $Debug_Messages::Debug_Styles = {
         'stderr' => 1,
         'html' => 1,
         'syslog' => 1 }

       will turn on all styles of printing. 'stderr', 'html', and
       'syslog' are the currently supported styles.
            $Debug_Messages::Indent_String, normally set to ' ' x
       8, is the string used to format errors printed to STDERR.

               $Debug_Messages::Auto_Clear_Errors, if set, eprint() will
       clear the $Debug_Messages::Errors variable after printing errors.
       Set to 1 by default.

               $Debug_Messages::Syslog_Facility is the facility syslog uses for
       syslog messages. Default is 'local3'. Do close_log();
       $Debug_Messages::Syslog_Facility='new_facility';
       if you want to change the facility.

               $Debug_Messages::Syslog_Debug_Priority is the priority used for debug
       messages. Default is 'debug'.

               $Debug_Messages::Syslog_Error_Priority is the priority used for error
       messages. Default is 'err'.

               $Debug_Messages::Print_Args is used to determine whether or not to
       print the arguments to a sub in marker(). Not putting in the args can be
       useful in the situation where you don't want the user to see them, e.g.
       you pass a password to a routine. marker will then print the number
       of args passed to the sub.

               $Debug is the depth of debug calls that will be printed when debug
       is called. If unset no debugging messages will be printed. Notice that
       Debug can be set B<In the caller's package> or in main::. This is an extremely
       important point. The reason for this is to make it possible to only debug
       messages from a certain package, if the user so wishes. If
       the caller's $Debug is set, its value will have precedence over
       $Debug_Messages::$Debug, which has precedence over $main::Debug.

       Setting $Debug to a large number and putting debug; at the
       top of each of your subs will generate a call trace when
       your program is run.

               $Syslog_SockType is set to 'unix' by default. This is the type
       of socket Sys::Syslog will use when Debug_Messages internally calls
       setlogsock(). This only applies if you are logging to syslog, naturally.
       See perldoc Sys::Syslog for clarification.







2/Dec/2000             perl 5.005, patch 03                     6





Debug_Messages(User Contributed Perl DocumentaDebug_Messages(3pm)


       Internals


               The $Debug_Messages::Errors variable contains the output from
       eappend. It looks is an arrayref that looks like:

       [
         [ $depth, [ @messages ] ],
         [ $depth2, [ @messages2] ],
         ...
         [$depthN, [ @messagesN] ] ]

               Where each depth is how far back caller($depth) could go before coming
       up undefined. The messages are the messages (including a marker as the
       first item in the array) passed to eappend.

               In practice having a single variable to hold all errors seems to work
       well. It is much more convenient than having one per package or
       using a scheme like blessing a 'Debug_Messages' object and doing
       $object->eappend(@messages).


       BUGS


               My experience with Sys::syslog::openlog() is that is returns undef
       even when it succeeds. Syslog may silently fail, and I'm not really sure
       what to do about that.

               'recursive depth' is used by using caller() until it returns undef.
       See perldoc caller() for the limitations of caller().


       AUTHOR


               Austin Schutz (tex at off.org)




















2/Dec/2000             perl 5.005, patch 03                     7


TIMTOWTDI



More information about the Pdx-pm-list mailing list