package Log::ger::Manual::Internals;

# DATE
# VERSION

1;
# ABSTRACT: Log::ger internals

__END__

=pod

=encoding UTF-8

=head1 NAME

Log::ger::Manual::Internals - Log::ger internals

=head1 VERSION

version 0.032.001

=head1 DESCRIPTION

When an importer package does this:

 use Log::ger;

Basically all Log::ger does is construct logging routines and install them to
importer's package (target), e.g. C<log_warn>, C<log_debug>, C<log_is_debug> and
so on. Log::ger also records the target name. When a reinit is requested (e.g.
due to a change of log level or outputs), Log::ger will again construct logging
routines and install them to each target, replacing the old ones.

In addition to installing routines to a package, Log::ger can also target a hash
or an object (which is basically the same as installing to a package, but the
routines will expect to be called as object methods instead of plain
subroutines, i.e. they expect the first argument to be the object).

=head1 GLOSSARY

=head2 Category

A way to, or an attribute by which one can filter log messages. Category by
default is set to the Perl package from which a log message is produced, but
everytime you log a message, you can assign it an arbitrary string as the
category.

The other, most important, way to filter log messages is by its L<level|/Level>.

=head2 Composite output

An L<output|/Output> that simply multiplexes the log message it receives to one
or more other outputs.

Implemented in L<Log::ger::Output::Composite>.

=head2 Filter

