NAME

Tell - Print messages with balance, indentation, severity, and autoclosure.

***This documentation has not yet been completed. ***

VERSION

This document describes Tell version 0.1.4

SYNOPSIS

For a script like this:

use Tell qw/:all/;
tell "System parameter updates";
  tell "CLOCK_UTC";
  #...do_something();
  tell_ok;

  tell "NTP Servers";
  #...do_something();
  tell_error;

  tell "DNS Servers";
  #...do_something();
  tell_warn;
tell_done;

You get this output:

System parameter updates...
  CLOCK_UTC........................................................ [OK]
  NTP Servers...................................................... [ERROR]
  DNS Servers...................................................... [WARN]
System parameter updates........................................... [DONE]

DESCRIPTION

The Tell package is used to print balanced and nested messages with a completion status. These messages indent easily within each other, autocomplete on scope exit, are easily parsed, may be bulleted, can be filtered, and even can show status in color.

For example, you write code like this:

use Tell qw/:all/;
tell "Reconfiguring the grappolator";
do_whatchamacallit();
do_something_else();

It begins by printing:

Reconfiguring the grappolator...

Then it does "whatchamacallit" and "something else". When these are complete it adds the rest of the line: a bunch of dots and the [DONE].

Reconfiguring the grappolator...................................... [DONE]

Your do_whatchamacallit() and do_something_else() subroutines may also tell what they're doing, and indicate success or failure or whatever, so you can get nice output like this:

Reconfiguring the grappolator...
  Processing whatchamacallit....................................... [WARN]
  Fibulating something else...
    Fibulation phase one........................................... [OK]
    Fibulation phase two........................................... [ERROR]
    Wrapup of fibulation........................................... [OK]
Reconfiguring the grappolator...................................... [DONE]

Examples

A series of examples will make Tell easier to understand.

Getting started

use Tell ':all';
tell "Frobnicating the biffolator";
sleep 1; # simulate the frobnication process
tell_done;

First this prints:

Frobnicating the biffolator...

Then after the "frobnication" process is complete, the line is continued so it looks like this:

Frobnicating the biffolator....................................... [DONE]

Autocompletion

In the above example, we end with a tell_done call to indicate that the thing we told about (Frobnicating the biffolator) is now done. We don't need to do the tell_done. It will be called automatically for us when the current scope is exited (for this example: when the program ends). So the code example could be just this:

use Tell ':all';
tell "Frobnicating the biffolator";
sleep 1; # simulate the frobnication process

and we'd get the same results.

Yeah, autocompletion may not seem so useful YET, but hang in there and you'll soon see how wonderful it is.

Completion Severity

There's many ways a task can complete. It can be simply DONE, or it can complete with an ERROR, or it can be OK, etc. These completion codes are called the severity codes, Tell defines many different severity codes. The severity codes are borrowed from the UNIX syslog subsystem, plus a few from VMS and other sources. They should be familiar to you.

Most severity codes also have an associated numerical value. This value is called the severity level. It's useful for comparing severities to eachother or filtering out severities you don't want to be bothered with.

Here are the severity codes and their severity values. Those on the same line are considered equal in severity:

EMERG => 15,
ALERT => 13,
CRIT  => 11, FAIL => 11, FATAL => 11,
ERROR => 9,
WARN  => 7,
NOTE  => 6,
INFO  => 5, OK => 5,
DEBUG => 4,
NOTRY => 3,
UNK   => 2,

Anything severity not listed is given the value 1.

To complete with a different severity, call tell_done with the severity code like this:

tell_done "WARN";

tell_done returns with the severity value from the above table, otherwise it returns 1, unless there's an error in which case it returns false.

As a convienence, it's easier to use these functions which do the same thing, only simpler:

 Function          Equivalent                       Usual Meaning
----------      -----------------      -----------------------------------------------------
tell_emerg      tell_done "EMERG";     syslog: Off the scale!
tell_alert      tell_done "ALERT";     syslog: A major subsystem is unusable.
tell_crit       tell_done "CRIT";      syslog: a critical subsystem is not working entirely.
tell_fail       tell_done "FAIL";      Failure
tell_fatal      tell_done "FATAL";     Fatal error
tell_error      tell_done "ERROR";     syslog 'err': Bugs, bad data, files not found, ...
tell_warn       tell_done "WARN";      syslog 'warning'
tell_note       tell_done "NOTE";      syslog 'notice'
tell_info       tell_done "INFO";      syslog 'info'
tell_ok         tell_done "OK";        copacetic
tell_debug      tell_done "DEBUG";     syslog: Really boring diagnostic output.
tell_notry      tell_done "NOTRY";     Untried
tell_unk        tell_done "UNK";       Unknown

We'll change our simple example to give a FATAL completion:

use Tell ':all';
tell "Frobnicating the biffolator";
sleep 1; # simulate the frobnication process
tell_fatal;

