$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 $@;
eval { carp $code->() };
+ warn "could not emit warning during Flog_$level: $@" if $@;
exit($exit_value);
});
}
}
}
+sub _parse_selections {
+ my ($selections_string) = @_;
+ my %log_ok;
+
+ #example string:
+ #" * -Object::Remote::Logging Foo::Bar::Baz "
+ foreach(split(/\s+/, $selections_string)) {
+ next if $_ eq '';
+ if ($_ eq '*') {
+ $log_ok{$_} = 1;
+ } elsif (s/^-//) {
+ $log_ok{$_} = 0;
+ } else {
+ $log_ok{$_} = 1;
+ }
+ }
+
+ return %log_ok;
+}
+
#this is invoked on all nodes
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;
-
+
+ #TODO how can a third party module perform an action when a new
+ #interpreter is built on a remote node with out requiring support
+ #for that third party module baked into object::remote?
+ eval {
+ require Log::Any::Adapter;
+ Log::Any::Adapter->set('+Object::Remote::Logging::LogAnyInjector');
+ };
+
return unless defined $level;
$format = "[%l %r] %s" unless defined $format;
$selections = __PACKAGE__ unless defined $selections;
- %controller_should_log = map { $_ => 1 } split(' ', $selections);
+ %controller_should_log = _parse_selections($selections);
my $logger = Object::Remote::Logging::Logger->new(
min_level => lc($level), format => $format,
router()->connect(sub {
my $controller = $_[1]->{controller};
- return unless $controller_should_log{'*'} || $controller_should_log{$controller};
+ my $will_log = $controller_should_log{$controller};
+
+ $will_log = $controller_should_log{'*'} unless defined $will_log;
+
+ return unless $will_log;
#skip things from remote hosts because they log to STDERR
#when OBJECT_REMOTE_LOG_LEVEL is in effect
return if $_[1]->{remote}->{connection_id};
my ($self, %controller_info) = @_;
router()->_remote_metadata({ connection_id => $controller_info{connection_id} });
+ #TODO having an instance of an object in the remote interpreter causes it to hang
+ #on exit intermitently or leave a zombie laying around frequently - not a bug limited
+ #to log forwarding
router()->_forward_destination($controller_info{router}) if $ENV{OBJECT_REMOTE_LOG_FORWARDING};
}
1;
+
__END__
=head1 NAME
$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_SELECTIONS} = '* -Object::Remote::Logging';
+ $ENV{OBJECT_REMOTE_LOG_FORWARDING} = 0 || 1; #default 0
log_info { 'Trace log event' };
Dlog_verbose { "Debug event with Data::Dumper::Concise: $_" } { foo => 'bar' };
which implements log rendering and output and L<Object::Remote::Logging::Router>
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<Object::Remote::Logging::Logger>. 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 the 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. If the asterisk character (*) is used in the place of a class
+name then all class names will be selected by default instead of ignored. An individual
+class name can be turned off by prefixing the name with a hypen character (-). 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<Object::Remote::Logging::Logger> to be built
+and connected to the L<Object::Remote::Logging::Router> instance. It is also possible
+to manually build a logger instance and connect it to the router. See the documentation
+for the logger and router classes.
+
+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<Object::Remote::Logging::Router>.
+
=head1 EXPORTABLE SUBROUTINES
=over 4
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_<level> and DlogS_<level>
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<Object::Remote::Logging::Logger> 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
-