This is a routine that takes arguments supplied by the user to the log routine
(e.g. C<<log(level=>"info", message=>"blah", category=>"foo", ...>> and return a
false value or a per-message configuration.

If false is returned, logging is cancelled. Otherwise, the per-message
configuration hash is passed to the layouter and logger.

Filter is optional and can be used to do custom filtering based on some criteria
specified in the arguments, as well as to extract the per-message configuration
in the arguments to be passed to the logger routine (usually provided by an
output plugin). For example, a screen output might want to colorize a log
message based on the category passed in the argument.

Regardless of whether a filter is provided, logging will still be filtered by
level.

Filter is the way a "multilevel log routine" is implemented, i.e. instead of a
dedicated logging routine per level, a general log routine is produced and the
level information is passed as an argument. For example: C<<log("info",
"blah")>> or C<<log(level=>"info", message=>"blah")>>.

=head2 Formatter

A routine that takes arguments supplied by the user to the log routine (e.g. C<<
log_warn("blah %s", $args, ...) >> and converts it to the message (usually
string) that is sent to the output (or the layouter, if there is one).

See also L</Layouter>.

=head2 Hook

A code (provided by L<plugins|/Plugin>) that is called in various points of time
(L<phase|/Phase>). Please see L</HOOKS AND PLUGINS> for more details.

=head2 Hook priority

A way by which different hooks that register at the same phase are ordered for
execution. Implemented as a number between 0 to 100 where 0 means very high
(executed first) and 100 means very low (executed last). Hooks that do not care
about the order in which they are executed with regard to other hooks should set
their priority to 50 (normal).

=head2 Init

The process of constructing L<logger routines|/Log routine> and installing them
to targets. See L</"INIT"> for more details about the process.

=head2 Is routine

A type of routines that are constructed and installed to L<target|/Target> which
checks for a certain level. When logging level is at a certain level or "higher"
(more severe), the associated "is routine" will return true; and vice versa.
This is an efficient way to avoid doing work when unneeded.

The default "is routine" names are "log_is_<LEVEL>", e.g. "log_is_trace",
"log_is_debug", and so on. An example of using an "is routine":

    use Log::ger;

    sub foo {
        if (log_is_trace) {
            # perform some calculation
            ...
            log_trace "Result of calculation: ...";
        }
    }

The other type of routines that are constructed and installed to target is L<log
routines|/Log routine>.

=head2 Layouter

A routine that takes the formatted message (usually a string) and converts it to
the final message string that is sent to output. Usually a layouter is used to
add additional information along with the log message itself, e.g. timestamp,
source file/line number, PID (process ID), etc.

See also L</Formatter>.

=head2 Level

The main way by which log messages are filtered. Level refers to the important
or "severity" of a message. Log producer chooses to log a message with a certain
level, e.g.:

    log_trace "This is a low importance log message";
    $log->log("error", "This is a highly important log message!");

while log consumer chooses only to "see" log messages "above" certain level
only.

Another way to filter log messages is by its L<category|/Category>.

=head2 Log consumer

A log consumer is a process (Perl application/script) which configures an
L<output|/Output>. This causes all log messages that are produced (either by the
script itself or the modules that the script uses) to be directed to the
specified output.

=head2 Log producer

Any code (typically a Perl module) that uses Log::ger and invokes one or more
L<log routines|/Log routine>. For example, this tiny module is a producer:

 package MyPackage;
 use Log::ger;

 sub foo {
     log_trace "Entering foo";
     ...
     log_trace "Leaving foo";
 }

 1;

When C<foo()> is used somewhere else, it will produce two log messages at the
C<trace> level.

=head2 Log routine

Or logger routine. A type of routines that are constructed and installed to
L<target|/Target> which produces a log message.

The default "log routine" names are "log_<LEVEL>", e.g. "log_trace",
"log_debug", and so on. An example of using a "log routine":

    use Log::ger;

    sub foo {
        log_trace "Entering foo";
        ...
        log_trace "Leaving foo";
    }

Log routines is the main type of routines that you will typically use when
logging. The other type of routines that are constructed and installed to target
is L<is routines|/Is routine>.

=head2 Output

Destination for the formatted (see L</Formatter>), laid out (see L</Layouter>)
log message. Only log messages that have the sufficient level and matching
category get sent to the output. Examples of output include: null (no where),
screen (terminal), file, syslog.

In Log::ger, log messages are sent to a single output. But there is a
L<multiplexer output|/Composite output> that (re)sends the log messages to other
output(s).

=head2 Phase

The order in which L<hooks|/Hooks> are executed. For more details, see L</HOOKS
AND PLUGINS>.

=head2 Plugin

A Perl module that supplies hooks. For more details, see L</HOOKS AND PLUGINS>.

=head2 Target

A package that will be installed with logging routines. Aside from package,
Log::ger can also install routines to a hash or an object.

Installing to a hash is usually for internal testing purposes. As a Log::ger
user, you will very rarely need to target a hash.

Installing to an object is essentially the same as installing to a package:
Log::ger will pick a "random" package for the object and install the routines
there.

=head1 HOOKS AND PLUGINS

Hooks are how Log::ger provides its flexibility. At various times (phases),
Log::ger will turn to running hooks to get some behavior or result. For example
when wanting to construct a logging routine or formatting routine or
before/after installing logging routines. Plugins, which are modules in the
Log::ger::{Plugin,Output,Format,Layout,...} namespaces, can supply these hooks.

Hooks are stored in the C<%Global_Hooks> variable, where the key is phase name
and the value an array of hook records. There are also C<%Per_Package_Hooks>,
C<%Per_Object_Hooks>, and C<%Per_Hash_Hooks> to store per-target hooks that will
only be used for specific targets. This way, the logging routines for each
package and object/hash can be customized.

Each hook record is in the form of:

 [$key, $prio, $coderef]

where C<$key> is (plugin) package name, C<$prio> is a number between 0-100 (the
lower the number, the higher the priority and the earlier it is run),
C<$coderef> is the actual hook routine. A plugin is supposed to put only at most
one hook per phase.

=head2 Expected return value of hook

A hook routine is passed a hash argument and is expected to return an array:

 [$result, $flow_control, ...]

By default each hook will be executed in order of its priority. C<$flow_control>
can be set to 1 by a hook to stop immediately after this hook instead of
continuing to the next. Some phases will nevertheless stop after the first hook
that returns non-undef C<$result>. A hook that returns undef is effectively
declining and causing Log::ger to move to the next hook in the chain.

Some phases might return extra elements.

=head2 Arguments passed to hook

Aguments received by hook: C<target_type> (str, can be C<package> if installing
to a package, or C<hash> or C<object>), C<target_name> (str, when C<target_type>
is C<package>, will be the package name; when C<target_type> is C<hash> will be
the hash; when C<target_type> is C<object> will be the object),
C<per_target_conf> (hash, arguments passed to Log::ger when importing, e.g. C<<
{category => 'My::Package'} >>; it also serves as a per-target stash which
survives reinit, by convention you can put stuffs here under keys that start
with C<_>). In some phases, hook will receive more arguments (see phase
documentation below).

=head2 Phases

Available phases:

=over

=item * create_filter

Used to construct L<filter routine|/Filter>.

It should return:

 [\&filter, $flow_control]

The C<&filter> routine will be passed the logger routine arguments and should
return a false value (to signal that the logging is to be cancelled) or a
hashref of per-message configuration (usually extracted from the arguments).
This per-message configuraion hashref will be passed to the layouter and the
logger provided by an output plugin.

=item * create_formatter

Used to construct L<formatter routine|/Formatter>.

It should return:

 [\&formatter, $flow_control, $formatter_name]

C<$formatter_name> is optional and defaults to C<default>, which is the default
formatter used for all logging routines.

C<&formatter> will be passed the logger routine arguments and is responsible for
returning the formatted message (usually a string). For example, the default
Log::ger formatter either accepts a single argument (the message, which will be
passed as-is as the formatted message) or multiple arguments (the first argument
as template and for the rest of the arguments as variables, which will be
processed a la C<sprintf()> to produce the formatted string).

=item * create_layouter

Used to construct layouter routine.

It should return:

 [\&layouter, $flow_control]

C<&layouter> will be called with arguments:

 ($fmsg, \%per_target_conf, $lnum, $lname, \%per_msg_conf)

where C<$fmsg> is formatted message from the formatter, C<%per_target_conf> are
arguments given to C<< Log::ger->get_logger >> or to Log::ger's C<import()>,
C<$lnum> is numeric level, C<$lname> is string level, C<%per_msg_conf> is
information provided by the filter if there is one. The layouter must return the
laid-out message (usually a string).

=item * create_routine_names

Used to construct routine names. Hook must return this (all keys are optional):

 [{
   log_subs      => [ [NAME, STR_LEVEL, FMT_NAME, PER_TARGET_CONF, FLT_NAME], ... ],
   log_methods   => [ [NAME, STR_LEVEL, FMT_NAME, PER_TARGET_CONF, FLT_NAME], ... ],

   is_subs       => [ [NAME, STR_LEVEL, FMT_NAME, PER_TARGET_CONF, FLT_NAME], ... ],
   is_methods    => [ [NAME, STR_LEVEL, FMT_NAME, PER_TARGET_CONF, FLT_NAME], ... ],
 }, ...]

Where C<log_subs> and C<log_methods> are names of per-level log routines,
C<is_subs> an C<is_methods> are names of per-level level detection routines.

C<FMT_NAME> is optional and defaults to C<default> (the default formatter
routine). Can be set if the routine should use a custom formatter routine.

C<PER_TARGET_CONF> is an optional. hash. Can be set if the routine should use a
custom per-target configuration (e.g. different category).

C<FLT_NAME> is optional. Can be set if the routine should use a custom filter
routine.

=item * create_log_routine

Used to create per-level "log_I<level>" routines.

It should return:

 [\&create_log_routine, $flow_control]

C<&create_log_routine> will be called with:

 (\%per_target_conf, $msg, $lnum, \%per_msg_conf)

for each routine specified in the C<log_subs> (or C<log_methods>) in the routine
names (see documentation on C<create_routine_names> phase). Extra arguments
received by hook: C<routine_name> (routine name), C<level> (numeric level),
C<str_level>.

=item * create_is_routine

Used to create per-level "log_is_I<level>" routines.

It should return:

 [\&create_is_routine, $flow_control]

C<&create_is_routine> will be called for each routine specified in the
C<is_subs> (or C<is_methods>) in the routine names (see documentation on
C<create_routine_names> phase). Extra Arguments received by hooks:
C<routine_name> (routine name), C<level> (numeric level), C<str_level>.

=item * before_install_routines

Will be called before routines are installed to target.

Extra arguments received by hooks: C<routines> (array of routines to install),
C<formatters> (hashref of formatter routines, if any), C<layouter> (layouter
routine, if any).

C<routine> is in the form of:

 [
   [$coderef, $name, $num_level, $type],
   ...
 ]

Where C<$type> is either C<log_sub>, C<log_method>, C<is_sub>, C<is_method>.

=item * after_install_routines

Will be called after routines are installed to target.

Extra arguments received by hooks: C<routines>.

=back

Aside from the global hooks, there are also per-target hooks, which are stored
in C<%Per_Package_Hooks>, C<%Per_Hash_Hooks>, C<%Per_Object_Hooks>.

=head1 INIT

This section describes what C<init_target()>, which is the routine used to
initialize a target, does.

First, hooks in the C<create_formatter> phase are run. This will collect one or
more formatters. In the most common case, only the C<default> formatter will be
constructed. In some cases, like L<Log::ger::Like::LogAny>, we want to use
different formatters for method like C<warn> and C<debug> (arguments simply
joined by space) and for methods like C<warnf> and C<debugf> (sprintf-style with
data structure dumping, the default formatter used by Log::ger).

Next, hooks in the C<create_layouter> phase are run. This will create a layouter
(multiple layouters I<might> be supported in the future, but for now we only use
one layouter per target).

Next, hooks in the C<create_routine_names> phase are run. This will produce a
list of subroutine/method names to create, along with what formatter to use for
each (the default is C<default>). Plugins that want to mimic other interfaces
like L<Log::ger::Like::LogAny> or L<Log::ger::Like::Log4perl> will want to add
their hook here to provide names other than Log::ger's default. For example,
Log4perl has uppercase subroutine names like C<WARN> or C<DEBUG>.

There are two types of routines that are created, logging routines (like
C<log_warn>, C<log_debug>) and level detection routines, also called the "is"
routines (like C<log_is_warn>, C<log_is_debug>). Logging routines in turn can be
reguler, per-level routines or multilevel routines that accept numeric level as
the first argument.

Next, the logger routines are created by running the C<create_log_routine> hook,
for each log level. The code from the hooks (usually from output plugins) are
then combined with the formatter and layouter to form the final routine ready to
be installed.

Likewise, the log level detection routines are created by running the
C<create_is_routine> hooks for each log level.

Before installing the routines, we give a chance to plugins to do stuffs in the
C<before_install_routines> phase. Some plugins use this phase to, e.g. fixup
prototypes.

After installing the routines, we likewise give a chance to plugins to do stuffs
in the C<after_install_routines> phase. Some plugins like
L<Log::ger::Plugin::OptAway> this phase to actually replace the routines that
are not needed with a no-op.

=head1 TARGETS

Log::ger can install logger routines to a package, or an object (which is
similar to installing to a package), or a hash (usually for testing). The
routines that are installed into C<package> target are of type C<*_subs>. The
routines that are installed into C<object> target package are of type
C<*_methods>. The routines that are installed into C<hash> target are of type
C<*_methods> but they will expect to be called as a normal subroutine (i.e. no
object reference as first C<$self> argument).

=head1 AUTHOR

perlancar <perlancar@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2020, 2019, 2018, 2017 by perlancar@cpan.org.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut
