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