1 package Object::Remote::Logging;
4 use Scalar::Util qw(blessed);
5 use Object::Remote::Logging::Logger;
7 use Carp qw(carp croak);
9 extends 'Log::Contextual';
11 exports(qw( ____ router arg_levels ));
12 #exception log - log a message then die with that message
13 export_tag elog => ('____');
14 #fatal log - log a message then call exit(1)
15 export_tag flog => ('____');
18 our $Router_Instance ||= do {
19 require Object::Remote::Logging::Router;
20 Object::Remote::Logging::Router->new;
24 #log level descriptions
25 #info - standard log level - normal program output for the end user
26 #warn - output for program that is executing quietly
27 #error - output for program that is running more quietly
28 #fatal - it is not possible to continue execution; this level is as quiet as is possible
29 #verbose - output for program executing verbosely (-v)
30 #debug - output for program running more verbosely (-v -v)
31 #trace - output for program running extremely verbosely (-v -v -v)
33 #the order of the log levels is significant with the
34 #most verbose level being first in the list and the
35 #most quiet as the last item
36 return [qw( trace debug verbose info warn error fatal )];
40 my ($class, $importer, $spec) = @_;
41 my $router = $class->router;
43 $class->SUPER::before_import($importer, $spec);
45 my @levels = @{$class->arg_levels($spec->config->{levels})};
46 for my $level (@levels) {
47 if ($spec->config->{elog}) {
48 $spec->add_export("&Elog_$level", sub (&) {
49 my ($code, @args) = @_;
50 $router->handle_log_request({
52 package => scalar(caller),
56 #TODO this should get fed into a logger so it can be formatted
60 if ($spec->config->{flog}) {
61 #TODO that prototype isn't right
62 $spec->add_export("&Flog_$level", sub (&@) {
63 my ($code, $exit_value) = @_;
64 $exit_value = 1 unless defined $exit_value;
65 $router->handle_log_request({
67 package => scalar(caller),
71 #TODO this should get fed into a logger so it can be formatted
72 #don't let it going wrong stop us from calling exit()
73 eval { carp $code->() };
80 #this is invoked on all nodes
82 my $level = $ENV{OBJECT_REMOTE_LOG_LEVEL};
83 my $format = $ENV{OBJECT_REMOTE_LOG_FORMAT};
84 #TODO allow the selections value to be * so it selects everything
85 my $selections = $ENV{OBJECT_REMOTE_LOG_SELECTIONS};
86 my %controller_should_log;
88 return unless defined $level;
89 $format = "[%l %r] %s" unless defined $format;
90 $selections = __PACKAGE__ unless defined $selections;
91 %controller_should_log = map { $_ => 1 } split(' ', $selections);
93 my $logger = Object::Remote::Logging::Logger->new(
94 min_level => lc($level), format => $format,
95 level_names => Object::Remote::Logging::arg_levels(),
98 router()->connect(sub {
99 my $controller = $_[1]->{controller};
100 return unless $controller_should_log{'*'} || $controller_should_log{$controller};
101 #skip things from remote hosts because they log to STDERR
102 #when OBJECT_REMOTE_LOG_LEVEL is in effect
103 return if $_[1]->{remote}->{connection_id};
108 #this is invoked by the controlling node
110 sub init_logging_forwarding {
111 my ($self, %controller_info) = @_;
113 router()->_remote_metadata({ connection_id => $controller_info{connection_id} });
114 router()->_forward_destination($controller_info{router}) if $ENV{OBJECT_REMOTE_LOG_FORWARDING};
122 Object::Remote::Logging - Logging subsystem for Object::Remote
126 use Object::Remote::Logging qw( :log :dlog :elog :flog arg_levels router );
128 @levels = qw( trace debug verbose info warn error fatal );
129 @levels = arg_levels(); #same result
131 $ENV{OBJECT_REMOTE_LOG_LEVEL} = 'trace'; #or other level name
132 $ENV{OBJECT_REMOTE_LOG_FORMAT} = '%l %t: %p::%m %s'; #and more
133 $ENV{OBJECT_REMOTE_LOG_FORWARDING} = 0 || 1; #default 0
134 $ENV{OBJECT_REMOTE_LOG_SELECTIONS} = 'Object::Remote::Logging Some::Other::Subclass';
136 log_info { 'Trace log event' };
137 Dlog_verbose { "Debug event with Data::Dumper::Concise: $_" } { foo => 'bar' };
138 Elog_error { 'Error event that calls die() with this string' };
139 Flog_fatal { 'Fatal event calls warn() then exit()' } 1;
143 This is the logging framework for Object::Remote implemented as a subclass of
144 L<Log::Contextual> with a slightly incompatible API. This system allows
145 developers using Object::Remote and end users of that software to control
146 Object::Remote logging so operation can be tracked if needed. This is also
147 the API used to generate log messages inside the Object::Remote source code.
149 The rest of the logging system comes from L<Object::Remote::Logging::Logger>
150 which implements log rendering and output and L<Object::Remote::Logging::Router>
151 which delivers log events to the loggers.
153 =head1 EXPORTABLE SUBROUTINES
159 Returns an array reference that contains the ordered list of level names
160 with the lowest log level first and the highest log level last.
164 Returns the instance of L<Object::Remote::Logging::Router> that is in use. The router
165 instance is used in combination with L<Object::Remote::Logging::Logger> objects to
166 select then render and output log messages.
168 =item log_<level> and Dlog_<level>
170 These methods come direct from L<Log::Contextual>; see that documentation for a
171 complete reference. For each of the log level names there are subroutines with the log_
172 and Dlog_ prefix that will generate the log message. The first argument is a code block
173 that returns the log message contents and the optional further arguments are both passed
174 to the block as the argument list and returned from the log method as a list.
176 log_trace { "A fine log message $_[0] " } 'if I do say so myself';
177 $hashref = Dlog_trace { "Very handy: $_" } { foo => 'bar' };
179 =item logS_<level> and DlogS_<level>
181 Works just like log_ and Dlog_ except returns only the first argument as a scalar value.
183 my $beverage = log_info { "Customer ordered $_[0]" } 'Coffee';
187 Log an event and then generate an exception by calling die() with the log message.
189 Elog_error { "Could not open file: $!" };
193 Log the event, generate a warning with the log message, then call exit(). The exit
194 value will default to 1 or can be specified as an argument.
196 Flog_fatal { 'Could not lock resource' } 3;
202 Object::Remote uses an ordered list of log level names with the minimum level
203 first and the maximum level last. The list of level names can be accessed via
204 the arg_levels method which is exportable to the consumer of this class. The log
211 As much information about operation as possible including multiple line dumps of
212 large content. Tripple verbose operation (-v -v -v).
216 Messages about operations that could hang as well as internal state changes,
217 results from method invocations, and information useful when looking for faults.
218 Double verbose operation (-v -v).
222 Additional optional messages to the user that can be enabled at their will. Single
223 verbose operation (-v).
227 Messages from normal operation that are intended to be displayed to the end
228 user if quiet operation is not indicated and more verbose operation is not
233 Something wasn't supposed to happen but did. Operation was not impacted but
234 otherwise the event is noteworthy. Single quiet operation (-q).
238 Something went wrong. Operation of the system may continue but some operation
239 has most definitely failed. Double quiet operation (-q -q).
243 Something went wrong and recovery is not possible. The system should stop operating
244 as soon as possible. Tripple quiet operation (-q -q -q).
252 =item OBJECT_REMOTE_LOG_LEVEL
254 By default Object::Remote will generate log events but messages will not be
255 output to STDERR. If there is a defined value for this variable then logs will
256 be sent to STDERR if they are at this level or higher.
258 =item OBJECT_REMOTE_LOG_FORMAT
260 If logging output is enabled and this value is defined then the logger will
261 use this format string instead of the default '[%l %r] %s'; See
262 L<Object::Remote::Logging::Logger> for documentation on the format string.
264 =item OBJECT_REMOTE_LOG_SELECTIONS
266 By default Object::Remote log output will only be enabled for messages generated inside
267 Object::Remote packages. If logs should be generated for other log messages instead of just
268 Object::Remote messages set this variable to the names of any Object::Remote::Logging subclass or
269 Object::Remote::Logging itself seperated by a space. To output all logs generated set the value
272 =item OBJECT_REMOTE_LOG_FORWARDING
274 Object::Remote can forward log events from the remote Perl interpreter to the local Perl
275 interpreter in a transparent way. Currently this feature is disabled by default but
276 that will change Really Soon Now(TM); to enable it set the variable to '1'.