48110480e220db8025696281164d7a3efcef95d3
[scpubgit/Object-Remote.git] / lib / Object / Remote / Logging.pm
1 package Object::Remote::Logging;
2
3 use Moo;
4 use Scalar::Util qw(blessed);
5 use Object::Remote::Logging::Logger;
6 use Exporter::Declare;
7 use Carp qw(carp croak);
8
9 extends 'Log::Contextual';
10
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 => ('____');
16
17 sub router {
18   our $Router_Instance ||= do {
19     require Object::Remote::Logging::Router;
20     Object::Remote::Logging::Router->new;
21   }
22 }
23
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)
32 sub arg_levels {
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 )];
37 }
38
39 sub before_import {
40    my ($class, $importer, $spec) = @_;
41    my $router = $class->router;
42
43    $class->SUPER::before_import($importer, $spec);
44
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({
51                controller => $class,
52                package => scalar(caller),
53                caller_level => 1,
54                level => $level,
55             }, $code);
56             #TODO this should get fed into a logger so it can be formatted
57             croak $code->();
58          });
59       }
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({
66                controller => $class,
67                package => scalar(caller),
68                caller_level => 1,
69                level => $level,
70             }, $code);
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->() };
74             exit($exit_value);
75          });
76       }
77    }
78 }
79
80 #this is invoked on all nodes
81 sub init_logging {
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;
87   
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);
92   
93   my $logger = Object::Remote::Logging::Logger->new(
94     min_level => lc($level), format => $format,
95     level_names => Object::Remote::Logging::arg_levels(),
96   );
97
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};
104     $logger
105   });
106 }
107
108 #this is invoked by the controlling node
109 #on the remote nodes
110 sub init_logging_forwarding {
111   my ($self, %controller_info) = @_;
112   
113   router()->_remote_metadata({ connection_id => $controller_info{connection_id} });
114   router()->_forward_destination($controller_info{router}) if $ENV{OBJECT_REMOTE_LOG_FORWARDING};
115 }
116
117 1;
118 __END__
119
120 =head1 NAME
121
122 Object::Remote::Logging - Logging subsystem for Object::Remote
123
124 =head1 SYNOPSIS
125
126   use Object::Remote::Logging qw( :log :dlog :elog :flog arg_levels router );
127   
128   @levels = qw( trace debug verbose info warn error fatal );
129   @levels = arg_levels(); #same result
130   
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';
135   
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;
140
141 =head1 DESCRIPTION
142
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.
148
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.
152
153 =head1 EXPORTABLE SUBROUTINES
154
155 =over 4
156
157 =item arg_levels
158
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.
161
162 =item router
163
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.
167
168 =item log_<level> and Dlog_<level>
169
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.
175
176   log_trace { "A fine log message $_[0] " } 'if I do say so myself';
177   $hashref = Dlog_trace { "Very handy: $_" } { foo => 'bar' };
178
179 =item logS_<level> and DlogS_<level>
180
181 Works just like log_ and Dlog_ except returns only the first argument as a scalar value.
182
183   my $beverage = log_info { "Customer ordered $_[0]" } 'Coffee';
184
185 =item Elog_<level>
186
187 Log an event and then generate an exception by calling die() with the log message.
188
189   Elog_error { "Could not open file: $!" };
190
191 =item Flog_<level>
192
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.
195
196   Flog_fatal { 'Could not lock resource' } 3;
197
198 =back
199
200 =head1 LEVEL NAMES
201
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
205 level names are:
206
207 =over 4
208
209 =item trace
210
211 As much information about operation as possible including multiple line dumps of
212 large content. Tripple verbose operation (-v -v -v).
213
214 =item debug
215
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).
219
220 =item verbose
221
222 Additional optional messages to the user that can be enabled at their will. Single
223 verbose operation (-v).
224
225 =item info
226
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
229 in effect.
230
231 =item warn
232
233 Something wasn't supposed to happen but did. Operation was not impacted but
234 otherwise the event is noteworthy. Single quiet operation (-q).
235
236 =item error
237
238 Something went wrong. Operation of the system may continue but some operation
239 has most definitely failed. Double quiet operation (-q -q).
240
241 =item fatal
242
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).
245
246 =back
247
248 =head1 ENVIRONMENT
249
250 =over 4
251
252 =item OBJECT_REMOTE_LOG_LEVEL
253
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.
257
258 =item OBJECT_REMOTE_LOG_FORMAT
259
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.
263
264 =item OBJECT_REMOTE_LOG_SELECTIONS
265
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
270 to *.
271
272 =item OBJECT_REMOTE_LOG_FORWARDING
273
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'.
277
278 =back
279