=head1 NAME

XLog - Unified logging API, without performance penalties.



=head1 SYNPOSIS

    XLog::set_logger(sub { say $_[0] });
    XLog::set_formatter("%1t %c[%L/%1M]%C %f:%l,%F(): %m");
    XLog::set_level(XLog::INFO);
    
    XLog::info("number=%d", $num);
    XLog::warning("hello");
    XLog::log(XLog::ERROR, $msg);
    XLog::debug("msg=%s", $message); # will not be logged, because min level is INFO, and message will NOT be evaluated
    
    XLog::error($module, $message); # use certain log module for logging
    
    #callback will not be called if log level is insufficient
    XLog::notice(sub {
        my $msg = '';
        for (...) {
           ...
           $msg .= ...
        }
        return $msg;
    });
    
    {
        package MyPackage;
        our $xlog_module = XLog::Module->new("my module"); # every log in this package will use this log module
        $xlog_module->set_level(XLog::ERROR);
        $xlog_module->set_logger(sub { send_somewhere($_[0]) });
        
        ...
        
        XLog::error($message); # logged with module "my module"
        XLog::warning($message); # will not be logged / evaluated
    }
    
    # set custom formatter
    {
        package MyFormatter;
        use parent 'XLog::Formatter';
        sub format {
            my ($self, $msg, $level, $module, $file, $line, $func) = @_;
            return "formatted message";
        }
    }
    XLog::set_formatter(MyFormatter->new);
    
    # choose a backend for logging
    XLog::set_logger(XLog::Console->new);
    XLog::set_logger(XLog::File->new({file => "my.log"});
    
    # or log to multiple backends
    XLog::set_logger(XLog::Multi->new([
        {logger => XLog::Console->new, min_level => XLog::DEBUG},
        {logger => XLog::File->new({file => "my.log"}, min_level => XLog::NOTICE, formatter => XLog::Formatter::Pattern->new("f2:%m")},
        {logger => sub { send over network... }, min_level => XLog::ERROR},
    ]));

=head1 DESCRIPTION

XLog is a centralized thread-safe logging API both from perl and C level. It is written in C and is very fast.

One of its main features is that if message is not to be logged (due to log level) then arguments will not be evaluated and function is not called.
Closest example of this behaviour (but ugly and non-flexible)

log("something") if $DEBUG;

XLog is an API, not an implementation. You can choose which backend to use among available modules or use multiple backends via L<XLog::Multi>.
To create your own backend see L<XLog::Logger>.

XLog supports C<logging modules>. Modules are used to separate log levels in one part of the application from another so that you can enable for example debug
logs only for part of your application, not for the whole app. Also modules allow to log various parts of application to different destinations.

More detailed info on modules in L<XLog::Module>.



=head1 LOGGING

Logging is done by calling one of the logging functions, for example C<XLog::alert("message")>. Logging is only done if selected log level is equal to
or greater than selected minimal log level (via C<XLog::set_level()>). Otherwise arguments are not evaluated and the log line doesn't take any measurable time.

By default, min level is WARNING.

If logging is to be done, XLog will evaluate arguments, format log message and pass it to backend. Backend is just an object (or subroutine)
which receives a log message and must log it somewhere and somehow.



=head1 LOG MODULES

See L<XLog::Module>.



=head1 FUNCTIONS

=head4 set_level($level, [$module_name])

If C<$module_name> is omitted, set minimum log level globally (for all modules)

    XLog::set_level(XLog::DEBUG);
    XLog::debug($message); # message is logged
    XLog::set_level(XLog::INFO);
    XLog::debug($message); # message is neither logged nor evaluated
    
    See C<LOG LEVELS>

Otherwise, set minimum log level only for specified module and its children. Effect is the same as

    $module->set_level($level);


=head4 set_logger($logger)

Set backend globally (for root module). Must be a subref or logging object compatible with XLog.

If C<$logger> is subref it will receive formatted message and log level.

    XLog::set_logger({
        my ($msg, $level) = @_;
        say $msg;
    });
    
Or you can create an object using existing backends L<XLog::Console>, L<XLog::File>, etc...

Or you can create your own backend, see L<XLog::Logger>.


=head4 set_formatter($formatter)

Set log message formatter globally (for root module). Must be a subref, formatter object or pattern string.

If C<$formatter> is a subref, it will receive the following parameters:

    XLog::set_formatter(sub {
         my ($msg, $level, $module, $file, $line, $func) = @_;
         return "$msg at $file:$line";
    });
    
=over

=item $msg

Log message as supplied by user
    
=item $level

Log level constant, see C<LOG LEVELS>

=item $module

Log module name
    
=item $file

File in which log message was written
    
=item $line

Line on which log message was written
    
=item $func

Function in which log message was written
    
=back

Subroutine must return final log message which will be passed to backend.

C<$formatter> may also be an object, for example L<XLog::Formatter::Pattern>.
You can create your own formatter class, see L<XLog::Formatter>.

If C<$formatter> is a string, the effect is the same as 

    XLog::set_formatter(XLog::Formatter::Pattern->new($formatter));

See L<XLog::Formatter::Pattern> for details

The default global formatter is C<%1t %c[%L/%1M]%C %f:%l,%F(): %m>.


=head4 log($level, [$module], $message, [@args])

This function does logging.

C<$module> is optional and if not supplied will be automatically detected by looking for global variable $xlog_module in
class where logging line is. If no such variable detected, will look into upper class and so on. If no variable detected in the end of this process, will
use root logging module.

C<$message> must be a string or subref.

If C<$message> is a string, it supports C<printf>-like format. In this case optional C<@args> are used to replace placeholders. For example

    XLog::log(XLog::DEBUG, "message received: %s (%d bytes)", $msg, length($msg));
    
If C<$message> is a subref, it will be called and its result is used as a log message.

In either case if supplied C<$level> is not sufficient for logging (i.e. message will not be logged), arguments are not evaluated. For example

    XLog::set_level(XLog::INFO);
    XLog::log(XLog::DEBUG, "message: $very_long_message"); # string in quotes is not evaluated, and $very_long_message is not interpolated


=head4 debug([$module], $message, [@args])

=head4 info(...)

=head4 notice(...)

=head4 warning(...)

=head4 error(...)

=head4 critical(...)

=head4 alert(...)

=head4 emergency(...)

Same as C<XLog::log()> passing corresponding log level as a first arg.


=head4 get_module($name)

Returns module object registered as C<$name> or undef if no such module.

Saving this object for later use and accessing it after module is deleted is undefined behaviour.


=head4 resolve_module($depth = 0)

When L<XLog::log> is invoked, it should find the module having only C<caller> information. By default it uses the top frame (C<$depth = 0>),
and it is correct in the majority of use cases. However, sometimes logging function is written dedicated package with it's own
module and it is desirable to skip it and let the log message appear in the sake of the outer context. The L<resolve_module> function comes to help:

    package XXX {
        our $xlog_module = XLog::Module->new("XXX");

        sub log_event {
            my $message = shift;
            my $module = resolve_module(1);
            XLog::info($module, $message, ...);
        }
    }

    package YYY {
        our $xlog_module = XLog::Module->new("YYY");

        sub something1{
            XXX::log_event("lorem ipsum"); # will be routed via "YYY" module
        }
    }

    package ZZZ {
        our $xlog_module = XLog::Module->new("ZZZ");

        sub something_else {
            XXX::log_event("sit amet"); # will be routed via "ZZZ" module
        }
    }


=head4 disable_format_warnings()

Globally disables warnings when inappropriate arguments given to printf-like logging call. For example:

    XLog::info("message is %s", undef);
    
This would normally emit C<Use of uninitialized value in subroutine entry> warning. After C<disable_format_warnings()> call such warnings will be
supressed.


=head4 enable_format_warnings()

Rolls back the effect of C<disable_format_warnings()>.



=head1 LOG LEVELS

All constants are in XLog namespace (i.e. XLog::DEBUG).

=over

=item DEBUG

=item INFO

=item NOTICE

=item WARNING

=item ERROR

=item CRITICAL

=item ALERT

=item EMERGENCY

=back



=head1 EVALUATION OPTIMIZATION

XLog will not evaluate arguments and will not call any function (i.e. the line will be no-op) if min log level is higher than message log level and
XLog can understand where module and message are (in arguments), because module is needed to find out minimal log level.

Optimization enabled when:

=over

=item Module/message argument is simple

In the following examples, evaluation is skipped

    XLog::debug("message");
    XLog::debug($message);
    XLog::log(XLog::DEBUG, "hi");
    XLog::debug($module, "msg=$msg"); # doesn't matter how complex is message argument
    XLog::log(XLog::DEBUG, $module, "msg=$msg"); # doesn't matter how complex is message argument
    
In next examples, module/message is a complex expression, so optimizations are disabled.
    
    XLog::debug($cond ? $module1 : $module2, "msg=$msg");
    XLog::debug($cond ? $msg1 : $msg2, @printf_args);
    XLog::debug(function_returning_module_or_message(), "msg=$msg");
    
=item Message argument is an interpolation or concatenation (soon, not yet implemented)

    XLog::debug("msg=$msg bytes=$bytes");
    XLog::debug($msg1.$msg2.$msg3);

=back



=head1 SEE ALSO

L<XLog::Module>

L<XLog::Multi>

L<XLog::Console>

L<XLog::File>

L<XLog::Logger>

L<XLog::Formatter>

L<XLog::Formatter::Pattern>



=head1 AUTHOR

Pronin Oleg <syber@crazypanda.ru>

Ivan Baidakou <dmol@cpan.org>

Crazy Panda LTD



=head1 LICENSE

You may distribute this code under the same terms as Perl itself.

=cut

1;