fix incorrect indentation
[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`);
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
52sub 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
601;
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