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