1 package Object::Remote::Logging::Logger;
4 use Scalar::Util qw(weaken);
7 #TODO sigh invoking a logger with a log level name the same
8 #as an attribute could happen - restrict attributes to _ prefix
9 #and restrict log levels to not start with out that prefix?
10 has format => ( is => 'ro', required => 1, default => sub { '%l: %s' } );
11 has level_names => ( is => 'ro', required => 1 );
12 has min_level => ( is => 'ro', required => 1, default => sub { 'info' } );
13 has max_level => ( is => 'lazy', required => 1 );
14 has _level_active => ( is => 'lazy' );
16 #just a stub so it doesn't get to AUTOLOAD
22 (my $method) = (our $AUTOLOAD =~ /([^:]+)$/);
26 if ($method =~ m/^_/) {
27 croak "invalid method name $method for " . ref($self);
30 if ($method =~ m/^is_(.+)/) {
32 my $is_method = "is_$level_name";
33 *{$is_method} = sub { shift(@_)->_level_active->{$level_name} };
34 return $self->$is_method;
37 my $level_name = $method;
38 *{$level_name} = sub {
40 unless(exists($self->_level_active->{$level_name})) {
41 croak "$level_name is not a valid log level name";
44 $self->_log($level_name, @_);
47 return $self->$level_name(@_);
50 sub _build_max_level {
52 return $self->level_names->[-1];
55 sub _build__level_active {
58 my $min_level = $self->min_level;
59 my $max_level = $self->max_level;
62 foreach my $level (@{$self->level_names}) {
63 if($level eq $min_level) {
67 $active{$level} = $should_log;
69 if (defined $max_level && $level eq $max_level) {
78 my ($self, $level, $content, $metadata_in) = @_;
79 my %metadata = %$metadata_in;
80 my $rendered = $self->_render($level, \%metadata, @$content);
81 $self->_output($rendered);
84 sub _create_format_lookup {
85 my ($self, $level, $metadata, $content) = @_;
86 my $method = $metadata->{method};
88 $method = '(none)' unless defined $method;
91 '%' => '%', 'n' => "\n",
92 t => $self->_render_time($metadata->{timestamp}),
93 r => $self->_render_remote($metadata->{object_remote}),
94 s => $self->_render_log(@$content), l => $level,
95 c => $metadata->{exporter}, p => $metadata->{caller_package}, m => $method,
96 f => $metadata->{filename}, i => $metadata->{line},
97 h => $metadata->{hostname}, P => $metadata->{pid},
101 sub _get_format_var_value {
102 my ($self, $name, $data) = @_;
103 my $val = $data->{$name};
104 return $val if defined $val;
105 return '(undefined)';
109 my ($self, $time) = @_;
110 return scalar(localtime($time));
114 my ($self, $remote) = @_;
115 return 'local' unless defined $remote;
116 my $conn_id = $remote->{connection_id};
117 $conn_id = '(uninit)' unless defined $conn_id;
118 return "remote #$conn_id";
122 my ($self, @content) = @_;
123 return join('', @content);
126 my ($self, $level, $metadata, @content) = @_;
127 my $var_table = $self->_create_format_lookup($level, $metadata, [@content]);
128 my $template = $self->format;
130 $template =~ s/%([\w%])/$self->_get_format_var_value($1, $var_table)/ge;
133 $template =~ s/\n/\n /g;
139 my ($self, $content) = @_;
140 print STDERR $content;
149 Object::Remote::Logging::Logger - Format and output a log message
153 use Object::Remote::Logging::Logger;
154 use Object::Remote::Logging qw( router arg_levels );
156 my $app_output = Object::Remote::Logging::Logger->new(
157 level_names => arg_levels, format => '%t %s',
158 min_level => 'verbose', max_level => 'info',
161 #Selector method can return 0 or more logger
162 #objects that will receive the messages
164 my ($generating_package, $metadata) = @_;
165 return unless $metadata->{exporter} eq 'App::Logging::Subclass';
169 #true value as second argument causes the selector
170 #to be stored with a weak reference
171 router->connect($selector, 1);
173 #disconnect the selector from the router
176 #router will hold this logger forever
177 #and send it all log messages
178 router->connect(Object::Remote::Logging::Logger->new(
179 level_names => arg_levels, format => '%s at %f line %i, log level: %l'
180 min_level => 'warn', max_level => 'error',
185 This class receives log messages from an instance of L<Object::Remote::Logging::Router>,
186 formats them according to configuration, and then outputs them to STDERR. In between
187 the router and the logger is a selector method which inspects the log message metadata
188 and can return 0 or more loggers that should receive the log message.
192 A logger object receives the log messages that are generated and converts them to
193 formatted log entries then displays them to the end user. Each logger has a set
194 of active log levels and will only output a log entry if the log message is at
197 To gain access to the stream of log messages a connection is made to the log router.
198 A logger can directly connect to the router and receive an unfiltered stream of
199 log messages or a selector closure can be used instead. The selector will be executed
200 for each log message with the message metadata and returns a list of 0 or more loggers
201 that should receive the log message. When the selector is executed the first argument
202 is the name of the package that generated the log message and the second argument
203 is a hash reference containing the message metadata.
207 The message metadata is a hash reference with the following keys:
213 Name of the log level of the message.
217 Package name of the logging API that was used to generate the log message.
221 Name of the package that generated the log message.
225 Name of the method the message was generated inside of.
229 Unix time of the message generation.
233 Process id of the Perl interpreter the message was generated in.
237 Hostname of the system where the message was generated.
241 Name of the file the message was generated in.
245 Line of the source file the message was generated at.
249 This is a reference to another hash that contains the Object::Remote
250 specific information. The keys are
256 If the log message was generated on a remote Perl interpreter then the
257 Object::Remote::Connection id of that interpreter will be available here.
269 This is a required attribute. Must be an array ref with the list of log level names
270 in it. The list must be ordered with the lowest level as element 0 and the highest
271 level as the last element. There is no default value.
275 The lowest log level that will be output by the logger. There is no default value.
279 The highest log level that will be output by the logger. The default value is the
280 highest level present in level_names.
284 The printf style format string to use when rendering the log message. The following
285 sequences are significant:
291 Level name that the log message was generated at.
295 Log message rendered into a string with a leading space before any additional lines in a
296 multiple line message.
300 Time the log message was generated rendered into a string. The time value is taken from
301 the Perl interpreter that generated the log message; it is not the time that the logger
302 received the log message on the local interpreter if the log message was forwarded.
306 Object::Remote connection information rendered into a string.
310 Package name of the logging API that was used to generate the log message.
314 Name of the package that generated the log message.
318 Method name that generated the log message.
322 Filename that the log message was generated in.
326 Line number the log message was generated at.
330 Hostname the log message was generated on.
334 Process id of the Perl interpreter that generated the log message.