From: Tyler Riddle Date: Fri, 9 Nov 2012 04:56:13 +0000 (-0800) Subject: make Flog_* safer; document ::Logger; rewrite section of ::Logging docs X-Git-Tag: v0.003001_01~76 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=scpubgit%2FObject-Remote.git;a=commitdiff_plain;h=455d031ccaa22069757d53cac1e90fda622b80d0 make Flog_* safer; document ::Logger; rewrite section of ::Logging docs --- diff --git a/lib/Object/Remote/Logging.pm b/lib/Object/Remote/Logging.pm index 4811048..e049769 100644 --- a/lib/Object/Remote/Logging.pm +++ b/lib/Object/Remote/Logging.pm @@ -62,15 +62,16 @@ sub before_import { $spec->add_export("&Flog_$level", sub (&@) { my ($code, $exit_value) = @_; $exit_value = 1 unless defined $exit_value; - $router->handle_log_request({ + #don't let it going wrong stop us from calling exit() + eval { $router->handle_log_request({ controller => $class, package => scalar(caller), caller_level => 1, level => $level, - }, $code); - #TODO this should get fed into a logger so it can be formatted - #don't let it going wrong stop us from calling exit() + }, $code) }; + warn "could not deliver log event during Flog_$level: $@" if defined $@; eval { carp $code->() }; + warn "could not emit warning during Flog_$level: $@" if defined $@; exit($exit_value); }); } @@ -81,7 +82,6 @@ sub before_import { sub init_logging { my $level = $ENV{OBJECT_REMOTE_LOG_LEVEL}; my $format = $ENV{OBJECT_REMOTE_LOG_FORMAT}; - #TODO allow the selections value to be * so it selects everything my $selections = $ENV{OBJECT_REMOTE_LOG_SELECTIONS}; my %controller_should_log; @@ -97,7 +97,7 @@ sub init_logging { router()->connect(sub { my $controller = $_[1]->{controller}; - return unless $controller_should_log{'*'} || $controller_should_log{$controller}; + return unless $controller_should_log{'*'} || $controller_should_log{$controller}; #skip things from remote hosts because they log to STDERR #when OBJECT_REMOTE_LOG_LEVEL is in effect return if $_[1]->{remote}->{connection_id}; @@ -115,6 +115,7 @@ sub init_logging_forwarding { } 1; + __END__ =head1 NAME @@ -130,8 +131,8 @@ Object::Remote::Logging - Logging subsystem for Object::Remote $ENV{OBJECT_REMOTE_LOG_LEVEL} = 'trace'; #or other level name $ENV{OBJECT_REMOTE_LOG_FORMAT} = '%l %t: %p::%m %s'; #and more - $ENV{OBJECT_REMOTE_LOG_FORWARDING} = 0 || 1; #default 0 $ENV{OBJECT_REMOTE_LOG_SELECTIONS} = 'Object::Remote::Logging Some::Other::Subclass'; + $ENV{OBJECT_REMOTE_LOG_FORWARDING} = 0 || 1; #default 0 log_info { 'Trace log event' }; Dlog_verbose { "Debug event with Data::Dumper::Concise: $_" } { foo => 'bar' }; @@ -150,6 +151,48 @@ The rest of the logging system comes from L which implements log rendering and output and L which delivers log events to the loggers. +=head1 USAGE + +Object::Remote logging is not enabled by default. If you need to immediately start +debugging set the OBJECT_REMOTE_LOG_LEVEL environment variable to either 'trace' +or 'debug'. This will enable logging to STDERR on the local and all remote Perl +interpreters. By default STDERR for all remote interpreters is passed through +unmodified so this is sufficient to receive logs generated anywhere Object::Remote +is running. + +Every time the local interpreter creates a new Object::Remote::Connection the connection +is given an id that is unique to that connection on the local interpreter. The connection +id and other metadata is available in the log output via a log format string that can +be set via the OBJECT_REMOTE_LOG_FORMAT environment variable. The format string and +available metadata is documented in L. Setting this +environment variable on the local interpreter will cause it to be propagated to the +remote interpreter so all logs will be formated the same way. + +This class is designed so any module can create their own logging sub-class using it. +With out any additional configuration consumers of this logging class will automatically +be enabled via OBJECT_REMOTE_LOG_LEVEL and formated with OBJECT_REMOTE_LOG_FORMAT but +those additional log messages are not sent to STDERR. By setting the +OBJECT_REMOTE_LOG_SELECTIONS environment variable to a list of class names seperated +by spaces then logs generated by packages that use those classes will be sent to STDERR. +This is also a configuration item that is forwarded to the remote interpreters so all +logging is consistent. + +Regardless of OBJECT_REMOTE_LOG_LEVEL the logging system is still active and loggers +can access the stream of log messages to format and output them. Internally +OBJECT_REMOTE_LOG_LEVEL causes an L to be built +and connected to the L instance. It is also possible +to manually build a logger instance and connect it to the router. See the documentation +for the router and logger class. + +The logging system also supports a method of forwarding log messages from remote +interpreters to the local interpreter. Forwarded log messages are generated in the +remote interpreter and the logger for the message is invoked in the local interpreter. +Sub-classes of Object::Remote::Logging will have log messages forwarded automatically. +Loggers receive forwarded log messages exactly the same way as non-forwarded messages +except a forwarded message includes extra metadata about the remote interpreter. Log +forwarding is not currently enabled by default; to enable it set the +OBJECT_REMOTE_LOG_FORWARDING environment variable to 1. See L. + =head1 EXPORTABLE SUBROUTINES =over 4 @@ -174,7 +217,7 @@ that returns the log message contents and the optional further arguments are bot to the block as the argument list and returned from the log method as a list. log_trace { "A fine log message $_[0] " } 'if I do say so myself'; - $hashref = Dlog_trace { "Very handy: $_" } { foo => 'bar' }; + %hash = Dlog_trace { "Very handy: $_" } ( foo => 'bar' ); =item logS_ and DlogS_ @@ -244,36 +287,3 @@ Something went wrong and recovery is not possible. The system should stop operat as soon as possible. Tripple quiet operation (-q -q -q). =back - -=head1 ENVIRONMENT - -=over 4 - -=item OBJECT_REMOTE_LOG_LEVEL - -By default Object::Remote will generate log events but messages will not be -output to STDERR. If there is a defined value for this variable then logs will -be sent to STDERR if they are at this level or higher. - -=item OBJECT_REMOTE_LOG_FORMAT - -If logging output is enabled and this value is defined then the logger will -use this format string instead of the default '[%l %r] %s'; See -L for documentation on the format string. - -=item OBJECT_REMOTE_LOG_SELECTIONS - -By default Object::Remote log output will only be enabled for messages generated inside -Object::Remote packages. If logs should be generated for other log messages instead of just -Object::Remote messages set this variable to the names of any Object::Remote::Logging subclass or -Object::Remote::Logging itself seperated by a space. To output all logs generated set the value -to *. - -=item OBJECT_REMOTE_LOG_FORWARDING - -Object::Remote can forward log events from the remote Perl interpreter to the local Perl -interpreter in a transparent way. Currently this feature is disabled by default but -that will change Really Soon Now(TM); to enable it set the variable to '1'. - -=back - diff --git a/lib/Object/Remote/Logging/Logger.pm b/lib/Object/Remote/Logging/Logger.pm index 888d998..5c8295c 100644 --- a/lib/Object/Remote/Logging/Logger.pm +++ b/lib/Object/Remote/Logging/Logger.pm @@ -5,8 +5,8 @@ use Scalar::Util qw(weaken); has format => ( is => 'ro', required => 1, default => sub { '%l: %s' } ); has level_names => ( is => 'ro', required => 1 ); -has min_level => ( is => 'ro', required => 1 ); -has max_level => ( is => 'ro', required => 1, deafult => sub { 'info' } ); +has min_level => ( is => 'ro', required => 1, default => 'info' ); +has max_level => ( is => 'lazy', required => 1 ); has _level_active => ( is => 'lazy' ); sub BUILD { @@ -15,6 +15,11 @@ sub BUILD { $self->_install_methods unless $METHODS_INSTALLED; } +sub _build_max_level { + my ($self) = @_; + return $self->level_names->[-1]; +} + sub _build__level_active { my ($self) = @_; my $should_log = 0; @@ -70,7 +75,6 @@ sub _create_format_lookup { c => $metadata->{controller}, p => $metadata->{package}, m => $method, f => $metadata->{filename}, i => $metadata->{line}, h => $metadata->{hostname}, P => $metadata->{pid}, - }; } @@ -114,6 +118,206 @@ sub _output { print STDERR $content; } - 1; +__END__ + +=head1 NAME + +Object::Remote::Logging::Logger - Format and output a log message + +=head1 SYNOPSIS + + use Object::Remote::Logging::Logger; + use Object::Remote::Logging qw( router arg_levels ); + + my $app_output = Object::Remote::Logging::Logger->new( + level_names => arg_levels, format => '%t %s', + min_level => 'verbose', max_level => 'info', + ); + + #Selector method can return 0 or more logger + #objects that will receive the messages + my $selector = sub { + my ($generating_package, $metadata) = @_; + return unless $metadata->{controller} eq 'App::Logging::Subclass'; + return $app_output; + }; + + #true value as second argument causes the selector + #to be stored with a weak reference + router->connect($selector, 1); + + #disconnect the selector from the router + undef($selector); + + #router will hold this logger forever + #and send it all log messages + router->connect(Object::Remote::Logging::WarnLogger->new( + level_names => arg_levels, format => '%s at %f line %i, log level: %l' + min_level => 'warn', max_level => 'error', + )); + +=head1 DESCRIPTION + +This class receives log messages from an instance of L, +formats them according to configuration, and then outputs them to STDERR. In between +the router and the logger is a selector method which inspects the log message metadata +and can return 0 or more loggers that should receive the log message. + +=head1 USAGE + +A logger object receives the log messages that are generated and converts them to +formatted log entries then displays them to the end user. Each logger has a set +of active log levels and will only output a log entry if the log message is at +an active log level. + +To gain access to the stream of log messages a connection is made to the log router. +A logger can directly connect to the router and receive an unfiltered stream of +log messages or a selector closure can be used instead. The selector will be executed +for each log message with the message metadata and returns a list of 0 or more loggers +that should receive the log message. When the selector is executed the first argument +is the class name of the package that generated the log message and the second argument +is a hash reference containing the message metadata. + +=head1 METADATA + +The message metadata is a hash reference with the following keys: + +=over 4 + +=item level + +Name of the log level of the message. + +=item controller + +Name of the sub-class of Object::Remote::Logging in use by +the generating package. + +=item package + +Name of the package that generated the log message. + +=item method + +Name of the method the message was generated inside of. + +=item timestamp + +Unix time of the message generation. + +=item pid + +Process id of the Perl interpreter the message was generated in. + +=item hostname + +Hostname of the system where the message was generated. + +=item filename + +Name of the file the message was generated in. + +=item line + +Line of the source file the message was generated at. + +=item object_remote + +This is a reference to another hash that contains the Object::Remote +specific information. The keys are + +=over 4 + +=item connection_id + +If the log message was generated on a remote Perl interpreter then the +Object::Remote::Connection id of that interpreter will be available here. + +=back + +=back + +=head1 ATTRIBUTES + +=over 4 + +=item level_names + +This is a required attribute. Must be an array ref with the list of log level names +in it. The list must be ordered with the lowest level as element 0 and the highest +level as the last element. There is no default value. + +=item min_level + +The lowest log level that will be output by the logger. There is no default value. + +=item max_level + +The highest log level that will be output by the logger. The default value is the +highest level present in level_names. + +=item format + +The printf style format string to use when rendering the log message. The following +sequences are significant: + +=over 4 + +=item %l + +Level name that the log message was generated at. + +=item %s + +Log message rendered into a string with a leading space before any additional lines in a +multiple line message. + +=item %t + +Time the log message was generated rendered into a string. The time value is taken from +the Perl interpreter that generated the log message; it is not the time that the logger +received the log message on the local interpreter if the log message was forwarded. + +=item %r + +Log::Remote connection information rendered into a string. + +=item %c + +Name of the sub-class of Object::Remote::Logging that was used by the class +that generated the log message. Can also be Object::Remote::Logging itself. + +=item %p + +Package name of the class that generated the log message. + +=item %m + +Method name that generated the log message. + +=item %f + +Filename that the log message was generated in. + +=item %i + +Line number the log message was generated at. + +=item %h + +Hostname the log message was generated on. + +=item %P + +Process id of the Perl interpreter that generated the log message. + +=item %% + +A literal %. + +=back + +=back + diff --git a/lib/Object/Remote/Logging/WarnLogger.pm b/lib/Object/Remote/Logging/WarnLogger.pm index 431df4a..f9ffc92 100644 --- a/lib/Object/Remote/Logging/WarnLogger.pm +++ b/lib/Object/Remote/Logging/WarnLogger.pm @@ -5,7 +5,6 @@ use Moo; extends 'Object::Remote::Logging::Logger'; has format => ( is => 'ro', required => 1, default => sub { '%s at %f line %i, log level: %l' } ); -has max_level => ( is => 'ro', required => 1, default => sub { 'error' } ); has min_level => ( is => 'ro', required => 1, default => sub { 'warn' } ); sub output { warn $_[1] };