Here's how it looks:

Frobnicating the biffolator....................................... [FATAL]

Severity Colors

A spiffy little feature of Tell is that you can enable colorization of the severity codes. That means that the severity code inside the square brackets is printed in color, so it's easy to see. The standard ANSI color escape sequences are used to do the colorization.

Here's the colors:

EMERG    bold red on black
ALERT    bold magenta
CRIT     bold red
FAIL     bold red
FATAL    bold red
ERROR    red
WARN     yellow (usually looks orange)
NOTE     cyan
INFO     green
OK       green
DEBUG    grey on yellow/orange
NOTRY    black on grey
UNK      bold white on grey
DONE     default font color (unchanged)

To use colors, do this when you use Tell:

use Tell ":all", {-colors => 1};

Run sample003.pl, included with this module, to see how it looks on your terminal.

Nested Messages

Nesting Across Processes

If you write a Perl script that uses Tell, and this script invokes other scripts that also use Tell, some nice magic happens. The inner scripts become aware of the outer, and they "nest" their indentation levels appropriately. Pretty cool, eh?

Closing with Different Severities, or... Why Autocompletion is Nice

So far our examples have been rather boring. They're not vey real-world. In a real script, you'll be doing various steps, checking status as you go, and bailing out with an error status on each failed check. It's only when you get to the bottom of all the steps that you know it's succeeded. Here's where tell becomes more useful:

use Tell qw/:all/, {-closestat => "ERROR"};
tell "Juxquolating the garfibnotor";
return tell_fail
    if !do_kibvoration();
return tell_warn
    if !do_rumbalation();
$fail_reason = do_major_cleanup();
if ($fail_reason) {
    tell_text $fail_reason;
    return;
}
tell_ok;

In this example, we set -closestat to "ERROR". This means that if we exit scope without doing a tell_done (or its equivalents), a tell_done "ERROR" will automatically be called. This will handle exceptions or other blowouts. Next we tell the main title of what we're doing, and then invoke three sublevels of processing (the do_* calls). If any of those fail, we return with the error (tell_fail and tell_warn). The third captures some reason text, and uses tell_text to emit that reason before returning. That last return causes a scope exit and autocompletion kicks in, giving an "ERROR" as the final severity.

Output to Other File Handles

By default, Tell writes its output to STDOUT (or whatever select() is set to). You can tell Tell to use another file handle like this:

use Tell qw/:all/, {-fh => *LOG};

Individual "tell" lines may also take a file handle as the first argument, in a manner similar to a print statement:

tell *LOG, "this", " and ", "that";

Note the comma after the *LOG -- if it was a print you would omit the comma.

Output to Strings

If you give Tell a scalar (string) reference instead of a file handle, then Tell's output will be appended to this string.

For example:

my $out = "";
use Tell qw/:all/, {-fh => \$out};

Individual "tell" lines may also take a scalar reference as the first argument:

tell \$out, "this ", " and ", "that";

Output Independence

Tell separates output contexts by file handle. That means the indentation, autoclosure, bullet style, width, etc. for any output told to STDERR is independent of output told to STDOUT, and independent of output told to a string. All string output is lumped together into one context.

Return Status

Like print, the tell function returns a true value on success and false on failure. Failure can occur, for example, when attempting to tell to a closed filehandle.

Note that to get the return status, you must assign into a scalar context, not a list context:

my $stat;
$stat = tell "Whatever";      # OK. This puts status into $stat
($stat) = tell "Whatever";    # NOT what it looks like!

In list context, the closure for tell is bound to the list variable's scope and autoclosure is disabled.

Bullets

You may preceed each message with a bullet. The is usually a single character such as a dash or an asterix, but may be multiple characters. When it's multiple characters, typically it's just a single character with a space on either side.

You may have a different bullet for each nesting level. Levels deeper than the number of defined bulelts will use the last bullet.

Define bullets by passing an array reference of the bullet strings with -bullet. If you want the bullet to be the same for all levels, just pass the string. Here's some popular bullet definitions:

-bullets => "* "
-bullets => [" * ", " + ", " - ", "   "]

Basic Use

Features

TODO: take the feature snippets from the above example section and put 'em here.

* Automatic closure of messages * Colorized text * Bulleted output * Output independent by file descriptor

TBS

EXPORTS

Nothing is exported by default. You'll want to do one of these:

use Tell qw/tell tell_done/;    # To get just these two functions
use Tell qw/:all/;              # To get all functions
use Tell qw/:syslog/;           # To get base functions plus syslog severities only

Most of the time, you'll want the :all form.

INTERFACE

base

internal base object accessor.

clone

Clones the current object and returns a new copy. Any given attributes override the cloned object.

new

Constructor for a Tell object.

set

Sets attributes on a Tell object.

tell

Use tell to emit a message similar to how you would use print. Every use of tell increases the current message level

Procedural call syntax:

