revise log router api to match log::contextual router api change
[scpubgit/Object-Remote.git] / lib / Object / Remote / Logging.pm
CommitLineData
5e2b2229 1package Object::Remote::Logging;
2
3use strictures 1;
4
f7ea4120 5use Object::Remote::LogRouter;
624072a8 6use Object::Remote::LogDestination;
4a9fa1a5 7use Log::Contextual::SimpleLogger;
8use Carp qw(cluck);
5e2b2229 9
f7ea4120 10use base qw(Log::Contextual);
5e2b2229 11
4a9fa1a5 12sub arg_router {
5d59cb98 13 return $_[1] if defined $_[1];
14 our $Router_Instance;
4a9fa1a5 15
5d59cb98 16 return $Router_Instance if defined $Router_Instance;
4a9fa1a5 17
5d59cb98 18 $Router_Instance = Object::Remote::LogRouter->new(
19 description => $_[0],
20 );
4a9fa1a5 21}
5e2b2229 22
4a9fa1a5 23sub init_logging {
5d59cb98 24 my ($class) = @_;
25 our $Did_Init;
4a9fa1a5 26
5d59cb98 27 return if $Did_Init;
28 $Did_Init = 1;
4a9fa1a5 29
5d59cb98 30 if ($ENV{OBJECT_REMOTE_LOG_LEVEL}) {
31 $class->init_logging_stderr($ENV{OBJECT_REMOTE_LOG_LEVEL});
32 }
4a9fa1a5 33}
34
35sub init_logging_stderr {
5d59cb98 36 my ($class, $level) = @_;
37 our $Log_Level = $level;
38 chomp(my $hostname = `hostname`);
6536ccd3 39
5d59cb98 40 our $Log_Output = Object::Remote::LogDestination->new(
41 logger => Log::Contextual::SimpleLogger->new({
42 levels_upto => $Log_Level,
43 coderef => sub {
44 my @t = localtime();
45 my $time = sprintf("%0.2i:%0.2i:%0.2i", $t[2], $t[1], $t[0]);
6536ccd3 46 print STDERR "[$hostname $$] $time ", @_
5d59cb98 47 },
48 })
49 );
6536ccd3 50
5d59cb98 51 $Log_Output->connect($class->arg_router);
4a9fa1a5 52}
53
54sub init_logging_forwarding {
5d59cb98 55# my ($class, $remote_parent) = @_;
56# chomp(my $host = `hostname`);
57# $class->arg_router->description("$$ $host");
58# $class->arg_router->parent_router($remote_parent);
59# $remote_parent->add_child_router($class->arg_router);
4a9fa1a5 60}
5e2b2229 61
621;
63
6536ccd3 64__END__
65
66Hierarchical routed logging concept
67
68 Why?
69
70 Object::Remote and systems built on it would benefit from a standard model
71 for logging that enables simple and transparent log generation and consumption
72 that can cross the Perl interpreter instance boundaries. More
73 generally CPAN would benefit from a common logging framework that allows all
74 log message generators to play nicely with all log message consumers with out
75 making the generators or consumers jump through hoops to do what they want to do.
76 If these two solutions are the same then all modules built using the
77 logging framework will transparently operate properly when run under Object::Remote.
78
79 Such a solution needs to be flexible and have a low performance impact when it is not
80 actively logging. The hiearchy of log message routers is the way to achieve all of these
81 goals. The abstracted message router interface introduced to Log::Contextual allows
82 the hierarchical routing system to be built and tested inside Object::Remote with possible
83 larger scale deployment in the future.
84
85 Hierarchy of log routers
86
87 * Each Perl module ideally would use at least a router dedicated
88 to that module and may have child routers if the module is complex.
89
90 * Log messages inserted at low levels in the hierarchy
91 are available at routers at higher levels in the hierarchy.
92
93 * Each running Perl instance has a root router which receives
94 all log messages generated in the Perl instance.
95
96 * The routing hierarchy is available for introspection and connections
97 from child routers to parent routers have human readable descriptions
98
99 * The entire routing system is dynamic
100 * Add and remove routers while the system is in operation
101 * Add and remove subscriptions into routers while the system is in operation
102
103 * Auto-solves Object::Remote logging by setting the parent router of the
104 root router in the remote instance to a router in the local instance. The
105 log messages will flow into the local router via a proxy object.
106
107 * There needs to be two modes of operation for routed logging
108 * forwarding across Perl instances using Object::Remote proxies
109 for ease of use during normal operation
110
111 * STDERR output by default because not all logs can be forwarded
112 such as log messages for parts of Object::Remote that relate to
113 Object::Remote delivering the log.
114
115
116
117 Example hiearchy
118
119 * Root [1]
120 * System::Introspector
121 * Object::Remote [2]
122 * local [3]
123 * remote [4]
124 * connection #1 [5]
125 * Root
126 * System::Introspector
127 * Object::Remote
128 * local
129 * connection #2
130 * Root
131 * System::Introspector
132 * Object::Remote
133 * local
134
135 [1] This router has all logs generated anywhere
136 even on remote hosts
137 [2] Everything related to Object::Remote including
138 log messages from remote nodes for things other
139 than Object::Remote
140 [3] Log messages generated by Object::Remote on the local
141 node only
142 [4] All log messages from all remote nodes
143 [5] This is the connection from a remote instance to the
144 local instance using a proxy object - the name contains
145 the Object::Remote::Connection id for the remote node
146
147 As a demonstration of the flexibility of the this system consider a CPAN testers GUI
148 tool. This hypothetical tool would allow a tester to select a module by name and perform
149 the automated tests for that package and all dependent packages. Inside the tool is a pane for
150 the output of the process (STDOUT and STDERR), a pane for log messages, and a pane displaying
151 the modules that are participating in routed logging. The tester could then click on individual
152 packages and enable logging for that package dynamically. If neccassary more than one package
153 could be monitored if neccassary. If the GUI is wrapping a program that runs for long periods of
154 time or if the application is a daemon then being able to dynamically add and remove logging
155 becomes very useful.
156
157 Log message selection and output
158
159 * Assumptions
160 * Modules and packages know how they want to format log messages.
161 * Consumers of log messages want to know
162 * Which Perl module/package generated that message.
163 * When running with Object::Remote if the log message is from
164 a remote connection and if so which connection.
165 * Consumers of a log message know how they want to output them. The logger
166 should not be enforcing a specific type of log output.
167 * Most log messages most of the time will be completely ignored and unused.
168 * Router subscriptions
169 * A consumer of log messages will subscribe to a router at any arbitrary point
170 in the router hierarchy even across machines if Object::Remote is involved
171 * The subscription is used to access a stream of log data and is not used to select
172 which packages/modules should be logged
173 * For instance the Object::Remote log router has log messages flowing through
174 it that include logs generated on remote nodes even if those logs were generated
175 by a module other than Object::Remote
176
177 * Selection
178 * The module has defined what the log message format is
179 * The subscription has defined the scope of messages that will be
180 available for selection, ie: all log messages everywhere,
181 all logs generated on Object::Remote nodes, etc
182 * Selection defines what log messages are going to be delivered
183 to a logger object instance
184 * Selectors act as a gate between a subscription and the logger object
185 * Selectors are closures that perform introspection on the log
186 message and metadata; if the selector returns true the logger
187 will be invoked to log this message
188 * The logger still has a log level assigned to it and still will have
189 the is_$level method invoked to only log at that specific level
190
191 * Destinations
192 * A log destination is an instance of a logger object and the associated
193 subscriptions.
194 * Consuming logging data from this system is a matter of
195 * Constructing an instance of a logging destination object which has
196 the following attributes:
197 * logger - the logger object
198 * selectors - a list of closures; the first one that returns true
199 causes the logger to be checked for this log_level and
200 invoked if needed
201 * Register selectors with the destination by invoking a method and specifying
202 sub refs as an argument
203
204 Technical considerations
205 * The routing hierarchy has cycles where parent routers hold a reference to the child
206 and the child holds a reference to the parent. The cycles are not a problem if weak
207 references are used however proxy objects don't seem to currently work with weak
208 references.
209 * Once a logger hits a proxy object the caller information is totally blown; this
210 crossing isn't transparent yet
211 * If Object::Remote is logging its actions and those logs are being forwarded then
212 logs can be generated from the forwarding itself creating an infinite loop. Only
213 a portion of Object::Remote can be forwarded.
214
215
216
4a9fa1a5 217
218
219
220