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 )); |
f4a85080 |
12 | |
4e446335 |
13 | sub router { |
c0b2df05 |
14 | our $Router_Instance ||= do { |
15 | require Object::Remote::Logging::Router; |
16 | Object::Remote::Logging::Router->new; |
17 | } |
4e446335 |
18 | } |
5e2b2229 |
19 | |
9de32e1d |
20 | #log level descriptions |
21 | #info - standard log level - normal program output for the end user |
22 | #warn - output for program that is executing quietly |
23 | #error - output for program that is running more quietly |
24 | #fatal - it is not possible to continue execution; this level is as quiet as is possible |
25 | #verbose - output for program executing verbosely (-v) |
26 | #debug - output for program running more verbosely (-v -v) |
27 | #trace - output for program running extremely verbosely (-v -v -v) |
4e446335 |
28 | sub arg_levels { |
9de32e1d |
29 | #the order of the log levels is significant with the |
30 | #most verbose level being first in the list and the |
31 | #most quiet as the last item |
32 | return [qw( trace debug verbose info warn error fatal )]; |
4a9fa1a5 |
33 | } |
5e2b2229 |
34 | |
663fb34f |
35 | sub before_import { |
36 | my ($class, $importer, $spec) = @_; |
37 | my $router = $class->router; |
c2a69537 |
38 | our $DID_INIT; |
663fb34f |
39 | |
c2a69537 |
40 | unless($DID_INIT) { |
41 | $DID_INIT = 1; |
42 | init_logging(); |
43 | } |
44 | |
663fb34f |
45 | $class->SUPER::before_import($importer, $spec); |
663fb34f |
46 | } |
47 | |
ae198201 |
48 | sub _parse_selections { |
8f43bcd9 |
49 | my ($selections_string) = @_; |
50 | my %log_ok; |
ae198201 |
51 | |
8f43bcd9 |
52 | #example string: |
53 | #" * -Object::Remote::Logging Foo::Bar::Baz " |
54 | foreach(split(/\s+/, $selections_string)) { |
55 | next if $_ eq ''; |
56 | if ($_ eq '*') { |
57 | $log_ok{$_} = 1; |
58 | } elsif (s/^-//) { |
59 | $log_ok{$_} = 0; |
60 | } else { |
61 | $log_ok{$_} = 1; |
ae198201 |
62 | } |
8f43bcd9 |
63 | } |
ae198201 |
64 | |
8f43bcd9 |
65 | return %log_ok; |
ae198201 |
66 | } |
67 | |
4e446335 |
68 | #this is invoked on all nodes |
4a9fa1a5 |
69 | sub init_logging { |
c0b2df05 |
70 | my $level = $ENV{OBJECT_REMOTE_LOG_LEVEL}; |
0fe333eb |
71 | my $format = $ENV{OBJECT_REMOTE_LOG_FORMAT}; |
eb49c7df |
72 | my $selections = $ENV{OBJECT_REMOTE_LOG_SELECTIONS}; |
abef6e5b |
73 | my $test_logging = $ENV{OBJECT_REMOTE_TEST_LOGGER}; |
eb49c7df |
74 | my %controller_should_log; |
09f583a7 |
75 | |
76 | unless (defined $ENV{OBJECT_REMOTE_LOG_FORWARDING} && $ENV{OBJECT_REMOTE_LOG_FORWARDING} ne '') { |
f21d2f90 |
77 | $ENV{OBJECT_REMOTE_LOG_FORWARDING} = 0; |
09f583a7 |
78 | } |
abef6e5b |
79 | |
80 | if ($test_logging) { |
81 | require Object::Remote::Logging::TestLogger; |
8c352906 |
82 | router->connect(Object::Remote::Logging::TestLogger->new( |
abef6e5b |
83 | min_level => 'trace', max_level => 'error', |
84 | level_names => Object::Remote::Logging->arg_levels(), |
85 | )); |
86 | } |
5cd5276e |
87 | |
f1d70835 |
88 | { |
89 | no warnings 'once'; |
90 | if (defined $Object::Remote::FatNode::REMOTE_NODE) { |
91 | #the connection id for the remote node comes in later |
92 | #as the controlling node inits remote logging |
93 | router()->_remote_metadata({ connection_id => undef }); |
94 | } |
95 | } |
96 | |
f6a52399 |
97 | return unless defined $level && $level ne ''; |
98 | |
99 | $format = "[%l %r] %s" unless defined $format; |
100 | $selections = __PACKAGE__ unless defined $selections; |
101 | %controller_should_log = _parse_selections($selections); |
102 | |
c0b2df05 |
103 | my $logger = Object::Remote::Logging::Logger->new( |
0fe333eb |
104 | min_level => lc($level), format => $format, |
c0b2df05 |
105 | level_names => Object::Remote::Logging::arg_levels(), |
106 | ); |
107 | |
c0b2df05 |
108 | router()->connect(sub { |
eb49c7df |
109 | my $controller = $_[1]->{controller}; |
ae198201 |
110 | my $will_log = $controller_should_log{$controller}; |
08435f11 |
111 | my $remote_info = $_[1]->{object_remote}; |
ae198201 |
112 | |
113 | $will_log = $controller_should_log{'*'} unless defined $will_log; |
114 | |
115 | return unless $will_log; |
c0b2df05 |
116 | #skip things from remote hosts because they log to STDERR |
117 | #when OBJECT_REMOTE_LOG_LEVEL is in effect |
08435f11 |
118 | return if $remote_info->{forwarded}; |
119 | return $logger; |
c0b2df05 |
120 | }); |
4a9fa1a5 |
121 | } |
122 | |
4e446335 |
123 | #this is invoked by the controlling node |
124 | #on the remote nodes |
f1d70835 |
125 | sub init_remote_logging { |
4e446335 |
126 | my ($self, %controller_info) = @_; |
127 | |
f1d70835 |
128 | router()->_remote_metadata(\%controller_info); |
466ee2c4 |
129 | router()->_forward_destination($controller_info{router}) if $ENV{OBJECT_REMOTE_LOG_FORWARDING}; |
4a9fa1a5 |
130 | } |
5e2b2229 |
131 | |
132 | 1; |
455d031c |
133 | |
d672a9bf |
134 | __END__ |
135 | |
136 | =head1 NAME |
137 | |
138 | Object::Remote::Logging - Logging subsystem for Object::Remote |
139 | |
140 | =head1 SYNOPSIS |
141 | |
b8176a97 |
142 | use Object::Remote::Logging qw( :log :dlog arg_levels router ); |
d672a9bf |
143 | |
144 | @levels = qw( trace debug verbose info warn error fatal ); |
145 | @levels = arg_levels(); #same result |
146 | |
147 | $ENV{OBJECT_REMOTE_LOG_LEVEL} = 'trace'; #or other level name |
148 | $ENV{OBJECT_REMOTE_LOG_FORMAT} = '%l %t: %p::%m %s'; #and more |
d672a9bf |
149 | $ENV{OBJECT_REMOTE_LOG_SELECTIONS} = 'Object::Remote::Logging Some::Other::Subclass'; |
ae198201 |
150 | $ENV{OBJECT_REMOTE_LOG_SELECTIONS} = '* -Object::Remote::Logging'; |
09f583a7 |
151 | $ENV{OBJECT_REMOTE_LOG_FORWARDING} = 0; #default 1 |
d672a9bf |
152 | |
153 | log_info { 'Trace log event' }; |
154 | Dlog_verbose { "Debug event with Data::Dumper::Concise: $_" } { foo => 'bar' }; |
d672a9bf |
155 | |
156 | =head1 DESCRIPTION |
157 | |
158 | This is the logging framework for Object::Remote implemented as a subclass of |
159 | L<Log::Contextual> with a slightly incompatible API. This system allows |
160 | developers using Object::Remote and end users of that software to control |
161 | Object::Remote logging so operation can be tracked if needed. This is also |
162 | the API used to generate log messages inside the Object::Remote source code. |
163 | |
164 | The rest of the logging system comes from L<Object::Remote::Logging::Logger> |
165 | which implements log rendering and output and L<Object::Remote::Logging::Router> |
166 | which delivers log events to the loggers. |
167 | |
455d031c |
168 | =head1 USAGE |
169 | |
170 | Object::Remote logging is not enabled by default. If you need to immediately start |
171 | debugging set the OBJECT_REMOTE_LOG_LEVEL environment variable to either 'trace' |
172 | or 'debug'. This will enable logging to STDERR on the local and all remote Perl |
173 | interpreters. By default STDERR for all remote interpreters is passed through |
174 | unmodified so this is sufficient to receive logs generated anywhere Object::Remote |
175 | is running. |
176 | |
177 | Every time the local interpreter creates a new Object::Remote::Connection the connection |
178 | is given an id that is unique to that connection on the local interpreter. The connection |
179 | id and other metadata is available in the log output via a log format string that can |
180 | be set via the OBJECT_REMOTE_LOG_FORMAT environment variable. The format string and |
181 | available metadata is documented in L<Object::Remote::Logging::Logger>. Setting this |
182 | environment variable on the local interpreter will cause it to be propagated to the |
183 | remote interpreter so all logs will be formated the same way. |
184 | |
185 | This class is designed so any module can create their own logging sub-class using it. |
f21127fd |
186 | With out any additional configuration the consumers of this logging class will |
187 | automatically be enabled via OBJECT_REMOTE_LOG_LEVEL and formated with |
188 | OBJECT_REMOTE_LOG_FORMAT but those additional log messages are not sent to STDERR. |
189 | By setting the OBJECT_REMOTE_LOG_SELECTIONS environment variable to a list of |
190 | class names seperated by spaces then logs generated by packages that use those classes |
ae198201 |
191 | will be sent to STDERR. If the asterisk character (*) is used in the place of a class |
192 | name then all class names will be selected by default instead of ignored. An individual |
193 | class name can be turned off by prefixing the name with a hypen character (-). This is |
194 | also a configuration item that is forwarded to the remote interpreters so all logging |
195 | is consistent. |
455d031c |
196 | |
197 | Regardless of OBJECT_REMOTE_LOG_LEVEL the logging system is still active and loggers |
198 | can access the stream of log messages to format and output them. Internally |
199 | OBJECT_REMOTE_LOG_LEVEL causes an L<Object::Remote::Logging::Logger> to be built |
200 | and connected to the L<Object::Remote::Logging::Router> instance. It is also possible |
201 | to manually build a logger instance and connect it to the router. See the documentation |
f21127fd |
202 | for the logger and router classes. |
455d031c |
203 | |
204 | The logging system also supports a method of forwarding log messages from remote |
205 | interpreters to the local interpreter. Forwarded log messages are generated in the |
206 | remote interpreter and the logger for the message is invoked in the local interpreter. |
207 | Sub-classes of Object::Remote::Logging will have log messages forwarded automatically. |
208 | Loggers receive forwarded log messages exactly the same way as non-forwarded messages |
209 | except a forwarded message includes extra metadata about the remote interpreter. Log |
f21d2f90 |
210 | forwarding is disabled by default because it comes with a performance hit; to enable |
211 | it set the OBJECT_REMOTE_LOG_FORWARDING environment variable to 1. |
455d031c |
212 | |
d672a9bf |
213 | =head1 EXPORTABLE SUBROUTINES |
214 | |
215 | =over 4 |
216 | |
217 | =item arg_levels |
218 | |
219 | Returns an array reference that contains the ordered list of level names |
220 | with the lowest log level first and the highest log level last. |
221 | |
222 | =item router |
223 | |
224 | Returns the instance of L<Object::Remote::Logging::Router> that is in use. The router |
225 | instance is used in combination with L<Object::Remote::Logging::Logger> objects to |
226 | select then render and output log messages. |
227 | |
228 | =item log_<level> and Dlog_<level> |
229 | |
230 | These methods come direct from L<Log::Contextual>; see that documentation for a |
231 | complete reference. For each of the log level names there are subroutines with the log_ |
232 | and Dlog_ prefix that will generate the log message. The first argument is a code block |
233 | that returns the log message contents and the optional further arguments are both passed |
234 | to the block as the argument list and returned from the log method as a list. |
235 | |
236 | log_trace { "A fine log message $_[0] " } 'if I do say so myself'; |
455d031c |
237 | %hash = Dlog_trace { "Very handy: $_" } ( foo => 'bar' ); |
d672a9bf |
238 | |
239 | =item logS_<level> and DlogS_<level> |
240 | |
241 | Works just like log_ and Dlog_ except returns only the first argument as a scalar value. |
242 | |
302ecfbf |
243 | my $beverage = logS_info { "Customer ordered $_[0]" } 'Coffee'; |
d672a9bf |
244 | |
d672a9bf |
245 | =back |
246 | |
247 | =head1 LEVEL NAMES |
248 | |
293fb1ee |
249 | Object::Remote uses an ordered list of log level names with the lowest level |
250 | first and the highest level last. The list of level names can be accessed via |
d672a9bf |
251 | the arg_levels method which is exportable to the consumer of this class. The log |
252 | level names are: |
253 | |
254 | =over 4 |
255 | |
256 | =item trace |
257 | |
258 | As much information about operation as possible including multiple line dumps of |
259 | large content. Tripple verbose operation (-v -v -v). |
260 | |
261 | =item debug |
262 | |
263 | Messages about operations that could hang as well as internal state changes, |
264 | results from method invocations, and information useful when looking for faults. |
265 | Double verbose operation (-v -v). |
266 | |
267 | =item verbose |
268 | |
269 | Additional optional messages to the user that can be enabled at their will. Single |
270 | verbose operation (-v). |
271 | |
272 | =item info |
273 | |
274 | Messages from normal operation that are intended to be displayed to the end |
275 | user if quiet operation is not indicated and more verbose operation is not |
276 | in effect. |
277 | |
278 | =item warn |
279 | |
280 | Something wasn't supposed to happen but did. Operation was not impacted but |
281 | otherwise the event is noteworthy. Single quiet operation (-q). |
282 | |
283 | =item error |
284 | |
285 | Something went wrong. Operation of the system may continue but some operation |
286 | has most definitely failed. Double quiet operation (-q -q). |
287 | |
288 | =item fatal |
289 | |
290 | Something went wrong and recovery is not possible. The system should stop operating |
291 | as soon as possible. Tripple quiet operation (-q -q -q). |
292 | |
293 | =back |