tell LIST
tell *FH, LIST
tell \$out, LIST
tell {ATTRS}, LIST

Object-oriented call syntax:

$tobj->tell (LIST)
$tobj->tell (*FH, LIST)
$tobj->tell (\$out, LIST)
$tobj->tell ({ATTRS}, LIST)
tell_done

Closes the current message level, re-printing the message if necessary, printing dot-dot trailers to get proper alignment, and the given completion severity.

tell_alert
tell_crit
tell_debug
tell_emerg
tell_error
tell_fail
tell_fatal
tell_info
tell_note
tell_notry
tell_ok
tell_unk
tell_warn

All these are convienence methods that call tell_done) with the indicated severity. For example, tell_fail() is equivalent to tell_done "FAIL".

tell_none

This is equivalent to tell_done, except that it does NOT print a wrapup line or a completion severity. It simply closes out the current level with no message.

tell_over
tell_prog

Tells a progress indication, such as a percent or M/N or whatever you devise. In fact, this simply puts *any* string on the same line as the original message (for the current level).

Using tell_over will first backspace over a prior progress string (if any) to clear it, then it will write the progress string. The prior progress string could have been emitted by tell_over or tell_prog; it doesn't matter.

tell_prog does not backspace, it simply puts the string out there.

For example,

use Tell qw/:all/;
tell "Varigating the shaft";
tell_prog '10%...';
tell_prog '20%...';

gives this output:

Varigating the shaft...10%...20%...

Keep your progress string small! The string is treated as an indivisible entity and won't be split. If the progress string is too big to fit on the line, a new line will be started with the appropriate indentation.

With creativity, there's lots of progress indicator styles you could use. Percents, countdowns, spinners, etc. Look at sample005.pl included with this package. Here's some styles to get you thinking:

Style       Example output
-----       --------------
N           3       (overwrites prior number)
M/N         3/7     (overwrites prior numbers)
percent     20%     (overwrites prior percent)
dots        ....    (these just go on and on, one dot for every step)
tics        .........:.........:...
                    (like dots above but put a colon every tenth)
countdown   9... 8... 7...
                    (liftoff!)
tell_text

This prints the given text without changing the current level. Use it to give additional information, such as a blob of description. A lengthy lines will be wrapped to fit nicely in the given width.

CONFIGURATION AND ENVIRONMENT

Tell requires no configuration files or environment variables. However, it does set environment variables with this form of name:

tell_fd#_th#

This _envvar holds the current level of messages (represented visually by indentation), so that indentation can be smoothly maintained across process contexts.

In this _envvar's name, fd# is the fileno() of the output file handle to which the messages are written. By default output is to STDERR, which has a fileno of 2, so the _envvar would be tell_fd2. If output is being written to a string (-fh = \$some_string>), then fd# is the string "str", for example tell_fdstr

When Tell is used with threads, the thread ID is placed in th# in the _envvar. Thus for thread #7, writing Tell messages to STDERR, the _envvar would be tell_fd2_th7. For the main thread, th# and the leading underscore are omitted.

Under normal operation, this environment variable is deleted before the program exits, so generally you won't see it.

Note: If your program's output seems excessively indented, it may be that this _envvar has been left over from some other aborted run. Check for it and delete it if found.

DEPENDENCIES

This pure-Perl module depends upon Scope::Upper.

INCOMPATIBILITIES

None reported.

BUGS AND LIMITATIONS

Limitation: Output in a threaded environment isn't always pretty. It works OK and won't blow up, but indentation may get a bit screwy.

Bugs: No bugs have been reported.

Please report any bugs or feature requests to bug-Tell@rt.cpan.org, or through the web interface at http://rt.cpan.org.

SEE ALSO

Somewhat similar but not quite the same are:

Debug::Message
Log::Dispatch
PTools::Debug

AUTHOR

Steve Roscio <roscio@cpan.org>

ACKNOWLEDGEMENTS

Thanx to Paul Vencel for being an idea soundingboard for this package.

LICENCE AND COPYRIGHT

Copyright (c) 2009, Steve Roscio <roscio@cpan.org>. All rights reserved.

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See perlartistic.

DISCLAIMER OF WARRANTY

Because this software is licensed free of charge, there is no warranty for the software, to the extent permitted by applicable law. Except when otherwise stated in writing the copyright holders and/or other parties provide the software "as is" without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of the software is with you. Should the software prove defective, you assume the cost of all necessary servicing, repair, or correction.

In no event unless required by applicable law or agreed to in writing will any copyright holder, or any other party who may modify and/or redistribute the software as permitted by the above licence, be liable to you for damages, including any general, special, incidental, or consequential damages arising out of the use or inability to use the software (including but not limited to loss of data or data being rendered inaccurate or losses sustained by you or third parties or a failure of the software to operate with any other software), even if such holder or other party has been advised of the possibility of such damages.