revise log router api to match log::contextual router api change
[scpubgit/Object-Remote.git] / lib / Object / Remote / Logging.pm
1 package Object::Remote::Logging;
2
3 use strictures 1;
4
5 use Object::Remote::LogRouter;
6 use Object::Remote::LogDestination;
7 use Log::Contextual::SimpleLogger;
8 use Carp qw(cluck);
9
10 use base qw(Log::Contextual); 
11
12 sub arg_router {
13   return $_[1] if defined $_[1]; 
14   our $Router_Instance;
15  
16   return $Router_Instance if defined $Router_Instance; 
17  
18   $Router_Instance = Object::Remote::LogRouter->new(
19     description => $_[0],
20   );
21 }
22
23 sub init_logging {
24   my ($class) = @_; 
25   our $Did_Init;
26     
27   return if $Did_Init;
28   $Did_Init = 1; 
29     
30   if ($ENV{OBJECT_REMOTE_LOG_LEVEL}) {
31     $class->init_logging_stderr($ENV{OBJECT_REMOTE_LOG_LEVEL});
32   }
33 }
34
35 sub init_logging_stderr {
36   my ($class, $level) = @_;
37   our $Log_Level = $level;
38   chomp(my $hostname = `hostname`);
39   
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         print STDERR "[$hostname $$] $time ", @_ 
47       },
48     })
49   );
50   
51   $Log_Output->connect($class->arg_router);
52 }
53
54 sub init_logging_forwarding {
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);
60 }
61
62 1;
63
64 __END__
65
66 Hierarchical 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        
217
218
219
220