Commit | Line | Data |
5e2b2229 |
1 | package Object::Remote::Logging; |
2 | |
4e446335 |
3 | use Moo; |
4 | use Scalar::Util qw(blessed); |
5 | use Object::Remote::Logging::Logger; |
f4a85080 |
6 | use Exporter::Declare; |
663fb34f |
7 | use Carp qw(carp croak); |
5e2b2229 |
8 | |
4e446335 |
9 | extends 'Log::Contextual'; |
5e2b2229 |
10 | |
663fb34f |
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 => ('____'); |
f4a85080 |
16 | |
4e446335 |
17 | sub router { |
c0b2df05 |
18 | our $Router_Instance ||= do { |
19 | require Object::Remote::Logging::Router; |
20 | Object::Remote::Logging::Router->new; |
21 | } |
4e446335 |
22 | } |
5e2b2229 |
23 | |
9de32e1d |
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) |
4e446335 |
32 | sub arg_levels { |
9de32e1d |
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 )]; |
4a9fa1a5 |
37 | } |
5e2b2229 |
38 | |
663fb34f |
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 |
d672a9bf |
72 | #don't let it going wrong stop us from calling exit() |
73 | eval { carp $code->() }; |
663fb34f |
74 | exit($exit_value); |
75 | }); |
76 | } |
77 | } |
78 | } |
79 | |
4e446335 |
80 | #this is invoked on all nodes |
4a9fa1a5 |
81 | sub init_logging { |
c0b2df05 |
82 | my $level = $ENV{OBJECT_REMOTE_LOG_LEVEL}; |
0fe333eb |
83 | my $format = $ENV{OBJECT_REMOTE_LOG_FORMAT}; |
eb49c7df |
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 | |
c0b2df05 |
88 | return unless defined $level; |
0fe333eb |
89 | $format = "[%l %r] %s" unless defined $format; |
eb49c7df |
90 | $selections = __PACKAGE__ unless defined $selections; |
91 | %controller_should_log = map { $_ => 1 } split(' ', $selections); |
92 | |
c0b2df05 |
93 | my $logger = Object::Remote::Logging::Logger->new( |
0fe333eb |
94 | min_level => lc($level), format => $format, |
c0b2df05 |
95 | level_names => Object::Remote::Logging::arg_levels(), |
96 | ); |
97 | |
c0b2df05 |
98 | router()->connect(sub { |
eb49c7df |
99 | my $controller = $_[1]->{controller}; |
466ee2c4 |
100 | return unless $controller_should_log{'*'} || $controller_should_log{$controller}; |
c0b2df05 |
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 | }); |
4a9fa1a5 |
106 | } |
107 | |
4e446335 |
108 | #this is invoked by the controlling node |
109 | #on the remote nodes |
4a9fa1a5 |
110 | sub init_logging_forwarding { |
4e446335 |
111 | my ($self, %controller_info) = @_; |
112 | |
113 | router()->_remote_metadata({ connection_id => $controller_info{connection_id} }); |
466ee2c4 |
114 | router()->_forward_destination($controller_info{router}) if $ENV{OBJECT_REMOTE_LOG_FORWARDING}; |
4a9fa1a5 |
115 | } |
5e2b2229 |
116 | |
117 | 1; |
d672a9bf |
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 |
5e2b2229 |
279 | |