NAME
    Log::Defer - Deferred logs and timers

SYNOPSIS
        use Log::Defer;
        use JSON::XS; ## or whatever

        my $logger = Log::Defer->new(\&my_logger_function);
        $logger->info("hello world");
        undef $logger; # write out log message

        sub my_logger_function {
          my $msg = shift;
          print JSON::XS->new->pretty(1)->encode($msg);
        }

    Prints:

        {
           "start" : 1340421702.16684,
           "end" : 0.000249,
           "logs" : [
              [
                 0.000147,
                 30,
                 "hello world"
              ]
           ]
        }

DESCRIPTION
    This module doesn't actually log anything! To use this module for normal
    logging purposes you also need a logging library (some of them are
    mentioned in "SEE ALSO").

    What this module does is allow you to defer recording log messages until
    after some kind of "transaction" has completed. Typically this
    transaction is something like an HTTP request or a cron job. Generally
    log messages are easier to read if they are recorded "atomically" and
    are not intermingled with log messages created by other transactions.

    This module preserves as much structure as possible which allows you to
    record machine-parseable log messages if you so choose.

USAGE
    The simplest use case is outlined in the SYNOPSIS. You create a new
    Log::Defer object and pass in a code ref. This code ref will be called
    once the Log::Defer object is destroyed or all references to the object
    go out of scope.

    If a transaction has several possible paths it can take, there is no
    need to manually ensure that every possible path ends up calling your
    logging routine at the end. The log writing will be deferred until the
    logger object is destroyed or goes out of scope.

    In an asynchronous application where multiple asynchronous tasks are
    kicked off concurrently, each task can keep a reference to the logger
    object and the log writing will be deferred until all tasks are
    finished.

    Log::Defer makes it easy to gather timing information about the various
    stages of your request. This is explained further below.

STRUCTURED LOGS
    So what is the point of this module? Most logging libraries are
    convenient to use, usually even more-so than Log::Defer. However, this
    module allows you to record log messages in a format that can be easily
    analysed if you so choose.

    Line-based log protocols are nice because they are compact and since
    people are used to them they are "easy" to read.

    However, doing analysis on line-based or, even worse, ad-hoc
    unstructured multi-line formats is more difficult than it needs to be.
    And given the right tools, reading structured log messages can actually
    be easier than reading line-based logs.

LOG MESSAGES
    Log::Defer objects provide a very basic "log level" system. In order of
    increasing verbosity, here are the possible logging methods:

        $logger->error("...");  # 10
        $logger->warn("...");   # 20
        $logger->info("...");   # 30
        $logger->debug("...");  # 40

    The only thing that this module does with the log level is record it in
    the log message.

    Here is an example of issuing a warning:

        $logger->warn("something weird happened", { username => $username });

    In the deferred logging callback, the log messages are recorded in the
    "logs" element of the $msg hash. It is an array ref and here would be
    the element pushed onto "logs" by the "warn" method call above:

        [ 0.201223, 20, "something weird happened", { username => "jimmy" } ]

    The first element is a timestamp of when the "warn" method was called in
    seconds since the "start" (see TIMERS below).

    The second element is the verbosity level. If you wish to implement "log
    levels" (ie filter out debug messages), you can grep them out when your
    recording callback is called.

DATA
    Instead of log messages, you can directly access a "data" hash reference
    with the "data" method:

        $log->data->{junkdata} = 'some data';

    This is useful for recording info related to a whole transaction like
    say a connecting IP address. Anything you put in the "data" hash
    reference will be passed along untouched to your defered callback.

TIMERS
    Timer objects can be created by calling the "timer" method on the logger
    object. This method should be passed a description of what you are
    timing.

    The timer starts as soon as the timer object is created and only stops
    once the last reference to the timer is destroyed or goes out of scope,
    or if the logger object itself is destroyed/goes out of scope.

    When the logger object is first created, the current time is recorded
    and is stored in the "start" element of the log hash. "start" is a
    Time::HiRes absolute timestamp. All other times are relative offsets
    from this "start" time. Everything is in seconds.

    Here is a fairly complicated example that includes concurrent timers:

        sub handle_request {
          my $request = shift;
          my $logger = Log::Defer->new(\&my_logging_function);

          my $headers = do {
            my $parse_timer = $logger->timer('parsing request');
            parse_request($request);
          };

          my $fetch_timer = $logger->timer('fetching results');
          async_fetch_results($headers, sub {

            ## stop first timer by undefing ref, then start new timer
            undef $fetch_timer; $fetch_timer = $logger->timer('fetching stage 2');

            async_fetch_stage_2($headers, sub {

              $logger; ## keep reference alive
              undef $fetch_timer;
              send_response();

            });

            my $update_cache_timer = $logger->timer('update cache');

            async_update_cach(sub {

              $logger; ## keep reference alive
              undef $update_cache_timer;

            });

          });
        }

EXAMPLE LOG MESSAGE
    Each structured log message will be passed into the callback provided to
    "new". The message is a perl hash reference that contains various other
    perl data-structures. What you do at this point is up to you.

    What follows is a prettified example of a JSON-encoded log message.
    Normally all unnecessary white-space would be removed and it would be
    stored on a single line so that ad-hoc command-line "grep"ing still
    works.

        {
           "start" : 1340353046.93565,
           "end" : 0.202386,
           "logs" : [
              [
                 0.000158,
                 30,
                 "This is an info message (verbosity=30)"
              ],
              [
                 0.201223,
                 20,
                 "Warning! \n\n Here is some more data:",
                 {
                     "whatever" : 987
                 }
              ]
           ],
           "data" : {
              "junkdata" : "some data"
           },
           "timers" : {
              "junktimer" : [
                 0.000224,
                 0.100655
              ],
              "junktimer2" : [
                 0.000281,
                 0.202386
              ]
           }
        }

FUTURE WORK
    We plan do some cool stuff with structured logs. Here's a mock-up of a
    timer rendering idea:

        parsing request       |======|
        fetching results             |==========|
        fetching stage 2                        |==========================|
        update cache                            |==========|
                              0                 0.05073                    0.129351
                                     0.0012                 0.084622

SEE ALSO
    As mentioned above, this module doesn't actually log messages to
    disk/syslog/anything so you still must use some other module to record
    your log messages. There are many libraries on CPAN that can do this and
    there should be at least one that fits your requirements. Some examples
    are: Sys::Syslog, Log::Dispatch, Log::Handler, Log::Log4perl, Log::Fast,
    AnyEvent::Log.

    Some caveats related to non-monotonous clocks are discussed in
    Time::HiRes.

AUTHOR
    Doug Hoyte, "<doug@hcsw.org>"

COPYRIGHT & LICENSE
    Copyright 2012 Doug Hoyte.

    This module is licensed under the same terms as perl itself.

