File Coverage

blib/lib/Log/Dispatch.pm
Criterion Covered Total %
statement 123 146 84.2
branch 29 42 69.0
condition 1 5 20.0
subroutine 31 43 72.0
pod 12 24 50.0
total 196 260 75.3


line stmt bran cond sub pod time code
1             package Log::Dispatch;
2              
3 29     29   2607701 use 5.006;
  29         306  
4              
5 29     29   119 use strict;
  29         56  
  29         470  
6 29     29   117 use warnings;
  29         33  
  29         896  
7              
8             our $VERSION = '2.69';
9              
10 29     29   121 use Carp ();
  29         61  
  29         375  
11 29     29   8860 use Log::Dispatch::Types;
  29         84  
  29         318  
12 29     29   637965 use Log::Dispatch::Vars qw( %CanonicalLevelNames %LevelNamesToNumbers );
  29         81  
  29         3417  
13 29     29   176 use Module::Runtime qw( use_package_optimistically );
  29         57  
  29         251  
14 29     29   14386 use Params::ValidationCompiler qw( validation_for );
  29         377578  
  29         1558  
15              
16 29     29   232 use base qw( Log::Dispatch::Base );
  29         79  
  29         10949  
17              
18             BEGIN {
19 29     29   177 for my $l ( keys %CanonicalLevelNames ) {
20 348         575 my $level_num = $LevelNamesToNumbers{$l};
21             my $sub = sub {
22 172     172   20277291 my $self = shift;
23             $self->_log_with_num(
24             $level_num,
25 172 100       1778 level => $CanonicalLevelNames{$l},
26             message => @_ > 1 ? "@_" : $_[0],
27             );
28 348         1140 };
29              
30             ## no critic (TestingAndDebugging::ProhibitNoStrict)
31 29     29   201 no strict 'refs';
  29         57  
  29         941  
32 348         454 *{$l} = $sub;
  348         36859  
33             }
34             }
35              
36             {
37             my $validator = validation_for(
38             params => {
39             outputs => {
40             type => t('ArrayRef'),
41             optional => 1,
42             },
43             callbacks => {
44             type => t('Callbacks'),
45             optional => 1,
46             },
47             },
48             );
49              
50             sub new {
51 100     100 1 433277 my $class = shift;
52 100         7345 my %p = $validator->(@_);
53              
54 99         4676 my $self = bless {}, $class;
55              
56             $self->{callbacks} = $p{callbacks}
57 99 100       376 if $p{callbacks};
58              
59 99 100       302 if ( my $outputs = $p{outputs} ) {
60 62 100       210 if ( ref $outputs->[1] eq 'HASH' ) {
61              
62             # 2.23 API
63             # outputs => [
64             # File => { min_level => 'debug', filename => 'logfile' },
65             # Screen => { min_level => 'warning' }
66             # ]
67 1         6 while ( my ( $class, $params ) = splice @$outputs, 0, 2 ) {
68 2         7 $self->_add_output( $class, %$params );
69             }
70             }
71             else {
72              
73             # 2.24+ syntax
74             # outputs => [
75             # [ 'File', min_level => 'debug', filename => 'logfile' ],
76             # [ 'Screen', min_level => 'warning' ]
77             # ]
78 61         86 for my $arr ( @{$outputs} ) {
  61         257  
79 63 100       405 die "expected arrayref, not '$arr'"
80             unless ref $arr eq 'ARRAY';
81 62         158 $self->_add_output( @{$arr} );
  62         395  
82             }
83             }
84             }
85              
86 98         371 return $self;
87             }
88             }
89              
90             sub clone {
91 1     1 1 2 my $self = shift;
92              
93             my %clone = (
94 1 50       4 callbacks => [ @{ $self->{callbacks} || [] } ],
95 1 50       3 outputs => { %{ $self->{outputs} || {} } },
  1         6  
96             );
97              
98 1         4 return bless \%clone, ref $self;
99             }
100              
101             sub _add_output {
102 64     64   108 my $self = shift;
103 64         113 my $class = shift;
104              
105 64 100       379 my $full_class
106             = substr( $class, 0, 1 ) eq '+'
107             ? substr( $class, 1 )
108             : "Log::Dispatch::$class";
109              
110 64         823 use_package_optimistically($full_class);
111              
112 64         8368 $self->add( $full_class->new(@_) );
113             }
114              
115             sub add {
116 104     104 1 1111 my $self = shift;
117 104         147 my $object = shift;
118              
119             # Once 5.6 is more established start using the warnings module.
120 104 0 33     608 if ( exists $self->{outputs}{ $object->name } && $^W ) {
121 0         0 Carp::carp(
122             'Log::Dispatch::* object ', $object->name,
123             ' already exists.'
124             );
125             }
126              
127 104         268 $self->{outputs}{ $object->name } = $object;
128             }
129              
130             sub remove {
131 0     0 1 0 my $self = shift;
132 0         0 my $name = shift;
133              
134 0         0 return delete $self->{outputs}{$name};
135             }
136              
137             sub outputs {
138 3     3 1 8 my $self = shift;
139              
140 3         5 return values %{ $self->{outputs} };
  3         15  
141             }
142              
143             sub callbacks {
144 3     3 1 552 my $self = shift;
145              
146 3         4 return @{ $self->{callbacks} };
  3         9  
147             }
148              
149             ## no critic (Subroutines::ProhibitBuiltinHomonyms)
150             sub log {
151 48     48 1 820 my $self = shift;
152 48         167 my %p = @_;
153              
154 48         133 my $level_num = $self->_level_as_number( $p{level} );
155 47 50       92 return unless defined $level_num;
156              
157 47         122 return $self->_log_with_num( $level_num, %p );
158              
159             }
160             ## use critic
161              
162             sub _log_with_num {
163 219     219   628 my $self = shift;
164 219         502 my $level_num = shift;
165 219         1022 my %p = @_;
166              
167 219 100       1047 return unless $self->_would_log($level_num);
168              
169 128         584 $p{message} = $self->_prepare_message(%p);
170 128         287 $_->_log_with_num( $level_num, %p ) for values %{ $self->{outputs} };
  128         850  
171              
172 128         805 return;
173             }
174              
175             sub _prepare_message {
176 130     130   363 my $self = shift;
177 130         417 my %p = @_;
178              
179             $p{message} = $p{message}->()
180 130 100       424 if ref $p{message} eq 'CODE';
181              
182             $p{message} = $self->_apply_callbacks(%p)
183 130 100       440 if $self->{callbacks};
184              
185 130         344 return $p{message};
186             }
187              
188             sub _log_to_outputs {
189 2     2   4 my $self = shift;
190 2         6 my %p = @_;
191              
192 2         4 for ( values %{ $self->{outputs} } ) {
  2         5  
193 2         12 $_->log(%p);
194             }
195             }
196              
197             sub log_and_die {
198 2     2 1 82 my $self = shift;
199 2         7 my %p = @_;
200              
201 2         6 $p{message} = $self->_prepare_message(%p);
202              
203 2 50       7 $self->_log_to_outputs(%p) if $self->would_log( $p{level} );
204              
205 2         23 $self->_die_with_message(%p);
206             }
207              
208             sub log_and_croak {
209 1     1 1 2115 my $self = shift;
210              
211 1         4 $self->log_and_die(@_);
212             }
213              
214             sub _die_with_message {
215 2     2   3 my $self = shift;
216 2         5 my %p = @_;
217              
218 2         3 my $msg = $p{message};
219              
220             local $Carp::CarpLevel = ( $Carp::CarpLevel || 0 ) + $p{carp_level}
221 2 50 0     13 if exists $p{carp_level};
222              
223 2         232 Carp::croak($msg);
224             }
225              
226             sub log_to {
227 0     0 1 0 my $self = shift;
228 0         0 my %p = @_;
229              
230             $p{message} = $self->_apply_callbacks(%p)
231 0 0       0 if $self->{callbacks};
232              
233 0         0 $self->_log_to(%p);
234             }
235              
236             sub _log_to {
237 0     0   0 my $self = shift;
238 0         0 my %p = @_;
239 0         0 my $name = $p{name};
240              
241 0 0       0 if ( exists $self->{outputs}{$name} ) {
    0          
242 0         0 $self->{outputs}{$name}->log(@_);
243             }
244             elsif ($^W) {
245 0         0 Carp::carp(
246             "Log::Dispatch::* object named '$name' not in dispatcher\n");
247             }
248             }
249              
250             sub output {
251 17     17 1 3314 my $self = shift;
252 17         24 my $name = shift;
253              
254 17 100       42 return unless exists $self->{outputs}{$name};
255              
256 16         71 return $self->{outputs}{$name};
257             }
258              
259             sub would_log {
260 10     10 1 21 my $self = shift;
261 10         14 my $level = shift;
262              
263 10         23 my $level_num = $self->_level_as_number($level);
264 10 100       30 return 0 unless defined $level_num;
265              
266 9         21 return $self->_would_log($level_num);
267             }
268              
269             sub _would_log {
270 228     228   981 my $self = shift;
271 228         470 my $level_num = shift;
272              
273 228         301 for ( values %{ $self->{outputs} } ) {
  228         730  
274 228 100       1758 return 1 if $_->_should_log($level_num);
275             }
276              
277 93         239 return 0;
278             }
279              
280 1     1 0 4 sub is_debug { $_[0]->would_log('debug') }
281 0     0 0 0 sub is_info { $_[0]->would_log('info') }
282 0     0 0 0 sub is_notice { $_[0]->would_log('notice') }
283 1     1 0 5 sub is_warning { $_[0]->would_log('warning') }
284 0     0 0 0 sub is_warn { $_[0]->would_log('warn') }
285 0     0 0 0 sub is_error { $_[0]->would_log('error') }
286 0     0 0 0 sub is_err { $_[0]->would_log('err') }
287 0     0 0 0 sub is_critical { $_[0]->would_log('critical') }
288 1     1 0 4 sub is_crit { $_[0]->would_log('crit') }
289 0     0 0   sub is_alert { $_[0]->would_log('alert') }
290 0     0 0   sub is_emerg { $_[0]->would_log('emerg') }
291 0     0 0   sub is_emergency { $_[0]->would_log('emergency') }
292              
293             1;
294              
295             # ABSTRACT: Dispatches messages to one or more outputs
296              
297             __END__
298              
299             =pod
300              
301             =encoding UTF-8
302              
303             =head1 NAME
304              
305             Log::Dispatch - Dispatches messages to one or more outputs
306              
307             =head1 VERSION
308              
309             version 2.69
310              
311             =head1 SYNOPSIS
312              
313             use Log::Dispatch;
314              
315             # Simple API
316             #
317             my $log = Log::Dispatch->new(
318             outputs => [
319             [ 'File', min_level => 'debug', filename => 'logfile' ],
320             [ 'Screen', min_level => 'warning' ],
321             ],
322             );
323              
324             $log->info('Blah, blah');
325              
326             # More verbose API
327             #
328             my $log = Log::Dispatch->new();
329             $log->add(
330             Log::Dispatch::File->new(
331             name => 'file1',
332             min_level => 'debug',
333             filename => 'logfile'
334             )
335             );
336             $log->add(
337             Log::Dispatch::Screen->new(
338             name => 'screen',
339             min_level => 'warning',
340             )
341             );
342              
343             $log->log( level => 'info', message => 'Blah, blah' );
344              
345             my $sub = sub { my %p = @_; return reverse $p{message}; };
346             my $reversing_dispatcher = Log::Dispatch->new( callbacks => $sub );
347              
348             =head1 DESCRIPTION
349              
350             This module manages a set of Log::Dispatch::* output objects that can be
351             logged to via a unified interface.
352              
353             The idea is that you create a Log::Dispatch object and then add various
354             logging objects to it (such as a file logger or screen logger). Then you
355             call the C<log> method of the dispatch object, which passes the message to
356             each of the objects, which in turn decide whether or not to accept the
357             message and what to do with it.
358              
359             This makes it possible to call single method and send a message to a
360             log file, via email, to the screen, and anywhere else, all with very
361             little code needed on your part, once the dispatching object has been
362             created.
363              
364             =head1 METHODS
365              
366             This class provides the following methods:
367              
368             =head2 Log::Dispatch->new(...)
369              
370             This method takes the following parameters:
371              
372             =over 4
373              
374             =item * outputs( [ [ class, params, ... ], [ class, params, ... ], ... ] )
375              
376             This parameter is a reference to a list of lists. Each inner list consists of
377             a class name and a set of constructor params. The class is automatically
378             prefixed with 'Log::Dispatch::' unless it begins with '+', in which case the
379             string following '+' is taken to be a full classname. e.g.
380              
381             outputs => [ [ 'File', min_level => 'debug', filename => 'logfile' ],
382             [ '+My::Dispatch', min_level => 'info' ] ]
383              
384             For each inner list, a new output object is created and added to the
385             dispatcher (via the C<add()> method).
386              
387             See L</"OUTPUT CLASSES"> for the parameters that can be used when creating an
388             output object.
389              
390             =item * callbacks( \& or [ \&, \&, ... ] )
391              
392             This parameter may be a single subroutine reference or an array
393             reference of subroutine references. These callbacks will be called in
394             the order they are given and passed a hash containing the following keys:
395              
396             ( message => $log_message, level => $log_level )
397              
398             In addition, any key/value pairs passed to a logging method will be
399             passed onto your callback.
400              
401             The callbacks are expected to modify the message and then return a
402             single scalar containing that modified message. These callbacks will
403             be called when either the C<log> or C<log_to> methods are called and
404             will only be applied to a given message once. If they do not return
405             the message then you will get no output. Make sure to return the
406             message!
407              
408             =back
409              
410             =head2 $dispatch->clone()
411              
412             This returns a I<shallow> clone of the original object. The underlying output
413             objects and callbacks are shared between the two objects. However any changes
414             made to the outputs or callbacks that the object contains are not shared.
415              
416             =head2 $dispatch->log( level => $, message => $ or \& )
417              
418             Sends the message (at the appropriate level) to all the output objects that
419             the dispatcher contains (by calling the C<log_to> method repeatedly).
420              
421             The level can be specified by name or by an integer from 0 (debug) to 7
422             (emergency).
423              
424             This method also accepts a subroutine reference as the message
425             argument. This reference will be called only if there is an output
426             that will accept a message of the specified level.
427              
428             =head2 $dispatch->debug (message), info (message), ...
429              
430             You may call any valid log level (including valid abbreviations) as a method
431             with a single argument that is the message to be logged. This is converted
432             into a call to the C<log> method with the appropriate level.
433              
434             For example:
435              
436             $log->alert('Strange data in incoming request');
437              
438             translates to:
439              
440             $log->log( level => 'alert', message => 'Strange data in incoming request' );
441              
442             If you pass an array to these methods, it will be stringified as is:
443              
444             my @array = ('Something', 'bad', 'is', 'here');
445             $log->alert(@array);
446              
447             # is equivalent to
448              
449             $log->alert("@array");
450              
451             You can also pass a subroutine reference, just like passing one to the
452             C<log()> method.
453              
454             =head2 $dispatch->log_and_die( level => $, message => $ or \& )
455              
456             Has the same behavior as calling C<log()> but calls
457             C<_die_with_message()> at the end.
458              
459             You can throw exception objects by subclassing this method.
460              
461             If the C<carp_level> parameter is present its value will be added to
462             the current value of C<$Carp::CarpLevel>.
463              
464             =head2 $dispatch->log_and_croak( level => $, message => $ or \& )
465              
466             A synonym for C<$dispatch->log_and_die()>.
467              
468             =head2 $dispatch->log_to( name => $, level => $, message => $ )
469              
470             Sends the message only to the named object. Note: this will not properly
471             handle a subroutine reference as the message.
472              
473             =head2 $dispatch->add_callback( $code )
474              
475             Adds a callback (like those given during construction). It is added to the end
476             of the list of callbacks. Note that this can also be called on individual
477             output objects.
478              
479             =head2 $dispatch->remove_callback( $code )
480              
481             Remove the given callback from the list of callbacks. Note that this can also
482             be called on individual output objects.
483              
484             =head2 $dispatch->callbacks()
485              
486             Returns a list of the callbacks in a given output.
487              
488             =head2 $dispatch->level_is_valid( $string )
489              
490             Returns true or false to indicate whether or not the given string is a
491             valid log level. Can be called as either a class or object method.
492              
493             =head2 $dispatch->would_log( $string )
494              
495             Given a log level, returns true or false to indicate whether or not
496             anything would be logged for that log level.
497              
498             =head2 $dispatch->is_C<$level>
499              
500             There are methods for every log level: C<is_debug()>, C<is_warning()>, etc.
501              
502             This returns true if the logger will log a message at the given level.
503              
504             =head2 $dispatch->add( Log::Dispatch::* OBJECT )
505              
506             Adds a new L<output object|/"OUTPUT CLASSES"> to the dispatcher. If an object
507             of the same name already exists, then that object is replaced, with
508             a warning if C<$^W> is true.
509              
510             =head2 $dispatch->remove($)
511              
512             Removes the output object that matches the name given to the remove method.
513             The return value is the object being removed or undef if no object
514             matched this.
515              
516             =head2 $dispatch->outputs()
517              
518             Returns a list of output objects.
519              
520             =head2 $dispatch->output( $name )
521              
522             Returns the output object of the given name. Returns undef or an empty
523             list, depending on context, if the given output does not exist.
524              
525             =head2 $dispatch->_die_with_message( message => $, carp_level => $ )
526              
527             This method is used by C<log_and_die> and will either die() or croak()
528             depending on the value of C<message>: if it's a reference or it ends
529             with a new line then a plain die will be used, otherwise it will
530             croak.
531              
532             =head1 OUTPUT CLASSES
533              
534             An output class - e.g. L<Log::Dispatch::File> or
535             L<Log::Dispatch::Screen> - implements a particular way
536             of dispatching logs. Many output classes come with this distribution,
537             and others are available separately on CPAN.
538              
539             The following common parameters can be used when creating an output class.
540             All are optional. Most output classes will have additional parameters beyond
541             these, see their documentation for details.
542              
543             =over 4
544              
545             =item * name ($)
546              
547             A name for the object (not the filename!). This is useful if you want to
548             refer to the object later, e.g. to log specifically to it or remove it.
549              
550             By default a unique name will be generated. You should not depend on the
551             form of generated names, as they may change.
552              
553             =item * min_level ($)
554              
555             The minimum L<logging level|/"LOG LEVELS"> this object will accept. Required.
556              
557             =item * max_level ($)
558              
559             The maximum L<logging level|/"LOG LEVELS"> this object will accept. By default
560             the maximum is the highest possible level (which means functionally that the
561             object has no maximum).
562              
563             =item * callbacks( \& or [ \&, \&, ... ] )
564              
565             This parameter may be a single subroutine reference or an array
566             reference of subroutine references. These callbacks will be called in
567             the order they are given and passed a hash containing the following keys:
568              
569             ( message => $log_message, level => $log_level )
570              
571             The callbacks are expected to modify the message and then return a
572             single scalar containing that modified message. These callbacks will
573             be called when either the C<log> or C<log_to> methods are called and
574             will only be applied to a given message once. If they do not return
575             the message then you will get no output. Make sure to return the
576             message!
577              
578             =item * newline (0|1)
579              
580             If true, a callback will be added to the end of the callbacks list that adds
581             a newline to the end of each message. Default is false, but some
582             output classes may decide to make the default true.
583              
584             =back
585              
586             =head1 LOG LEVELS
587              
588             The log levels that Log::Dispatch uses are taken directly from the
589             syslog man pages (except that I expanded them to full words). Valid
590             levels are:
591              
592             =over 4
593              
594             =item debug
595              
596             =item info
597              
598             =item notice
599              
600             =item warning
601              
602             =item error
603              
604             =item critical
605              
606             =item alert
607              
608             =item emergency
609              
610             =back
611              
612             Alternately, the numbers 0 through 7 may be used (debug is 0 and emergency is
613             7). The syslog standard of 'err', 'crit', and 'emerg' is also acceptable. We
614             also allow 'warn' as a synonym for 'warning'.
615              
616             =head1 SUBCLASSING
617              
618             This module was designed to be easy to subclass. If you want to handle
619             messaging in a way not implemented in this package, you should be able to add
620             this with minimal effort. It is generally as simple as subclassing
621             Log::Dispatch::Output and overriding the C<new> and C<log_message>
622             methods. See the L<Log::Dispatch::Output> docs for more details.
623              
624             If you would like to create your own subclass for sending email then
625             it is even simpler. Simply subclass L<Log::Dispatch::Email> and
626             override the C<send_email> method. See the L<Log::Dispatch::Email>
627             docs for more details.
628              
629             The logging levels that Log::Dispatch uses are borrowed from the standard
630             UNIX syslog levels, except that where syslog uses partial words ("err")
631             Log::Dispatch also allows the use of the full word as well ("error").
632              
633             =head1 RELATED MODULES
634              
635             =head2 Log::Dispatch::DBI
636              
637             Written by Tatsuhiko Miyagawa. Log output to a database table.
638              
639             =head2 Log::Dispatch::FileRotate
640              
641             Written by Mark Pfeiffer. Rotates log files periodically as part of
642             its usage.
643              
644             =head2 Log::Dispatch::File::Stamped
645              
646             Written by Eric Cholet. Stamps log files with date and time
647             information.
648              
649             =head2 Log::Dispatch::Jabber
650              
651             Written by Aaron Straup Cope. Logs messages via Jabber.
652              
653             =head2 Log::Dispatch::Tk
654              
655             Written by Dominique Dumont. Logs messages to a Tk window.
656              
657             =head2 Log::Dispatch::Win32EventLog
658              
659             Written by Arthur Bergman. Logs messages to the Windows event log.
660              
661             =head2 Log::Log4perl
662              
663             An implementation of Java's log4j API in Perl. Log messages can be limited by
664             fine-grained controls, and if they end up being logged, both native Log4perl
665             and Log::Dispatch appenders can be used to perform the actual logging
666             job. Created by Mike Schilli and Kevin Goess.
667              
668             =head2 Log::Dispatch::Config
669              
670             Written by Tatsuhiko Miyagawa. Allows configuration of logging via a
671             text file similar (or so I'm told) to how it is done with log4j.
672             Simpler than Log::Log4perl.
673              
674             =head2 Log::Agent
675              
676             A very different API for doing many of the same things that
677             Log::Dispatch does. Originally written by Raphael Manfredi.
678              
679             =head1 SEE ALSO
680              
681             L<Log::Dispatch::ApacheLog>, L<Log::Dispatch::Email>,
682             L<Log::Dispatch::Email::MailSend>, L<Log::Dispatch::Email::MailSender>,
683             L<Log::Dispatch::Email::MailSendmail>, L<Log::Dispatch::Email::MIMELite>,
684             L<Log::Dispatch::File>, L<Log::Dispatch::File::Locked>,
685             L<Log::Dispatch::Handle>, L<Log::Dispatch::Output>, L<Log::Dispatch::Screen>,
686             L<Log::Dispatch::Syslog>
687              
688             =head1 SUPPORT
689              
690             Bugs may be submitted at L<https://github.com/houseabsolute/Log-Dispatch/issues>.
691              
692             I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
693              
694             =head1 SOURCE
695              
696             The source code repository for Log-Dispatch can be found at L<https://github.com/houseabsolute/Log-Dispatch>.
697              
698             =head1 DONATIONS
699              
700             If you'd like to thank me for the work I've done on this module, please
701             consider making a "donation" to me via PayPal. I spend a lot of free time
702             creating free software, and would appreciate any support you'd care to offer.
703              
704             Please note that B<I am not suggesting that you must do this> in order for me
705             to continue working on this particular software. I will continue to do so,
706             inasmuch as I have in the past, for as long as it interests me.
707              
708             Similarly, a donation made in this way will probably not make me work on this
709             software much more, unless I get so many donations that I can consider working
710             on free software full time (let's all have a chuckle at that together).
711              
712             To donate, log into PayPal and send money to autarch@urth.org, or use the
713             button at L<http://www.urth.org/~autarch/fs-donation.html>.
714              
715             =head1 AUTHOR
716              
717             Dave Rolsky <autarch@urth.org>
718              
719             =head1 CONTRIBUTORS
720              
721             =for stopwords Anirvan Chatterjee Carsten Grohmann Doug Bell Graham Knop Ollis Gregory Oschwald hartzell Johann Rolschewski Jonathan Swartz Karen Etheridge Kerin Millar Kivanc Yazan Konrad Bucheli Michael Schout Olaf Alders Olivier Mengué Rohan Carly Ross Attrill Salvador Fandiño Sergey Leschenko Slaven Rezic Steve Bertrand Whitney Jackson
722              
723             =over 4
724              
725             =item *
726              
727             Anirvan Chatterjee <anirvan@users.noreply.github.com>
728              
729             =item *
730              
731             Carsten Grohmann <mail@carstengrohmann.de>
732              
733             =item *
734              
735             Doug Bell <doug@preaction.me>
736              
737             =item *
738              
739             Graham Knop <haarg@haarg.org>
740              
741             =item *
742              
743             Graham Ollis <plicease@cpan.org>
744              
745             =item *
746              
747             Gregory Oschwald <goschwald@maxmind.com>
748              
749             =item *
750              
751             hartzell <hartzell@alerce.com>
752              
753             =item *
754              
755             Johann Rolschewski <jorol@cpan.org>
756              
757             =item *
758              
759             Jonathan Swartz <swartz@pobox.com>
760              
761             =item *
762              
763             Karen Etheridge <ether@cpan.org>
764              
765             =item *
766              
767             Kerin Millar <kfm@plushkava.net>
768              
769             =item *
770              
771             Kivanc Yazan <kivancyazan@gmail.com>
772              
773             =item *
774              
775             Konrad Bucheli <kb@open.ch>
776              
777             =item *
778              
779             Michael Schout <mschout@gkg.net>
780              
781             =item *
782              
783             Olaf Alders <olaf@wundersolutions.com>
784              
785             =item *
786              
787             Olivier Mengué <dolmen@cpan.org>
788              
789             =item *
790              
791             Rohan Carly <se456@rohan.id.au>
792              
793             =item *
794              
795             Ross Attrill <ross.attrill@gmail.com>
796              
797             =item *
798              
799             Salvador Fandiño <sfandino@yahoo.com>
800              
801             =item *
802              
803             Sergey Leschenko <sergle.ua@gmail.com>
804              
805             =item *
806              
807             Slaven Rezic <srezic@cpan.org>
808              
809             =item *
810              
811             Steve Bertrand <steveb@cpan.org>
812              
813             =item *
814              
815             Whitney Jackson <whitney.jackson@baml.com>
816              
817             =back
818              
819             =head1 COPYRIGHT AND LICENSE
820              
821             This software is Copyright (c) 2019 by Dave Rolsky.
822              
823             This is free software, licensed under:
824              
825             The Artistic License 2.0 (GPL Compatible)
826              
827             The full text of the license can be found in the
828             F<LICENSE> file included with this distribution.
829              
830             =cut