1package Log::Dispatch::Output;
2
3use strict;
4use warnings;
5
6our $VERSION = '2.70';
7
8use Carp ();
9use Try::Tiny;
10use Log::Dispatch;
11use Log::Dispatch::Types;
12use Log::Dispatch::Vars qw( @OrderedLevels );
13use Params::ValidationCompiler qw( validation_for );
14
15use base qw( Log::Dispatch::Base );
16
17sub new {
18    my $proto = shift;
19    my $class = ref $proto || $proto;
20
21    die "The new method must be overridden in the $class subclass";
22}
23
24{
25    my $validator = validation_for(
26        params => {
27            level => { type => t('LogLevel') },
28
29            # Pre-PVC we accepted empty strings, which is weird, but we don't
30            # want to break back-compat. See
31            # https://github.com/houseabsolute/Log-Dispatch/issues/38.
32            message => { type => t('Str') },
33        },
34        slurpy => 1,
35    );
36
37    ## no critic (Subroutines::ProhibitBuiltinHomonyms)
38    sub log {
39        my $self = shift;
40        my %p    = $validator->(@_);
41
42        my $level_num = $self->_level_as_number( $p{level} );
43        return unless $self->_should_log($level_num);
44
45        local $! = undef;
46        $p{message} = $self->_apply_callbacks(%p)
47            if $self->{callbacks};
48
49        $self->log_message(%p);
50    }
51
52    ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
53    sub _log_with_num {
54        my $self      = shift;
55        my $level_num = shift;
56        my %p         = @_;
57
58        return unless $self->_should_log($level_num);
59
60        local $! = undef;
61        $p{message} = $self->_apply_callbacks(%p)
62            if $self->{callbacks};
63
64        $self->log_message(%p);
65    }
66    ## use critic
67}
68
69{
70    my $validator = validation_for(
71        params => {
72            name => {
73                type     => t('NonEmptyStr'),
74                optional => 1,
75            },
76            min_level => { type => t('LogLevel') },
77            max_level => {
78                type     => t('LogLevel'),
79                optional => 1,
80            },
81            callbacks => {
82                type     => t('Callbacks'),
83                optional => 1,
84            },
85            newline => {
86                type    => t('Bool'),
87                default => 0,
88            },
89        },
90
91        # This is primarily here for the benefit of outputs outside of this
92        # distro which may be passing who-knows-what to this method.
93        slurpy => 1,
94    );
95
96    ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
97    sub _basic_init {
98        my $self = shift;
99        my %p    = $validator->(@_);
100
101        $self->{level_names} = \@OrderedLevels;
102
103        $self->{name} = $p{name} || $self->_unique_name();
104
105        $self->{min_level} = $self->_level_as_number( $p{min_level} );
106
107        # Either use the parameter supplied or just the highest possible level.
108        $self->{max_level} = (
109            exists $p{max_level}
110            ? $self->_level_as_number( $p{max_level} )
111            : $#{ $self->{level_names} }
112        );
113
114        $self->{callbacks} = $p{callbacks} if $p{callbacks};
115
116        if ( $p{newline} ) {
117            push @{ $self->{callbacks} }, \&_add_newline_callback;
118        }
119    }
120}
121
122sub name {
123    my $self = shift;
124
125    return $self->{name};
126}
127
128sub min_level {
129    my $self = shift;
130
131    return $self->{level_names}[ $self->{min_level} ];
132}
133
134sub max_level {
135    my $self = shift;
136
137    return $self->{level_names}[ $self->{max_level} ];
138}
139
140sub accepted_levels {
141    my $self = shift;
142
143    return @{ $self->{level_names} }
144        [ $self->{min_level} .. $self->{max_level} ];
145}
146
147sub _should_log {
148    my $self      = shift;
149    my $level_num = shift;
150
151    return (   ( $level_num >= $self->{min_level} )
152            && ( $level_num <= $self->{max_level} ) );
153}
154
155## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
156sub _level_as_name {
157    my $self  = shift;
158    my $level = shift;
159
160    unless ( defined $level ) {
161        Carp::croak 'undefined value provided for log level';
162    }
163
164    my $canonical_level;
165    unless ( $canonical_level = Log::Dispatch->level_is_valid($level) ) {
166        Carp::croak "$level is not a valid Log::Dispatch log level";
167    }
168
169    return $canonical_level unless $level =~ /\A[0-7]+\z/;
170
171    return $self->{level_names}[$level];
172}
173## use critic
174
175my $_unique_name_counter = 0;
176
177sub _unique_name {
178    my $self = shift;
179
180    return '_anon_' . $_unique_name_counter++;
181}
182
183sub _add_newline_callback {
184
185    # This weird construct is an optimization since this might be called a lot
186    # - see https://github.com/autarch/Log-Dispatch/pull/7
187    +{@_}->{message} . "\n";
188}
189
1901;
191
192# ABSTRACT: Base class for all Log::Dispatch::* objects
193
194__END__
195
196=pod
197
198=encoding UTF-8
199
200=head1 NAME
201
202Log::Dispatch::Output - Base class for all Log::Dispatch::* objects
203
204=head1 VERSION
205
206version 2.70
207
208=head1 SYNOPSIS
209
210  package Log::Dispatch::MySubclass;
211
212  use Log::Dispatch::Output;
213  use base qw( Log::Dispatch::Output );
214
215  sub new {
216      my $proto = shift;
217      my $class = ref $proto || $proto;
218
219      my %p = @_;
220
221      my $self = bless {}, $class;
222
223      $self->_basic_init(%p);
224
225      # Do more if you like
226
227      return $self;
228  }
229
230  sub log_message {
231      my $self = shift;
232      my %p    = @_;
233
234      # Do something with message in $p{message}
235  }
236
237  1;
238
239=head1 DESCRIPTION
240
241This module is the base class from which all Log::Dispatch::* objects
242should be derived.
243
244=head1 CONSTRUCTOR
245
246The constructor, C<new>, must be overridden in a subclass. See L<Output
247Classes|Log::Dispatch/OUTPUT CLASSES> for a description of the common
248parameters accepted by this constructor.
249
250=head1 METHODS
251
252This class provides the following methods:
253
254=head2 $output->_basic_init(%p)
255
256This should be called from a subclass's constructor. Make sure to
257pass the arguments in @_ to it. It sets the object's name and minimum
258level from the passed parameters  It also sets up two other attributes which
259are used by other Log::Dispatch::Output methods, level_names and level_numbers.
260Subclasses will perform parameter validation in this method, and must also call
261the superclass's method.
262
263=head2 $output->name
264
265Returns the object's name.
266
267=head2 $output->min_level
268
269Returns the object's minimum log level.
270
271=head2 $output->max_level
272
273Returns the object's maximum log level.
274
275=head2 $output->accepted_levels
276
277Returns a list of the object's accepted levels (by name) from minimum
278to maximum.
279
280=head2 $output->log( level => $, message => $ )
281
282Sends a message if the level is greater than or equal to the object's
283minimum level. This method applies any message formatting callbacks
284that the object may have.
285
286=head2 $output->_should_log ($)
287
288This method is called from the C<log()> method with the log level of
289the message to be logged as an argument. It returns a boolean value
290indicating whether or not the message should be logged by this
291particular object. The C<log()> method will not process the message
292if the return value is false.
293
294=head2 $output->_level_as_number ($)
295
296This method will take a log level as a string (or a number) and return
297the number of that log level. If not given an argument, it returns
298the calling object's log level instead. If it cannot determine the
299level then it will croak.
300
301=head2 $output->add_callback( $code )
302
303Adds a callback (like those given during construction). It is added to the end
304of the list of callbacks.
305
306=head2 $dispatch->remove_callback( $code )
307
308Remove the given callback from the list of callbacks.
309
310=head1 SUBCLASSING
311
312This class should be used as the base class for all logging objects
313you create that you would like to work under the Log::Dispatch
314architecture. Subclassing is fairly trivial. For most subclasses, if
315you simply copy the code in the SYNOPSIS and then put some
316functionality into the C<log_message> method then you should be all
317set. Please make sure to use the C<_basic_init> method as described above.
318
319The actual logging implementation should be done in a C<log_message>
320method that you write. B<Do not override C<log>!>.
321
322=head1 SUPPORT
323
324Bugs may be submitted at L<https://github.com/houseabsolute/Log-Dispatch/issues>.
325
326I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
327
328=head1 SOURCE
329
330The source code repository for Log-Dispatch can be found at L<https://github.com/houseabsolute/Log-Dispatch>.
331
332=head1 AUTHOR
333
334Dave Rolsky <autarch@urth.org>
335
336=head1 COPYRIGHT AND LICENSE
337
338This software is Copyright (c) 2020 by Dave Rolsky.
339
340This is free software, licensed under:
341
342  The Artistic License 2.0 (GPL Compatible)
343
344The full text of the license can be found in the
345F<LICENSE> file included with this distribution.
346
347=cut
348