summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2015-06-14 16:34:55 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2015-06-14 16:34:55 +0000
commit2e0d2bec52bd345ef05ea12ea9052643ef135029 (patch)
treed128cd9759bc672fa01bac1561911aa8a895981b
downloadLog-Dispatch-tarball-2e0d2bec52bd345ef05ea12ea9052643ef135029.tar.gz
-rw-r--r--Changes635
-rw-r--r--INSTALL43
-rw-r--r--LICENSE207
-rw-r--r--MANIFEST54
-rw-r--r--META.json801
-rw-r--r--META.yml589
-rw-r--r--Makefile.PL119
-rw-r--r--README.md425
-rw-r--r--cpanfile50
-rw-r--r--dist.ini33
-rw-r--r--lib/Log/Dispatch.pm755
-rw-r--r--lib/Log/Dispatch/ApacheLog.pm115
-rw-r--r--lib/Log/Dispatch/Base.pm91
-rw-r--r--lib/Log/Dispatch/Code.pm122
-rw-r--r--lib/Log/Dispatch/Conflicts.pm33
-rw-r--r--lib/Log/Dispatch/Email.pm207
-rw-r--r--lib/Log/Dispatch/Email/MIMELite.pm83
-rw-r--r--lib/Log/Dispatch/Email/MailSend.pm102
-rw-r--r--lib/Log/Dispatch/Email/MailSender.pm130
-rw-r--r--lib/Log/Dispatch/Email/MailSendmail.pm83
-rw-r--r--lib/Log/Dispatch/File.pm285
-rw-r--r--lib/Log/Dispatch/File/Locked.pm96
-rw-r--r--lib/Log/Dispatch/Handle.pm102
-rw-r--r--lib/Log/Dispatch/Null.pm69
-rw-r--r--lib/Log/Dispatch/Output.pm315
-rw-r--r--lib/Log/Dispatch/Screen.pm118
-rw-r--r--lib/Log/Dispatch/Syslog.pm220
-rw-r--r--perlcriticrc58
-rw-r--r--perltidyrc22
-rw-r--r--t/00-compile.t38
-rw-r--r--t/00-report-prereqs.dd60
-rw-r--r--t/00-report-prereqs.t183
-rw-r--r--t/01-basic.t1195
-rw-r--r--t/02-email-exit.t15
-rw-r--r--t/03-short-syntax.t77
-rw-r--r--t/04-binmode.t54
-rw-r--r--t/05-close-after-write.t97
-rw-r--r--t/06-syslog.t66
-rw-r--r--t/author-eol.t60
-rw-r--r--t/author-no-tabs.t60
-rw-r--r--t/author-pod-spell.t97
-rwxr-xr-xt/email-exit-helper.pl20
-rw-r--r--t/lib/Log/Dispatch/TestUtil.pm44
-rw-r--r--t/release-cpan-changes.t19
-rw-r--r--t/release-pod-coverage.t63
-rw-r--r--t/release-pod-no404s.t29
-rw-r--r--t/release-pod-syntax.t14
-rw-r--r--t/release-portability.t20
-rw-r--r--t/release-test-version.t46
-rw-r--r--t/release-tidyall.t17
-rwxr-xr-xt/sendmail3
-rw-r--r--tidyall.ini10
-rw-r--r--weaver.ini17
53 files changed, 8266 insertions, 0 deletions
diff --git a/Changes b/Changes
new file mode 100644
index 0000000..1ad834c
--- /dev/null
+++ b/Changes
@@ -0,0 +1,635 @@
+2.45 2015-06-14
+
+- Don't include threads and threads::shared in list of dependencies. This is
+ only needed for Log::Dispatch::Syslog and is loaded at runtime as needed
+ (which has its own issues but ...). Reported by Kent Fredric. RT #103392.
+
+
+2.44 2014-10-18
+
+- The fix for a buffered email output in the last release introduced a bug
+ with _non-buffered_ email outputs. This would cause a fatal error during
+ global destruction when the DESTROY method was called. Reported by Christ
+ Hutchinson. RT #99474.
+
+
+2.43 2014-10-05
+
+- Fixed the thread locking in Log::Dispatch::Syslog (I hope). The previous
+ version caused Perl to crash when per-thread locking was enabled. Note that
+ I don't use threads so I haven't tested this. Patch by Sergio Fernández
+ Muñoz. RT # 99208.
+
+- If a buffered email output is being destroyed during global destruction and
+ still has messages in the buffer, we warn and do not attempt to send the
+ messages. During global destruction, the package we use to send email may
+ already be destroyed, leading to weird errors when we try to use
+ it. Reported by Mark Overmeer. RT #97733.
+
+- In 2.42 I added the ability to pass a hashref for the socket parameter given
+ to Log::Dispatch::Syslog, but I forgot to mention this here. This is
+ necessary to support remote logging. Patch by David Coppit. RT #93045.
+
+2.42 2014-08-12
+
+- Added a Log::Dispatch->clone() method. This returns shallow clone. The
+ outputs and callbacks are shared, but changes to outputs and callbacks in
+ the clone do not affect the original, or vice versa.
+
+- Added Log::Dispatch->outputs() method. This returns all the output objects
+ in a dispatch object.
+
+- Added Log::Dispatch->callbacks() method. This returns all the callback subs
+ in a dispatch object.
+
+- The Syslog output now calls Sys::Syslog::setlogsock() every time a message
+ is logged, since something else could have called it in between logging two
+ messages.
+
+- Added a lock parameter to the Syslog output. If this is true, then logging
+ is done in the scope of a per-thread lock. Reported by Cedric Carree and
+ Franck Youssef. RT #67988 and #85013.
+
+- Replaced Class::Load with Module::Runtime.
+
+
+2.41 2013-07-22
+
+- An error is now thrown if you call Log::Dispatch->log without a
+ level. Previously you'd just get a warning and then execution would continue
+ (without logging anything). Patch by Ross Attrill. RT #87133.
+
+
+2.40 2013-07-01
+
+- Added a conflict entry for older Log::Dispatch::File::Stamped to the
+ metadata. Patch by Karen Etheridge. RT #86215.
+
+
+2.39 2013-04-21
+
+- You can now pass a port option to the MailSender output. Patch by Whitney
+ Jackson.
+
+
+2.38 2013-04-14
+
+- Fix test that used undeclared prereqs so it does not do that.
+
+
+2.37 2013-04-14
+
+- Moved Log::Dispatch::File constructor parameter validation moved to
+ _basic_init() to facilitate proper subclassing. Patch by ether. RT #84545.
+
+
+2.36 2013-04-08
+
+- Added a very simple Log::Dispatch::Code output. This lets you log to a
+ subroutine reference.
+
+- Added Sys::Syslog 0.25 as a prereq. This is a temporary fix to the problem
+ of Log::Dispatch shipping lots of output modules with undeclared prereqs (so
+ as not to require mod_perl, four email sending modules, etc.). In the future
+ Log::Dispatch will be split into a core distro and a set of distros, one for
+ each output that has prereqs. Reported by Michael Schwern. RT #84481.
+
+
+2.35 2013-01-20
+
+- Added a big warning about the potential for deadlocks in the documentation
+ for Log::Dispatch::File::Locked. Patch by ether.
+
+
+2.34 2012-12-08
+
+- Fix a test bug that caused the tests to fail on all Perls before 5.16.0.
+
+
+2.33 2012-12-07
+
+- Added a 'syswrite' option to Log::Dispatch::File which causes all writes to
+ use syswrite (so they're atomic). Patched by ether. RT #81669.
+
+- The File output's DESTROY method now checks to see if it's associated handle
+ is open before trying to close it. Patch by Jeffrey Thalhammer.
+
+
+2.32 2012-05-24
+
+- Fix a test failure - test failed if you had 0.16 <= Sys::Syslog < 0.25
+ installed.
+
+- Added a kludgey test failure fix for failure on Cygwin. Patch by Christian
+ Carey. RT #77364.
+
+
+2.31 2012-05-21
+
+- Added missing prereq - Class::Load.
+
+
+2.30 2012-05-20
+
+- Remove Sys::Syslog as a prereq, since you can use this distro perfectly well
+ without it. Fixes RT #52065.
+
+- You can now pass a subroutine reference to the sugar methods like
+ $dispatch->debug() and friends. Requested by Jeffrey Thalhammer. RT #77308.
+
+- Calling sugar methods like $dispatch->warn or $dispatch->crit did not
+ normalize the log level, so the level would be passed to the outputs as
+ "warn", not "warning". Reported by Karen Etheridge. RT #77203.
+
+
+2.29 2011-03-18
+
+- Add is_$level methods for compatibility with Log::Contextual. Patch by frew.
+
+
+2.28 2010-12-13
+
+- The Log::Dispatch module still had version 2.26 in the last
+ release. Reported by Øyvind Skaar. RT #63876.
+
+
+2.27 2010-10-16
+
+- Fix docs on handling of arrays passed to ->debug, ->error, etc. Requested by
+ Andrew Hanenkamp. RT #61400.
+
+- Allow an arrayref for the Syslog socket option. Requested by Paul
+ Bennett. RT #57631.
+
+- License is now Artistic 2.0.
+
+
+2.26 2009-09-22
+
+- Doc updates. The 2.23 constructor API was still shown in all the output
+ subclasses. Fixed by Jon Swartz.
+
+
+2.25 2009-09-15
+
+- Added a workaround for a weird tainting issue with Params::Validate. This
+ caused a taint exception when a Log::Dispatch::Syslog was created under
+ taint mode. Note that there is still a problem in Params::Validate itself,
+ this is just a hack.
+
+
+2.24 2009-09-13
+
+- Simplified new constructor API (the 2.23 API is still silently supported but
+ not documented):
+
+ Log::Dispatch->new( outputs => [ [ 'File', ... ],
+ [ 'Screen', ... ],
+ ]
+ );
+
+ Implemented by Jon Swartz.
+
+- All of the mail sending modules now warn unconditionally if sending mail
+ fails. This removes the incorrect use of warnings::enabled() in some
+ modules. RT #43516.
+
+
+2.23 2009-09-12
+
+- New constructor API that simplifies creating your Log::Dispatch object.
+ Implemented by Jon Swartz.
+
+- Made name parameter optional. We now auto-generate a unique name if one is
+ not given. Implemented by Jon Swartz.
+
+- Added a newline parameter that causes a newline to be added to each message,
+ and updated the documentation regarding newlines. Implemented by Jon Swartz.
+
+- Removed repetitive boilerplate documentation from each output
+ class. Implemented by Jon Swartz.
+
+- The level_names and level_numbers used internally are now computed once and
+ shared between output objects. Implemented by Jon Swartz.
+
+- Updated repo url - now at http://hg.urth.org/hg/Log-Dispatch
+
+- Explicitly depend on Sys::Syslog 0.16.
+
+- Added warn as a synonym for warning. RT #44821. Requested by Dylan Martin.
+
+- Added an add_callback method to Log::Dispatch and
+ Log::Dispatch::Output. This lets you add a new formatting callback after an
+ object is created. Based on a patch from Ricardo Signes. RT #48283.
+
+- The Log::Dispatch docs mistakenly told you to provide a log() method when
+ creating a new output class. RT #40561.
+
+- Made all modules have the same version as Log::Dispatch itself.
+
+
+2.22 2008-11-11
+
+- Fixed a bug where Log::Dispatch::Email would die when it tried to
+ log under taint mode. Patch by Neil Hemingway. RT #40042.
+
+- Fixed a misuse of warnings::enabled(). Reported by Darian
+ Patrick. RT #39784.
+
+- Syslog logging now requires Sys::Syslog 0.16+.
+
+- If you don't pass a socket argument to Log::Dispatch::Syslog, it
+ does not call Sys::Syslog::setlogsock(), which is the preferred
+ option for portability.
+
+* If any of the syslog calls die, this is trapped and the error is
+ output as a warning if warnings are on. This is mostly a workaround
+ for Sys::Sylog not handling utf-8. RT #35270 & #37397.
+
+ This isn't backwards-compatible, but it's probably wrong for the
+ logging code to die because it can't log (even though some other
+ output modules still do).
+
+
+2.21 2008-02-06
+
+- Added log_and_die() and log_and_croak() methods. Patch by Yuval
+ Kogman.
+
+
+2.20 2007-11-02
+
+- One of the tests failed on Perl 5.6.x. Thanks to Slaven Rezic for
+ the report.
+
+
+2.19 2007-11-01
+
+- Switched to providing a traditional Makefile.PL as well as a
+ Build.PL file. RT #27208.
+
+- When permissions are specified for a Log::Dispatch::File object,
+ don't try to chmod the file unless the permissions of the file
+ differ from what the file already has. Based on a patch by Kevin. RT
+ #28151.
+
+- Require at least Perl 5.6.0.
+
+- Remove the tests for the email sending and exit codes, since the
+ test had a heisenbug I could not understand. I _think_ the code in
+ the email modules is correct, but the test isn't proving anything.
+
+- Added a binmode parameter for Log::Dispatch::File. Based on a patch
+ by Angelo. RT #26063.
+
+
+2.18 2007-05-12
+
+- Log::Dispatch::ApacheLog should really now work under mod_perl 2, as
+ well as mod_perl 1. RT #26910.
+
+
+2.17 2007-03-31
+
+- Log::Dispatch::ApacheLog should now work under mod_perl 2, as well
+ as mod_perl 1.
+
+
+2.16 2010-10-16
+
+- Don't require IO::String for running the tests. Reported by Andreas
+ Koenig. RT #23973.
+
+- Moved Test::More to build_requires. Suggested by Andreas Koenig. RT
+ #23973.
+
+
+2.15 2006-12-16
+
+- Don't try to test compilation of L::D::Syslog unless Sys::Syslog is
+ available. Patch by Kenichi Ishigaki. RT #23751.
+
+- Allow a subroutine reference as a log message when callin
+ Log::Dispatch->log(). Suggested by Craig Manley. RT #23913.
+
+- Added Log::Dispatch::Null output, primarily for testing.
+
+
+2.14 2006-11-18
+
+This release only involves changes to the test suite.
+
+- Make sure we don't fail if Apache::Log is not installed on the
+ system. RT #22791. Reported by Lee Goddard.
+
+- Separated out compilation tests from other tests.
+
+
+2.13 2006-09-25
+
+- No code changes, just added a SUPPORT section to the docs referring
+ folks to RT for bug reports & patches.
+
+
+2.12 2006-08-09
+
+- The various email sending modules could overwrite if they were in
+ buffered mode and they sent mail as a script exited. Reported by
+ Dean Kopesky.
+
+- Doc tweaks. Make reference to "Log Levels" section in output module
+ docs more explicit. RT #11224.
+
+
+2.11 2005-07-27
+
+- In tests, make sure filehandles are closed before reading or
+ unlinking the file. Patch from Ron Savage.
+
+
+2.10 2004-02-11
+
+- No changes to the core code, just a change to the included
+ Makefile.PL so it works with Module::Build 0.23, which breaks
+ backwards compatibility (grr).
+
+- Fix a doc bug in Log::Dispatch::Syslog. It defaults to using a unix
+ socket, not an inet socket.
+
+
+2.09 2004-01-09
+
+- Fix a test failure on Win32 platforms. The problem was in the test,
+ not the code. Patch by David Viner.
+
+- Distro is now signed with Module::Signature.
+
+
+2.08 2003-11-27
+
+- Added Log::Dispatch->would_log method, which indicates whether
+ logging will be done for a given log level. Suggested by Ruslan
+ Zakirov.
+
+- Switched tests to use Test::More.
+
+
+2.07 2003-09-27
+
+- Added Log::Dispatch::File::Locked. Based on code from JAA Klunder.
+
+- Check all system call return values.
+
+- Fix warning from Log::Dispatch::File if it was loaded after
+Attribute::Handlers. Reported by Mike Schilli.
+
+- Fixed up POD to pass pod tests.
+
+
+2.06 2003-05-01
+
+ "Arise ye workers from your slumbers
+ Arise ye criminals of want
+ For reason in revolt now thunders
+ and at last ends the age of cant."
+
+- Added a permissions parameter to Log::Dispatch::File->new. Based on
+ a patch from James FitzGibbon.
+
+
+2.05 2003-04-18
+
+- Changed a code construct that seems to provoke a bug for Meng Wong,
+ but no one else ;)
+
+- Switched to Module::Build and removed interactive portion of
+ installation process.
+
+- Log::Dispatch::Email::MailSender was causing Mail::Sender to send
+ debug output to STDERR if warnings were on. Now it's not.
+
+
+2.04 2003-03-21
+
+- The close_after_write option didn't actually do anything. Fixed by
+ JAA Klunder.
+
+
+2.03 2003-02-27
+
+- Log::Dispatch::ApacheLog would break if a log level was specified as
+ a number. Reported by Kevin Goess.
+
+
+2.02 2003-02-20
+
+- Added close_after_write option to Log::Dispatch::File. Based on
+ patch from JAA Klunder.
+
+
+2.01 2002-06-21
+
+- Added new module Log::Dispatch::Email::MailSender, provided by
+ Joseph Annino.
+
+- Log::Dispatch::Output now contains "use Log::Dispatch".
+
+- Now requires Params::Validate, which is used to validate parameter
+ for constructors and some other methods.
+
+- Add an 'autoflush' option to Log::Dispatch::File objects. Suggested
+ by Jerrad Pierce.
+
+- Added some error checking to ::Email::MailSend.
+
+- Changed a carp to a warn in ::Email::MailSendmail.
+
+- Only warn if $^W is true.
+
+
+2.00 2002-04-11
+
+** BACKWARDS INCOMPATIBILITY ALERT **
+
+- Use a standard hash reference for objects instead of pseudo-hashes.
+ ** THIS MAY BREAK EXISTING SUBCLASSES **.
+
+- Log::Dispatch::Screen claimed it defaulted to using STDERR but it
+ was lying, it defaulted to using STDOUT. This has been changed so
+ that it really does default to STDERR. Reported by James
+ FitzGibbon.
+
+
+1.80 2001-10-27
+
+- Log::Dispatch::Syslog no longer requires syslog.ph for Perl >=
+ 5.006. Patch by Benoit Beausejour.
+
+- If you passed a mode parameter to Log::Dispatch::File it always
+ thought the mode was append, no matter what was passed. Patch from
+ Luke Bakken.
+
+- Log::Dispatch::File no longer uses IO::File internally.
+
+
+1.79 2001-05-15
+
+- Don't use $, internally. Apparently this is usually undefined.
+ Instead, the convenience methods now simply take an array of
+ messages and turn it into a scalar by doing "@_". Thanks to Dean
+ Kopesky for the bug report.
+
+
+1.78 2001-04-19
+
+- Allow ApacheLog to take either an Apache or Apache::Server object.
+
+- Fix callback documentation in Log::Dispatch::Output. Thanks to Rob
+ Napier.
+
+- Add flush method to Log::Dispatch::Email. Patch submitted by Rob
+ Napier.
+
+
+1.77 2001-01-02
+
+- The convenience methods named after the log levels ($log->debug,
+ $log->alert, etc.) can now take a list of scalars. These are joined
+ together just like Perl's print function does. Suggested by Tim
+ Ayers.
+
+
+1.76 2000-10-10
+
+- New method: Log::Dispatch->level_is_valid($string). Suggested by
+ Jeff Hamman.
+
+- Fix for version issues between CPAN versions of
+ Log::Dispatch::ApacheLog. Reported by Jost Krieger.
+
+
+1.75 2000-09-28
+
+- Additional argument 'level' passed to message processing callbacks.
+ Suggested by Jeff MacDonald.
+
+- Log/Dispatch.pm: added docs section on Log::Dispatch::Tk.
+
+
+1.7 2000-08-30
+
+- Added Log/Dispatch/ApacheLog.pm. This logs to the Apache error log.
+ This is for use under mod_perl.
+
+
+1.6 2000-07-04
+
+NOTE: 1.5 was never released to CPAN.
+
+- Log/Dispatch.pm: Added convenience methods for log levels like
+ $dispatcher->alert($message). Suggested by Dominique Dumont.
+
+- This version introduces some changes into the interface that will
+ cause incompatibility with any Log::Dispatch::Output interface you may
+ have created. However, it is fairly simple to fix. Simply change the
+ method in your subclass named 'log' to be called 'log_message'. You
+ can also remove the line:
+
+ return unless $self->_should_log($params{level});
+
+ This is now done before the message ever gets to the Output subclass
+ (which is what it should have done in the first place, really.)
+
+ This was done so that I could allow per-Output object callbacks, a
+ feature which several people have requested and which seems useful
+ enough to warrant the breakage.
+
+ NOTE: This change is transparent if you are only using the Output
+ objects included with this distribution.
+
+- Many: Changed the interface to allow per-Output object level
+ callbacks and documented this.
+
+- Log/Dispatch/Base.pm: new base class for both Log::Dispatch and
+ Log::Dispatch::Output objects (contains callback related code). You
+ should never need to deal with this class unless you are me.
+
+- Log/Dispatch/Output.pm: document _accepted_levels.
+
+- Log/Dispatch/Output.pm: Fix _accepted_levels so that emergency level
+ is returned as 'emergency', not 'emerg'.
+
+- Log/Dispatch.pm: Fix doc bug (change 'file' to 'filename'). Thanks
+ to Clayton Scott.
+
+- Log/Dispatch/File.pm: Do compile time check for O_APPEND constant
+ rather than run time check.
+
+
+1.2 2000-05-05
+
+- Log/Dispatch.pm: Added callbacks parameter to Log::Dispatch->new. I
+ will also be adding this to the Log::Dispatch::* classes via
+ Log::Dispatch::Output but I wanted to get this new version out there
+ because I think there are people out there who would benefit from
+ this.
+
+- Log/Dispatch.pm: Added docs section on why Log::Dispatch doesn't add
+ newlines to outgoing messages.
+
+
+1.11 2000-02-24
+
+- Realized I need to tweak the $VERSION in Log::Dispatch
+
+
+1.1 2000-02-24
+
+- Upped version to 1.1 to indicate my confidence in this release (I'm
+ just asking for bug reports, I know).
+
+- Added accepted_levels method to Log::Dispatch::Output based on
+ discussions with Dominique Dumont (author of the Log::Dispatch::Tk
+ suite).
+
+- Canonical names for log levels are now the unabbreviated form
+ (though the abbreviated ones used by syslog are still fine and there
+ is no plan to deprecate them). This really only affects what is
+ returned by the new accepted_levels method.
+
+
+1.010 2000-01-17
+
+- Fixed a bug in the DESTROY method of Log::Dispatch::Email that
+ caused an annoying error and may have caused spurious emails to be
+ sent (thanks to Radu Greab).
+
+- Fixed a bug in Log::Dispatch::Email::MailSendmail. For some reason
+ this module demands a 'from' address.
+
+
+1.009 2000-01-02
+
+- Created this version simply to address an issue with CPAN and my
+ internal version numbers having a conflict. This has no changes
+ from 1.008.
+
+
+1.008 1999-12-30
+
+- Fixed a bug causing unitialized value warnings with -w (oops).
+
+- Fixed a minor mistake in Log::Dispatch::Syslog docs (thanks to Ilya
+ Martynov)
+
+- Put newlines into messages in SYNOPSIS sections for some modules.
+ This is to clarify that you need to do this. Just to be clear,
+ Log::Dispatch does not alter message content in any manner
+ whatsoever (and never will). However, it would be trivial to
+ subclass Log::Dispatch to do this.
+
+
+1.007 1999-12-01
+
+- First public release. It passes its own test suite so it should
+ work (he says hopefully).
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..ebd228a
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,43 @@
+This is the Perl distribution Log-Dispatch.
+
+Installing Log-Dispatch is straightforward.
+
+## Installation with cpanm
+
+If you have cpanm, you only need one line:
+
+ % cpanm Log::Dispatch
+
+If you are installing into a system-wide directory, you may need to pass the
+"-S" flag to cpanm, which uses sudo to install the module:
+
+ % cpanm -S Log::Dispatch
+
+## Installing with the CPAN shell
+
+Alternatively, if your CPAN shell is set up, you should just be able to do:
+
+ % cpan Log::Dispatch
+
+## Manual installation
+
+As a last resort, you can manually install it. Download the tarball, untar it,
+then build it:
+
+ % perl Makefile.PL
+ % make && make test
+
+Then install it:
+
+ % make install
+
+If you are installing into a system-wide directory, you may need to run:
+
+ % sudo make install
+
+## Documentation
+
+Log-Dispatch documentation is available as POD.
+You can run perldoc from a shell to read the documentation:
+
+ % perldoc Log::Dispatch
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e503ef0
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,207 @@
+This software is Copyright (c) 2015 by Dave Rolsky.
+
+This is free software, licensed under:
+
+ The Artistic License 2.0 (GPL Compatible)
+
+ The Artistic License 2.0
+
+ Copyright (c) 2000-2006, The Perl Foundation.
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+Preamble
+
+This license establishes the terms under which a given free software
+Package may be copied, modified, distributed, and/or redistributed.
+The intent is that the Copyright Holder maintains some artistic
+control over the development of that Package while still keeping the
+Package available as open source and free software.
+
+You are always permitted to make arrangements wholly outside of this
+license directly with the Copyright Holder of a given Package. If the
+terms of this license do not permit the full use that you propose to
+make of the Package, you should contact the Copyright Holder and seek
+a different licensing arrangement.
+
+Definitions
+
+ "Copyright Holder" means the individual(s) or organization(s)
+ named in the copyright notice for the entire Package.
+
+ "Contributor" means any party that has contributed code or other
+ material to the Package, in accordance with the Copyright Holder's
+ procedures.
+
+ "You" and "your" means any person who would like to copy,
+ distribute, or modify the Package.
+
+ "Package" means the collection of files distributed by the
+ Copyright Holder, and derivatives of that collection and/or of
+ those files. A given Package may consist of either the Standard
+ Version, or a Modified Version.
+
+ "Distribute" means providing a copy of the Package or making it
+ accessible to anyone else, or in the case of a company or
+ organization, to others outside of your company or organization.
+
+ "Distributor Fee" means any fee that you charge for Distributing
+ this Package or providing support for this Package to another
+ party. It does not mean licensing fees.
+
+ "Standard Version" refers to the Package if it has not been
+ modified, or has been modified only in ways explicitly requested
+ by the Copyright Holder.
+
+ "Modified Version" means the Package, if it has been changed, and
+ such changes were not explicitly requested by the Copyright
+ Holder.
+
+ "Original License" means this Artistic License as Distributed with
+ the Standard Version of the Package, in its current version or as
+ it may be modified by The Perl Foundation in the future.
+
+ "Source" form means the source code, documentation source, and
+ configuration files for the Package.
+
+ "Compiled" form means the compiled bytecode, object code, binary,
+ or any other form resulting from mechanical transformation or
+ translation of the Source form.
+
+
+Permission for Use and Modification Without Distribution
+
+(1) You are permitted to use the Standard Version and create and use
+Modified Versions for any purpose without restriction, provided that
+you do not Distribute the Modified Version.
+
+
+Permissions for Redistribution of the Standard Version
+
+(2) You may Distribute verbatim copies of the Source form of the
+Standard Version of this Package in any medium without restriction,
+either gratis or for a Distributor Fee, provided that you duplicate
+all of the original copyright notices and associated disclaimers. At
+your discretion, such verbatim copies may or may not include a
+Compiled form of the Package.
+
+(3) You may apply any bug fixes, portability changes, and other
+modifications made available from the Copyright Holder. The resulting
+Package will still be considered the Standard Version, and as such
+will be subject to the Original License.
+
+
+Distribution of Modified Versions of the Package as Source
+
+(4) You may Distribute your Modified Version as Source (either gratis
+or for a Distributor Fee, and with or without a Compiled form of the
+Modified Version) provided that you clearly document how it differs
+from the Standard Version, including, but not limited to, documenting
+any non-standard features, executables, or modules, and provided that
+you do at least ONE of the following:
+
+ (a) make the Modified Version available to the Copyright Holder
+ of the Standard Version, under the Original License, so that the
+ Copyright Holder may include your modifications in the Standard
+ Version.
+
+ (b) ensure that installation of your Modified Version does not
+ prevent the user installing or running the Standard Version. In
+ addition, the Modified Version must bear a name that is different
+ from the name of the Standard Version.
+
+ (c) allow anyone who receives a copy of the Modified Version to
+ make the Source form of the Modified Version available to others
+ under
+
+ (i) the Original License or
+
+ (ii) a license that permits the licensee to freely copy,
+ modify and redistribute the Modified Version using the same
+ licensing terms that apply to the copy that the licensee
+ received, and requires that the Source form of the Modified
+ Version, and of any works derived from it, be made freely
+ available in that license fees are prohibited but Distributor
+ Fees are allowed.
+
+
+Distribution of Compiled Forms of the Standard Version
+or Modified Versions without the Source
+
+(5) You may Distribute Compiled forms of the Standard Version without
+the Source, provided that you include complete instructions on how to
+get the Source of the Standard Version. Such instructions must be
+valid at the time of your distribution. If these instructions, at any
+time while you are carrying out such distribution, become invalid, you
+must provide new instructions on demand or cease further distribution.
+If you provide valid instructions or cease distribution within thirty
+days after you become aware that the instructions are invalid, then
+you do not forfeit any of your rights under this license.
+
+(6) You may Distribute a Modified Version in Compiled form without
+the Source, provided that you comply with Section 4 with respect to
+the Source of the Modified Version.
+
+
+Aggregating or Linking the Package
+
+(7) You may aggregate the Package (either the Standard Version or
+Modified Version) with other packages and Distribute the resulting
+aggregation provided that you do not charge a licensing fee for the
+Package. Distributor Fees are permitted, and licensing fees for other
+components in the aggregation are permitted. The terms of this license
+apply to the use and Distribution of the Standard or Modified Versions
+as included in the aggregation.
+
+(8) You are permitted to link Modified and Standard Versions with
+other works, to embed the Package in a larger work of your own, or to
+build stand-alone binary or bytecode versions of applications that
+include the Package, and Distribute the result without restriction,
+provided the result does not expose a direct interface to the Package.
+
+
+Items That are Not Considered Part of a Modified Version
+
+(9) Works (including, but not limited to, modules and scripts) that
+merely extend or make use of the Package, do not, by themselves, cause
+the Package to be a Modified Version. In addition, such works are not
+considered parts of the Package itself, and are not subject to the
+terms of this license.
+
+
+General Provisions
+
+(10) Any use, modification, and distribution of the Standard or
+Modified Versions is governed by this Artistic License. By using,
+modifying or distributing the Package, you accept this license. Do not
+use, modify, or distribute the Package, if you do not accept this
+license.
+
+(11) If your Modified Version has been derived from a Modified
+Version made by someone other than you, you are nevertheless required
+to ensure that your Modified Version complies with the requirements of
+this license.
+
+(12) This license does not grant you the right to use any trademark,
+service mark, tradename, or logo of the Copyright Holder.
+
+(13) This license includes the non-exclusive, worldwide,
+free-of-charge patent license to make, have made, use, offer to sell,
+sell, import and otherwise transfer the Package with respect to any
+patent claims licensable by the Copyright Holder that are necessarily
+infringed by the Package. If you institute patent litigation
+(including a cross-claim or counterclaim) against any party alleging
+that the Package constitutes direct or contributory patent
+infringement, then this Artistic License to you shall terminate on the
+date that such litigation is filed.
+
+(14) Disclaimer of Warranty:
+THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
+LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..a93c577
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,54 @@
+# This file was automatically generated by Dist::Zilla::Plugin::Manifest v5.037.
+Changes
+INSTALL
+LICENSE
+MANIFEST
+META.json
+META.yml
+Makefile.PL
+README.md
+cpanfile
+dist.ini
+lib/Log/Dispatch.pm
+lib/Log/Dispatch/ApacheLog.pm
+lib/Log/Dispatch/Base.pm
+lib/Log/Dispatch/Code.pm
+lib/Log/Dispatch/Conflicts.pm
+lib/Log/Dispatch/Email.pm
+lib/Log/Dispatch/Email/MIMELite.pm
+lib/Log/Dispatch/Email/MailSend.pm
+lib/Log/Dispatch/Email/MailSender.pm
+lib/Log/Dispatch/Email/MailSendmail.pm
+lib/Log/Dispatch/File.pm
+lib/Log/Dispatch/File/Locked.pm
+lib/Log/Dispatch/Handle.pm
+lib/Log/Dispatch/Null.pm
+lib/Log/Dispatch/Output.pm
+lib/Log/Dispatch/Screen.pm
+lib/Log/Dispatch/Syslog.pm
+perlcriticrc
+perltidyrc
+t/00-compile.t
+t/00-report-prereqs.dd
+t/00-report-prereqs.t
+t/01-basic.t
+t/02-email-exit.t
+t/03-short-syntax.t
+t/04-binmode.t
+t/05-close-after-write.t
+t/06-syslog.t
+t/author-eol.t
+t/author-no-tabs.t
+t/author-pod-spell.t
+t/email-exit-helper.pl
+t/lib/Log/Dispatch/TestUtil.pm
+t/release-cpan-changes.t
+t/release-pod-coverage.t
+t/release-pod-no404s.t
+t/release-pod-syntax.t
+t/release-portability.t
+t/release-test-version.t
+t/release-tidyall.t
+t/sendmail
+tidyall.ini
+weaver.ini
diff --git a/META.json b/META.json
new file mode 100644
index 0000000..297e6cd
--- /dev/null
+++ b/META.json
@@ -0,0 +1,801 @@
+{
+ "abstract" : "Dispatches messages to one or more outputs",
+ "author" : [
+ "Dave Rolsky <autarch@urth.org>"
+ ],
+ "dynamic_config" : 0,
+ "generated_by" : "Dist::Zilla version 5.037, CPAN::Meta::Converter version 2.150005",
+ "license" : [
+ "artistic_2"
+ ],
+ "meta-spec" : {
+ "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
+ "version" : 2
+ },
+ "name" : "Log-Dispatch",
+ "prereqs" : {
+ "configure" : {
+ "requires" : {
+ "Dist::CheckConflicts" : "0.02",
+ "ExtUtils::MakeMaker" : "0"
+ }
+ },
+ "develop" : {
+ "requires" : {
+ "Code::TidyAll" : "0.24",
+ "Perl::Critic" : "1.123",
+ "Perl::Tidy" : "20140711",
+ "Pod::Coverage::TrustPod" : "0",
+ "Test::CPAN::Changes" : "0.19",
+ "Test::Code::TidyAll" : "0.24",
+ "Test::EOL" : "0",
+ "Test::More" : "0.88",
+ "Test::NoTabs" : "0",
+ "Test::Pod" : "1.41",
+ "Test::Pod::Coverage" : "1.08",
+ "Test::Spelling" : "0.12",
+ "Test::Version" : "1"
+ }
+ },
+ "runtime" : {
+ "requires" : {
+ "Carp" : "0",
+ "Devel::GlobalDestruction" : "0",
+ "Dist::CheckConflicts" : "0.02",
+ "Fcntl" : "0",
+ "Module::Runtime" : "0",
+ "Params::Validate" : "0.15",
+ "Scalar::Util" : "0",
+ "Sys::Syslog" : "0.28",
+ "base" : "0",
+ "perl" : "5.006",
+ "strict" : "0",
+ "warnings" : "0"
+ }
+ },
+ "test" : {
+ "recommends" : {
+ "CPAN::Meta" : "2.120900"
+ },
+ "requires" : {
+ "Data::Dumper" : "0",
+ "Exporter" : "0",
+ "ExtUtils::MakeMaker" : "0",
+ "File::Spec" : "0",
+ "File::Temp" : "0",
+ "IO::File" : "0",
+ "Test::Fatal" : "0",
+ "Test::More" : "0.96",
+ "Test::Requires" : "0",
+ "lib" : "0"
+ }
+ }
+ },
+ "provides" : {
+ "Log::Dispatch" : {
+ "file" : "lib/Log/Dispatch.pm",
+ "version" : "2.45"
+ },
+ "Log::Dispatch::ApacheLog" : {
+ "file" : "lib/Log/Dispatch/ApacheLog.pm",
+ "version" : "2.45"
+ },
+ "Log::Dispatch::Base" : {
+ "file" : "lib/Log/Dispatch/Base.pm",
+ "version" : "2.45"
+ },
+ "Log::Dispatch::Code" : {
+ "file" : "lib/Log/Dispatch/Code.pm",
+ "version" : "2.45"
+ },
+ "Log::Dispatch::Email" : {
+ "file" : "lib/Log/Dispatch/Email.pm",
+ "version" : "2.45"
+ },
+ "Log::Dispatch::Email::MIMELite" : {
+ "file" : "lib/Log/Dispatch/Email/MIMELite.pm",
+ "version" : "2.45"
+ },
+ "Log::Dispatch::Email::MailSend" : {
+ "file" : "lib/Log/Dispatch/Email/MailSend.pm",
+ "version" : "2.45"
+ },
+ "Log::Dispatch::Email::MailSender" : {
+ "file" : "lib/Log/Dispatch/Email/MailSender.pm",
+ "version" : "2.45"
+ },
+ "Log::Dispatch::Email::MailSendmail" : {
+ "file" : "lib/Log/Dispatch/Email/MailSendmail.pm",
+ "version" : "2.45"
+ },
+ "Log::Dispatch::File" : {
+ "file" : "lib/Log/Dispatch/File.pm",
+ "version" : "2.45"
+ },
+ "Log::Dispatch::File::Locked" : {
+ "file" : "lib/Log/Dispatch/File/Locked.pm",
+ "version" : "2.45"
+ },
+ "Log::Dispatch::Handle" : {
+ "file" : "lib/Log/Dispatch/Handle.pm",
+ "version" : "2.45"
+ },
+ "Log::Dispatch::Null" : {
+ "file" : "lib/Log/Dispatch/Null.pm",
+ "version" : "2.45"
+ },
+ "Log::Dispatch::Output" : {
+ "file" : "lib/Log/Dispatch/Output.pm",
+ "version" : "2.45"
+ },
+ "Log::Dispatch::Screen" : {
+ "file" : "lib/Log/Dispatch/Screen.pm",
+ "version" : "2.45"
+ },
+ "Log::Dispatch::Syslog" : {
+ "file" : "lib/Log/Dispatch/Syslog.pm",
+ "version" : "2.45"
+ }
+ },
+ "release_status" : "stable",
+ "resources" : {
+ "bugtracker" : {
+ "mailto" : "bug-log-dispatch@rt.cpan.org",
+ "web" : "http://rt.cpan.org/Public/Dist/Display.html?Name=Log-Dispatch"
+ },
+ "homepage" : "http://metacpan.org/release/Log-Dispatch",
+ "repository" : {
+ "type" : "git",
+ "url" : "git://github.com/autarch/Log-Dispatch.git",
+ "web" : "https://github.com/autarch/Log-Dispatch"
+ }
+ },
+ "version" : "2.45",
+ "x_Dist_Zilla" : {
+ "perl" : {
+ "version" : "5.020001"
+ },
+ "plugins" : [
+ {
+ "class" : "Dist::Zilla::Plugin::MakeMaker",
+ "config" : {
+ "Dist::Zilla::Role::TestRunner" : {
+ "default_jobs" : 1
+ }
+ },
+ "name" : "@DROLSKY/MakeMaker",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Authority",
+ "name" : "@DROLSKY/Authority",
+ "version" : "1.009"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::AutoPrereqs",
+ "name" : "@DROLSKY/AutoPrereqs",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::CopyFilesFromBuild",
+ "name" : "@DROLSKY/CopyFilesFromBuild",
+ "version" : "0.150250"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Git::GatherDir",
+ "config" : {
+ "Dist::Zilla::Plugin::GatherDir" : {
+ "exclude_filename" : [
+ "LICENSE",
+ "cpanfile",
+ "README.md",
+ "Makefile.PL",
+ "Build.PL"
+ ],
+ "exclude_match" : [],
+ "follow_symlinks" : "0",
+ "include_dotfiles" : "0",
+ "prefix" : "",
+ "prune_directory" : [],
+ "root" : "."
+ },
+ "Dist::Zilla::Plugin::Git::GatherDir" : {
+ "include_untracked" : "0"
+ }
+ },
+ "name" : "@DROLSKY/Git::GatherDir",
+ "version" : "2.035"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::GitHub::Meta",
+ "name" : "@DROLSKY/GitHub::Meta",
+ "version" : "0.40"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::GitHub::Update",
+ "name" : "@DROLSKY/GitHub::Update",
+ "version" : "0.40"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::MetaResources",
+ "name" : "@DROLSKY/MetaResources",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::MetaProvides::Package",
+ "config" : {
+ "Dist::Zilla::Plugin::MetaProvides::Package" : {
+ "finder_objects" : [
+ {
+ "class" : "Dist::Zilla::Plugin::FinderCode",
+ "name" : "@DROLSKY/MetaProvides::Package/AUTOVIV/:InstallModulesPM",
+ "version" : "5.037"
+ }
+ ]
+ },
+ "Dist::Zilla::Role::MetaProvider::Provider" : {
+ "inherit_missing" : "1",
+ "inherit_version" : "1",
+ "meta_noindex" : "1"
+ }
+ },
+ "name" : "@DROLSKY/MetaProvides::Package",
+ "version" : "2.003001"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::NextRelease",
+ "name" : "@DROLSKY/NextRelease",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Prereqs",
+ "config" : {
+ "Dist::Zilla::Plugin::Prereqs" : {
+ "phase" : "test",
+ "type" : "requires"
+ }
+ },
+ "name" : "@DROLSKY/Test::More with subtest()",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Prereqs",
+ "config" : {
+ "Dist::Zilla::Plugin::Prereqs" : {
+ "phase" : "develop",
+ "type" : "requires"
+ }
+ },
+ "name" : "@DROLSKY/Modules for use with tidyall",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::PromptIfStale",
+ "config" : {
+ "Dist::Zilla::Plugin::PromptIfStale" : {
+ "check_all_plugins" : "1",
+ "check_all_prereqs" : "1",
+ "modules" : [],
+ "phase" : "release",
+ "skip" : [
+ "Dist::Zilla::Plugin::DROLSKY::Contributors",
+ "Dist::Zilla::Plugin::DROLSKY::License",
+ "Dist::Zilla::Plugin::DROLSKY::TidyAll"
+ ]
+ }
+ },
+ "name" : "@DROLSKY/PromptIfStale",
+ "version" : "0.044"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::ReadmeAnyFromPod",
+ "name" : "@DROLSKY/README.md in build",
+ "version" : "0.150250"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::ReadmeAnyFromPod",
+ "name" : "@DROLSKY/README.md in root",
+ "version" : "0.150250"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Test::Pod::Coverage::Configurable",
+ "name" : "@DROLSKY/Test::Pod::Coverage::Configurable",
+ "version" : "0.05"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Test::PodSpelling",
+ "name" : "@DROLSKY/Test::PodSpelling",
+ "version" : "2.006009"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Test::ReportPrereqs",
+ "name" : "@DROLSKY/Test::ReportPrereqs",
+ "version" : "0.021"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::ManifestSkip",
+ "name" : "@DROLSKY/ManifestSkip",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::MetaYAML",
+ "name" : "@DROLSKY/MetaYAML",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::License",
+ "name" : "@DROLSKY/License",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::ExtraTests",
+ "name" : "@DROLSKY/ExtraTests",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::ExecDir",
+ "name" : "@DROLSKY/ExecDir",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::ShareDir",
+ "name" : "@DROLSKY/ShareDir",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Manifest",
+ "name" : "@DROLSKY/Manifest",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::CheckVersionIncrement",
+ "name" : "@DROLSKY/CheckVersionIncrement",
+ "version" : "0.121750"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::TestRelease",
+ "name" : "@DROLSKY/TestRelease",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::ConfirmRelease",
+ "name" : "@DROLSKY/ConfirmRelease",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::UploadToCPAN",
+ "name" : "@DROLSKY/UploadToCPAN",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::CheckPrereqsIndexed",
+ "name" : "@DROLSKY/CheckPrereqsIndexed",
+ "version" : "0.016"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::CPANFile",
+ "name" : "@DROLSKY/CPANFile",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::DROLSKY::Contributors",
+ "name" : "@DROLSKY/DROLSKY::Contributors",
+ "version" : "0.34"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::DROLSKY::License",
+ "name" : "@DROLSKY/DROLSKY::License",
+ "version" : "0.34"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::DROLSKY::TidyAll",
+ "name" : "@DROLSKY/DROLSKY::TidyAll",
+ "version" : "0.34"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch",
+ "config" : {
+ "Dist::Zilla::Role::Git::Repo" : {
+ "repo_root" : "."
+ }
+ },
+ "name" : "@DROLSKY/Git::CheckFor::CorrectBranch",
+ "version" : "0.013"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts",
+ "config" : {
+ "Dist::Zilla::Role::Git::Repo" : {
+ "repo_root" : "."
+ }
+ },
+ "name" : "@DROLSKY/Git::CheckFor::MergeConflicts",
+ "version" : "0.013"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Git::Contributors",
+ "config" : {
+ "Dist::Zilla::Plugin::Git::Contributors" : {
+ "include_authors" : "0",
+ "include_releaser" : "1",
+ "order_by" : "name",
+ "paths" : []
+ }
+ },
+ "name" : "@DROLSKY/Git::Contributors",
+ "version" : "0.011"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::InstallGuide",
+ "name" : "@DROLSKY/InstallGuide",
+ "version" : "1.200006"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Meta::Contributors",
+ "name" : "@DROLSKY/Meta::Contributors",
+ "version" : "0.002"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::MetaConfig",
+ "name" : "@DROLSKY/MetaConfig",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::MetaJSON",
+ "name" : "@DROLSKY/MetaJSON",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::RewriteVersion",
+ "name" : "@DROLSKY/RewriteVersion",
+ "version" : "0.009"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::SurgicalPodWeaver",
+ "config" : {
+ "Dist::Zilla::Plugin::PodWeaver" : {
+ "finder" : [
+ ":InstallModules",
+ ":ExecFiles"
+ ],
+ "plugins" : [
+ {
+ "class" : "Pod::Weaver::Plugin::EnsurePod5",
+ "name" : "@CorePrep/EnsurePod5",
+ "version" : "4.010"
+ },
+ {
+ "class" : "Pod::Weaver::Plugin::H1Nester",
+ "name" : "@CorePrep/H1Nester",
+ "version" : "4.010"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Name",
+ "name" : "Name",
+ "version" : "4.010"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Version",
+ "name" : "Version",
+ "version" : "4.010"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Region",
+ "name" : "prelude",
+ "version" : "4.010"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Generic",
+ "name" : "SYNOPSIS",
+ "version" : "4.010"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Generic",
+ "name" : "DESCRIPTION",
+ "version" : "4.010"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Leftovers",
+ "name" : "Leftovers",
+ "version" : "4.010"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Region",
+ "name" : "postlude",
+ "version" : "4.010"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Authors",
+ "name" : "Authors",
+ "version" : "4.010"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Contributors",
+ "name" : "Contributors",
+ "version" : "0.009"
+ },
+ {
+ "class" : "Pod::Weaver::Section::Legal",
+ "name" : "Legal",
+ "version" : "4.010"
+ }
+ ]
+ }
+ },
+ "name" : "@DROLSKY/SurgicalPodWeaver",
+ "version" : "0.0023"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::PodSyntaxTests",
+ "name" : "@DROLSKY/PodSyntaxTests",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Test::CPAN::Changes",
+ "name" : "@DROLSKY/Test::CPAN::Changes",
+ "version" : "0.009"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Test::EOL",
+ "config" : {
+ "Dist::Zilla::Plugin::Test::EOL" : {
+ "filename" : "xt/author/eol.t",
+ "finder" : [
+ ":InstallModules",
+ ":ExecFiles",
+ ":TestFiles"
+ ],
+ "trailing_whitespace" : "1"
+ }
+ },
+ "name" : "@DROLSKY/Test::EOL",
+ "version" : "0.18"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Test::NoTabs",
+ "config" : {
+ "Dist::Zilla::Plugin::Test::NoTabs" : {
+ "filename" : "xt/author/no-tabs.t",
+ "finder" : [
+ ":InstallModules",
+ ":ExecFiles",
+ ":TestFiles"
+ ]
+ }
+ },
+ "name" : "@DROLSKY/Test::NoTabs",
+ "version" : "0.15"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Test::Pod::No404s",
+ "name" : "@DROLSKY/Test::Pod::No404s",
+ "version" : "1.001"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Test::Portability",
+ "name" : "@DROLSKY/Test::Portability",
+ "version" : "2.000006"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Test::TidyAll",
+ "name" : "@DROLSKY/Test::TidyAll",
+ "version" : "0.01"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Git::Check",
+ "config" : {
+ "Dist::Zilla::Plugin::Git::Check" : {
+ "untracked_files" : "die"
+ },
+ "Dist::Zilla::Role::Git::DirtyFiles" : {
+ "allow_dirty" : [
+ "LICENSE",
+ "cpanfile",
+ "README.md",
+ "Makefile.PL",
+ "Build.PL",
+ "Changes",
+ "CONTRIBUTING.md"
+ ],
+ "allow_dirty_match" : [],
+ "changelog" : "Changes"
+ },
+ "Dist::Zilla::Role::Git::Repo" : {
+ "repo_root" : "."
+ }
+ },
+ "name" : "@DROLSKY/Git::Check",
+ "version" : "2.035"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Git::Commit",
+ "config" : {
+ "Dist::Zilla::Plugin::Git::Commit" : {
+ "add_files_in" : [],
+ "commit_msg" : "v%v%n%n%c",
+ "time_zone" : "local"
+ },
+ "Dist::Zilla::Role::Git::DirtyFiles" : {
+ "allow_dirty" : [
+ "LICENSE",
+ "cpanfile",
+ "README.md",
+ "Makefile.PL",
+ "Build.PL",
+ "Changes",
+ "CONTRIBUTING.md"
+ ],
+ "allow_dirty_match" : [],
+ "changelog" : "Changes"
+ },
+ "Dist::Zilla::Role::Git::Repo" : {
+ "repo_root" : "."
+ }
+ },
+ "name" : "@DROLSKY/commit generated files",
+ "version" : "2.035"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Git::Tag",
+ "config" : {
+ "Dist::Zilla::Plugin::Git::Tag" : {
+ "branch" : null,
+ "signed" : 0,
+ "tag" : "v2.45",
+ "tag_format" : "v%v",
+ "tag_message" : "v%v",
+ "time_zone" : "local"
+ },
+ "Dist::Zilla::Role::Git::Repo" : {
+ "repo_root" : "."
+ }
+ },
+ "name" : "@DROLSKY/Git::Tag",
+ "version" : "2.035"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Git::Push",
+ "config" : {
+ "Dist::Zilla::Plugin::Git::Push" : {
+ "push_to" : [
+ "origin"
+ ],
+ "remotes_must_exist" : 1
+ },
+ "Dist::Zilla::Role::Git::Repo" : {
+ "repo_root" : "."
+ }
+ },
+ "name" : "@DROLSKY/Git::Push",
+ "version" : "2.035"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::BumpVersionAfterRelease",
+ "name" : "@DROLSKY/BumpVersionAfterRelease",
+ "version" : "0.009"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Git::Commit",
+ "config" : {
+ "Dist::Zilla::Plugin::Git::Commit" : {
+ "add_files_in" : [],
+ "commit_msg" : "Bump version after release",
+ "time_zone" : "local"
+ },
+ "Dist::Zilla::Role::Git::DirtyFiles" : {
+ "allow_dirty" : [
+ "dist.ini",
+ "Changes"
+ ],
+ "allow_dirty_match" : [
+ "(?^:.+)"
+ ],
+ "changelog" : "Changes"
+ },
+ "Dist::Zilla::Role::Git::Repo" : {
+ "repo_root" : "."
+ }
+ },
+ "name" : "@DROLSKY/commit version bump",
+ "version" : "2.035"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Git::Push",
+ "config" : {
+ "Dist::Zilla::Plugin::Git::Push" : {
+ "push_to" : [
+ "origin"
+ ],
+ "remotes_must_exist" : 1
+ },
+ "Dist::Zilla::Role::Git::Repo" : {
+ "repo_root" : "."
+ }
+ },
+ "name" : "@DROLSKY/push version bump",
+ "version" : "2.035"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::FileFinder::ByName",
+ "name" : "MostLibs",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Test::Version",
+ "name" : "Test::Version",
+ "version" : "1.03"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Conflicts",
+ "name" : "Conflicts",
+ "version" : "0.17"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::FinderCode",
+ "name" : ":InstallModules",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::FinderCode",
+ "name" : ":IncModules",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::FinderCode",
+ "name" : ":TestFiles",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::FinderCode",
+ "name" : ":ExecFiles",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::FinderCode",
+ "name" : ":ShareFiles",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::FinderCode",
+ "name" : ":MainModule",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::FinderCode",
+ "name" : ":AllFiles",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::FinderCode",
+ "name" : ":NoFiles",
+ "version" : "5.037"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::FinderCode",
+ "name" : "@DROLSKY/MetaProvides::Package/AUTOVIV/:InstallModulesPM",
+ "version" : "5.037"
+ }
+ ],
+ "zilla" : {
+ "class" : "Dist::Zilla::Dist::Builder",
+ "config" : {
+ "is_trial" : "0"
+ },
+ "version" : "5.037"
+ }
+ },
+ "x_authority" : "cpan:DROLSKY",
+ "x_breaks" : {
+ "Log::Dispatch::File::Stamped" : "<= 0.10"
+ },
+ "x_contributors" : [
+ "Karen Etheridge <ether@cpan.org>",
+ "Olaf Alders <olaf@wundersolutions.com>",
+ "Olivier Mengué <dolmen@cpan.org>",
+ "Ross Attrill <ross.attrill@gmail.com>",
+ "swartz@jonathan-swartzs-macbook-4.local <swartz@jonathan-swartzs-macbook-4.local>",
+ "swartz@pobox.com <swartz@pobox.com>",
+ "Whitney Jackson <whitney.jackson@baml.com>"
+ ]
+}
+
diff --git a/META.yml b/META.yml
new file mode 100644
index 0000000..647310b
--- /dev/null
+++ b/META.yml
@@ -0,0 +1,589 @@
+---
+abstract: 'Dispatches messages to one or more outputs'
+author:
+ - 'Dave Rolsky <autarch@urth.org>'
+build_requires:
+ Data::Dumper: '0'
+ Exporter: '0'
+ ExtUtils::MakeMaker: '0'
+ File::Spec: '0'
+ File::Temp: '0'
+ IO::File: '0'
+ Test::Fatal: '0'
+ Test::More: '0.96'
+ Test::Requires: '0'
+ lib: '0'
+configure_requires:
+ Dist::CheckConflicts: '0.02'
+ ExtUtils::MakeMaker: '0'
+dynamic_config: 0
+generated_by: 'Dist::Zilla version 5.037, CPAN::Meta::Converter version 2.150005'
+license: artistic_2
+meta-spec:
+ url: http://module-build.sourceforge.net/META-spec-v1.4.html
+ version: '1.4'
+name: Log-Dispatch
+provides:
+ Log::Dispatch:
+ file: lib/Log/Dispatch.pm
+ version: '2.45'
+ Log::Dispatch::ApacheLog:
+ file: lib/Log/Dispatch/ApacheLog.pm
+ version: '2.45'
+ Log::Dispatch::Base:
+ file: lib/Log/Dispatch/Base.pm
+ version: '2.45'
+ Log::Dispatch::Code:
+ file: lib/Log/Dispatch/Code.pm
+ version: '2.45'
+ Log::Dispatch::Email:
+ file: lib/Log/Dispatch/Email.pm
+ version: '2.45'
+ Log::Dispatch::Email::MIMELite:
+ file: lib/Log/Dispatch/Email/MIMELite.pm
+ version: '2.45'
+ Log::Dispatch::Email::MailSend:
+ file: lib/Log/Dispatch/Email/MailSend.pm
+ version: '2.45'
+ Log::Dispatch::Email::MailSender:
+ file: lib/Log/Dispatch/Email/MailSender.pm
+ version: '2.45'
+ Log::Dispatch::Email::MailSendmail:
+ file: lib/Log/Dispatch/Email/MailSendmail.pm
+ version: '2.45'
+ Log::Dispatch::File:
+ file: lib/Log/Dispatch/File.pm
+ version: '2.45'
+ Log::Dispatch::File::Locked:
+ file: lib/Log/Dispatch/File/Locked.pm
+ version: '2.45'
+ Log::Dispatch::Handle:
+ file: lib/Log/Dispatch/Handle.pm
+ version: '2.45'
+ Log::Dispatch::Null:
+ file: lib/Log/Dispatch/Null.pm
+ version: '2.45'
+ Log::Dispatch::Output:
+ file: lib/Log/Dispatch/Output.pm
+ version: '2.45'
+ Log::Dispatch::Screen:
+ file: lib/Log/Dispatch/Screen.pm
+ version: '2.45'
+ Log::Dispatch::Syslog:
+ file: lib/Log/Dispatch/Syslog.pm
+ version: '2.45'
+requires:
+ Carp: '0'
+ Devel::GlobalDestruction: '0'
+ Dist::CheckConflicts: '0.02'
+ Fcntl: '0'
+ Module::Runtime: '0'
+ Params::Validate: '0.15'
+ Scalar::Util: '0'
+ Sys::Syslog: '0.28'
+ base: '0'
+ perl: '5.006'
+ strict: '0'
+ warnings: '0'
+resources:
+ bugtracker: http://rt.cpan.org/Public/Dist/Display.html?Name=Log-Dispatch
+ homepage: http://metacpan.org/release/Log-Dispatch
+ repository: git://github.com/autarch/Log-Dispatch.git
+version: '2.45'
+x_Dist_Zilla:
+ perl:
+ version: '5.020001'
+ plugins:
+ -
+ class: Dist::Zilla::Plugin::MakeMaker
+ config:
+ Dist::Zilla::Role::TestRunner:
+ default_jobs: 1
+ name: '@DROLSKY/MakeMaker'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::Authority
+ name: '@DROLSKY/Authority'
+ version: '1.009'
+ -
+ class: Dist::Zilla::Plugin::AutoPrereqs
+ name: '@DROLSKY/AutoPrereqs'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::CopyFilesFromBuild
+ name: '@DROLSKY/CopyFilesFromBuild'
+ version: '0.150250'
+ -
+ class: Dist::Zilla::Plugin::Git::GatherDir
+ config:
+ Dist::Zilla::Plugin::GatherDir:
+ exclude_filename:
+ - LICENSE
+ - cpanfile
+ - README.md
+ - Makefile.PL
+ - Build.PL
+ exclude_match: []
+ follow_symlinks: '0'
+ include_dotfiles: '0'
+ prefix: ''
+ prune_directory: []
+ root: .
+ Dist::Zilla::Plugin::Git::GatherDir:
+ include_untracked: '0'
+ name: '@DROLSKY/Git::GatherDir'
+ version: '2.035'
+ -
+ class: Dist::Zilla::Plugin::GitHub::Meta
+ name: '@DROLSKY/GitHub::Meta'
+ version: '0.40'
+ -
+ class: Dist::Zilla::Plugin::GitHub::Update
+ name: '@DROLSKY/GitHub::Update'
+ version: '0.40'
+ -
+ class: Dist::Zilla::Plugin::MetaResources
+ name: '@DROLSKY/MetaResources'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::MetaProvides::Package
+ config:
+ Dist::Zilla::Plugin::MetaProvides::Package:
+ finder_objects:
+ -
+ class: Dist::Zilla::Plugin::FinderCode
+ name: '@DROLSKY/MetaProvides::Package/AUTOVIV/:InstallModulesPM'
+ version: '5.037'
+ Dist::Zilla::Role::MetaProvider::Provider:
+ inherit_missing: '1'
+ inherit_version: '1'
+ meta_noindex: '1'
+ name: '@DROLSKY/MetaProvides::Package'
+ version: '2.003001'
+ -
+ class: Dist::Zilla::Plugin::NextRelease
+ name: '@DROLSKY/NextRelease'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::Prereqs
+ config:
+ Dist::Zilla::Plugin::Prereqs:
+ phase: test
+ type: requires
+ name: '@DROLSKY/Test::More with subtest()'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::Prereqs
+ config:
+ Dist::Zilla::Plugin::Prereqs:
+ phase: develop
+ type: requires
+ name: '@DROLSKY/Modules for use with tidyall'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::PromptIfStale
+ config:
+ Dist::Zilla::Plugin::PromptIfStale:
+ check_all_plugins: '1'
+ check_all_prereqs: '1'
+ modules: []
+ phase: release
+ skip:
+ - Dist::Zilla::Plugin::DROLSKY::Contributors
+ - Dist::Zilla::Plugin::DROLSKY::License
+ - Dist::Zilla::Plugin::DROLSKY::TidyAll
+ name: '@DROLSKY/PromptIfStale'
+ version: '0.044'
+ -
+ class: Dist::Zilla::Plugin::ReadmeAnyFromPod
+ name: '@DROLSKY/README.md in build'
+ version: '0.150250'
+ -
+ class: Dist::Zilla::Plugin::ReadmeAnyFromPod
+ name: '@DROLSKY/README.md in root'
+ version: '0.150250'
+ -
+ class: Dist::Zilla::Plugin::Test::Pod::Coverage::Configurable
+ name: '@DROLSKY/Test::Pod::Coverage::Configurable'
+ version: '0.05'
+ -
+ class: Dist::Zilla::Plugin::Test::PodSpelling
+ name: '@DROLSKY/Test::PodSpelling'
+ version: '2.006009'
+ -
+ class: Dist::Zilla::Plugin::Test::ReportPrereqs
+ name: '@DROLSKY/Test::ReportPrereqs'
+ version: '0.021'
+ -
+ class: Dist::Zilla::Plugin::ManifestSkip
+ name: '@DROLSKY/ManifestSkip'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::MetaYAML
+ name: '@DROLSKY/MetaYAML'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::License
+ name: '@DROLSKY/License'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::ExtraTests
+ name: '@DROLSKY/ExtraTests'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::ExecDir
+ name: '@DROLSKY/ExecDir'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::ShareDir
+ name: '@DROLSKY/ShareDir'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::Manifest
+ name: '@DROLSKY/Manifest'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::CheckVersionIncrement
+ name: '@DROLSKY/CheckVersionIncrement'
+ version: '0.121750'
+ -
+ class: Dist::Zilla::Plugin::TestRelease
+ name: '@DROLSKY/TestRelease'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::ConfirmRelease
+ name: '@DROLSKY/ConfirmRelease'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::UploadToCPAN
+ name: '@DROLSKY/UploadToCPAN'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::CheckPrereqsIndexed
+ name: '@DROLSKY/CheckPrereqsIndexed'
+ version: '0.016'
+ -
+ class: Dist::Zilla::Plugin::CPANFile
+ name: '@DROLSKY/CPANFile'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::DROLSKY::Contributors
+ name: '@DROLSKY/DROLSKY::Contributors'
+ version: '0.34'
+ -
+ class: Dist::Zilla::Plugin::DROLSKY::License
+ name: '@DROLSKY/DROLSKY::License'
+ version: '0.34'
+ -
+ class: Dist::Zilla::Plugin::DROLSKY::TidyAll
+ name: '@DROLSKY/DROLSKY::TidyAll'
+ version: '0.34'
+ -
+ class: Dist::Zilla::Plugin::Git::CheckFor::CorrectBranch
+ config:
+ Dist::Zilla::Role::Git::Repo:
+ repo_root: .
+ name: '@DROLSKY/Git::CheckFor::CorrectBranch'
+ version: '0.013'
+ -
+ class: Dist::Zilla::Plugin::Git::CheckFor::MergeConflicts
+ config:
+ Dist::Zilla::Role::Git::Repo:
+ repo_root: .
+ name: '@DROLSKY/Git::CheckFor::MergeConflicts'
+ version: '0.013'
+ -
+ class: Dist::Zilla::Plugin::Git::Contributors
+ config:
+ Dist::Zilla::Plugin::Git::Contributors:
+ include_authors: '0'
+ include_releaser: '1'
+ order_by: name
+ paths: []
+ name: '@DROLSKY/Git::Contributors'
+ version: '0.011'
+ -
+ class: Dist::Zilla::Plugin::InstallGuide
+ name: '@DROLSKY/InstallGuide'
+ version: '1.200006'
+ -
+ class: Dist::Zilla::Plugin::Meta::Contributors
+ name: '@DROLSKY/Meta::Contributors'
+ version: '0.002'
+ -
+ class: Dist::Zilla::Plugin::MetaConfig
+ name: '@DROLSKY/MetaConfig'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::MetaJSON
+ name: '@DROLSKY/MetaJSON'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::RewriteVersion
+ name: '@DROLSKY/RewriteVersion'
+ version: '0.009'
+ -
+ class: Dist::Zilla::Plugin::SurgicalPodWeaver
+ config:
+ Dist::Zilla::Plugin::PodWeaver:
+ finder:
+ - ':InstallModules'
+ - ':ExecFiles'
+ plugins:
+ -
+ class: Pod::Weaver::Plugin::EnsurePod5
+ name: '@CorePrep/EnsurePod5'
+ version: '4.010'
+ -
+ class: Pod::Weaver::Plugin::H1Nester
+ name: '@CorePrep/H1Nester'
+ version: '4.010'
+ -
+ class: Pod::Weaver::Section::Name
+ name: Name
+ version: '4.010'
+ -
+ class: Pod::Weaver::Section::Version
+ name: Version
+ version: '4.010'
+ -
+ class: Pod::Weaver::Section::Region
+ name: prelude
+ version: '4.010'
+ -
+ class: Pod::Weaver::Section::Generic
+ name: SYNOPSIS
+ version: '4.010'
+ -
+ class: Pod::Weaver::Section::Generic
+ name: DESCRIPTION
+ version: '4.010'
+ -
+ class: Pod::Weaver::Section::Leftovers
+ name: Leftovers
+ version: '4.010'
+ -
+ class: Pod::Weaver::Section::Region
+ name: postlude
+ version: '4.010'
+ -
+ class: Pod::Weaver::Section::Authors
+ name: Authors
+ version: '4.010'
+ -
+ class: Pod::Weaver::Section::Contributors
+ name: Contributors
+ version: '0.009'
+ -
+ class: Pod::Weaver::Section::Legal
+ name: Legal
+ version: '4.010'
+ name: '@DROLSKY/SurgicalPodWeaver'
+ version: '0.0023'
+ -
+ class: Dist::Zilla::Plugin::PodSyntaxTests
+ name: '@DROLSKY/PodSyntaxTests'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::Test::CPAN::Changes
+ name: '@DROLSKY/Test::CPAN::Changes'
+ version: '0.009'
+ -
+ class: Dist::Zilla::Plugin::Test::EOL
+ config:
+ Dist::Zilla::Plugin::Test::EOL:
+ filename: xt/author/eol.t
+ finder:
+ - ':InstallModules'
+ - ':ExecFiles'
+ - ':TestFiles'
+ trailing_whitespace: '1'
+ name: '@DROLSKY/Test::EOL'
+ version: '0.18'
+ -
+ class: Dist::Zilla::Plugin::Test::NoTabs
+ config:
+ Dist::Zilla::Plugin::Test::NoTabs:
+ filename: xt/author/no-tabs.t
+ finder:
+ - ':InstallModules'
+ - ':ExecFiles'
+ - ':TestFiles'
+ name: '@DROLSKY/Test::NoTabs'
+ version: '0.15'
+ -
+ class: Dist::Zilla::Plugin::Test::Pod::No404s
+ name: '@DROLSKY/Test::Pod::No404s'
+ version: '1.001'
+ -
+ class: Dist::Zilla::Plugin::Test::Portability
+ name: '@DROLSKY/Test::Portability'
+ version: '2.000006'
+ -
+ class: Dist::Zilla::Plugin::Test::TidyAll
+ name: '@DROLSKY/Test::TidyAll'
+ version: '0.01'
+ -
+ class: Dist::Zilla::Plugin::Git::Check
+ config:
+ Dist::Zilla::Plugin::Git::Check:
+ untracked_files: die
+ Dist::Zilla::Role::Git::DirtyFiles:
+ allow_dirty:
+ - LICENSE
+ - cpanfile
+ - README.md
+ - Makefile.PL
+ - Build.PL
+ - Changes
+ - CONTRIBUTING.md
+ allow_dirty_match: []
+ changelog: Changes
+ Dist::Zilla::Role::Git::Repo:
+ repo_root: .
+ name: '@DROLSKY/Git::Check'
+ version: '2.035'
+ -
+ class: Dist::Zilla::Plugin::Git::Commit
+ config:
+ Dist::Zilla::Plugin::Git::Commit:
+ add_files_in: []
+ commit_msg: v%v%n%n%c
+ time_zone: local
+ Dist::Zilla::Role::Git::DirtyFiles:
+ allow_dirty:
+ - LICENSE
+ - cpanfile
+ - README.md
+ - Makefile.PL
+ - Build.PL
+ - Changes
+ - CONTRIBUTING.md
+ allow_dirty_match: []
+ changelog: Changes
+ Dist::Zilla::Role::Git::Repo:
+ repo_root: .
+ name: '@DROLSKY/commit generated files'
+ version: '2.035'
+ -
+ class: Dist::Zilla::Plugin::Git::Tag
+ config:
+ Dist::Zilla::Plugin::Git::Tag:
+ branch: ~
+ signed: 0
+ tag: v2.45
+ tag_format: v%v
+ tag_message: v%v
+ time_zone: local
+ Dist::Zilla::Role::Git::Repo:
+ repo_root: .
+ name: '@DROLSKY/Git::Tag'
+ version: '2.035'
+ -
+ class: Dist::Zilla::Plugin::Git::Push
+ config:
+ Dist::Zilla::Plugin::Git::Push:
+ push_to:
+ - origin
+ remotes_must_exist: 1
+ Dist::Zilla::Role::Git::Repo:
+ repo_root: .
+ name: '@DROLSKY/Git::Push'
+ version: '2.035'
+ -
+ class: Dist::Zilla::Plugin::BumpVersionAfterRelease
+ name: '@DROLSKY/BumpVersionAfterRelease'
+ version: '0.009'
+ -
+ class: Dist::Zilla::Plugin::Git::Commit
+ config:
+ Dist::Zilla::Plugin::Git::Commit:
+ add_files_in: []
+ commit_msg: 'Bump version after release'
+ time_zone: local
+ Dist::Zilla::Role::Git::DirtyFiles:
+ allow_dirty:
+ - dist.ini
+ - Changes
+ allow_dirty_match:
+ - (?^:.+)
+ changelog: Changes
+ Dist::Zilla::Role::Git::Repo:
+ repo_root: .
+ name: '@DROLSKY/commit version bump'
+ version: '2.035'
+ -
+ class: Dist::Zilla::Plugin::Git::Push
+ config:
+ Dist::Zilla::Plugin::Git::Push:
+ push_to:
+ - origin
+ remotes_must_exist: 1
+ Dist::Zilla::Role::Git::Repo:
+ repo_root: .
+ name: '@DROLSKY/push version bump'
+ version: '2.035'
+ -
+ class: Dist::Zilla::Plugin::FileFinder::ByName
+ name: MostLibs
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::Test::Version
+ name: Test::Version
+ version: '1.03'
+ -
+ class: Dist::Zilla::Plugin::Conflicts
+ name: Conflicts
+ version: '0.17'
+ -
+ class: Dist::Zilla::Plugin::FinderCode
+ name: ':InstallModules'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::FinderCode
+ name: ':IncModules'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::FinderCode
+ name: ':TestFiles'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::FinderCode
+ name: ':ExecFiles'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::FinderCode
+ name: ':ShareFiles'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::FinderCode
+ name: ':MainModule'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::FinderCode
+ name: ':AllFiles'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::FinderCode
+ name: ':NoFiles'
+ version: '5.037'
+ -
+ class: Dist::Zilla::Plugin::FinderCode
+ name: '@DROLSKY/MetaProvides::Package/AUTOVIV/:InstallModulesPM'
+ version: '5.037'
+ zilla:
+ class: Dist::Zilla::Dist::Builder
+ config:
+ is_trial: '0'
+ version: '5.037'
+x_authority: cpan:DROLSKY
+x_breaks:
+ Log::Dispatch::File::Stamped: '<= 0.10'
+x_contributors:
+ - 'Karen Etheridge <ether@cpan.org>'
+ - 'Olaf Alders <olaf@wundersolutions.com>'
+ - 'Olivier Mengué <dolmen@cpan.org>'
+ - 'Ross Attrill <ross.attrill@gmail.com>'
+ - 'swartz@jonathan-swartzs-macbook-4.local <swartz@jonathan-swartzs-macbook-4.local>'
+ - 'swartz@pobox.com <swartz@pobox.com>'
+ - 'Whitney Jackson <whitney.jackson@baml.com>'
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644
index 0000000..1af8ced
--- /dev/null
+++ b/Makefile.PL
@@ -0,0 +1,119 @@
+# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v5.037.
+use strict;
+use warnings;
+
+use 5.006;
+
+use ExtUtils::MakeMaker;
+check_conflicts();
+
+my %WriteMakefileArgs = (
+ "ABSTRACT" => "Dispatches messages to one or more outputs",
+ "AUTHOR" => "Dave Rolsky <autarch\@urth.org>",
+ "CONFIGURE_REQUIRES" => {
+ "Dist::CheckConflicts" => "0.02",
+ "ExtUtils::MakeMaker" => 0
+ },
+ "DISTNAME" => "Log-Dispatch",
+ "EXE_FILES" => [],
+ "LICENSE" => "artistic_2",
+ "MIN_PERL_VERSION" => "5.006",
+ "NAME" => "Log::Dispatch",
+ "PREREQ_PM" => {
+ "Carp" => 0,
+ "Devel::GlobalDestruction" => 0,
+ "Dist::CheckConflicts" => "0.02",
+ "Fcntl" => 0,
+ "Module::Runtime" => 0,
+ "Params::Validate" => "0.15",
+ "Scalar::Util" => 0,
+ "Sys::Syslog" => "0.28",
+ "base" => 0,
+ "strict" => 0,
+ "warnings" => 0
+ },
+ "TEST_REQUIRES" => {
+ "Data::Dumper" => 0,
+ "Exporter" => 0,
+ "ExtUtils::MakeMaker" => 0,
+ "File::Spec" => 0,
+ "File::Temp" => 0,
+ "IO::File" => 0,
+ "Test::Fatal" => 0,
+ "Test::More" => "0.96",
+ "Test::Requires" => 0,
+ "lib" => 0
+ },
+ "VERSION" => "2.45",
+ "test" => {
+ "TESTS" => "t/*.t"
+ }
+);
+
+
+my %FallbackPrereqs = (
+ "Carp" => 0,
+ "Data::Dumper" => 0,
+ "Devel::GlobalDestruction" => 0,
+ "Dist::CheckConflicts" => "0.02",
+ "Exporter" => 0,
+ "ExtUtils::MakeMaker" => 0,
+ "Fcntl" => 0,
+ "File::Spec" => 0,
+ "File::Temp" => 0,
+ "IO::File" => 0,
+ "Module::Runtime" => 0,
+ "Params::Validate" => "0.15",
+ "Scalar::Util" => 0,
+ "Sys::Syslog" => "0.28",
+ "Test::Fatal" => 0,
+ "Test::More" => "0.96",
+ "Test::Requires" => 0,
+ "base" => 0,
+ "lib" => 0,
+ "strict" => 0,
+ "warnings" => 0
+);
+
+
+unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) {
+ delete $WriteMakefileArgs{TEST_REQUIRES};
+ delete $WriteMakefileArgs{BUILD_REQUIRES};
+ $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs;
+}
+
+delete $WriteMakefileArgs{CONFIGURE_REQUIRES}
+ unless eval { ExtUtils::MakeMaker->VERSION(6.52) };
+
+WriteMakefile(%WriteMakefileArgs);
+
+sub check_conflicts {
+ if ( eval { require 'lib/Log/Dispatch/Conflicts.pm'; 1; } ) {
+ if ( eval { Log::Dispatch::Conflicts->check_conflicts; 1 } ) {
+ return;
+ }
+ else {
+ my $err = $@;
+ $err =~ s/^/ /mg;
+ warn "***\n$err***\n";
+ }
+ }
+ else {
+ print <<'EOF';
+***
+ Your toolchain doesn't support configure_requires, so Dist::CheckConflicts
+ hasn't been installed yet. You should check for conflicting modules
+ manually by examining the list of conflicts in Log::Dispatch::Conflicts once the installation
+ finishes.
+***
+EOF
+ }
+
+ return if $ENV{AUTOMATED_TESTING} || $ENV{NONINTERACTIVE_TESTING};
+
+ # More or less copied from Module::Build
+ return if $ENV{PERL_MM_USE_DEFAULT};
+ return unless -t STDIN && ( -t STDOUT || !( -f STDOUT || -c STDOUT ) );
+
+ sleep 4;
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..56706c1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,425 @@
+NAME
+
+ Log::Dispatch - Dispatches messages to one or more outputs
+
+VERSION
+
+ version 2.45
+
+SYNOPSIS
+
+ use Log::Dispatch;
+
+ # Simple API
+ #
+ my $log = Log::Dispatch->new(
+ outputs => [
+ [ 'File', min_level => 'debug', filename => 'logfile' ],
+ [ 'Screen', min_level => 'warning' ],
+ ],
+ );
+
+ $log->info('Blah, blah');
+
+ # More verbose API
+ #
+ my $log = Log::Dispatch->new();
+ $log->add(
+ Log::Dispatch::File->new(
+ name => 'file1',
+ min_level => 'debug',
+ filename => 'logfile'
+ )
+ );
+ $log->add(
+ Log::Dispatch::Screen->new(
+ name => 'screen',
+ min_level => 'warning',
+ )
+ );
+
+ $log->log( level => 'info', message => 'Blah, blah' );
+
+ my $sub = sub { my %p = @_; return reverse $p{message}; };
+ my $reversing_dispatcher = Log::Dispatch->new( callbacks => $sub );
+
+DESCRIPTION
+
+ This module manages a set of Log::Dispatch::* output objects that can
+ be logged to via a unified interface.
+
+ The idea is that you create a Log::Dispatch object and then add various
+ logging objects to it (such as a file logger or screen logger). Then
+ you call the log method of the dispatch object, which passes the
+ message to each of the objects, which in turn decide whether or not to
+ accept the message and what to do with it.
+
+ This makes it possible to call single method and send a message to a
+ log file, via email, to the screen, and anywhere else, all with very
+ little code needed on your part, once the dispatching object has been
+ created.
+
+METHODS
+
+ This class provides the following methods:
+
+ Log::Dispatch->new(...)
+
+ This method takes the following parameters:
+
+ * outputs( [ [ class, params, ... ], [ class, params, ... ], ... ] )
+
+ This parameter is a reference to a list of lists. Each inner list
+ consists of a class name and a set of constructor params. The class
+ is automatically prefixed with 'Log::Dispatch::' unless it begins
+ with '+', in which case the string following '+' is taken to be a
+ full classname. e.g.
+
+ outputs => [ [ 'File', min_level => 'debug', filename => 'logfile' ],
+ [ '+My::Dispatch', min_level => 'info' ] ]
+
+ For each inner list, a new output object is created and added to the
+ dispatcher (via the add() method).
+
+ See "OUTPUT CLASSES" for the parameters that can be used when
+ creating an output object.
+
+ * callbacks( \& or [ \&, \&, ... ] )
+
+ This parameter may be a single subroutine reference or an array
+ reference of subroutine references. These callbacks will be called in
+ the order they are given and passed a hash containing the following
+ keys:
+
+ ( message => $log_message, level => $log_level )
+
+ In addition, any key/value pairs passed to a logging method will be
+ passed onto your callback.
+
+ The callbacks are expected to modify the message and then return a
+ single scalar containing that modified message. These callbacks will
+ be called when either the log or log_to methods are called and will
+ only be applied to a given message once. If they do not return the
+ message then you will get no output. Make sure to return the message!
+
+ $dispatch->clone()
+
+ This returns a shallow clone of the original object. The underlying
+ output objects and callbacks are shared between the two objects.
+ However any changes made to the outputs or callbacks that the object
+ contains are not shared.
+
+ $dispatch->log( level => $, message => $ or \& )
+
+ Sends the message (at the appropriate level) to all the output objects
+ that the dispatcher contains (by calling the log_to method repeatedly).
+
+ This method also accepts a subroutine reference as the message
+ argument. This reference will be called only if there is an output that
+ will accept a message of the specified level.
+
+ $dispatch->debug (message), info (message), ...
+
+ You may call any valid log level (including valid abbreviations) as a
+ method with a single argument that is the message to be logged. This is
+ converted into a call to the log method with the appropriate level.
+
+ For example:
+
+ $log->alert('Strange data in incoming request');
+
+ translates to:
+
+ $log->log( level => 'alert', message => 'Strange data in incoming request' );
+
+ If you pass an array to these methods, it will be stringified as is:
+
+ my @array = ('Something', 'bad', 'is', 'here');
+ $log->alert(@array);
+
+ # is equivalent to
+
+ $log->alert("@array");
+
+ You can also pass a subroutine reference, just like passing one to the
+ log() method.
+
+ $dispatch->log_and_die( level => $, message => $ or \& )
+
+ Has the same behavior as calling log() but calls _die_with_message() at
+ the end.
+
+ $dispatch->log_and_croak( level => $, message => $ or \& )
+
+ This method adjusts the $Carp::CarpLevel scalar so that the croak comes
+ from the context in which it is called.
+
+ You can throw exception objects by subclassing this method.
+
+ If the carp_level parameter is present its value will be added to the
+ current value of $Carp::CarpLevel.
+
+ $dispatch->log_to( name => $, level => $, message => $ )
+
+ Sends the message only to the named object. Note: this will not
+ properly handle a subroutine reference as the message.
+
+ $dispatch->add_callback( $code )
+
+ Adds a callback (like those given during construction). It is added to
+ the end of the list of callbacks. Note that this can also be called on
+ individual output objects.
+
+ $dispatch->callbacks()
+
+ Returns a list of the callbacks in a given output.
+
+ $dispatch->level_is_valid( $string )
+
+ Returns true or false to indicate whether or not the given string is a
+ valid log level. Can be called as either a class or object method.
+
+ $dispatch->would_log( $string )
+
+ Given a log level, returns true or false to indicate whether or not
+ anything would be logged for that log level.
+
+ $dispatch->is_$level
+
+ There are methods for every log level: is_debug(), is_warning(), etc.
+
+ This returns true if the logger will log a message at the given level.
+
+ $dispatch->add( Log::Dispatch::* OBJECT )
+
+ Adds a new output object to the dispatcher. If an object of the same
+ name already exists, then that object is replaced, with a warning if
+ $^W is true.
+
+ $dispatch->remove($)
+
+ Removes the object that matches the name given to the remove method.
+ The return value is the object being removed or undef if no object
+ matched this.
+
+ $dispatch->outputs()
+
+ Returns a list of output objects.
+
+ $dispatch->output( $name )
+
+ Returns the output object of the given name. Returns undef or an empty
+ list, depending on context, if the given output does not exist.
+
+ $dispatch->_die_with_message( message => $, carp_level => $ )
+
+ This method is used by log_and_die and will either die() or croak()
+ depending on the value of message: if it's a reference or it ends with
+ a new line then a plain die will be used, otherwise it will croak.
+
+OUTPUT CLASSES
+
+ An output class - e.g. Log::Dispatch::File or Log::Dispatch::Screen -
+ implements a particular way of dispatching logs. Many output classes
+ come with this distribution, and others are available separately on
+ CPAN.
+
+ The following common parameters can be used when creating an output
+ class. All are optional. Most output classes will have additional
+ parameters beyond these, see their documentation for details.
+
+ * name ($)
+
+ A name for the object (not the filename!). This is useful if you want
+ to refer to the object later, e.g. to log specifically to it or
+ remove it.
+
+ By default a unique name will be generated. You should not depend on
+ the form of generated names, as they may change.
+
+ * min_level ($)
+
+ The minimum logging level this object will accept. Required.
+
+ * max_level ($)
+
+ The maximum logging level this object will accept. By default the
+ maximum is the highest possible level (which means functionally that
+ the object has no maximum).
+
+ * callbacks( \& or [ \&, \&, ... ] )
+
+ This parameter may be a single subroutine reference or an array
+ reference of subroutine references. These callbacks will be called in
+ the order they are given and passed a hash containing the following
+ keys:
+
+ ( message => $log_message, level => $log_level )
+
+ The callbacks are expected to modify the message and then return a
+ single scalar containing that modified message. These callbacks will
+ be called when either the log or log_to methods are called and will
+ only be applied to a given message once. If they do not return the
+ message then you will get no output. Make sure to return the message!
+
+ * newline (0|1)
+
+ If true, a callback will be added to the end of the callbacks list
+ that adds a newline to the end of each message. Default is false, but
+ some output classes may decide to make the default true.
+
+LOG LEVELS
+
+ The log levels that Log::Dispatch uses are taken directly from the
+ syslog man pages (except that I expanded them to full words). Valid
+ levels are:
+
+ debug
+
+ info
+
+ notice
+
+ warning
+
+ error
+
+ critical
+
+ alert
+
+ emergency
+
+ Alternately, the numbers 0 through 7 may be used (debug is 0 and
+ emergency is 7). The syslog standard of 'err', 'crit', and 'emerg' is
+ also acceptable. We also allow 'warn' as a synonym for 'warning'.
+
+SUBCLASSING
+
+ This module was designed to be easy to subclass. If you want to handle
+ messaging in a way not implemented in this package, you should be able
+ to add this with minimal effort. It is generally as simple as
+ subclassing Log::Dispatch::Output and overriding the new and
+ log_message methods. See the Log::Dispatch::Output docs for more
+ details.
+
+ If you would like to create your own subclass for sending email then it
+ is even simpler. Simply subclass Log::Dispatch::Email and override the
+ send_email method. See the Log::Dispatch::Email docs for more details.
+
+ The logging levels that Log::Dispatch uses are borrowed from the
+ standard UNIX syslog levels, except that where syslog uses partial
+ words ("err") Log::Dispatch also allows the use of the full word as
+ well ("error").
+
+RELATED MODULES
+
+ Log::Dispatch::DBI
+
+ Written by Tatsuhiko Miyagawa. Log output to a database table.
+
+ Log::Dispatch::FileRotate
+
+ Written by Mark Pfeiffer. Rotates log files periodically as part of its
+ usage.
+
+ Log::Dispatch::File::Stamped
+
+ Written by Eric Cholet. Stamps log files with date and time
+ information.
+
+ Log::Dispatch::Jabber
+
+ Written by Aaron Straup Cope. Logs messages via Jabber.
+
+ Log::Dispatch::Tk
+
+ Written by Dominique Dumont. Logs messages to a Tk window.
+
+ Log::Dispatch::Win32EventLog
+
+ Written by Arthur Bergman. Logs messages to the Windows event log.
+
+ Log::Log4perl
+
+ An implementation of Java's log4j API in Perl. Log messages can be
+ limited by fine-grained controls, and if they end up being logged, both
+ native Log4perl and Log::Dispatch appenders can be used to perform the
+ actual logging job. Created by Mike Schilli and Kevin Goess.
+
+ Log::Dispatch::Config
+
+ Written by Tatsuhiko Miyagawa. Allows configuration of logging via a
+ text file similar (or so I'm told) to how it is done with log4j.
+ Simpler than Log::Log4perl.
+
+ Log::Agent
+
+ A very different API for doing many of the same things that
+ Log::Dispatch does. Originally written by Raphael Manfredi.
+
+SUPPORT
+
+ Please submit bugs and patches to the CPAN RT system at
+ http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Log%3A%3ADispatch or via
+ email at bug-log-dispatch@rt.cpan.org.
+
+ Support questions can be sent to me at my email address, shown below.
+
+DONATIONS
+
+ If you'd like to thank me for the work I've done on this module, please
+ consider making a "donation" to me via PayPal. I spend a lot of free
+ time creating free software, and would appreciate any support you'd
+ care to offer.
+
+ Please note that I am not suggesting that you must do this in order for
+ me to continue working on this particular software. I will continue to
+ do so, inasmuch as I have in the past, for as long as it interests me.
+
+ Similarly, a donation made in this way will probably not make me work
+ on this software much more, unless I get so many donations that I can
+ consider working on free software full time, which seems unlikely at
+ best.
+
+ To donate, log into PayPal and send money to autarch@urth.org or use
+ the button on this page: http://www.urth.org/~autarch/fs-donation.html
+
+SEE ALSO
+
+ Log::Dispatch::ApacheLog, Log::Dispatch::Email,
+ Log::Dispatch::Email::MailSend, Log::Dispatch::Email::MailSender,
+ Log::Dispatch::Email::MailSendmail, Log::Dispatch::Email::MIMELite,
+ Log::Dispatch::File, Log::Dispatch::File::Locked,
+ Log::Dispatch::Handle, Log::Dispatch::Output, Log::Dispatch::Screen,
+ Log::Dispatch::Syslog
+
+AUTHOR
+
+ Dave Rolsky <autarch@urth.org>
+
+CONTRIBUTORS
+
+ * Karen Etheridge <ether@cpan.org>
+
+ * Olaf Alders <olaf@wundersolutions.com>
+
+ * Olivier Mengué <dolmen@cpan.org>
+
+ * Ross Attrill <ross.attrill@gmail.com>
+
+ * swartz@jonathan-swartzs-macbook-4.local
+ <swartz@jonathan-swartzs-macbook-4.local>
+
+ * swartz@pobox.com <swartz@pobox.com>
+
+ * Whitney Jackson <whitney.jackson@baml.com>
+
+COPYRIGHT AND LICENSE
+
+ This software is Copyright (c) 2015 by Dave Rolsky.
+
+ This is free software, licensed under:
+
+ The Artistic License 2.0 (GPL Compatible)
+
diff --git a/cpanfile b/cpanfile
new file mode 100644
index 0000000..9c17656
--- /dev/null
+++ b/cpanfile
@@ -0,0 +1,50 @@
+requires "Carp" => "0";
+requires "Devel::GlobalDestruction" => "0";
+requires "Dist::CheckConflicts" => "0.02";
+requires "Fcntl" => "0";
+requires "Module::Runtime" => "0";
+requires "Params::Validate" => "0.15";
+requires "Scalar::Util" => "0";
+requires "Sys::Syslog" => "0.28";
+requires "base" => "0";
+requires "perl" => "5.006";
+requires "strict" => "0";
+requires "warnings" => "0";
+
+on 'test' => sub {
+ requires "Data::Dumper" => "0";
+ requires "Exporter" => "0";
+ requires "ExtUtils::MakeMaker" => "0";
+ requires "File::Spec" => "0";
+ requires "File::Temp" => "0";
+ requires "IO::File" => "0";
+ requires "Test::Fatal" => "0";
+ requires "Test::More" => "0.96";
+ requires "Test::Requires" => "0";
+ requires "lib" => "0";
+};
+
+on 'test' => sub {
+ recommends "CPAN::Meta" => "2.120900";
+};
+
+on 'configure' => sub {
+ requires "Dist::CheckConflicts" => "0.02";
+ requires "ExtUtils::MakeMaker" => "0";
+};
+
+on 'develop' => sub {
+ requires "Code::TidyAll" => "0.24";
+ requires "Perl::Critic" => "1.123";
+ requires "Perl::Tidy" => "20140711";
+ requires "Pod::Coverage::TrustPod" => "0";
+ requires "Test::CPAN::Changes" => "0.19";
+ requires "Test::Code::TidyAll" => "0.24";
+ requires "Test::EOL" => "0";
+ requires "Test::More" => "0.88";
+ requires "Test::NoTabs" => "0";
+ requires "Test::Pod" => "1.41";
+ requires "Test::Pod::Coverage" => "1.08";
+ requires "Test::Spelling" => "0.12";
+ requires "Test::Version" => "1";
+};
diff --git a/dist.ini b/dist.ini
new file mode 100644
index 0000000..084afa9
--- /dev/null
+++ b/dist.ini
@@ -0,0 +1,33 @@
+name = Log-Dispatch
+author = Dave Rolsky <autarch@urth.org>
+license = Artistic_2_0
+copyright_holder = Dave Rolsky
+
+[@DROLSKY]
+dist = Log-Dispatch
+pod_coverage_skip = Log::Dispatch::ApacheLog
+pod_coverage_skip = Log::Dispatch::Conflicts
+pod_coverage_trustme = Log::Dispatch => qr/^(?:warn|err|crit|emerg)$/
+pod_coverage_trustme = Log::Dispatch => qr/^is_\w+$/
+pod_coverage_trustme = Log::Dispatch::File => qr/^(?:O_)?APPEND$/
+pod_coverage_trustme = Log::Dispatch::Output => qr/^new$/
+stopwords_file = .stopwords
+prereqs_skip = Apache2?::Log
+prereqs_skip = ^Mail::
+prereqs_skip = MIME::Lite
+prereqs_skip = threads
+prereqs_skip = threads::shared
+-remove = Test::Compile
+-remove = Test::Pod::LinkCheck
+-remove = Test::Synopsis
+-remove = Test::Version
+
+[FileFinder::ByName / MostLibs]
+dir = lib
+skip = Log/Dispatch/Conflicts.pm
+
+[Test::Version]
+finder = MostLibs
+
+[Conflicts]
+Log::Dispatch::File::Stamped = 0.10
diff --git a/lib/Log/Dispatch.pm b/lib/Log/Dispatch.pm
new file mode 100644
index 0000000..a611d6b
--- /dev/null
+++ b/lib/Log/Dispatch.pm
@@ -0,0 +1,755 @@
+package Log::Dispatch;
+
+use 5.006;
+
+use strict;
+use warnings;
+
+our $VERSION = '2.45';
+
+use base qw( Log::Dispatch::Base );
+
+use Module::Runtime qw( use_package_optimistically );
+use Params::Validate 0.15 qw(validate_with ARRAYREF CODEREF);
+use Carp ();
+
+our %LEVELS;
+
+BEGIN {
+ my %level_map = (
+ (
+ map { $_ => $_ }
+ qw(
+ debug
+ info
+ notice
+ warning
+ error
+ critical
+ alert
+ emergency
+ )
+ ),
+ warn => 'warning',
+ err => 'error',
+ crit => 'critical',
+ emerg => 'emergency',
+ );
+
+ foreach my $l ( keys %level_map ) {
+ my $sub = sub {
+ my $self = shift;
+ $self->log(
+ level => $level_map{$l},
+ message => @_ > 1 ? "@_" : $_[0],
+ );
+ };
+
+ $LEVELS{$l} = 1;
+
+ no strict 'refs';
+ *{$l} = $sub;
+ }
+}
+
+sub new {
+ my $proto = shift;
+ my $class = ref $proto || $proto;
+
+ my %p = validate_with(
+ params => \@_,
+ spec => {
+ outputs => { type => ARRAYREF, optional => 1 },
+ callbacks => { type => ARRAYREF | CODEREF, optional => 1 }
+ },
+ allow_extra => 1, # for backward compatibility
+ );
+
+ my $self = bless {}, $class;
+
+ my @cb = $self->_get_callbacks(%p);
+ $self->{callbacks} = \@cb if @cb;
+
+ if ( my $outputs = $p{outputs} ) {
+ if ( ref $outputs->[1] eq 'HASH' ) {
+
+ # 2.23 API
+ # outputs => [
+ # File => { min_level => 'debug', filename => 'logfile' },
+ # Screen => { min_level => 'warning' }
+ # ]
+ while ( my ( $class, $params ) = splice @$outputs, 0, 2 ) {
+ $self->_add_output( $class, %$params );
+ }
+ }
+ else {
+
+ # 2.24+ syntax
+ # outputs => [
+ # [ 'File', min_level => 'debug', filename => 'logfile' ],
+ # [ 'Screen', min_level => 'warning' ]
+ # ]
+ foreach my $arr (@$outputs) {
+ die "expected arrayref, not '$arr'"
+ unless ref $arr eq 'ARRAY';
+ $self->_add_output(@$arr);
+ }
+ }
+ }
+
+ return $self;
+}
+
+sub clone {
+ my $self = shift;
+
+ my %clone = (
+ callbacks => [ @{ $self->{callbacks} || [] } ],
+ outputs => { %{ $self->{outputs} || {} } },
+ );
+
+ return bless \%clone, ref $self;
+}
+
+sub _add_output {
+ my $self = shift;
+ my $class = shift;
+
+ my $full_class
+ = substr( $class, 0, 1 ) eq '+'
+ ? substr( $class, 1 )
+ : "Log::Dispatch::$class";
+
+ use_package_optimistically($full_class);
+
+ $self->add( $full_class->new(@_) );
+}
+
+sub add {
+ my $self = shift;
+ my $object = shift;
+
+ # Once 5.6 is more established start using the warnings module.
+ if ( exists $self->{outputs}{ $object->name } && $^W ) {
+ Carp::carp(
+ "Log::Dispatch::* object ", $object->name,
+ " already exists."
+ );
+ }
+
+ $self->{outputs}{ $object->name } = $object;
+}
+
+sub remove {
+ my $self = shift;
+ my $name = shift;
+
+ return delete $self->{outputs}{$name};
+}
+
+sub outputs {
+ my $self = shift;
+
+ return values %{ $self->{outputs} };
+}
+
+sub callbacks {
+ my $self = shift;
+
+ return @{ $self->{callbacks} };
+}
+
+sub log {
+ my $self = shift;
+ my %p = @_;
+
+ return unless $self->would_log( $p{level} );
+
+ $self->_log_to_outputs( $self->_prepare_message(%p) );
+}
+
+sub _prepare_message {
+ my $self = shift;
+ my %p = @_;
+
+ $p{message} = $p{message}->()
+ if ref $p{message} eq 'CODE';
+
+ $p{message} = $self->_apply_callbacks(%p)
+ if $self->{callbacks};
+
+ return %p;
+}
+
+sub _log_to_outputs {
+ my $self = shift;
+ my %p = @_;
+
+ foreach ( keys %{ $self->{outputs} } ) {
+ $p{name} = $_;
+ $self->_log_to(%p);
+ }
+}
+
+sub log_and_die {
+ my $self = shift;
+
+ my %p = $self->_prepare_message(@_);
+
+ $self->_log_to_outputs(%p) if $self->would_log( $p{level} );
+
+ $self->_die_with_message(%p);
+}
+
+sub log_and_croak {
+ my $self = shift;
+
+ $self->log_and_die( @_, carp_level => 3 );
+}
+
+sub _die_with_message {
+ my $self = shift;
+ my %p = @_;
+
+ my $msg = $p{message};
+
+ local $Carp::CarpLevel = ( $Carp::CarpLevel || 0 ) + $p{carp_level}
+ if exists $p{carp_level};
+
+ Carp::croak($msg);
+}
+
+sub log_to {
+ my $self = shift;
+ my %p = @_;
+
+ $p{message} = $self->_apply_callbacks(%p)
+ if $self->{callbacks};
+
+ $self->_log_to(%p);
+}
+
+sub _log_to {
+ my $self = shift;
+ my %p = @_;
+ my $name = $p{name};
+
+ if ( exists $self->{outputs}{$name} ) {
+ $self->{outputs}{$name}->log(@_);
+ }
+ elsif ($^W) {
+ Carp::carp(
+ "Log::Dispatch::* object named '$name' not in dispatcher\n");
+ }
+}
+
+sub output {
+ my $self = shift;
+ my $name = shift;
+
+ return unless exists $self->{outputs}{$name};
+
+ return $self->{outputs}{$name};
+}
+
+sub level_is_valid {
+ shift;
+ my $level = shift
+ or Carp::croak('Logging level was not provided');
+
+ return $LEVELS{$level};
+}
+
+sub would_log {
+ my $self = shift;
+ my $level = shift;
+
+ return 0 unless $self->level_is_valid($level);
+
+ foreach ( values %{ $self->{outputs} } ) {
+ return 1 if $_->_should_log($level);
+ }
+
+ return 0;
+}
+
+sub is_debug { $_[0]->would_log('debug') }
+sub is_info { $_[0]->would_log('info') }
+sub is_notice { $_[0]->would_log('notice') }
+sub is_warning { $_[0]->would_log('warning') }
+sub is_warn { $_[0]->would_log('warn') }
+sub is_error { $_[0]->would_log('error') }
+sub is_err { $_[0]->would_log('err') }
+sub is_critical { $_[0]->would_log('critical') }
+sub is_crit { $_[0]->would_log('crit') }
+sub is_alert { $_[0]->would_log('alert') }
+sub is_emerg { $_[0]->would_log('emerg') }
+sub is_emergency { $_[0]->would_log('emergency') }
+
+1;
+
+# ABSTRACT: Dispatches messages to one or more outputs
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Log::Dispatch - Dispatches messages to one or more outputs
+
+=head1 VERSION
+
+version 2.45
+
+=head1 SYNOPSIS
+
+ use Log::Dispatch;
+
+ # Simple API
+ #
+ my $log = Log::Dispatch->new(
+ outputs => [
+ [ 'File', min_level => 'debug', filename => 'logfile' ],
+ [ 'Screen', min_level => 'warning' ],
+ ],
+ );
+
+ $log->info('Blah, blah');
+
+ # More verbose API
+ #
+ my $log = Log::Dispatch->new();
+ $log->add(
+ Log::Dispatch::File->new(
+ name => 'file1',
+ min_level => 'debug',
+ filename => 'logfile'
+ )
+ );
+ $log->add(
+ Log::Dispatch::Screen->new(
+ name => 'screen',
+ min_level => 'warning',
+ )
+ );
+
+ $log->log( level => 'info', message => 'Blah, blah' );
+
+ my $sub = sub { my %p = @_; return reverse $p{message}; };
+ my $reversing_dispatcher = Log::Dispatch->new( callbacks => $sub );
+
+=head1 DESCRIPTION
+
+This module manages a set of Log::Dispatch::* output objects that can be
+logged to via a unified interface.
+
+The idea is that you create a Log::Dispatch object and then add various
+logging objects to it (such as a file logger or screen logger). Then you
+call the C<log> method of the dispatch object, which passes the message to
+each of the objects, which in turn decide whether or not to accept the
+message and what to do with it.
+
+This makes it possible to call single method and send a message to a
+log file, via email, to the screen, and anywhere else, all with very
+little code needed on your part, once the dispatching object has been
+created.
+
+=encoding UTF-8
+
+=head1 METHODS
+
+This class provides the following methods:
+
+=head2 Log::Dispatch->new(...)
+
+This method takes the following parameters:
+
+=over 4
+
+=item * outputs( [ [ class, params, ... ], [ class, params, ... ], ... ] )
+
+This parameter is a reference to a list of lists. Each inner list consists of
+a class name and a set of constructor params. The class is automatically
+prefixed with 'Log::Dispatch::' unless it begins with '+', in which case the
+string following '+' is taken to be a full classname. e.g.
+
+ outputs => [ [ 'File', min_level => 'debug', filename => 'logfile' ],
+ [ '+My::Dispatch', min_level => 'info' ] ]
+
+For each inner list, a new output object is created and added to the
+dispatcher (via the C<add()> method).
+
+See L<OUTPUT CLASSES> for the parameters that can be used when creating an
+output object.
+
+=item * callbacks( \& or [ \&, \&, ... ] )
+
+This parameter may be a single subroutine reference or an array
+reference of subroutine references. These callbacks will be called in
+the order they are given and passed a hash containing the following keys:
+
+ ( message => $log_message, level => $log_level )
+
+In addition, any key/value pairs passed to a logging method will be
+passed onto your callback.
+
+The callbacks are expected to modify the message and then return a
+single scalar containing that modified message. These callbacks will
+be called when either the C<log> or C<log_to> methods are called and
+will only be applied to a given message once. If they do not return
+the message then you will get no output. Make sure to return the
+message!
+
+=back
+
+=head2 $dispatch->clone()
+
+This returns a I<shallow> clone of the original object. The underlying output
+objects and callbacks are shared between the two objects. However any changes
+made to the outputs or callbacks that the object contains are not shared.
+
+=head2 $dispatch->log( level => $, message => $ or \& )
+
+Sends the message (at the appropriate level) to all the
+output objects that the dispatcher contains (by calling the
+C<log_to> method repeatedly).
+
+This method also accepts a subroutine reference as the message
+argument. This reference will be called only if there is an output
+that will accept a message of the specified level.
+
+=head2 $dispatch->debug (message), info (message), ...
+
+You may call any valid log level (including valid abbreviations) as a method
+with a single argument that is the message to be logged. This is converted
+into a call to the C<log> method with the appropriate level.
+
+For example:
+
+ $log->alert('Strange data in incoming request');
+
+translates to:
+
+ $log->log( level => 'alert', message => 'Strange data in incoming request' );
+
+If you pass an array to these methods, it will be stringified as is:
+
+ my @array = ('Something', 'bad', 'is', 'here');
+ $log->alert(@array);
+
+ # is equivalent to
+
+ $log->alert("@array");
+
+You can also pass a subroutine reference, just like passing one to the
+C<log()> method.
+
+=head2 $dispatch->log_and_die( level => $, message => $ or \& )
+
+Has the same behavior as calling C<log()> but calls
+C<_die_with_message()> at the end.
+
+=head2 $dispatch->log_and_croak( level => $, message => $ or \& )
+
+This method adjusts the C<$Carp::CarpLevel> scalar so that the croak
+comes from the context in which it is called.
+
+You can throw exception objects by subclassing this method.
+
+If the C<carp_level> parameter is present its value will be added to
+the current value of C<$Carp::CarpLevel>.
+
+=head2 $dispatch->log_to( name => $, level => $, message => $ )
+
+Sends the message only to the named object. Note: this will not properly
+handle a subroutine reference as the message.
+
+=head2 $dispatch->add_callback( $code )
+
+Adds a callback (like those given during construction). It is added to the end
+of the list of callbacks. Note that this can also be called on individual
+output objects.
+
+=head2 $dispatch->callbacks()
+
+Returns a list of the callbacks in a given output.
+
+=head2 $dispatch->level_is_valid( $string )
+
+Returns true or false to indicate whether or not the given string is a
+valid log level. Can be called as either a class or object method.
+
+=head2 $dispatch->would_log( $string )
+
+Given a log level, returns true or false to indicate whether or not
+anything would be logged for that log level.
+
+=head2 $dispatch->is_C<$level>
+
+There are methods for every log level: C<is_debug()>, C<is_warning()>, etc.
+
+This returns true if the logger will log a message at the given level.
+
+=head2 $dispatch->add( Log::Dispatch::* OBJECT )
+
+Adds a new L<output object|OUTPUT CLASSES> to the dispatcher. If an object
+of the same name already exists, then that object is replaced, with
+a warning if C<$^W> is true.
+
+=head2 $dispatch->remove($)
+
+Removes the object that matches the name given to the remove method.
+The return value is the object being removed or undef if no object
+matched this.
+
+=head2 $dispatch->outputs()
+
+Returns a list of output objects.
+
+=head2 $dispatch->output( $name )
+
+Returns the output object of the given name. Returns undef or an empty
+list, depending on context, if the given output does not exist.
+
+=head2 $dispatch->_die_with_message( message => $, carp_level => $ )
+
+This method is used by C<log_and_die> and will either die() or croak()
+depending on the value of C<message>: if it's a reference or it ends
+with a new line then a plain die will be used, otherwise it will
+croak.
+
+=head1 OUTPUT CLASSES
+
+An output class - e.g. L<Log::Dispatch::File> or
+L<Log::Dispatch::Screen> - implements a particular way
+of dispatching logs. Many output classes come with this distribution,
+and others are available separately on CPAN.
+
+The following common parameters can be used when creating an output class.
+All are optional. Most output classes will have additional parameters beyond
+these, see their documentation for details.
+
+=over 4
+
+=item * name ($)
+
+A name for the object (not the filename!). This is useful if you want to
+refer to the object later, e.g. to log specifically to it or remove it.
+
+By default a unique name will be generated. You should not depend on the
+form of generated names, as they may change.
+
+=item * min_level ($)
+
+The minimum L<logging level|LOG LEVELS> this object will accept. Required.
+
+=item * max_level ($)
+
+The maximum L<logging level|LOG LEVELS> this object will accept. By default
+the maximum is the highest possible level (which means functionally that the
+object has no maximum).
+
+=item * callbacks( \& or [ \&, \&, ... ] )
+
+This parameter may be a single subroutine reference or an array
+reference of subroutine references. These callbacks will be called in
+the order they are given and passed a hash containing the following keys:
+
+ ( message => $log_message, level => $log_level )
+
+The callbacks are expected to modify the message and then return a
+single scalar containing that modified message. These callbacks will
+be called when either the C<log> or C<log_to> methods are called and
+will only be applied to a given message once. If they do not return
+the message then you will get no output. Make sure to return the
+message!
+
+=item * newline (0|1)
+
+If true, a callback will be added to the end of the callbacks list that adds
+a newline to the end of each message. Default is false, but some
+output classes may decide to make the default true.
+
+=back
+
+=head1 LOG LEVELS
+
+The log levels that Log::Dispatch uses are taken directly from the
+syslog man pages (except that I expanded them to full words). Valid
+levels are:
+
+=over 4
+
+=item debug
+
+=item info
+
+=item notice
+
+=item warning
+
+=item error
+
+=item critical
+
+=item alert
+
+=item emergency
+
+=back
+
+Alternately, the numbers 0 through 7 may be used (debug is 0 and emergency is
+7). The syslog standard of 'err', 'crit', and 'emerg' is also acceptable. We
+also allow 'warn' as a synonym for 'warning'.
+
+=head1 SUBCLASSING
+
+This module was designed to be easy to subclass. If you want to handle
+messaging in a way not implemented in this package, you should be able to add
+this with minimal effort. It is generally as simple as subclassing
+Log::Dispatch::Output and overriding the C<new> and C<log_message>
+methods. See the L<Log::Dispatch::Output> docs for more details.
+
+If you would like to create your own subclass for sending email then
+it is even simpler. Simply subclass L<Log::Dispatch::Email> and
+override the C<send_email> method. See the L<Log::Dispatch::Email>
+docs for more details.
+
+The logging levels that Log::Dispatch uses are borrowed from the standard
+UNIX syslog levels, except that where syslog uses partial words ("err")
+Log::Dispatch also allows the use of the full word as well ("error").
+
+=head1 RELATED MODULES
+
+=head2 Log::Dispatch::DBI
+
+Written by Tatsuhiko Miyagawa. Log output to a database table.
+
+=head2 Log::Dispatch::FileRotate
+
+Written by Mark Pfeiffer. Rotates log files periodically as part of
+its usage.
+
+=head2 Log::Dispatch::File::Stamped
+
+Written by Eric Cholet. Stamps log files with date and time
+information.
+
+=head2 Log::Dispatch::Jabber
+
+Written by Aaron Straup Cope. Logs messages via Jabber.
+
+=head2 Log::Dispatch::Tk
+
+Written by Dominique Dumont. Logs messages to a Tk window.
+
+=head2 Log::Dispatch::Win32EventLog
+
+Written by Arthur Bergman. Logs messages to the Windows event log.
+
+=head2 Log::Log4perl
+
+An implementation of Java's log4j API in Perl. Log messages can be limited by
+fine-grained controls, and if they end up being logged, both native Log4perl
+and Log::Dispatch appenders can be used to perform the actual logging
+job. Created by Mike Schilli and Kevin Goess.
+
+=head2 Log::Dispatch::Config
+
+Written by Tatsuhiko Miyagawa. Allows configuration of logging via a
+text file similar (or so I'm told) to how it is done with log4j.
+Simpler than Log::Log4perl.
+
+=head2 Log::Agent
+
+A very different API for doing many of the same things that
+Log::Dispatch does. Originally written by Raphael Manfredi.
+
+=head1 SUPPORT
+
+Please submit bugs and patches to the CPAN RT system at
+http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Log%3A%3ADispatch
+or via email at bug-log-dispatch@rt.cpan.org.
+
+Support questions can be sent to me at my email address, shown below.
+
+=head1 DONATIONS
+
+If you'd like to thank me for the work I've done on this module,
+please consider making a "donation" to me via PayPal. I spend a lot of
+free time creating free software, and would appreciate any support
+you'd care to offer.
+
+Please note that B<I am not suggesting that you must do this> in order
+for me to continue working on this particular software. I will
+continue to do so, inasmuch as I have in the past, for as long as it
+interests me.
+
+Similarly, a donation made in this way will probably not make me work
+on this software much more, unless I get so many donations that I can
+consider working on free software full time, which seems unlikely at
+best.
+
+To donate, log into PayPal and send money to autarch@urth.org or use
+the button on this page:
+L<http://www.urth.org/~autarch/fs-donation.html>
+
+=head1 SEE ALSO
+
+L<Log::Dispatch::ApacheLog>, L<Log::Dispatch::Email>,
+L<Log::Dispatch::Email::MailSend>, L<Log::Dispatch::Email::MailSender>,
+L<Log::Dispatch::Email::MailSendmail>, L<Log::Dispatch::Email::MIMELite>,
+L<Log::Dispatch::File>, L<Log::Dispatch::File::Locked>,
+L<Log::Dispatch::Handle>, L<Log::Dispatch::Output>, L<Log::Dispatch::Screen>,
+L<Log::Dispatch::Syslog>
+
+=head1 AUTHOR
+
+Dave Rolsky <autarch@urth.org>
+
+=head1 CONTRIBUTORS
+
+=for stopwords Karen Etheridge Olaf Alders Olivier Mengué Ross Attrill swartz@jonathan-swartzs-macbook-4.local swartz@pobox.com Whitney Jackson
+
+=over 4
+
+=item *
+
+Karen Etheridge <ether@cpan.org>
+
+=item *
+
+Olaf Alders <olaf@wundersolutions.com>
+
+=item *
+
+Olivier Mengué <dolmen@cpan.org>
+
+=item *
+
+Ross Attrill <ross.attrill@gmail.com>
+
+=item *
+
+swartz@jonathan-swartzs-macbook-4.local <swartz@jonathan-swartzs-macbook-4.local>
+
+=item *
+
+swartz@pobox.com <swartz@pobox.com>
+
+=item *
+
+Whitney Jackson <whitney.jackson@baml.com>
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is Copyright (c) 2015 by Dave Rolsky.
+
+This is free software, licensed under:
+
+ The Artistic License 2.0 (GPL Compatible)
+
+=cut
diff --git a/lib/Log/Dispatch/ApacheLog.pm b/lib/Log/Dispatch/ApacheLog.pm
new file mode 100644
index 0000000..4c5ccb9
--- /dev/null
+++ b/lib/Log/Dispatch/ApacheLog.pm
@@ -0,0 +1,115 @@
+package Log::Dispatch::ApacheLog;
+
+use strict;
+use warnings;
+
+our $VERSION = '2.45';
+
+use Log::Dispatch::Output;
+
+use base qw( Log::Dispatch::Output );
+
+use Params::Validate qw(validate);
+Params::Validate::validation_options( allow_extra => 1 );
+
+BEGIN {
+ if ( $ENV{MOD_PERL} && $ENV{MOD_PERL} =~ /2\./ ) {
+ require Apache2::Log;
+ }
+ else {
+ require Apache::Log;
+ }
+}
+
+sub new {
+ my $proto = shift;
+ my $class = ref $proto || $proto;
+
+ my %p = validate( @_, { apache => { can => 'log' } } );
+
+ my $self = bless {}, $class;
+
+ $self->_basic_init(%p);
+ $self->{apache_log} = $p{apache}->log;
+
+ return $self;
+}
+
+{
+ my %methods = (
+ emergency => 'emerg',
+ critical => 'crit',
+ warning => 'warn',
+ );
+
+ sub log_message {
+ my $self = shift;
+ my %p = @_;
+
+ my $level = $self->_level_as_name( $p{level} );
+
+ my $method = $methods{$level} || $level;
+
+ $self->{apache_log}->$method( $p{message} );
+ }
+}
+
+1;
+
+# ABSTRACT: Object for logging to Apache::Log objects
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Log::Dispatch::ApacheLog - Object for logging to Apache::Log objects
+
+=head1 VERSION
+
+version 2.45
+
+=head1 SYNOPSIS
+
+ use Log::Dispatch;
+
+ my $log = Log::Dispatch->new(
+ outputs => [
+ [ 'ApacheLog', apache => $r ],
+ ],
+ );
+
+ $log->emerg('Kaboom');
+
+=head1 DESCRIPTION
+
+This module allows you to pass messages to Apache's log object,
+represented by the L<Apache::Log> class.
+
+=head1 CONSTRUCTOR
+
+The constructor takes the following parameters in addition to the standard
+parameters documented in L<Log::Dispatch::Output>:
+
+=over 4
+
+=item * apache ($)
+
+An object of either the L<Apache> or L<Apache::Server> classes. Required.
+
+=back
+
+=head1 AUTHOR
+
+Dave Rolsky <autarch@urth.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is Copyright (c) 2015 by Dave Rolsky.
+
+This is free software, licensed under:
+
+ The Artistic License 2.0 (GPL Compatible)
+
+=cut
diff --git a/lib/Log/Dispatch/Base.pm b/lib/Log/Dispatch/Base.pm
new file mode 100644
index 0000000..368c6fa
--- /dev/null
+++ b/lib/Log/Dispatch/Base.pm
@@ -0,0 +1,91 @@
+package Log::Dispatch::Base;
+
+use strict;
+use warnings;
+
+our $VERSION = '2.45';
+
+sub _get_callbacks {
+ shift;
+ my %p = @_;
+
+ return unless exists $p{callbacks};
+
+ return @{ $p{callbacks} }
+ if ref $p{callbacks} eq 'ARRAY';
+
+ return $p{callbacks}
+ if ref $p{callbacks} eq 'CODE';
+
+ return;
+}
+
+sub _apply_callbacks {
+ my $self = shift;
+ my %p = @_;
+
+ my $msg = delete $p{message};
+ foreach my $cb ( @{ $self->{callbacks} } ) {
+ $msg = $cb->( message => $msg, %p );
+ }
+
+ return $msg;
+}
+
+sub add_callback {
+ my $self = shift;
+ my $value = shift;
+
+ Carp::carp("given value $value is not a valid callback")
+ unless ref $value eq 'CODE';
+
+ $self->{callbacks} ||= [];
+ push @{ $self->{callbacks} }, $value;
+
+ return;
+}
+
+1;
+
+# ABSTRACT: Code shared by dispatch and output objects.
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Log::Dispatch::Base - Code shared by dispatch and output objects.
+
+=head1 VERSION
+
+version 2.45
+
+=head1 SYNOPSIS
+
+ use Log::Dispatch::Base;
+
+ ...
+
+ @ISA = qw(Log::Dispatch::Base);
+
+=head1 DESCRIPTION
+
+Unless you are me, you probably don't need to know what this class
+does.
+
+=for Pod::Coverage add_callback
+
+=head1 AUTHOR
+
+Dave Rolsky <autarch@urth.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is Copyright (c) 2015 by Dave Rolsky.
+
+This is free software, licensed under:
+
+ The Artistic License 2.0 (GPL Compatible)
+
+=cut
diff --git a/lib/Log/Dispatch/Code.pm b/lib/Log/Dispatch/Code.pm
new file mode 100644
index 0000000..e06bbfd
--- /dev/null
+++ b/lib/Log/Dispatch/Code.pm
@@ -0,0 +1,122 @@
+package Log::Dispatch::Code;
+
+use strict;
+use warnings;
+
+our $VERSION = '2.45';
+
+use Log::Dispatch::Output;
+
+use base qw( Log::Dispatch::Output );
+
+use Params::Validate qw(validate CODEREF);
+Params::Validate::validation_options( allow_extra => 1 );
+
+sub new {
+ my $proto = shift;
+ my $class = ref $proto || $proto;
+
+ my %p = validate( @_, { code => CODEREF } );
+
+ my $self = bless {}, $class;
+
+ $self->_basic_init(%p);
+ $self->{code} = $p{code};
+
+ return $self;
+}
+
+sub log_message {
+ my $self = shift;
+ my %p = @_;
+
+ delete $p{name};
+
+ $self->{code}->(%p);
+}
+
+1;
+
+# ABSTRACT: Object for logging to a subroutine reference
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Log::Dispatch::Code - Object for logging to a subroutine reference
+
+=head1 VERSION
+
+version 2.45
+
+=head1 SYNOPSIS
+
+ use Log::Dispatch;
+
+ my $log = Log::Dispatch->new(
+ outputs => [
+ [
+ 'Code',
+ min_level => 'emerg',
+ code => \&_log_it,
+ ],
+ ]
+ );
+
+ sub _log_it {
+ my %p = @_;
+
+ warn $p{message};
+ }
+
+=head1 DESCRIPTION
+
+This module supplies a simple object for logging to a subroutine reference.
+
+=for Pod::Coverage new log_message
+
+=head1 CONSTRUCTOR
+
+The constructor takes the following parameters in addition to the standard
+parameters documented in L<Log::Dispatch::Output>:
+
+=over 4
+
+=item * code ($)
+
+The subroutine reference.
+
+=back
+
+=head1 HOW IT WORKS
+
+The subroutine you provide will be called with a hash of named arguments. The
+two arguments are:
+
+=over 4
+
+=item * level
+
+The log level of the message. This will be a string like "info" or "error".
+
+=item * message
+
+The message being logged.
+
+=back
+
+=head1 AUTHOR
+
+Dave Rolsky <autarch@urth.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is Copyright (c) 2015 by Dave Rolsky.
+
+This is free software, licensed under:
+
+ The Artistic License 2.0 (GPL Compatible)
+
+=cut
diff --git a/lib/Log/Dispatch/Conflicts.pm b/lib/Log/Dispatch/Conflicts.pm
new file mode 100644
index 0000000..9f528e5
--- /dev/null
+++ b/lib/Log/Dispatch/Conflicts.pm
@@ -0,0 +1,33 @@
+package # hide from PAUSE
+ Log::Dispatch::Conflicts;
+
+use strict;
+use warnings;
+
+# this module was generated with Dist::Zilla::Plugin::Conflicts 0.17
+
+use Dist::CheckConflicts
+ -dist => 'Log::Dispatch',
+ -conflicts => {
+ 'Log::Dispatch::File::Stamped' => '0.10',
+ },
+ -also => [ qw(
+ Carp
+ Devel::GlobalDestruction
+ Dist::CheckConflicts
+ Fcntl
+ Module::Runtime
+ Params::Validate
+ Scalar::Util
+ Sys::Syslog
+ base
+ strict
+ warnings
+ ) ],
+
+;
+
+1;
+
+# ABSTRACT: Provide information on conflicts for Log::Dispatch
+# Dist::Zilla: -PodWeaver
diff --git a/lib/Log/Dispatch/Email.pm b/lib/Log/Dispatch/Email.pm
new file mode 100644
index 0000000..b10604a
--- /dev/null
+++ b/lib/Log/Dispatch/Email.pm
@@ -0,0 +1,207 @@
+package Log::Dispatch::Email;
+
+use strict;
+use warnings;
+
+our $VERSION = '2.45';
+
+use Log::Dispatch::Output;
+
+use base qw( Log::Dispatch::Output );
+
+use Devel::GlobalDestruction qw( in_global_destruction );
+use Params::Validate qw(validate SCALAR ARRAYREF BOOLEAN);
+Params::Validate::validation_options( allow_extra => 1 );
+
+# need to untaint this value
+my ($program) = $0 =~ /(.+)/;
+
+sub new {
+ my $proto = shift;
+ my $class = ref $proto || $proto;
+
+ my %p = validate(
+ @_, {
+ subject => {
+ type => SCALAR,
+ default => "$program: log email"
+ },
+ to => { type => SCALAR | ARRAYREF },
+ from => {
+ type => SCALAR,
+ optional => 1
+ },
+ buffered => {
+ type => BOOLEAN,
+ default => 1
+ },
+ }
+ );
+
+ my $self = bless {}, $class;
+
+ $self->_basic_init(%p);
+
+ $self->{subject} = $p{subject} || "$0: log email";
+ $self->{to} = ref $p{to} ? $p{to} : [ $p{to} ];
+ $self->{from} = $p{from};
+
+ # Default to buffered for obvious reasons!
+ $self->{buffered} = $p{buffered};
+
+ $self->{buffer} = [] if $self->{buffered};
+
+ return $self;
+}
+
+sub log_message {
+ my $self = shift;
+ my %p = @_;
+
+ if ( $self->{buffered} ) {
+ push @{ $self->{buffer} }, $p{message};
+ }
+ else {
+ $self->send_email(@_);
+ }
+}
+
+sub send_email {
+ my $self = shift;
+ my $class = ref $self;
+
+ die "The send_email method must be overridden in the $class subclass";
+}
+
+sub flush {
+ my $self = shift;
+
+ if ( $self->{buffered} && @{ $self->{buffer} } ) {
+ my $message = join '', @{ $self->{buffer} };
+
+ $self->send_email( message => $message );
+ $self->{buffer} = [];
+ }
+}
+
+sub DESTROY {
+ my $self = shift;
+
+ if ( in_global_destruction()
+ && $self->{buffered}
+ && @{ $self->{buffer} } ) {
+
+ my $name = $self->name();
+ my $class = ref $self;
+ my $message
+ = "Log messages for the $name output (a $class object) remain unsent but the program is terminating.\n";
+ $message .= "The messages are:\n";
+ $message .= " $_\n" for @{ $self->{buffer} };
+ }
+ else {
+ $self->flush();
+ }
+}
+
+1;
+
+# ABSTRACT: Base class for objects that send log messages via email
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Log::Dispatch::Email - Base class for objects that send log messages via email
+
+=head1 VERSION
+
+version 2.45
+
+=head1 SYNOPSIS
+
+ package Log::Dispatch::Email::MySender;
+
+ use Log::Dispatch::Email;
+ use base qw( Log::Dispatch::Email );
+
+ sub send_email {
+ my $self = shift;
+ my %p = @_;
+
+ # Send email somehow. Message is in $p{message}
+ }
+
+=head1 DESCRIPTION
+
+This module should be used as a base class to implement
+Log::Dispatch::* objects that send their log messages via email.
+Implementing a subclass simply requires the code shown in the
+L<SYNOPSIS> with a real implementation of the C<send_email()> method.
+
+=for Pod::Coverage new log_message
+
+=head1 CONSTRUCTOR
+
+The constructor takes the following parameters in addition to the standard
+parameters documented in L<Log::Dispatch::Output>:
+
+=over 4
+
+=item * subject ($)
+
+The subject of the email messages which are sent. Defaults to "$0:
+log email"
+
+=item * to ($ or \@)
+
+Either a string or a list reference of strings containing email
+addresses. Required.
+
+=item * from ($)
+
+A string containing an email address. This is optional and may not
+work with all mail sending methods.
+
+=item * buffered (0 or 1)
+
+This determines whether the object sends one email per message it is
+given or whether it stores them up and sends them all at once. The
+default is to buffer messages.
+
+=back
+
+=head1 METHODS
+
+This class provides the following methods:
+
+=head2 $email->send_email(%p)
+
+This is the method that must be subclassed. For now the only
+parameter in the hash is 'message'.
+
+=head2 $email->flush
+
+If the object is buffered, then this method will call the
+C<send_email()> method to send the contents of the buffer and then
+clear the buffer.
+
+=head2 $email->DESTROY
+
+On destruction, the object will call C<flush()> to send any pending
+email.
+
+=head1 AUTHOR
+
+Dave Rolsky <autarch@urth.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is Copyright (c) 2015 by Dave Rolsky.
+
+This is free software, licensed under:
+
+ The Artistic License 2.0 (GPL Compatible)
+
+=cut
diff --git a/lib/Log/Dispatch/Email/MIMELite.pm b/lib/Log/Dispatch/Email/MIMELite.pm
new file mode 100644
index 0000000..aadf5de
--- /dev/null
+++ b/lib/Log/Dispatch/Email/MIMELite.pm
@@ -0,0 +1,83 @@
+package Log::Dispatch::Email::MIMELite;
+
+use strict;
+use warnings;
+
+our $VERSION = '2.45';
+
+use Log::Dispatch::Email;
+
+use base qw( Log::Dispatch::Email );
+
+use MIME::Lite;
+
+sub send_email {
+ my $self = shift;
+ my %p = @_;
+
+ my %mail = (
+ To => ( join ',', @{ $self->{to} } ),
+ Subject => $self->{subject},
+ Type => 'TEXT',
+ Data => $p{message},
+ );
+
+ $mail{From} = $self->{from} if defined $self->{from};
+
+ local $?;
+ unless ( MIME::Lite->new(%mail)->send ) {
+ warn "Error sending mail with MIME::Lite";
+ }
+}
+
+1;
+
+# ABSTRACT: Subclass of Log::Dispatch::Email that uses the MIME::Lite module
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Log::Dispatch::Email::MIMELite - Subclass of Log::Dispatch::Email that uses the MIME::Lite module
+
+=head1 VERSION
+
+version 2.45
+
+=head1 SYNOPSIS
+
+ use Log::Dispatch;
+
+ my $log = Log::Dispatch->new(
+ outputs => [
+ [
+ 'Email::MIMELite',
+ min_level => 'emerg',
+ to => [qw( foo@example.com bar@example.org )],
+ subject => 'Big error!'
+ ]
+ ],
+ );
+
+ $log->emerg("Something bad is happening");
+
+=head1 DESCRIPTION
+
+This is a subclass of L<Log::Dispatch::Email> that implements the
+send_email method using the L<MIME::Lite> module.
+
+=head1 AUTHOR
+
+Dave Rolsky <autarch@urth.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is Copyright (c) 2015 by Dave Rolsky.
+
+This is free software, licensed under:
+
+ The Artistic License 2.0 (GPL Compatible)
+
+=cut
diff --git a/lib/Log/Dispatch/Email/MailSend.pm b/lib/Log/Dispatch/Email/MailSend.pm
new file mode 100644
index 0000000..10a7018
--- /dev/null
+++ b/lib/Log/Dispatch/Email/MailSend.pm
@@ -0,0 +1,102 @@
+package Log::Dispatch::Email::MailSend;
+
+use strict;
+use warnings;
+
+our $VERSION = '2.45';
+
+use Log::Dispatch::Email;
+
+use base qw( Log::Dispatch::Email );
+
+use Mail::Send;
+
+sub send_email {
+ my $self = shift;
+ my %p = @_;
+
+ my $msg = Mail::Send->new;
+
+ $msg->to( join ',', @{ $self->{to} } );
+ $msg->subject( $self->{subject} );
+
+ # Does this ever work for this module?
+ $msg->set( 'From', $self->{from} ) if $self->{from};
+
+ local $?;
+ eval {
+ my $fh = $msg->open
+ or die "Cannot open handle to mail program";
+
+ $fh->print( $p{message} )
+ or die "Cannot print message to mail program handle";
+
+ $fh->close
+ or die "Cannot close handle to mail program";
+ };
+
+ warn $@ if $@;
+}
+
+1;
+
+# ABSTRACT: Subclass of Log::Dispatch::Email that uses the Mail::Send module
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Log::Dispatch::Email::MailSend - Subclass of Log::Dispatch::Email that uses the Mail::Send module
+
+=head1 VERSION
+
+version 2.45
+
+=head1 SYNOPSIS
+
+ use Log::Dispatch;
+
+ my $log = Log::Dispatch->new(
+ outputs => [
+ [
+ 'Email::MailSend',
+ min_level => 'emerg',
+ to => [qw( foo@example.com bar@example.org )],
+ subject => 'Big error!'
+ ]
+ ],
+ );
+
+ $log->emerg("Something bad is happening");
+
+=head1 DESCRIPTION
+
+This is a subclass of L<Log::Dispatch::Email> that implements the send_email
+method using the L<Mail::Send> module.
+
+=head1 CHANGING HOW MAIL IS SENT
+
+Since L<Mail::Send> is a subclass of L<Mail::Mailer>, you can change
+how mail is sent from this module by simply C<use>ing L<Mail::Mailer>
+in your code before mail is sent. For example, to send mail via smtp,
+you could do:
+
+ use Mail::Mailer 'smtp', Server => 'foo.example.com';
+
+For more details, see the L<Mail::Mailer> docs.
+
+=head1 AUTHOR
+
+Dave Rolsky <autarch@urth.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is Copyright (c) 2015 by Dave Rolsky.
+
+This is free software, licensed under:
+
+ The Artistic License 2.0 (GPL Compatible)
+
+=cut
diff --git a/lib/Log/Dispatch/Email/MailSender.pm b/lib/Log/Dispatch/Email/MailSender.pm
new file mode 100644
index 0000000..8d561c7
--- /dev/null
+++ b/lib/Log/Dispatch/Email/MailSender.pm
@@ -0,0 +1,130 @@
+package Log::Dispatch::Email::MailSender;
+
+# By: Joseph Annino
+# (c) 2002
+# Licensed under the same terms as Perl
+#
+
+use strict;
+use warnings;
+
+our $VERSION = '2.45';
+
+use Log::Dispatch::Email;
+
+use base qw( Log::Dispatch::Email );
+
+use Mail::Sender ();
+
+sub new {
+ my $proto = shift;
+ my $class = ref $proto || $proto;
+
+ my %p = @_;
+
+ my $smtp = delete $p{smtp} || 'localhost';
+ my $port = delete $p{port} || '25';
+
+ my $self = $class->SUPER::new(%p);
+
+ $self->{smtp} = $smtp;
+ $self->{port} = $port;
+
+ return $self;
+}
+
+sub send_email {
+ my $self = shift;
+ my %p = @_;
+
+ local $?;
+ eval {
+ my $sender = Mail::Sender->new(
+ {
+ from => $self->{from} || 'LogDispatch@foo.bar',
+ replyto => $self->{from} || 'LogDispatch@foo.bar',
+ to => ( join ',', @{ $self->{to} } ),
+ subject => $self->{subject},
+ smtp => $self->{smtp},
+ port => $self->{port},
+ }
+ );
+
+ die "Error sending mail ($sender): $Mail::Sender::Error"
+ unless ref $sender;
+
+ ref $sender->MailMsg( { msg => $p{message} } )
+ or die "Error sending mail: $Mail::Sender::Error";
+ };
+
+ warn $@ if $@;
+}
+
+1;
+
+# ABSTRACT: Subclass of Log::Dispatch::Email that uses the Mail::Sender module
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Log::Dispatch::Email::MailSender - Subclass of Log::Dispatch::Email that uses the Mail::Sender module
+
+=head1 VERSION
+
+version 2.45
+
+=head1 SYNOPSIS
+
+ use Log::Dispatch;
+
+ my $log = Log::Dispatch->new(
+ outputs => [
+ [
+ 'Email::MailSender',
+ min_level => 'emerg',
+ to => [qw( foo@example.com bar@example.org )],
+ subject => 'Big error!'
+ ]
+ ],
+ );
+
+ $log->emerg("Something bad is happening");
+
+=head1 DESCRIPTION
+
+This is a subclass of L<Log::Dispatch::Email> that implements the send_email
+method using the L<Mail::Sender> module.
+
+=head1 CONSTRUCTOR
+
+The constructor takes the following parameters in addition to the parameters
+documented in L<Log::Dispatch::Output> and L<Log::Dispatch::Email>:
+
+=over 4
+
+=item * smtp ($)
+
+The smtp server to connect to. This defaults to "localhost".
+
+=item * port ($)
+
+The port to use when connecting. This defaults to 25.
+
+=back
+
+=head1 AUTHOR
+
+Dave Rolsky <autarch@urth.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is Copyright (c) 2015 by Dave Rolsky.
+
+This is free software, licensed under:
+
+ The Artistic License 2.0 (GPL Compatible)
+
+=cut
diff --git a/lib/Log/Dispatch/Email/MailSendmail.pm b/lib/Log/Dispatch/Email/MailSendmail.pm
new file mode 100644
index 0000000..c00f8c4
--- /dev/null
+++ b/lib/Log/Dispatch/Email/MailSendmail.pm
@@ -0,0 +1,83 @@
+package Log::Dispatch::Email::MailSendmail;
+
+use strict;
+use warnings;
+
+our $VERSION = '2.45';
+
+use Log::Dispatch::Email;
+
+use base qw( Log::Dispatch::Email );
+
+use Mail::Sendmail ();
+
+sub send_email {
+ my $self = shift;
+ my %p = @_;
+
+ my %mail = (
+ To => ( join ',', @{ $self->{to} } ),
+ Subject => $self->{subject},
+ Message => $p{message},
+
+ # Mail::Sendmail insists on having this parameter.
+ From => $self->{from} || 'LogDispatch@foo.bar',
+ );
+
+ local $?;
+ unless ( Mail::Sendmail::sendmail(%mail) ) {
+ warn "Error sending mail: $Mail::Sendmail::error";
+ }
+}
+
+1;
+
+# ABSTRACT: Subclass of Log::Dispatch::Email that uses the Mail::Sendmail module
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Log::Dispatch::Email::MailSendmail - Subclass of Log::Dispatch::Email that uses the Mail::Sendmail module
+
+=head1 VERSION
+
+version 2.45
+
+=head1 SYNOPSIS
+
+ use Log::Dispatch;
+
+ my $log = Log::Dispatch->new(
+ outputs => [
+ [
+ 'Email::MailSendmail',
+ min_level => 'emerg',
+ to => [qw( foo@example.com bar@example.org )],
+ subject => 'Big error!'
+ ]
+ ],
+ );
+
+ $log->emerg("Something bad is happening");
+
+=head1 DESCRIPTION
+
+This is a subclass of L<Log::Dispatch::Email> that implements the
+send_email method using the L<Mail::Sendmail> module.
+
+=head1 AUTHOR
+
+Dave Rolsky <autarch@urth.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is Copyright (c) 2015 by Dave Rolsky.
+
+This is free software, licensed under:
+
+ The Artistic License 2.0 (GPL Compatible)
+
+=cut
diff --git a/lib/Log/Dispatch/File.pm b/lib/Log/Dispatch/File.pm
new file mode 100644
index 0000000..4b39ecb
--- /dev/null
+++ b/lib/Log/Dispatch/File.pm
@@ -0,0 +1,285 @@
+package Log::Dispatch::File;
+
+use strict;
+use warnings;
+
+our $VERSION = '2.45';
+
+use Log::Dispatch::Output;
+
+use base qw( Log::Dispatch::Output );
+
+use Params::Validate qw(validate SCALAR BOOLEAN);
+Params::Validate::validation_options( allow_extra => 1 );
+
+use Scalar::Util qw( openhandle );
+
+# Prevents death later on if IO::File can't export this constant.
+*O_APPEND = \&APPEND unless defined &O_APPEND;
+
+sub APPEND {0}
+
+sub new {
+ my $proto = shift;
+ my $class = ref $proto || $proto;
+
+ my %p = @_;
+
+ my $self = bless {}, $class;
+
+ $self->_basic_init(%p);
+ $self->_make_handle;
+
+ return $self;
+}
+
+sub _basic_init {
+ my $self = shift;
+
+ $self->SUPER::_basic_init(@_);
+
+ my %p = validate(
+ @_, {
+ filename => { type => SCALAR },
+ mode => {
+ type => SCALAR,
+ default => '>'
+ },
+ binmode => {
+ type => SCALAR,
+ default => undef
+ },
+ autoflush => {
+ type => BOOLEAN,
+ default => 1
+ },
+ close_after_write => {
+ type => BOOLEAN,
+ default => 0
+ },
+ permissions => {
+ type => SCALAR,
+ optional => 1
+ },
+ syswrite => {
+ type => BOOLEAN,
+ default => 0
+ },
+ }
+ );
+
+ $self->{filename} = $p{filename};
+ $self->{binmode} = $p{binmode};
+ $self->{autoflush} = $p{autoflush};
+ $self->{close} = $p{close_after_write};
+ $self->{permissions} = $p{permissions};
+ $self->{syswrite} = $p{syswrite};
+
+ if ( $self->{close} ) {
+ $self->{mode} = '>>';
+ }
+ elsif (
+ exists $p{mode}
+ && defined $p{mode}
+ && (
+ $p{mode} =~ /^(?:>>|append)$/
+ || ( $p{mode} =~ /^\d+$/
+ && $p{mode} == O_APPEND() )
+ )
+ ) {
+ $self->{mode} = '>>';
+ }
+ else {
+ $self->{mode} = '>';
+ }
+
+}
+
+sub _make_handle {
+ my $self = shift;
+
+ $self->_open_file() unless $self->{close};
+}
+
+sub _open_file {
+ my $self = shift;
+
+ open my $fh, $self->{mode}, $self->{filename}
+ or die "Cannot write to '$self->{filename}': $!";
+
+ if ( $self->{autoflush} ) {
+ my $oldfh = select $fh;
+ $| = 1;
+ select $oldfh;
+ }
+
+ if ( $self->{permissions}
+ && !$self->{chmodded} ) {
+ my $current_mode = ( stat $self->{filename} )[2] & 07777;
+ if ( $current_mode ne $self->{permissions} ) {
+ chmod $self->{permissions}, $self->{filename}
+ or die
+ "Cannot chmod $self->{filename} to $self->{permissions}: $!";
+ }
+
+ $self->{chmodded} = 1;
+ }
+
+ if ( $self->{binmode} ) {
+ binmode $fh, $self->{binmode};
+ }
+
+ $self->{fh} = $fh;
+}
+
+sub log_message {
+ my $self = shift;
+ my %p = @_;
+
+ if ( $self->{close} ) {
+ $self->_open_file;
+ }
+
+ my $fh = $self->{fh};
+
+ if ( $self->{syswrite} ) {
+ defined syswrite( $fh, $p{message} )
+ or die "Cannot write to '$self->{filename}': $!";
+ }
+ else {
+ print $fh $p{message}
+ or die "Cannot write to '$self->{filename}': $!";
+ }
+
+ if ( $self->{close} ) {
+ close $fh
+ or die "Cannot close '$self->{filename}': $!";
+ }
+}
+
+sub DESTROY {
+ my $self = shift;
+
+ if ( $self->{fh} ) {
+ my $fh = $self->{fh};
+ close $fh if openhandle($fh);
+ }
+}
+
+1;
+
+# ABSTRACT: Object for logging to files
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Log::Dispatch::File - Object for logging to files
+
+=head1 VERSION
+
+version 2.45
+
+=head1 SYNOPSIS
+
+ use Log::Dispatch;
+
+ my $log = Log::Dispatch->new(
+ outputs => [
+ [
+ 'File',
+ min_level => 'info',
+ filename => 'Somefile.log',
+ mode => '>>',
+ newline => 1
+ ]
+ ],
+ );
+
+ $log->emerg("I've fallen and I can't get up");
+
+=head1 DESCRIPTION
+
+This module provides a simple object for logging to files under the
+Log::Dispatch::* system.
+
+Note that a newline will I<not> be added automatically at the end of a message
+by default. To do that, pass C<< newline => 1 >>.
+
+=for Pod::Coverage new log_message
+
+=head1 CONSTRUCTOR
+
+The constructor takes the following parameters in addition to the standard
+parameters documented in L<Log::Dispatch::Output>:
+
+=over 4
+
+=item * filename ($)
+
+The filename to be opened for writing.
+
+=item * mode ($)
+
+The mode the file should be opened with. Valid options are 'write',
+'>', 'append', '>>', or the relevant constants from Fcntl. The
+default is 'write'.
+
+=item * binmode ($)
+
+A layer name to be passed to binmode, like ":encoding(UTF-8)" or ":raw".
+
+=item * close_after_write ($)
+
+Whether or not the file should be closed after each write. This
+defaults to false.
+
+If this is true, then the mode will always be append, so that the file is not
+re-written for each new message.
+
+=item * autoflush ($)
+
+Whether or not the file should be autoflushed. This defaults to true.
+
+=item * syswrite ($)
+
+Whether or not to perform the write using L<perlfunc/syswrite>(),
+as opposed to L<perlfunc/print>(). This defaults to false.
+The usual caveats and warnings as documented in L<perlfunc/syswrite> apply.
+
+=item * permissions ($)
+
+If the file does not already exist, the permissions that it should
+be created with. Optional. The argument passed must be a valid
+octal value, such as 0600 or the constants available from Fcntl, like
+S_IRUSR|S_IWUSR.
+
+See L<perlfunc/chmod> for more on potential traps when passing octal
+values around. Most importantly, remember that if you pass a string
+that looks like an octal value, like this:
+
+ my $mode = '0644';
+
+Then the resulting file will end up with permissions like this:
+
+ --w----r-T
+
+which is probably not what you want.
+
+=back
+
+=head1 AUTHOR
+
+Dave Rolsky <autarch@urth.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is Copyright (c) 2015 by Dave Rolsky.
+
+This is free software, licensed under:
+
+ The Artistic License 2.0 (GPL Compatible)
+
+=cut
diff --git a/lib/Log/Dispatch/File/Locked.pm b/lib/Log/Dispatch/File/Locked.pm
new file mode 100644
index 0000000..aac9496
--- /dev/null
+++ b/lib/Log/Dispatch/File/Locked.pm
@@ -0,0 +1,96 @@
+package Log::Dispatch::File::Locked;
+
+use strict;
+use warnings;
+
+use base qw( Log::Dispatch::File );
+
+our $VERSION = '2.45';
+
+use Fcntl qw(:DEFAULT :flock);
+
+sub _open_file {
+ my $self = shift;
+
+ $self->SUPER::_open_file();
+
+ my $fh = $self->{fh};
+
+ flock( $fh, LOCK_EX )
+ or die "Cannot lock '$self->{filename}' for writing: $!";
+
+ # just in case there was an append while we waited for the lock
+ seek( $fh, 0, 2 )
+ or die "Cannot seek to end of '$self->{filename}': $!";
+}
+
+1;
+
+# ABSTRACT: Subclass of Log::Dispatch::File to facilitate locking
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Log::Dispatch::File::Locked - Subclass of Log::Dispatch::File to facilitate locking
+
+=head1 VERSION
+
+version 2.45
+
+=head1 SYNOPSIS
+
+ use Log::Dispatch;
+
+ my $log = Log::Dispatch->new(
+ outputs => [
+ [
+ 'File::Locked',
+ min_level => 'info',
+ filename => 'Somefile.log',
+ mode => '>>',
+ newline => 1
+ ]
+ ],
+ );
+
+ $log->emerg("I've fallen and I can't get up");
+
+=head1 DESCRIPTION
+
+This module acts exactly like L<Log::Dispatch::File> except that it
+obtains an exclusive lock on the file while opening it.
+
+=head1 CAVEATS
+
+B<DANGER!> Use very carefully in multi-process environments. Because the lock
+is obtained at file open time, not at write time, you may experience deadlocks
+in your system.
+
+You can partially work around this by using the C<close_after_write> option,
+which causes the file to be re-opened every time a log message is written.
+
+Alternatively, the C<syswrite> option does atomic writes, which may mean that
+you don't need locking at all.
+
+See L<Log::Dispatch::File>) for details on these options.
+
+=head1 SEE ALSO
+
+L<perlfunc/flock>
+
+=head1 AUTHOR
+
+Dave Rolsky <autarch@urth.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is Copyright (c) 2015 by Dave Rolsky.
+
+This is free software, licensed under:
+
+ The Artistic License 2.0 (GPL Compatible)
+
+=cut
diff --git a/lib/Log/Dispatch/Handle.pm b/lib/Log/Dispatch/Handle.pm
new file mode 100644
index 0000000..3e3ed45
--- /dev/null
+++ b/lib/Log/Dispatch/Handle.pm
@@ -0,0 +1,102 @@
+package Log::Dispatch::Handle;
+
+use strict;
+use warnings;
+
+our $VERSION = '2.45';
+
+use Log::Dispatch::Output;
+
+use base qw( Log::Dispatch::Output );
+
+use Params::Validate qw(validate SCALAR ARRAYREF BOOLEAN);
+Params::Validate::validation_options( allow_extra => 1 );
+
+sub new {
+ my $proto = shift;
+ my $class = ref $proto || $proto;
+
+ my %p = validate( @_, { handle => { can => 'print' } } );
+
+ my $self = bless {}, $class;
+
+ $self->_basic_init(%p);
+ $self->{handle} = $p{handle};
+
+ return $self;
+}
+
+sub log_message {
+ my $self = shift;
+ my %p = @_;
+
+ $self->{handle}->print( $p{message} )
+ or die "Cannot write to handle: $!";
+}
+
+1;
+
+# ABSTRACT: Object for logging to IO::Handle classes
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Log::Dispatch::Handle - Object for logging to IO::Handle classes
+
+=head1 VERSION
+
+version 2.45
+
+=head1 SYNOPSIS
+
+ use Log::Dispatch;
+
+ my $log = Log::Dispatch->new(
+ outputs => [
+ [
+ 'Handle',
+ min_level => 'emerg',
+ handle => $io_socket_object,
+ ],
+ ]
+ );
+
+ $log->emerg('I am the Lizard King!');
+
+=head1 DESCRIPTION
+
+This module supplies a very simple object for logging to some sort of
+handle object. Basically, anything that implements a C<print()>
+method can be passed the object constructor and it should work.
+
+=for Pod::Coverage new log_message
+
+=head1 CONSTRUCTOR
+
+The constructor takes the following parameters in addition to the standard
+parameters documented in L<Log::Dispatch::Output>:
+
+=over 4
+
+=item * handle ($)
+
+The handle object. This object must implement a C<print()> method.
+
+=back
+
+=head1 AUTHOR
+
+Dave Rolsky <autarch@urth.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is Copyright (c) 2015 by Dave Rolsky.
+
+This is free software, licensed under:
+
+ The Artistic License 2.0 (GPL Compatible)
+
+=cut
diff --git a/lib/Log/Dispatch/Null.pm b/lib/Log/Dispatch/Null.pm
new file mode 100644
index 0000000..09495b7
--- /dev/null
+++ b/lib/Log/Dispatch/Null.pm
@@ -0,0 +1,69 @@
+package Log::Dispatch::Null;
+
+use strict;
+use warnings;
+
+our $VERSION = '2.45';
+
+use Log::Dispatch::Output;
+
+use base qw( Log::Dispatch::Output );
+
+sub new {
+ my $proto = shift;
+ my $class = ref $proto || $proto;
+
+ my $self = bless {}, $class;
+
+ $self->_basic_init(@_);
+
+ return $self;
+}
+
+sub log_message { }
+
+1;
+
+# ABSTRACT: Object that accepts messages and does nothing
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Log::Dispatch::Null - Object that accepts messages and does nothing
+
+=head1 VERSION
+
+version 2.45
+
+=head1 SYNOPSIS
+
+ use Log::Dispatch;
+
+ my $null
+ = Log::Dispatch->new( outputs => [ [ 'Null', min_level => 'debug' ] ] );
+
+ $null->emerg( "I've fallen and I can't get up" );
+
+=head1 DESCRIPTION
+
+This class provides a null logging object. Messages can be sent to the
+object but it does nothing with them.
+
+=for Pod::Coverage new log_message
+
+=head1 AUTHOR
+
+Dave Rolsky <autarch@urth.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is Copyright (c) 2015 by Dave Rolsky.
+
+This is free software, licensed under:
+
+ The Artistic License 2.0 (GPL Compatible)
+
+=cut
diff --git a/lib/Log/Dispatch/Output.pm b/lib/Log/Dispatch/Output.pm
new file mode 100644
index 0000000..7c6ca09
--- /dev/null
+++ b/lib/Log/Dispatch/Output.pm
@@ -0,0 +1,315 @@
+package Log::Dispatch::Output;
+
+use strict;
+use warnings;
+
+our $VERSION = '2.45';
+
+use Log::Dispatch;
+
+use base qw( Log::Dispatch::Base );
+
+use Params::Validate qw(validate SCALAR ARRAYREF CODEREF BOOLEAN);
+Params::Validate::validation_options( allow_extra => 1 );
+
+use Carp ();
+
+my $level_names
+ = [qw( debug info notice warning error critical alert emergency )];
+my $ln = 0;
+my $level_numbers = {
+ ( map { $_ => $ln++ } @{$level_names} ),
+ warn => 3,
+ err => 4,
+ crit => 5,
+ emerg => 7
+};
+
+sub new {
+ my $proto = shift;
+ my $class = ref $proto || $proto;
+
+ die "The new method must be overridden in the $class subclass";
+}
+
+sub log {
+ my $self = shift;
+
+ my %p = validate(
+ @_, {
+ level => { type => SCALAR },
+ message => { type => SCALAR },
+ }
+ );
+
+ return unless $self->_should_log( $p{level} );
+
+ $p{message} = $self->_apply_callbacks(%p)
+ if $self->{callbacks};
+
+ $self->log_message(%p);
+}
+
+sub _basic_init {
+ my $self = shift;
+
+ my %p = validate(
+ @_, {
+ name => { type => SCALAR, optional => 1 },
+ min_level => { type => SCALAR, required => 1 },
+ max_level => {
+ type => SCALAR,
+ optional => 1
+ },
+ callbacks => {
+ type => ARRAYREF | CODEREF,
+ optional => 1
+ },
+ newline => { type => BOOLEAN, optional => 1 },
+ }
+ );
+
+ $self->{level_names} = $level_names;
+ $self->{level_numbers} = $level_numbers;
+
+ $self->{name} = $p{name} || $self->_unique_name();
+
+ $self->{min_level} = $self->_level_as_number( $p{min_level} );
+ die "Invalid level specified for min_level"
+ unless defined $self->{min_level};
+
+ # Either use the parameter supplied or just the highest possible level.
+ $self->{max_level} = (
+ exists $p{max_level}
+ ? $self->_level_as_number( $p{max_level} )
+ : $#{ $self->{level_names} }
+ );
+
+ die "Invalid level specified for max_level"
+ unless defined $self->{max_level};
+
+ my @cb = $self->_get_callbacks(%p);
+ $self->{callbacks} = \@cb if @cb;
+
+ if ( $p{newline} ) {
+ push @{ $self->{callbacks} }, \&_add_newline_callback;
+ }
+}
+
+sub name {
+ my $self = shift;
+
+ return $self->{name};
+}
+
+sub min_level {
+ my $self = shift;
+
+ return $self->{level_names}[ $self->{min_level} ];
+}
+
+sub max_level {
+ my $self = shift;
+
+ return $self->{level_names}[ $self->{max_level} ];
+}
+
+sub accepted_levels {
+ my $self = shift;
+
+ return @{ $self->{level_names} }
+ [ $self->{min_level} .. $self->{max_level} ];
+}
+
+sub _should_log {
+ my $self = shift;
+
+ my $msg_level = $self->_level_as_number(shift);
+ return ( ( $msg_level >= $self->{min_level} )
+ && ( $msg_level <= $self->{max_level} ) );
+}
+
+sub _level_as_number {
+ my $self = shift;
+ my $level = shift;
+
+ unless ( defined $level ) {
+ Carp::croak "undefined value provided for log level";
+ }
+
+ return $level if $level =~ /^\d$/;
+
+ unless ( Log::Dispatch->level_is_valid($level) ) {
+ Carp::croak "$level is not a valid Log::Dispatch log level";
+ }
+
+ return $self->{level_numbers}{$level};
+}
+
+sub _level_as_name {
+ my $self = shift;
+ my $level = shift;
+
+ unless ( defined $level ) {
+ Carp::croak "undefined value provided for log level";
+ }
+
+ return $level unless $level =~ /^\d$/;
+
+ return $self->{level_names}[$level];
+}
+
+my $_unique_name_counter = 0;
+
+sub _unique_name {
+ my $self = shift;
+
+ return '_anon_' . $_unique_name_counter++;
+}
+
+sub _add_newline_callback {
+
+ # This weird construct is an optimization since this might be called a lot
+ # - see https://github.com/autarch/Log-Dispatch/pull/7
+ +{@_}->{message} . "\n";
+}
+
+1;
+
+# ABSTRACT: Base class for all Log::Dispatch::* objects
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Log::Dispatch::Output - Base class for all Log::Dispatch::* objects
+
+=head1 VERSION
+
+version 2.45
+
+=head1 SYNOPSIS
+
+ package Log::Dispatch::MySubclass;
+
+ use Log::Dispatch::Output;
+ use base qw( Log::Dispatch::Output );
+
+ sub new {
+ my $proto = shift;
+ my $class = ref $proto || $proto;
+
+ my %p = @_;
+
+ my $self = bless {}, $class;
+
+ $self->_basic_init(%p);
+
+ # Do more if you like
+
+ return $self;
+ }
+
+ sub log_message {
+ my $self = shift;
+ my %p = @_;
+
+ # Do something with message in $p{message}
+ }
+
+ 1;
+
+=head1 DESCRIPTION
+
+This module is the base class from which all Log::Dispatch::* objects
+should be derived.
+
+=head1 CONSTRUCTOR
+
+The constructor, C<new>, must be overridden in a subclass. See L<Output
+Classes|Log::Dispatch/OUTPUT CLASSES> for a description of the common
+parameters accepted by this constructor.
+
+=head1 METHODS
+
+This class provides the following methods:
+
+=head2 $output->_basic_init(%p)
+
+This should be called from a subclass's constructor. Make sure to
+pass the arguments in @_ to it. It sets the object's name and minimum
+level from the passed parameters It also sets up two other attributes which
+are used by other Log::Dispatch::Output methods, level_names and level_numbers.
+Subclasses will perform parameter validation in this method, and must also call
+the superclass's method.
+
+=head2 $output->name
+
+Returns the object's name.
+
+=head2 $output->min_level
+
+Returns the object's minimum log level.
+
+=head2 $output->max_level
+
+Returns the object's maximum log level.
+
+=head2 $output->accepted_levels
+
+Returns a list of the object's accepted levels (by name) from minimum
+to maximum.
+
+=head2 $output->log( level => $, message => $ )
+
+Sends a message if the level is greater than or equal to the object's
+minimum level. This method applies any message formatting callbacks
+that the object may have.
+
+=head2 $output->_should_log ($)
+
+This method is called from the C<log()> method with the log level of
+the message to be logged as an argument. It returns a boolean value
+indicating whether or not the message should be logged by this
+particular object. The C<log()> method will not process the message
+if the return value is false.
+
+=head2 $output->_level_as_number ($)
+
+This method will take a log level as a string (or a number) and return
+the number of that log level. If not given an argument, it returns
+the calling object's log level instead. If it cannot determine the
+level then it will croak.
+
+=head2 $output->add_callback( $code )
+
+Adds a callback (like those given during construction). It is added to the end
+of the list of callbacks.
+
+=head1 SUBCLASSING
+
+This class should be used as the base class for all logging objects
+you create that you would like to work under the Log::Dispatch
+architecture. Subclassing is fairly trivial. For most subclasses, if
+you simply copy the code in the SYNOPSIS and then put some
+functionality into the C<log_message> method then you should be all
+set. Please make sure to use the C<_basic_init> method as described above.
+
+The actual logging implementation should be done in a C<log_message>
+method that you write. B<Do not override C<log>!>.
+
+=head1 AUTHOR
+
+Dave Rolsky <autarch@urth.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is Copyright (c) 2015 by Dave Rolsky.
+
+This is free software, licensed under:
+
+ The Artistic License 2.0 (GPL Compatible)
+
+=cut
diff --git a/lib/Log/Dispatch/Screen.pm b/lib/Log/Dispatch/Screen.pm
new file mode 100644
index 0000000..4b93464
--- /dev/null
+++ b/lib/Log/Dispatch/Screen.pm
@@ -0,0 +1,118 @@
+package Log::Dispatch::Screen;
+
+use strict;
+use warnings;
+
+our $VERSION = '2.45';
+
+use Log::Dispatch::Output;
+
+use base qw( Log::Dispatch::Output );
+
+use Params::Validate qw(validate BOOLEAN);
+Params::Validate::validation_options( allow_extra => 1 );
+
+sub new {
+ my $proto = shift;
+ my $class = ref $proto || $proto;
+
+ my %p = validate(
+ @_, {
+ stderr => {
+ type => BOOLEAN,
+ default => 1
+ },
+ }
+ );
+
+ my $self = bless {}, $class;
+
+ $self->_basic_init(%p);
+ $self->{stderr} = exists $p{stderr} ? $p{stderr} : 1;
+
+ return $self;
+}
+
+sub log_message {
+ my $self = shift;
+ my %p = @_;
+
+ if ( $self->{stderr} ) {
+ print STDERR $p{message};
+ }
+ else {
+ print STDOUT $p{message};
+ }
+}
+
+1;
+
+# ABSTRACT: Object for logging to the screen
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Log::Dispatch::Screen - Object for logging to the screen
+
+=head1 VERSION
+
+version 2.45
+
+=head1 SYNOPSIS
+
+ use Log::Dispatch;
+
+ my $log = Log::Dispatch->new(
+ outputs => [
+ [
+ 'Screen',
+ min_level => 'debug',
+ stderr => 1,
+ newline => 1
+ ]
+ ],
+ );
+
+ $log->alert("I'm searching the city for sci-fi wasabi");
+
+=head1 DESCRIPTION
+
+This module provides an object for logging to the screen (really
+STDOUT or STDERR).
+
+Note that a newline will I<not> be added automatically at the end of a
+message by default. To do that, pass C<< newline => 1 >>.
+
+=for Pod::Coverage new log_message
+
+=head1 CONSTRUCTOR
+
+The constructor takes the following parameters in addition to the standard
+parameters documented in L<Log::Dispatch::Output>:
+
+=over 4
+
+=item * stderr (0 or 1)
+
+Indicates whether or not logging information should go to STDERR. If
+false, logging information is printed to STDOUT instead. This
+defaults to true.
+
+=back
+
+=head1 AUTHOR
+
+Dave Rolsky <autarch@urth.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is Copyright (c) 2015 by Dave Rolsky.
+
+This is free software, licensed under:
+
+ The Artistic License 2.0 (GPL Compatible)
+
+=cut
diff --git a/lib/Log/Dispatch/Syslog.pm b/lib/Log/Dispatch/Syslog.pm
new file mode 100644
index 0000000..6084025
--- /dev/null
+++ b/lib/Log/Dispatch/Syslog.pm
@@ -0,0 +1,220 @@
+package Log::Dispatch::Syslog;
+
+use strict;
+use warnings;
+
+our $VERSION = '2.45';
+
+use Log::Dispatch::Output;
+
+use base qw( Log::Dispatch::Output );
+
+use Params::Validate qw(validate ARRAYREF BOOLEAN HASHREF SCALAR);
+Params::Validate::validation_options( allow_extra => 1 );
+
+use Scalar::Util qw( reftype );
+use Sys::Syslog 0.28 ();
+
+sub new {
+ my $proto = shift;
+ my $class = ref $proto || $proto;
+
+ my %p = @_;
+
+ my $self = bless {}, $class;
+
+ $self->_basic_init(%p);
+ $self->_init(%p);
+
+ return $self;
+}
+
+my ($Ident) = $0 =~ /(.+)/;
+
+sub _init {
+ my $self = shift;
+
+ my %p = validate(
+ @_, {
+ ident => {
+ type => SCALAR,
+ default => $Ident
+ },
+ logopt => {
+ type => SCALAR,
+ default => ''
+ },
+ facility => {
+ type => SCALAR,
+ default => 'user'
+ },
+ socket => {
+ type => SCALAR | ARRAYREF | HASHREF,
+ default => undef
+ },
+ lock => {
+ type => BOOLEAN,
+ default => 0,
+ },
+ }
+ );
+
+ $self->{$_} = $p{$_} for qw( ident logopt facility socket lock );
+ if ( $self->{lock} ) {
+ require threads;
+ require threads::shared;
+ }
+
+ $self->{priorities} = [
+ 'DEBUG',
+ 'INFO',
+ 'NOTICE',
+ 'WARNING',
+ 'ERR',
+ 'CRIT',
+ 'ALERT',
+ 'EMERG'
+ ];
+}
+
+my $thread_lock : shared = 0;
+
+sub log_message {
+ my $self = shift;
+ my %p = @_;
+
+ my $pri = $self->_level_as_number( $p{level} );
+
+ lock($thread_lock) if $self->{lock};
+
+ eval {
+ if ( defined $self->{socket} ) {
+ Sys::Syslog::setlogsock(
+ ref $self->{socket} && reftype( $self->{socket} ) eq 'ARRAY'
+ ? @{ $self->{socket} }
+ : $self->{socket}
+ );
+ }
+
+ Sys::Syslog::openlog(
+ $self->{ident},
+ $self->{logopt},
+ $self->{facility}
+ );
+ Sys::Syslog::syslog( $self->{priorities}[$pri], $p{message} );
+ Sys::Syslog::closelog;
+ };
+
+ warn $@ if $@ and $^W;
+}
+
+1;
+
+# ABSTRACT: Object for logging to system log.
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Log::Dispatch::Syslog - Object for logging to system log.
+
+=head1 VERSION
+
+version 2.45
+
+=head1 SYNOPSIS
+
+ use Log::Dispatch;
+
+ my $log = Log::Dispatch->new(
+ outputs => [
+ [
+ 'Syslog',
+ min_level => 'info',
+ ident => 'Yadda yadda'
+ ]
+ ]
+ );
+
+ $log->emerg("Time to die.");
+
+=head1 DESCRIPTION
+
+This module provides a simple object for sending messages to the
+system log (via UNIX syslog calls).
+
+Note that logging may fail if you try to pass UTF-8 characters in the
+log message. If logging fails and warnings are enabled, the error
+message will be output using Perl's C<warn>.
+
+=for Pod::Coverage new log_message
+
+=head1 CONSTRUCTOR
+
+The constructor takes the following parameters in addition to the standard
+parameters documented in L<Log::Dispatch::Output>:
+
+=over 4
+
+=item * ident ($)
+
+This string will be prepended to all messages in the system log.
+Defaults to $0.
+
+=item * logopt ($)
+
+A string containing the log options (separated by any separator you
+like). See the openlog(3) and Sys::Syslog docs for more details.
+Defaults to ''.
+
+=item * facility ($)
+
+Specifies what type of program is doing the logging to the system log.
+Valid options are 'auth', 'authpriv', 'cron', 'daemon', 'kern',
+'local0' through 'local7', 'mail, 'news', 'syslog', 'user',
+'uucp'. Defaults to 'user'
+
+=item * socket ($, \@, or \%)
+
+Tells what type of socket to use for sending syslog messages. Valid
+options are listed in C<Sys::Syslog>.
+
+If you don't provide this, then we let C<Sys::Syslog> simply pick one
+that works, which is the preferred option, as it makes your code more
+portable.
+
+If you pass an array reference, it is dereferenced and passed to
+C<Sys::Syslog::setlogsock()>.
+
+If you pass a hash reference, it is passed to C<Sys::Syslog::setlogsock()> as
+is.
+
+=item * lock ($)
+
+If this is set to a true value, then the calls to C<setlogsock()>,
+C<openlog()>, C<syslog()>, and C<closelog()> will all be guarded by a
+thread-locked variable.
+
+This is only relevant when running you are using Perl threads in your
+application. Setting this to a true value will cause the L<threads> and
+L<threads::shared> modules to be loaded.
+
+This defaults to false.
+
+=back
+
+=head1 AUTHOR
+
+Dave Rolsky <autarch@urth.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is Copyright (c) 2015 by Dave Rolsky.
+
+This is free software, licensed under:
+
+ The Artistic License 2.0 (GPL Compatible)
+
+=cut
diff --git a/perlcriticrc b/perlcriticrc
new file mode 100644
index 0000000..1ab0851
--- /dev/null
+++ b/perlcriticrc
@@ -0,0 +1,58 @@
+severity = 3
+verbose = 11
+theme = core + pbp + bugs + maintenance + cosmetic + complexity + security + tests + moose
+
+exclude = Subroutines::ProhibitCallsToUndeclaredSubs
+
+[BuiltinFunctions::ProhibitStringySplit]
+severity = 3
+
+[CodeLayout::RequireTrailingCommas]
+severity = 3
+
+[ControlStructures::ProhibitCStyleForLoops]
+severity = 3
+
+[InputOutput::RequireCheckedSyscalls]
+functions = :builtins
+exclude_functions = sleep
+severity = 3
+
+[RegularExpressions::ProhibitComplexRegexes]
+max_characters = 200
+
+[RegularExpressions::ProhibitUnusualDelimiters]
+severity = 3
+
+[Subroutines::ProhibitUnusedPrivateSubroutines]
+private_name_regex = _(?!build)\w+
+
+[TestingAndDebugging::ProhibitNoWarnings]
+allow = redefine
+
+[ValuesAndExpressions::ProhibitEmptyQuotes]
+severity = 3
+
+[ValuesAndExpressions::ProhibitInterpolationOfLiterals]
+severity = 3
+
+[ValuesAndExpressions::RequireUpperCaseHeredocTerminator]
+severity = 3
+
+[Variables::ProhibitPackageVars]
+add_packages = Carp Test::Builder
+
+[-Subroutines::RequireFinalReturn]
+
+[-ErrorHandling::RequireCarping]
+
+# No need for /xsm everywhere
+[-RegularExpressions::RequireDotMatchAnything]
+[-RegularExpressions::RequireExtendedFormatting]
+[-RegularExpressions::RequireLineBoundaryMatching]
+
+# http://stackoverflow.com/questions/2275317/why-does-perlcritic-dislike-using-shift-to-populate-subroutine-variables
+[-Subroutines::RequireArgUnpacking]
+
+# "use v5.14" is more readable than "use 5.014"
+[-ValuesAndExpressions::ProhibitVersionStrings]
diff --git a/perltidyrc b/perltidyrc
new file mode 100644
index 0000000..b54e60d
--- /dev/null
+++ b/perltidyrc
@@ -0,0 +1,22 @@
+-l=78
+-i=4
+-ci=4
+-se
+-b
+-bar
+-boc
+-vt=0
+-vtc=0
+-cti=0
+-pt=1
+-bt=1
+-sbt=1
+-bbt=1
+-nolq
+-npro
+-nsfs
+--blank-lines-before-packages=0
+--opening-hash-brace-right
+--no-outdent-long-comments
+--iterations=2
+-wbb="% + - * / x != == >= <= =~ !~ < > | & >= < = **= += *= &= <<= &&= -= /= |= >>= ||= .= %= ^= x="
diff --git a/t/00-compile.t b/t/00-compile.t
new file mode 100644
index 0000000..9e006b4
--- /dev/null
+++ b/t/00-compile.t
@@ -0,0 +1,38 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+my %deps = (
+ ApacheLog => 'Apache::Log',
+ Code => q{},
+ File => q{},
+ 'File::Locked' => q{},
+ Handle => q{},
+ Null => q{},
+ Screen => q{},
+ Syslog => 'Sys::Syslog 0.28',
+ 'Email::MailSend' => 'Mail::Send',
+ 'Email::MIMELite' => 'MIME::Lite',
+ 'Email::MailSendmail' => 'Mail::Sendmail',
+ 'Email::MailSender' => 'Mail::Sender',
+);
+
+use_ok('Log::Dispatch');
+
+for my $subclass ( sort keys %deps ) {
+ my $module = "Log::Dispatch::$subclass";
+
+ if ( !$deps{$subclass}
+ || ( eval "use $deps{$subclass}; 1" && !$@ ) ) {
+ use_ok($module);
+ }
+ else {
+ SKIP:
+ {
+ skip "Cannot load $module without $deps{$subclass}", 1;
+ }
+ }
+}
+
+done_testing();
diff --git a/t/00-report-prereqs.dd b/t/00-report-prereqs.dd
new file mode 100644
index 0000000..0f214b2
--- /dev/null
+++ b/t/00-report-prereqs.dd
@@ -0,0 +1,60 @@
+do { my $x = {
+ 'configure' => {
+ 'requires' => {
+ 'Dist::CheckConflicts' => '0.02',
+ 'ExtUtils::MakeMaker' => '0'
+ }
+ },
+ 'develop' => {
+ 'requires' => {
+ 'Code::TidyAll' => '0.24',
+ 'Perl::Critic' => '1.123',
+ 'Perl::Tidy' => '20140711',
+ 'Pod::Coverage::TrustPod' => '0',
+ 'Test::CPAN::Changes' => '0.19',
+ 'Test::Code::TidyAll' => '0.24',
+ 'Test::EOL' => '0',
+ 'Test::More' => '0.88',
+ 'Test::NoTabs' => '0',
+ 'Test::Pod' => '1.41',
+ 'Test::Pod::Coverage' => '1.08',
+ 'Test::Spelling' => '0.12',
+ 'Test::Version' => '1'
+ }
+ },
+ 'runtime' => {
+ 'requires' => {
+ 'Carp' => '0',
+ 'Devel::GlobalDestruction' => '0',
+ 'Dist::CheckConflicts' => '0.02',
+ 'Fcntl' => '0',
+ 'Module::Runtime' => '0',
+ 'Params::Validate' => '0.15',
+ 'Scalar::Util' => '0',
+ 'Sys::Syslog' => '0.28',
+ 'base' => '0',
+ 'perl' => '5.006',
+ 'strict' => '0',
+ 'warnings' => '0'
+ }
+ },
+ 'test' => {
+ 'recommends' => {
+ 'CPAN::Meta' => '2.120900'
+ },
+ 'requires' => {
+ 'Data::Dumper' => '0',
+ 'Exporter' => '0',
+ 'ExtUtils::MakeMaker' => '0',
+ 'File::Spec' => '0',
+ 'File::Temp' => '0',
+ 'IO::File' => '0',
+ 'Test::Fatal' => '0',
+ 'Test::More' => '0.96',
+ 'Test::Requires' => '0',
+ 'lib' => '0'
+ }
+ }
+ };
+ $x;
+ } \ No newline at end of file
diff --git a/t/00-report-prereqs.t b/t/00-report-prereqs.t
new file mode 100644
index 0000000..d8d15ba
--- /dev/null
+++ b/t/00-report-prereqs.t
@@ -0,0 +1,183 @@
+#!perl
+
+use strict;
+use warnings;
+
+# This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.021
+
+use Test::More tests => 1;
+
+use ExtUtils::MakeMaker;
+use File::Spec;
+
+# from $version::LAX
+my $lax_version_re =
+ qr/(?: undef | (?: (?:[0-9]+) (?: \. | (?:\.[0-9]+) (?:_[0-9]+)? )?
+ |
+ (?:\.[0-9]+) (?:_[0-9]+)?
+ ) | (?:
+ v (?:[0-9]+) (?: (?:\.[0-9]+)+ (?:_[0-9]+)? )?
+ |
+ (?:[0-9]+)? (?:\.[0-9]+){2,} (?:_[0-9]+)?
+ )
+ )/x;
+
+# hide optional CPAN::Meta modules from prereq scanner
+# and check if they are available
+my $cpan_meta = "CPAN::Meta";
+my $cpan_meta_pre = "CPAN::Meta::Prereqs";
+my $HAS_CPAN_META = eval "require $cpan_meta; $cpan_meta->VERSION('2.120900')" && eval "require $cpan_meta_pre"; ## no critic
+
+# Verify requirements?
+my $DO_VERIFY_PREREQS = 1;
+
+sub _max {
+ my $max = shift;
+ $max = ( $_ > $max ) ? $_ : $max for @_;
+ return $max;
+}
+
+sub _merge_prereqs {
+ my ($collector, $prereqs) = @_;
+
+ # CPAN::Meta::Prereqs object
+ if (ref $collector eq $cpan_meta_pre) {
+ return $collector->with_merged_prereqs(
+ CPAN::Meta::Prereqs->new( $prereqs )
+ );
+ }
+
+ # Raw hashrefs
+ for my $phase ( keys %$prereqs ) {
+ for my $type ( keys %{ $prereqs->{$phase} } ) {
+ for my $module ( keys %{ $prereqs->{$phase}{$type} } ) {
+ $collector->{$phase}{$type}{$module} = $prereqs->{$phase}{$type}{$module};
+ }
+ }
+ }
+
+ return $collector;
+}
+
+my @include = qw(
+
+);
+
+my @exclude = qw(
+
+);
+
+# Add static prereqs to the included modules list
+my $static_prereqs = do 't/00-report-prereqs.dd';
+
+# Merge all prereqs (either with ::Prereqs or a hashref)
+my $full_prereqs = _merge_prereqs(
+ ( $HAS_CPAN_META ? $cpan_meta_pre->new : {} ),
+ $static_prereqs
+);
+
+# Add dynamic prereqs to the included modules list (if we can)
+my ($source) = grep { -f } 'MYMETA.json', 'MYMETA.yml';
+if ( $source && $HAS_CPAN_META ) {
+ if ( my $meta = eval { CPAN::Meta->load_file($source) } ) {
+ $full_prereqs = _merge_prereqs($full_prereqs, $meta->prereqs);
+ }
+}
+else {
+ $source = 'static metadata';
+}
+
+my @full_reports;
+my @dep_errors;
+my $req_hash = $HAS_CPAN_META ? $full_prereqs->as_string_hash : $full_prereqs;
+
+# Add static includes into a fake section
+for my $mod (@include) {
+ $req_hash->{other}{modules}{$mod} = 0;
+}
+
+for my $phase ( qw(configure build test runtime develop other) ) {
+ next unless $req_hash->{$phase};
+ next if ($phase eq 'develop' and not $ENV{AUTHOR_TESTING});
+
+ for my $type ( qw(requires recommends suggests conflicts modules) ) {
+ next unless $req_hash->{$phase}{$type};
+
+ my $title = ucfirst($phase).' '.ucfirst($type);
+ my @reports = [qw/Module Want Have/];
+
+ for my $mod ( sort keys %{ $req_hash->{$phase}{$type} } ) {
+ next if $mod eq 'perl';
+ next if grep { $_ eq $mod } @exclude;
+
+ my $file = $mod;
+ $file =~ s{::}{/}g;
+ $file .= ".pm";
+ my ($prefix) = grep { -e File::Spec->catfile($_, $file) } @INC;
+
+ my $want = $req_hash->{$phase}{$type}{$mod};
+ $want = "undef" unless defined $want;
+ $want = "any" if !$want && $want == 0;
+
+ my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required";
+
+ if ($prefix) {
+ my $have = MM->parse_version( File::Spec->catfile($prefix, $file) );
+ $have = "undef" unless defined $have;
+ push @reports, [$mod, $want, $have];
+
+ if ( $DO_VERIFY_PREREQS && $HAS_CPAN_META && $type eq 'requires' ) {
+ if ( $have !~ /\A$lax_version_re\z/ ) {
+ push @dep_errors, "$mod version '$have' cannot be parsed ($req_string)";
+ }
+ elsif ( ! $full_prereqs->requirements_for( $phase, $type )->accepts_module( $mod => $have ) ) {
+ push @dep_errors, "$mod version '$have' is not in required range '$want'";
+ }
+ }
+ }
+ else {
+ push @reports, [$mod, $want, "missing"];
+
+ if ( $DO_VERIFY_PREREQS && $type eq 'requires' ) {
+ push @dep_errors, "$mod is not installed ($req_string)";
+ }
+ }
+ }
+
+ if ( @reports ) {
+ push @full_reports, "=== $title ===\n\n";
+
+ my $ml = _max( map { length $_->[0] } @reports );
+ my $wl = _max( map { length $_->[1] } @reports );
+ my $hl = _max( map { length $_->[2] } @reports );
+
+ if ($type eq 'modules') {
+ splice @reports, 1, 0, ["-" x $ml, "", "-" x $hl];
+ push @full_reports, map { sprintf(" %*s %*s\n", -$ml, $_->[0], $hl, $_->[2]) } @reports;
+ }
+ else {
+ splice @reports, 1, 0, ["-" x $ml, "-" x $wl, "-" x $hl];
+ push @full_reports, map { sprintf(" %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2]) } @reports;
+ }
+
+ push @full_reports, "\n";
+ }
+ }
+}
+
+if ( @full_reports ) {
+ diag "\nVersions for all modules listed in $source (including optional ones):\n\n", @full_reports;
+}
+
+if ( @dep_errors ) {
+ diag join("\n",
+ "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n",
+ "The following REQUIRED prerequisites were not satisfied:\n",
+ @dep_errors,
+ "\n"
+ );
+}
+
+pass;
+
+# vim: ts=4 sts=4 sw=4 et:
diff --git a/t/01-basic.t b/t/01-basic.t
new file mode 100644
index 0000000..293b098
--- /dev/null
+++ b/t/01-basic.t
@@ -0,0 +1,1195 @@
+use strict;
+use warnings;
+
+use Test::More 0.88;
+use Test::Fatal;
+
+use File::Spec;
+use File::Temp qw( tempdir );
+use Module::Runtime qw( use_module );
+
+use Log::Dispatch;
+
+my %tests;
+
+BEGIN {
+ local $@;
+ foreach (qw( MailSend MIMELite MailSendmail MailSender )) {
+ eval "use Log::Dispatch::Email::$_";
+ $tests{$_} = !$@;
+ $tests{$_} = 0 if $ENV{LD_NO_MAIL};
+ }
+}
+
+my %TestConfig;
+if ( my $email_address = $ENV{LOG_DISPATCH_TEST_EMAIL} ) {
+ %TestConfig = ( email_address => $email_address );
+}
+
+my @syswrite_strs;
+
+BEGIN {
+ if ( $] >= 5.016 ) {
+ my $syswrite = \&CORE::syswrite;
+ *CORE::GLOBAL::syswrite = sub {
+ my ( $fh, $str, @other ) = @_;
+ push @syswrite_strs, $_[1];
+
+ return $syswrite->( $fh, $str, @other );
+ };
+ }
+}
+
+use Log::Dispatch::File;
+use Log::Dispatch::Handle;
+use Log::Dispatch::Null;
+use Log::Dispatch::Screen;
+
+use IO::File;
+
+my $tempdir = tempdir( CLEANUP => 1 );
+
+my $dispatch = Log::Dispatch->new;
+ok( $dispatch, "created Log::Dispatch object" );
+
+# Test Log::Dispatch::File
+{
+ my $emerg_log = File::Spec->catdir( $tempdir, 'emerg.log' );
+
+ $dispatch->add(
+ Log::Dispatch::File->new(
+ name => 'file1',
+ min_level => 'emerg',
+ filename => $emerg_log
+ )
+ );
+
+ $dispatch->log( level => 'info', message => "info level 1\n" );
+ $dispatch->log( level => 'emerg', message => "emerg level 1\n" );
+
+ my $debug_log = File::Spec->catdir( $tempdir, 'debug.log' );
+
+ $dispatch->add(
+ Log::Dispatch::File->new(
+ name => 'file2',
+ min_level => 'debug',
+ syswrite => 1,
+ filename => $debug_log
+ )
+ );
+
+ my %outputs = map { $_->name() => ref $_ } $dispatch->outputs();
+ is_deeply(
+ \%outputs, {
+ file1 => 'Log::Dispatch::File',
+ file2 => 'Log::Dispatch::File',
+ },
+ '->outputs() method returns all output objects'
+ );
+
+ $dispatch->log( level => 'info', message => "info level 2\n" );
+ $dispatch->log( level => 'emerg', message => "emerg level 2\n" );
+
+ # This'll close them filehandles!
+ undef $dispatch;
+
+ open my $emerg_fh, '<', $emerg_log
+ or die "Can't read $emerg_log: $!";
+ open my $debug_fh, '<', $debug_log
+ or die "Can't read $debug_log: $!";
+
+ my @log = <$emerg_fh>;
+ is(
+ $log[0], "emerg level 1\n",
+ "First line in log file set to level 'emerg' is 'emerg level 1'"
+ );
+
+ is(
+ $log[1], "emerg level 2\n",
+ "Second line in log file set to level 'emerg' is 'emerg level 2'"
+ );
+
+ @log = <$debug_fh>;
+ is(
+ $log[0], "info level 2\n",
+ "First line in log file set to level 'debug' is 'info level 2'"
+ );
+
+ is(
+ $log[1], "emerg level 2\n",
+ "Second line in log file set to level 'debug' is 'emerg level 2'"
+ );
+
+SKIP:
+ {
+ skip 'This test requires Perl 5.16+', 1
+ unless $] >= 5.016;
+ is_deeply(
+ \@syswrite_strs,
+ [
+ "info level 2\n",
+ "emerg level 2\n",
+ ],
+ 'second LD object used syswrite',
+ );
+ }
+}
+
+# max_level test
+{
+ my $max_log = File::Spec->catfile( $tempdir, 'max.log' );
+
+ my $dispatch = Log::Dispatch->new;
+ $dispatch->add(
+ Log::Dispatch::File->new(
+ name => 'file1',
+ min_level => 'debug',
+ max_level => 'crit',
+ filename => $max_log
+ )
+ );
+
+ $dispatch->log( level => 'emerg', message => "emergency\n" );
+ $dispatch->log( level => 'crit', message => "critical\n" );
+
+ undef $dispatch; # close file handles
+
+ open my $fh, '<', $max_log
+ or die "Can't read $max_log: $!";
+ my @log = <$fh>;
+
+ is(
+ $log[0], "critical\n",
+ "First line in log file with a max level of 'crit' is 'critical'"
+ );
+}
+
+# Log::Dispatch::Handle test
+{
+ my $handle_log = File::Spec->catfile( $tempdir, 'handle.log' );
+
+ my $fh = IO::File->new( $handle_log, 'w' )
+ or die "Can't write to $handle_log: $!";
+
+ my $dispatch = Log::Dispatch->new;
+ $dispatch->add(
+ Log::Dispatch::Handle->new(
+ name => 'handle',
+ min_level => 'debug',
+ handle => $fh
+ )
+ );
+
+ $dispatch->log( level => 'notice', message => "handle test\n" );
+
+ # close file handles
+ undef $dispatch;
+ undef $fh;
+
+ open $fh, '<', $handle_log
+ or die "Can't open $handle_log: $!";
+
+ my @log = <$fh>;
+
+ close $fh;
+
+ is(
+ $log[0], "handle test\n",
+ "Log::Dispatch::Handle created log file should contain 'handle test\\n'"
+ );
+}
+
+# Log::Dispatch::Email::MailSend
+SKIP:
+{
+ skip "Cannot do MailSend tests", 1
+ unless $tests{MailSend} && $TestConfig{email_address};
+
+ my $dispatch = Log::Dispatch->new;
+
+ $dispatch->add(
+ Log::Dispatch::Email::MailSend->new(
+ name => 'Mail::Send',
+ min_level => 'debug',
+ to => $TestConfig{email_address},
+ subject => 'Log::Dispatch test suite'
+ )
+ );
+
+ $dispatch->log(
+ level => 'emerg',
+ message =>
+ "Mail::Send test - If you can read this then the test succeeded (PID $$)"
+ );
+
+ diag(
+ "Sending email with Mail::Send to $TestConfig{email_address}.\nIf you get it then the test succeeded (PID $$)\n"
+ );
+ undef $dispatch;
+
+ ok( 1, 'sent email via MailSend' );
+}
+
+# Log::Dispatch::Email::MailSendmail
+SKIP:
+{
+ skip "Cannot do MailSendmail tests", 1
+ unless $tests{MailSendmail} && $TestConfig{email_address};
+
+ my $dispatch = Log::Dispatch->new;
+
+ $dispatch->add(
+ Log::Dispatch::Email::MailSendmail->new(
+ name => 'Mail::Sendmail',
+ min_level => 'debug',
+ to => $TestConfig{email_address},
+ subject => 'Log::Dispatch test suite'
+ )
+ );
+
+ $dispatch->log(
+ level => 'emerg',
+ message =>
+ "Mail::Sendmail test - If you can read this then the test succeeded (PID $$)"
+ );
+
+ diag(
+ "Sending email with Mail::Sendmail to $TestConfig{email_address}.\nIf you get it then the test succeeded (PID $$)\n"
+ );
+ undef $dispatch;
+
+ ok( 1, 'sent email via MailSendmail' );
+}
+
+# Log::Dispatch::Email::MIMELite
+SKIP:
+{
+
+ skip "Cannot do MIMELite tests", 1
+ unless $tests{MIMELite} && $TestConfig{email_address};
+
+ my $dispatch = Log::Dispatch->new;
+
+ $dispatch->add(
+ Log::Dispatch::Email::MIMELite->new(
+ name => 'Mime::Lite',
+ min_level => 'debug',
+ to => $TestConfig{email_address},
+ subject => 'Log::Dispatch test suite'
+ )
+ );
+
+ $dispatch->log(
+ level => 'emerg',
+ message =>
+ "MIME::Lite - If you can read this then the test succeeded (PID $$)"
+ );
+
+ diag(
+ "Sending email with MIME::Lite to $TestConfig{email_address}.\nIf you get it then the test succeeded (PID $$)\n"
+ );
+ undef $dispatch;
+
+ ok( 1, 'sent mail via MIMELite' );
+}
+
+# Log::Dispatch::Screen
+{
+ my $dispatch = Log::Dispatch->new;
+
+ $dispatch->add(
+ Log::Dispatch::Screen->new(
+ name => 'screen',
+ min_level => 'debug',
+ stderr => 0
+ )
+ );
+
+ my $text;
+ tie *STDOUT, 'Test::Tie::STDOUT', \$text;
+ $dispatch->log( level => 'crit', message => 'testing screen' );
+ untie *STDOUT;
+
+ is(
+ $text, 'testing screen',
+ "Log::Dispatch::Screen outputs to STDOUT"
+ );
+}
+
+# Log::Dispatch::Output->accepted_levels
+{
+ my $l = Log::Dispatch::Screen->new(
+ name => 'foo',
+ min_level => 'warning',
+ max_level => 'alert',
+ stderr => 0
+ );
+
+ my @expected = qw(warning error critical alert);
+ my @levels = $l->accepted_levels;
+
+ my $pass = 1;
+ for ( my $x = 0; $x < scalar @expected; $x++ ) {
+ $pass = 0 unless $expected[$x] eq $levels[$x];
+ }
+
+ is(
+ scalar @expected, scalar @levels,
+ "number of levels matched"
+ );
+
+ ok( $pass, "levels matched" );
+}
+
+# Log::Dispatch single callback
+{
+ my $reverse = sub { my %p = @_; return reverse $p{message}; };
+ my $dispatch = Log::Dispatch->new( callbacks => $reverse );
+
+ my $string;
+ $dispatch->add(
+ Log::Dispatch::String->new(
+ name => 'foo',
+ string => \$string,
+ min_level => 'warning',
+ max_level => 'alert',
+ )
+ );
+
+ $dispatch->log( level => 'warning', message => 'esrever' );
+
+ is(
+ $string, 'reverse',
+ "callback to reverse text"
+ );
+}
+
+# Log::Dispatch multiple callbacks
+{
+ my $reverse = sub { my %p = @_; return reverse $p{message}; };
+ my $uc = sub { my %p = @_; return uc $p{message}; };
+
+ my $dispatch = Log::Dispatch->new( callbacks => [ $reverse, $uc ] );
+
+ my $string;
+ $dispatch->add(
+ Log::Dispatch::String->new(
+ name => 'foo',
+ string => \$string,
+ min_level => 'warning',
+ max_level => 'alert',
+ )
+ );
+
+ $dispatch->log( level => 'warning', message => 'esrever' );
+
+ is(
+ $string, 'REVERSE',
+ "callback to reverse and uppercase text"
+ );
+
+ is_deeply(
+ [ $dispatch->callbacks() ],
+ [ $reverse, $uc ],
+ '->callbacks() method returns all of the callback subs'
+ );
+
+ my $clone = $dispatch->clone();
+ is_deeply(
+ $clone,
+ $dispatch,
+ 'clone is a shallow clone of the original object'
+ );
+
+ $clone->add(
+ Log::Dispatch::Screen->new(
+ name => 'screen',
+ min_level => 'debug',
+ )
+ );
+ my @orig_outputs = map { $_->name() } $dispatch->outputs();
+ my @clone_outputs = map { $_->name() } $clone->outputs();
+ isnt(
+ scalar(@orig_outputs),
+ scalar(@clone_outputs),
+ 'clone is not the same as original after adding an output'
+ );
+
+ $clone->add_callback( sub { return 'foo' } );
+ my @orig_cb = $dispatch->callbacks();
+ my @clone_cb = $clone->callbacks();
+ isnt(
+ scalar(@orig_cb),
+ scalar(@clone_cb),
+ 'clone is not the same as original after adding a callback'
+ );
+}
+
+# Log::Dispatch::Output single callback
+{
+ my $reverse = sub { my %p = @_; return reverse $p{message}; };
+
+ my $dispatch = Log::Dispatch->new;
+
+ my $string;
+ $dispatch->add(
+ Log::Dispatch::String->new(
+ name => 'foo',
+ string => \$string,
+ min_level => 'warning',
+ max_level => 'alert',
+ callbacks => $reverse
+ )
+ );
+
+ $dispatch->log( level => 'warning', message => 'esrever' );
+
+ is(
+ $string, 'reverse',
+ "Log::Dispatch::Output callback to reverse text"
+ );
+}
+
+# Log::Dispatch::Output multiple callbacks
+{
+ my $reverse = sub { my %p = @_; return reverse $p{message}; };
+ my $uc = sub { my %p = @_; return uc $p{message}; };
+
+ my $dispatch = Log::Dispatch->new;
+
+ my $string;
+ $dispatch->add(
+ Log::Dispatch::String->new(
+ name => 'foo',
+ string => \$string,
+ min_level => 'warning',
+ max_level => 'alert',
+ callbacks => [ $reverse, $uc ]
+ )
+ );
+
+ $dispatch->log( level => 'warning', message => 'esrever' );
+
+ is(
+ $string, 'REVERSE',
+ "Log::Dispatch::Output callbacks to reverse and uppercase text"
+ );
+}
+
+# test level parameter to callbacks
+{
+ my $level = sub { my %p = @_; return uc $p{level}; };
+
+ my $dispatch = Log::Dispatch->new( callbacks => $level );
+
+ my $string;
+ $dispatch->add(
+ Log::Dispatch::String->new(
+ name => 'foo',
+ string => \$string,
+ min_level => 'warning',
+ max_level => 'alert',
+ stderr => 0
+ )
+ );
+
+ $dispatch->log( level => 'warning', message => 'esrever' );
+
+ is(
+ $string, 'WARNING',
+ "Log::Dispatch callback to uppercase the level parameter"
+ );
+}
+
+# Comprehensive test of new methods that match level names
+{
+ my %levels = map { $_ => $_ }
+ (qw( debug info notice warning error critical alert emergency ));
+ @levels{qw( warn err crit emerg )}
+ = (qw( warning error critical emergency ));
+
+ foreach my $allowed_level (
+ qw( debug info notice warning error critical alert emergency )) {
+ my $dispatch = Log::Dispatch->new;
+
+ my $string;
+ $dispatch->add(
+ Log::Dispatch::String->new(
+ name => 'foo',
+ string => \$string,
+ min_level => $allowed_level,
+ max_level => $allowed_level,
+ )
+ );
+
+ foreach my $test_level (
+ qw( debug info notice warn warning err
+ error crit critical alert emerg emergency )
+ ) {
+ $string = '';
+ $dispatch->$test_level( $test_level, 'test' );
+
+ if ( $levels{$test_level} eq $allowed_level ) {
+ my $expect = join $", $test_level, 'test';
+ is(
+ $string, $expect,
+ "Calling $test_level method should send message '$expect'"
+ );
+ }
+ else {
+ ok(
+ !length $string,
+ "Calling $test_level method should not log anything"
+ );
+ }
+ }
+ }
+}
+
+{
+ my $string;
+ my $dispatch = Log::Dispatch->new(
+ outputs => [
+ [
+ 'String',
+ name => 'string',
+ string => \$string,
+ min_level => 'debug',
+ ],
+ ],
+ );
+
+ $dispatch->debug( 'foo', 'bar' );
+ is(
+ $string,
+ 'foo bar',
+ 'passing multiple elements to ->debug stringifies them like an array'
+ );
+
+ $string = q{};
+ $dispatch->debug( sub {'foo'} );
+ is(
+ $string,
+ 'foo',
+ 'passing single sub ref to ->debug calls the sub ref'
+ );
+
+}
+
+# Log::Dispatch->level_is_valid method
+{
+ foreach my $l (
+ qw( debug info notice warning err error
+ crit critical alert emerg emergency )
+ ) {
+ ok( Log::Dispatch->level_is_valid($l), "$l is valid level" );
+ }
+
+ foreach my $l (qw( debu inf foo bar )) {
+ ok( !Log::Dispatch->level_is_valid($l), "$l is not valid level" );
+ }
+
+ # Provide calling line if level missing
+ my $string;
+ my $dispatch = Log::Dispatch->new(
+ outputs => [
+ [
+ 'String',
+ name => 'string',
+ string => \$string,
+ min_level => 'debug',
+ ],
+ ],
+ );
+
+ like(
+ exception { $dispatch->log( msg => "Message" ) },
+ qr/Logging level was not provided at .* line \d+./,
+ "Provide calling line if level not provided"
+ );
+}
+
+# make sure passing mode as write works
+{
+ my $mode_log = File::Spec->catfile( $tempdir, 'mode.log' );
+
+ my $f1 = Log::Dispatch::File->new(
+ name => 'file',
+ min_level => 1,
+ filename => $mode_log,
+ mode => 'write',
+ );
+ $f1->log(
+ level => 'emerg',
+ message => "test2\n"
+ );
+
+ undef $f1;
+
+ open my $fh, '<', $mode_log
+ or die "Cannot read $mode_log: $!";
+ my $data = join '', <$fh>;
+ close $fh;
+
+ like( $data, qr/^test2/, "test write mode" );
+}
+
+# Log::Dispatch::Email::MailSender
+SKIP:
+{
+ skip "Cannot do MailSender tests", 1
+ unless $tests{MailSender} && $TestConfig{email_address};
+
+ my $dispatch = Log::Dispatch->new;
+
+ $dispatch->add(
+ Log::Dispatch::Email::MailSender->new(
+ name => 'Mail::Sender',
+ min_level => 'debug',
+ smtp => 'localhost',
+ to => $TestConfig{email_address},
+ subject => 'Log::Dispatch test suite'
+ )
+ );
+
+ $dispatch->log(
+ level => 'emerg',
+ message =>
+ "Mail::Sender - If you can read this then the test succeeded (PID $$)"
+ );
+
+ diag(
+ "Sending email with Mail::Sender to $TestConfig{email_address}.\nIf you get it then the test succeeded (PID $$)\n"
+ );
+ undef $dispatch;
+
+ ok( 1, 'sent email via MailSender' );
+}
+
+# dispatcher exists
+{
+ my $dispatch = Log::Dispatch->new;
+
+ $dispatch->add(
+ Log::Dispatch::Screen->new(
+ name => 'yomama',
+ min_level => 'alert'
+ )
+ );
+
+ ok(
+ $dispatch->output('yomama'),
+ "yomama output should exist"
+ );
+
+ ok(
+ !$dispatch->output('nomama'),
+ "nomama output should not exist"
+ );
+}
+
+# Test Log::Dispatch::File - close_after_write & permissions
+{
+ my $dispatch = Log::Dispatch->new;
+
+ my $close_log = File::Spec->catfile( $tempdir, 'close.log' );
+
+ $dispatch->add(
+ Log::Dispatch::File->new(
+ name => 'close',
+ min_level => 'info',
+ filename => $close_log,
+ permissions => 0777,
+ close_after_write => 1
+ )
+ );
+
+ $dispatch->log( level => 'info', message => "info\n" );
+
+ open my $fh, '<', $close_log
+ or die "Can't read $close_log: $!";
+
+ my @log = <$fh>;
+ close $fh;
+
+ is(
+ $log[0], "info\n",
+ "First line in log file should be 'info\\n'"
+ );
+
+ my $mode = ( stat $close_log )[2]
+ or die "Cannot stat $close_log: $!";
+
+ my $mode_string = sprintf( '%04o', $mode & 07777 );
+
+ if ( $^O =~ /win32/i ) {
+ ok(
+ $mode_string == '0777' || $mode_string == '0666',
+ "Mode should be 0777 or 0666"
+ );
+ }
+ elsif ( $^O =~ /cygwin/i ) {
+ ok(
+ $mode_string == '0777' || $mode_string == '0644',
+ "Mode should be 0777 or 0644"
+ );
+ }
+ else {
+ is(
+ $mode_string, '0777',
+ "Mode should be 0777"
+ );
+ }
+}
+
+{
+ my $dispatch = Log::Dispatch->new;
+
+ my $chmod_log = File::Spec->catfile( $tempdir, 'chmod.log' );
+
+ open my $fh, '>', $chmod_log
+ or die "Cannot write to $chmod_log: $!";
+ close $fh;
+
+ chmod 0777, $chmod_log
+ or die "Cannot chmod 0777 $chmod_log: $!";
+
+ my @chmod;
+ no warnings 'once';
+ local *CORE::GLOBAL::chmod = sub { @chmod = @_; warn @chmod };
+
+ $dispatch->add(
+ Log::Dispatch::File->new(
+ name => 'chmod',
+ min_level => 'info',
+ filename => $chmod_log,
+ permissions => 0777,
+ )
+ );
+
+ $dispatch->warning('test');
+
+ ok(
+ !scalar @chmod,
+ 'chmod() was not called when permissions already matched what was specified'
+ );
+}
+
+SKIP:
+{
+ skip "Cannot test utf8 files with this version of Perl ($])", 1
+ unless $] >= 5.008;
+
+ my $dispatch = Log::Dispatch->new;
+
+ my $utf8_log = File::Spec->catfile( $tempdir, 'utf8.log' );
+
+ $dispatch->add(
+ Log::Dispatch::File->new(
+ name => 'utf8',
+ min_level => 'info',
+ filename => $utf8_log,
+ binmode => ':encoding(UTF-8)',
+ )
+ );
+
+ my @warnings;
+
+ {
+ local $SIG{__WARN__} = sub { push @warnings, @_ };
+ $dispatch->warning("\x{999A}");
+ }
+
+ ok(
+ !scalar @warnings,
+ 'utf8 binmode was applied to file and no warnings were issued'
+ );
+}
+
+# would_log
+{
+ my $dispatch = Log::Dispatch->new;
+
+ $dispatch->add(
+ Log::Dispatch::Null->new(
+ name => 'null',
+ min_level => 'warning',
+ )
+ );
+
+ ok(
+ !$dispatch->would_log('foo'),
+ "will not log 'foo'"
+ );
+
+ ok(
+ !$dispatch->would_log('debug'),
+ "will not log 'debug'"
+ );
+
+ ok(
+ !$dispatch->is_debug(),
+ 'is_debug returns false'
+ );
+
+ ok(
+ $dispatch->is_warning(),
+ 'is_warning returns true'
+ );
+
+ ok(
+ $dispatch->would_log('crit'),
+ "will log 'crit'"
+ );
+
+ ok(
+ $dispatch->is_crit,
+ "will log 'crit'"
+ );
+}
+
+{
+ my $dispatch = Log::Dispatch->new;
+
+ $dispatch->add(
+ Log::Dispatch::Null->new(
+ name => 'null',
+ min_level => 'info',
+ max_level => 'critical',
+ )
+ );
+
+ my $called = 0;
+ my $message = sub { $called = 1 };
+
+ $dispatch->log( level => 'debug', message => $message );
+ ok( !$called, 'subref is not called if the message would not be logged' );
+
+ $called = 0;
+ $dispatch->log( level => 'warning', message => $message );
+ ok( $called, 'subref is called when message is logged' );
+
+ $called = 0;
+ $dispatch->log( level => 'emergency', message => $message );
+ ok( !$called, 'subref is not called when message would not be logged' );
+}
+
+{
+ my $string;
+
+ my $dispatch = Log::Dispatch->new;
+ $dispatch->add(
+ Log::Dispatch::String->new(
+ name => 'handle',
+ string => \$string,
+ min_level => 'debug',
+ )
+ );
+
+ $dispatch->log(
+ level => 'debug',
+ message => sub {'this is my message'},
+ );
+
+ is(
+ $string, 'this is my message',
+ 'message returned by subref is logged'
+ );
+}
+
+{
+ my $string;
+
+ my $dispatch = Log::Dispatch->new;
+ $dispatch->add(
+ Log::Dispatch::String->new(
+ name => 'handle',
+ string => \$string,
+ min_level => 'debug',
+ newline => 1,
+ )
+ );
+ $dispatch->debug('hello');
+ $dispatch->debug('goodbye');
+
+ is( $string, "hello\ngoodbye\n", 'added newlines' );
+}
+
+{
+ my $string;
+
+ my $dispatch = Log::Dispatch->new;
+ $dispatch->add(
+ Log::Dispatch::String->new(
+ name => 'handle',
+ string => \$string,
+ min_level => 'debug',
+ )
+ );
+
+ my $e = exception {
+ $dispatch->log_and_die(
+ level => 'error',
+ message => 'this is my message',
+ );
+ };
+
+ ok( $e, 'died when calling log_and_die()' );
+ like( $e, qr{this is my message}, 'error contains expected message' );
+ like( $e, qr{01-basic\.t line 9\d\d}, 'error croaked' );
+
+ is( $string, 'this is my message', 'message is logged' );
+
+ undef $string;
+
+ $e = do {
+ local $@;
+ eval { Croaker::croak($dispatch) };
+ $@;
+ };
+
+ ok( $e, 'died when calling log_and_croak()' );
+ like( $e, qr{croak}, 'error contains expected message' );
+ like(
+ $e, qr{01-basic\.t line 10005},
+ 'error croaked from perspective of caller'
+ );
+
+ is( $string, 'croak', 'message is logged' );
+}
+
+{
+ my $string;
+
+ my $dispatch = Log::Dispatch->new;
+ $dispatch->add(
+ Log::Dispatch::String->new(
+ name => 'handle',
+ string => \$string,
+ min_level => 'debug',
+ )
+ );
+
+ $dispatch->log( level => 'debug', message => 'foo' );
+ is( $string, 'foo', 'first test w/o callback' );
+
+ $string = '';
+ $dispatch->add_callback( sub { return 'bar' } );
+ $dispatch->log( level => 'debug', message => 'foo' );
+ is( $string, 'bar', 'second call, callback overrides message' );
+}
+
+{
+ my $string;
+
+ my $dispatch = Log::Dispatch->new(
+ callbacks => sub { return 'baz' },
+ );
+ $dispatch->add(
+ Log::Dispatch::String->new(
+ name => 'handle',
+ string => \$string,
+ min_level => 'debug',
+ )
+ );
+
+ $dispatch->log( level => 'debug', message => 'foo' );
+ is( $string, 'baz', 'first test gets orig callback result' );
+
+ $string = '';
+ $dispatch->add_callback( sub { return 'bar' } );
+ $dispatch->log( level => 'debug', message => 'foo' );
+ is( $string, 'bar', 'second call, callback overrides message' );
+}
+
+{
+ my $string;
+
+ my $dispatch = Log::Dispatch->new;
+ $dispatch->add(
+ Log::Dispatch::String->new(
+ name => 'handle',
+ string => \$string,
+ min_level => 'debug',
+ )
+ );
+
+ $dispatch->log( level => 'debug', message => 'foo' );
+ is( $string, 'foo', 'first test w/o callback' );
+
+ $string = '';
+ $dispatch->add_callback( sub { return 'bar' } );
+ $dispatch->log( level => 'debug', message => 'foo' );
+ is( $string, 'bar', 'second call, callback overrides message' );
+}
+
+{
+ my $string;
+
+ my $dispatch = Log::Dispatch->new(
+ callbacks => sub { return 'baz' },
+ );
+ $dispatch->add(
+ Log::Dispatch::String->new(
+ name => 'handle',
+ string => \$string,
+ min_level => 'debug',
+ )
+ );
+
+ $dispatch->log( level => 'debug', message => 'foo' );
+ is( $string, 'baz', 'first test gets orig callback result' );
+
+ $string = '';
+ $dispatch->add_callback( sub { return 'bar' } );
+ $dispatch->log( level => 'debug', message => 'foo' );
+ is( $string, 'bar', 'second call, callback overrides message' );
+}
+
+{
+
+ # Test defaults
+ my $dispatch = Log::Dispatch::Null->new( min_level => 'debug' );
+ like( $dispatch->name, qr/anon/, 'generated anon name' );
+ is( $dispatch->max_level, 'emergency', 'max_level is emergency' );
+}
+
+{
+ my $level;
+ my $record_level = sub {
+ my %p = @_;
+ $level = $p{level};
+ return %p;
+ };
+
+ my $dispatch = Log::Dispatch->new(
+ callbacks => $record_level,
+ outputs => [
+ [
+ 'Null',
+ name => 'null',
+ min_level => 'debug',
+ ],
+ ],
+ );
+
+ $dispatch->warn('foo');
+ is(
+ $level,
+ 'warning',
+ 'level for call to ->warn is warning'
+ );
+
+ $dispatch->err('foo');
+ is(
+ $level,
+ 'error',
+ 'level for call to ->err is error'
+ );
+
+ $dispatch->crit('foo');
+ is(
+ $level,
+ 'critical',
+ 'level for call to ->crit is critical'
+ );
+
+ $dispatch->emerg('foo');
+ is(
+ $level,
+ 'emergency',
+ 'level for call to ->emerg is emergency'
+ );
+}
+
+{
+ my @calls;
+ my $log = Log::Dispatch->new(
+ outputs => [
+ [
+ 'Code',
+ min_level => 'error',
+ code => sub { push @calls, {@_} },
+ ],
+ ]
+ );
+
+ $log->error('foo');
+ $log->info('bar');
+ $log->critical('baz');
+
+ is_deeply(
+ \@calls,
+ [
+ {
+ level => 'error',
+ message => 'foo',
+ }, {
+ level => 'critical',
+ message => 'baz',
+ },
+ ],
+ 'code received the expected messages'
+ );
+}
+
+done_testing();
+
+package Log::Dispatch::String;
+
+use strict;
+
+use Log::Dispatch::Output;
+
+use base qw( Log::Dispatch::Output );
+
+sub new {
+ my $proto = shift;
+ my $class = ref $proto || $proto;
+ my %p = @_;
+
+ my $self = bless { string => $p{string} }, $class;
+
+ $self->_basic_init(%p);
+
+ return $self;
+}
+
+sub log_message {
+ my $self = shift;
+ my %p = @_;
+
+ ${ $self->{string} } .= $p{message};
+}
+
+# Used for testing Log::Dispatch::Screen
+package Test::Tie::STDOUT;
+
+sub TIEHANDLE {
+ my $class = shift;
+ my $self = {};
+ $self->{string} = shift;
+ ${ $self->{string} } ||= '';
+
+ return bless $self, $class;
+}
+
+sub PRINT {
+ my $self = shift;
+ ${ $self->{string} } .= join '', @_;
+}
+
+sub PRINTF {
+ my $self = shift;
+ my $format = shift;
+ ${ $self->{string} } .= sprintf( $format, @_ );
+}
+
+#line 10000
+package Croaker;
+
+sub croak {
+ my $log = shift;
+
+ $log->log_and_croak( level => 'error', message => 'croak' );
+}
diff --git a/t/02-email-exit.t b/t/02-email-exit.t
new file mode 100644
index 0000000..056b52e
--- /dev/null
+++ b/t/02-email-exit.t
@@ -0,0 +1,15 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+unless ( -d '.git' ) {
+ plan skip_all => 'This test only runs for the maintainer';
+ exit;
+}
+
+system( $^X, 't/email-exit-helper.pl' );
+
+is( $? >> 8, 5, 'exit code of helper was 5' );
+
+done_testing();
diff --git a/t/03-short-syntax.t b/t/03-short-syntax.t
new file mode 100644
index 0000000..2b972bd
--- /dev/null
+++ b/t/03-short-syntax.t
@@ -0,0 +1,77 @@
+use strict;
+use warnings;
+use lib qw(t/lib);
+use Test::More;
+use Log::Dispatch;
+use Log::Dispatch::TestUtil qw(cmp_deeply);
+use File::Temp qw( tempdir );
+
+my $tempdir = tempdir( CLEANUP => 1 );
+
+{
+ my $emerg_log = File::Spec->catdir( $tempdir, 'emerg.log' );
+
+ # Short syntax
+ my $dispatch0 = Log::Dispatch->new(
+ outputs => [
+ [
+ 'File', name => 'file', min_level => 'emerg',
+ filename => $emerg_log
+ ],
+ [
+ '+Log::Dispatch::Screen', name => 'screen',
+ min_level => 'debug'
+ ]
+ ]
+ );
+
+ # Short syntax alternate (2.23)
+ my $dispatch1 = Log::Dispatch->new(
+ outputs => [
+ 'File' => {
+ name => 'file', min_level => 'emerg', filename => $emerg_log
+ },
+ '+Log::Dispatch::Screen' =>
+ { name => 'screen', min_level => 'debug' }
+ ]
+ );
+
+ # Long syntax
+ my $dispatch2 = Log::Dispatch->new;
+ $dispatch2->add(
+ Log::Dispatch::File->new(
+ name => 'file',
+ min_level => 'emerg',
+ filename => $emerg_log
+ )
+ );
+ $dispatch2->add(
+ Log::Dispatch::Screen->new( name => 'screen', min_level => 'debug' )
+ );
+
+ cmp_deeply(
+ $dispatch0, $dispatch2,
+ "created equivalent dispatchers - 0"
+ );
+ cmp_deeply(
+ $dispatch1, $dispatch2,
+ "created equivalent dispatchers - 1"
+ );
+}
+
+{
+ eval { Log::Dispatch->new( outputs => ['File'] ) };
+ like(
+ $@, qr/expected arrayref/,
+ "got error for expected inner arrayref"
+ );
+}
+{
+ eval { Log::Dispatch->new( outputs => 'File' ) };
+ like(
+ $@, qr/not one of the allowed types: arrayref/,
+ "got error for expected outer arrayref"
+ );
+}
+
+done_testing();
diff --git a/t/04-binmode.t b/t/04-binmode.t
new file mode 100644
index 0000000..b5ac1fd
--- /dev/null
+++ b/t/04-binmode.t
@@ -0,0 +1,54 @@
+use strict;
+use warnings;
+
+use File::Spec;
+use File::Temp qw( tempdir );
+use Test::More 0.88;
+
+use Log::Dispatch;
+use Log::Dispatch::File;
+
+plan skip_all => "Cannot test utf8 files with this version of Perl ($])"
+ unless $] >= 5.008;
+
+my $dir = tempdir( CLEANUP => 1 );
+
+my %params = (
+ name => 'file',
+ min_level => 'debug',
+ filename => File::Spec->catfile( $dir, 'logfile_X.txt' ),
+);
+
+my @tests = (
+ {
+ params => { %params, 'binmode' => ':utf8' },
+ message => "foo bar\x{20AC}",
+ expected_message => "foo bar\xe2\x82\xac",
+ },
+);
+
+my $count = 0;
+for my $t (@tests) {
+ my $dispatcher = Log::Dispatch->new();
+ ok( $dispatcher, 'got a logger object' );
+
+ $t->{params}{filename} =~ s/X\.txt$/$count++ . '.txt'/e;
+ my $file = $t->{params}{filename};
+
+ my $logger = Log::Dispatch::File->new( %{ $t->{params} } );
+ ok( $logger, 'got a file output object' );
+
+ $dispatcher->add($logger);
+ $dispatcher->log( level => 'info', message => $t->{message} );
+
+ ok( -e $file, "$file exists" );
+ open my $fh, '<', $file;
+
+ my $line = do { local $/; <$fh> };
+ close $fh;
+
+ is( $line, $t->{expected_message}, 'output contains UTF-8 bytes' );
+}
+
+done_testing();
+
diff --git a/t/05-close-after-write.t b/t/05-close-after-write.t
new file mode 100644
index 0000000..7875e8a
--- /dev/null
+++ b/t/05-close-after-write.t
@@ -0,0 +1,97 @@
+use strict;
+use warnings FATAL => 'all';
+
+use Test::More 0.88;
+
+use File::Spec;
+use File::Temp qw( tempdir );
+use Log::Dispatch;
+
+my $dir = tempdir( CLEANUP => 1 );
+
+# test that the same handle is returned if close-on-write is not set...
+
+{
+ my $logger = Log::Dispatch->new(
+ outputs => [
+ [
+ 'File',
+ min_level => 'debug',
+ newline => 1,
+ name => 'no_caw',
+ filename => File::Spec->catfile( $dir, 'no_caw.log' ),
+ close_after_write => 0,
+ ],
+ [
+ 'File',
+ min_level => 'debug',
+ newline => 1,
+ name => 'caw',
+ filename => File::Spec->catfile( $dir, 'caw.log' ),
+ close_after_write => 1,
+ ],
+ ],
+ );
+
+ ok(
+ $logger->output('no_caw')->{fh},
+ 'no_caw output has created a fh before first write'
+ );
+ ok(
+ !$logger->output('caw')->{fh},
+ 'caw output has not created a fh before first write'
+ );
+
+ $logger->log( level => 'info', message => 'first message' );
+ is(
+ _slurp( $logger->output('no_caw')->{filename} ),
+ "first message\n",
+ 'first line from no_caw output'
+ );
+ is(
+ _slurp( $logger->output('caw')->{filename} ),
+ "first message\n",
+ 'first line from caw output'
+ );
+
+ my %handle = (
+ no_caw => $logger->output('no_caw')->{fh},
+ caw => $logger->output('caw')->{fh},
+ );
+
+ $logger->log( level => 'info', message => 'second message' );
+
+ is(
+ _slurp( $logger->output('no_caw')->{filename} ),
+ "first message\nsecond message\n",
+ 'full content from no_caw output'
+ );
+ is(
+ _slurp( $logger->output('caw')->{filename} ),
+ "first message\nsecond message\n",
+ 'full content from caw output'
+ );
+
+ # check the filehandles again...
+ is(
+ $logger->output('no_caw')->{fh},
+ $handle{no_caw},
+ 'handle has not changed when not using CAW'
+ );
+ isnt(
+ $logger->output('caw')->{fh},
+ $handle{caw},
+ 'handle has changed when using CAW'
+ );
+}
+
+done_testing();
+
+sub _slurp {
+ open my $fh, '<', $_[0]
+ or die "Cannot read $_[0]: $!";
+ return do {
+ local $/;
+ <$fh>;
+ };
+}
diff --git a/t/06-syslog.t b/t/06-syslog.t
new file mode 100644
index 0000000..7ae6eb2
--- /dev/null
+++ b/t/06-syslog.t
@@ -0,0 +1,66 @@
+use strict;
+use warnings;
+
+use Test::More 0.88;
+
+use Test::Requires {
+ 'Sys::Syslog' => '0.28',
+};
+
+use Log::Dispatch;
+use Log::Dispatch::Syslog;
+
+no warnings 'redefine', 'once';
+
+my @sock;
+local *Sys::Syslog::setlogsock = sub { @sock = @_ };
+
+local *Sys::Syslog::openlog = sub { return 1 };
+local *Sys::Syslog::closelog = sub { return 1 };
+
+my @log;
+local *Sys::Syslog::syslog = sub { push @log, [@_] };
+
+{
+ my $dispatch = Log::Dispatch->new;
+ $dispatch->add(
+ Log::Dispatch::Syslog->new(
+ name => 'syslog',
+ min_level => 'debug',
+ )
+ );
+
+ $dispatch->info('Foo');
+
+ ok(
+ !@sock,
+ 'no call to setlogsock unless socket is set explicitly'
+ );
+
+ is_deeply(
+ \@log,
+ [ [ 'INFO', 'Foo' ] ],
+ 'passed message to syslog'
+ );
+}
+
+{
+ my $dispatch = Log::Dispatch->new;
+ $dispatch->add(
+ Log::Dispatch::Syslog->new(
+ name => 'syslog',
+ min_level => 'debug',
+ socket => { type => 'foo' },
+ )
+ );
+
+ $dispatch->info('Foo');
+
+ is_deeply(
+ \@sock,
+ [ { type => 'foo' } ],
+ 'call to setlogsock is made when logging a message if socket was passed to LD::Syslog constructor'
+ );
+}
+
+done_testing();
diff --git a/t/author-eol.t b/t/author-eol.t
new file mode 100644
index 0000000..8a36b70
--- /dev/null
+++ b/t/author-eol.t
@@ -0,0 +1,60 @@
+
+BEGIN {
+ unless ($ENV{AUTHOR_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for testing by the author');
+ }
+}
+
+use strict;
+use warnings;
+
+# this test was generated with Dist::Zilla::Plugin::Test::EOL 0.18
+
+use Test::More 0.88;
+use Test::EOL;
+
+my @files = (
+ 'lib/Log/Dispatch.pm',
+ 'lib/Log/Dispatch/ApacheLog.pm',
+ 'lib/Log/Dispatch/Base.pm',
+ 'lib/Log/Dispatch/Code.pm',
+ 'lib/Log/Dispatch/Conflicts.pm',
+ 'lib/Log/Dispatch/Email.pm',
+ 'lib/Log/Dispatch/Email/MIMELite.pm',
+ 'lib/Log/Dispatch/Email/MailSend.pm',
+ 'lib/Log/Dispatch/Email/MailSender.pm',
+ 'lib/Log/Dispatch/Email/MailSendmail.pm',
+ 'lib/Log/Dispatch/File.pm',
+ 'lib/Log/Dispatch/File/Locked.pm',
+ 'lib/Log/Dispatch/Handle.pm',
+ 'lib/Log/Dispatch/Null.pm',
+ 'lib/Log/Dispatch/Output.pm',
+ 'lib/Log/Dispatch/Screen.pm',
+ 'lib/Log/Dispatch/Syslog.pm',
+ 't/00-compile.t',
+ 't/00-report-prereqs.dd',
+ 't/00-report-prereqs.t',
+ 't/01-basic.t',
+ 't/02-email-exit.t',
+ 't/03-short-syntax.t',
+ 't/04-binmode.t',
+ 't/05-close-after-write.t',
+ 't/06-syslog.t',
+ 't/author-eol.t',
+ 't/author-no-tabs.t',
+ 't/author-pod-spell.t',
+ 't/email-exit-helper.pl',
+ 't/lib/Log/Dispatch/TestUtil.pm',
+ 't/release-cpan-changes.t',
+ 't/release-pod-coverage.t',
+ 't/release-pod-no404s.t',
+ 't/release-pod-syntax.t',
+ 't/release-portability.t',
+ 't/release-test-version.t',
+ 't/release-tidyall.t',
+ 't/sendmail'
+);
+
+eol_unix_ok($_, { trailing_whitespace => 1 }) foreach @files;
+done_testing;
diff --git a/t/author-no-tabs.t b/t/author-no-tabs.t
new file mode 100644
index 0000000..6c3dc9c
--- /dev/null
+++ b/t/author-no-tabs.t
@@ -0,0 +1,60 @@
+
+BEGIN {
+ unless ($ENV{AUTHOR_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for testing by the author');
+ }
+}
+
+use strict;
+use warnings;
+
+# this test was generated with Dist::Zilla::Plugin::Test::NoTabs 0.15
+
+use Test::More 0.88;
+use Test::NoTabs;
+
+my @files = (
+ 'lib/Log/Dispatch.pm',
+ 'lib/Log/Dispatch/ApacheLog.pm',
+ 'lib/Log/Dispatch/Base.pm',
+ 'lib/Log/Dispatch/Code.pm',
+ 'lib/Log/Dispatch/Conflicts.pm',
+ 'lib/Log/Dispatch/Email.pm',
+ 'lib/Log/Dispatch/Email/MIMELite.pm',
+ 'lib/Log/Dispatch/Email/MailSend.pm',
+ 'lib/Log/Dispatch/Email/MailSender.pm',
+ 'lib/Log/Dispatch/Email/MailSendmail.pm',
+ 'lib/Log/Dispatch/File.pm',
+ 'lib/Log/Dispatch/File/Locked.pm',
+ 'lib/Log/Dispatch/Handle.pm',
+ 'lib/Log/Dispatch/Null.pm',
+ 'lib/Log/Dispatch/Output.pm',
+ 'lib/Log/Dispatch/Screen.pm',
+ 'lib/Log/Dispatch/Syslog.pm',
+ 't/00-compile.t',
+ 't/00-report-prereqs.dd',
+ 't/00-report-prereqs.t',
+ 't/01-basic.t',
+ 't/02-email-exit.t',
+ 't/03-short-syntax.t',
+ 't/04-binmode.t',
+ 't/05-close-after-write.t',
+ 't/06-syslog.t',
+ 't/author-eol.t',
+ 't/author-no-tabs.t',
+ 't/author-pod-spell.t',
+ 't/email-exit-helper.pl',
+ 't/lib/Log/Dispatch/TestUtil.pm',
+ 't/release-cpan-changes.t',
+ 't/release-pod-coverage.t',
+ 't/release-pod-no404s.t',
+ 't/release-pod-syntax.t',
+ 't/release-portability.t',
+ 't/release-test-version.t',
+ 't/release-tidyall.t',
+ 't/sendmail'
+);
+
+notabs_ok($_) foreach @files;
+done_testing;
diff --git a/t/author-pod-spell.t b/t/author-pod-spell.t
new file mode 100644
index 0000000..e17f428
--- /dev/null
+++ b/t/author-pod-spell.t
@@ -0,0 +1,97 @@
+
+BEGIN {
+ unless ($ENV{AUTHOR_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for testing by the author');
+ }
+}
+
+use strict;
+use warnings;
+use Test::More;
+
+# generated by Dist::Zilla::Plugin::Test::PodSpelling 2.006009
+use Test::Spelling 0.12;
+use Pod::Wordlist;
+
+
+add_stopwords(<DATA>);
+all_pod_files_spelling_ok( qw( bin lib ) );
+__DATA__
+DROLSKY
+DROLSKY's
+Rolsky
+Rolsky's
+API
+CPAN
+Cholet
+Dumont
+Goess
+Manfredi
+Miyagawa
+PayPal
+Pfeiffer
+STDERR
+STDOUT
+Schilli
+Straup
+Subclasses
+Swartz
+Tatsuhiko
+UTF
+apache
+appenders
+auth
+authpriv
+autoflushed
+classname
+crit
+emerg
+filename
+ident
+kern
+logopt
+multi
+params
+smtp
+stderr
+subclass's
+subclasses
+uucp
+Dave
+autarch
+Karen
+Etheridge
+ether
+Olaf
+Alders
+olaf
+Olivier
+Mengué
+dolmen
+Ross
+Attrill
+ross
+swartz
+Whitney
+Jackson
+whitney
+lib
+Log
+Dispatch
+ApacheLog
+Base
+Code
+Conflicts
+Email
+MIMELite
+MailSend
+MailSender
+MailSendmail
+File
+Locked
+Handle
+Null
+Output
+Screen
+Syslog
diff --git a/t/email-exit-helper.pl b/t/email-exit-helper.pl
new file mode 100755
index 0000000..1ffe5c5
--- /dev/null
+++ b/t/email-exit-helper.pl
@@ -0,0 +1,20 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+use lib './lib', '../lib';
+
+use Log::Dispatch::Email::MailSend;
+
+Mail::Mailer->import( sendmail => 't/sendmail' );
+
+my $email = Log::Dispatch::Email::MailSend->new(
+ name => 'email',
+ min_level => 'emerg',
+ to => 'foo@example.com',
+ subject => 'Log this',
+);
+
+$email->log( message => 'Something bad is happening', level => 'emerg' );
+
+exit 5;
diff --git a/t/lib/Log/Dispatch/TestUtil.pm b/t/lib/Log/Dispatch/TestUtil.pm
new file mode 100644
index 0000000..5d9f5ab
--- /dev/null
+++ b/t/lib/Log/Dispatch/TestUtil.pm
@@ -0,0 +1,44 @@
+package Log::Dispatch::TestUtil;
+use Data::Dumper;
+use strict;
+use warnings;
+use base qw(Exporter);
+
+our @EXPORT_OK = qw(
+ cmp_deeply
+ dump_one_line
+);
+
+sub cmp_deeply {
+ my ( $ref1, $ref2, $name ) = @_;
+
+ my $tb = Test::Builder->new();
+ $tb->is_eq( dump_one_line($ref1), dump_one_line($ref2), $name );
+}
+
+sub dump_one_line {
+ my ($value) = @_;
+
+ return Data::Dumper->new( [$value] )->Indent(0)->Sortkeys(1)
+ ->Quotekeys(0)->Terse(1)->Dump();
+}
+
+1;
+
+# ABSTRACT: Utilities used internally by Log::Dispatch for testing
+
+__END__
+
+=head1 METHODS
+
+=over
+
+=item cmp_deeply
+
+A cheap version of Test::Deep::cmp_deeply.
+
+=item dump_one_line
+
+Dump a value to a single line using Data::Dumper.
+
+=cut
diff --git a/t/release-cpan-changes.t b/t/release-cpan-changes.t
new file mode 100644
index 0000000..214650f
--- /dev/null
+++ b/t/release-cpan-changes.t
@@ -0,0 +1,19 @@
+#!perl
+
+BEGIN {
+ unless ($ENV{RELEASE_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for release candidate testing');
+ }
+}
+
+
+use strict;
+use warnings;
+
+use Test::More 0.96 tests => 2;
+use_ok('Test::CPAN::Changes');
+subtest 'changes_ok' => sub {
+ changes_file_ok('Changes');
+};
+done_testing();
diff --git a/t/release-pod-coverage.t b/t/release-pod-coverage.t
new file mode 100644
index 0000000..55bb523
--- /dev/null
+++ b/t/release-pod-coverage.t
@@ -0,0 +1,63 @@
+#!perl
+
+BEGIN {
+ unless ($ENV{RELEASE_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for release candidate testing');
+ }
+}
+
+# This file was automatically generated by Dist::Zilla::Plugin::Test::Pod::Coverage::Configurable.
+
+use Test::Pod::Coverage 1.08;
+use Test::More 0.88;
+
+BEGIN {
+ if ( $] <= 5.008008 ) {
+ plan skip_all => 'These tests require Pod::Coverage::TrustPod, which only works with Perl 5.8.9+';
+ }
+}
+use Pod::Coverage::TrustPod;
+
+my %skip = map { $_ => 1 } qw( Log::Dispatch::ApacheLog Log::Dispatch::Conflicts );
+
+my @modules;
+for my $module ( all_modules() ) {
+ next if $skip{$module};
+
+ push @modules, $module;
+}
+
+plan skip_all => 'All the modules we found were excluded from POD coverage test.'
+ unless @modules;
+
+plan tests => scalar @modules;
+
+my %trustme = (
+ 'Log::Dispatch::File' => [
+ qr/^(?:O_)?APPEND$/
+ ],
+ 'Log::Dispatch::Output' => [
+ qr/^new$/
+ ],
+ 'Log::Dispatch' => [
+ qr/^(?:warn|err|crit|emerg)$/,
+ qr/^is_\w+$/
+ ]
+ );
+
+my @also_private;
+
+for my $module ( sort @modules ) {
+ pod_coverage_ok(
+ $module,
+ {
+ coverage_class => 'Pod::Coverage::TrustPod',
+ also_private => \@also_private,
+ trustme => $trustme{$module} || [],
+ },
+ "pod coverage for $module"
+ );
+}
+
+done_testing();
diff --git a/t/release-pod-no404s.t b/t/release-pod-no404s.t
new file mode 100644
index 0000000..da185ec
--- /dev/null
+++ b/t/release-pod-no404s.t
@@ -0,0 +1,29 @@
+#!perl
+
+BEGIN {
+ unless ($ENV{RELEASE_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for release candidate testing');
+ }
+}
+
+
+use strict;
+use warnings;
+use Test::More;
+
+foreach my $env_skip ( qw(
+ SKIP_POD_NO404S
+ AUTOMATED_TESTING
+) ){
+ plan skip_all => "\$ENV{$env_skip} is set, skipping"
+ if $ENV{$env_skip};
+}
+
+eval "use Test::Pod::No404s";
+if ( $@ ) {
+ plan skip_all => 'Test::Pod::No404s required for testing POD';
+}
+else {
+ all_pod_files_ok();
+}
diff --git a/t/release-pod-syntax.t b/t/release-pod-syntax.t
new file mode 100644
index 0000000..cdd6a6c
--- /dev/null
+++ b/t/release-pod-syntax.t
@@ -0,0 +1,14 @@
+#!perl
+
+BEGIN {
+ unless ($ENV{RELEASE_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for release candidate testing');
+ }
+}
+
+# This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests.
+use Test::More;
+use Test::Pod 1.41;
+
+all_pod_files_ok();
diff --git a/t/release-portability.t b/t/release-portability.t
new file mode 100644
index 0000000..ad285b4
--- /dev/null
+++ b/t/release-portability.t
@@ -0,0 +1,20 @@
+#!perl
+
+BEGIN {
+ unless ($ENV{RELEASE_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for release candidate testing');
+ }
+}
+
+
+use strict;
+use warnings;
+
+use Test::More;
+
+eval 'use Test::Portability::Files';
+plan skip_all => 'Test::Portability::Files required for testing portability'
+ if $@;
+
+run_tests();
diff --git a/t/release-test-version.t b/t/release-test-version.t
new file mode 100644
index 0000000..aef8636
--- /dev/null
+++ b/t/release-test-version.t
@@ -0,0 +1,46 @@
+
+BEGIN {
+ unless ($ENV{RELEASE_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for release candidate testing');
+ }
+}
+
+use strict;
+use warnings;
+use Test::More;
+
+# generated by Dist::Zilla::Plugin::Test::Version 1.03
+use Test::Version;
+
+my @imports = qw( version_ok );
+
+my $params = {
+ is_strict => 0,
+ has_version => 1,
+
+};
+
+push @imports, $params
+ if version->parse( $Test::Version::VERSION ) >= version->parse('1.002');
+
+
+Test::Version->import(@imports);
+
+version_ok('lib/Log/Dispatch.pm');
+version_ok('lib/Log/Dispatch/ApacheLog.pm');
+version_ok('lib/Log/Dispatch/Base.pm');
+version_ok('lib/Log/Dispatch/Code.pm');
+version_ok('lib/Log/Dispatch/Email.pm');
+version_ok('lib/Log/Dispatch/Email/MIMELite.pm');
+version_ok('lib/Log/Dispatch/Email/MailSend.pm');
+version_ok('lib/Log/Dispatch/Email/MailSender.pm');
+version_ok('lib/Log/Dispatch/Email/MailSendmail.pm');
+version_ok('lib/Log/Dispatch/File.pm');
+version_ok('lib/Log/Dispatch/File/Locked.pm');
+version_ok('lib/Log/Dispatch/Handle.pm');
+version_ok('lib/Log/Dispatch/Null.pm');
+version_ok('lib/Log/Dispatch/Output.pm');
+version_ok('lib/Log/Dispatch/Screen.pm');
+version_ok('lib/Log/Dispatch/Syslog.pm');
+done_testing;
diff --git a/t/release-tidyall.t b/t/release-tidyall.t
new file mode 100644
index 0000000..9625e53
--- /dev/null
+++ b/t/release-tidyall.t
@@ -0,0 +1,17 @@
+#!perl
+
+BEGIN {
+ unless ($ENV{RELEASE_TESTING}) {
+ require Test::More;
+ Test::More::plan(skip_all => 'these tests are for release candidate testing');
+ }
+}
+
+# This file was automatically generated by Dist::Zilla::Plugin::Test::TidyAll
+
+use Test::Code::TidyAll 0.24;
+use Test::More 0.88;
+
+tidyall_ok();
+
+done_testing();
diff --git a/t/sendmail b/t/sendmail
new file mode 100755
index 0000000..d41087f
--- /dev/null
+++ b/t/sendmail
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+exit 0;
diff --git a/tidyall.ini b/tidyall.ini
new file mode 100644
index 0000000..61a76ce
--- /dev/null
+++ b/tidyall.ini
@@ -0,0 +1,10 @@
+[PerlTidy]
+select = **/*.{pl,pm,t,psgi}
+ignore = t/00-*
+ignore = t/author-*
+ignore = t/release-*
+ignore = blib/**/*
+ignore = .build/**/*
+ignore = Log-Dispatch-*/**/*
+ignore = lib/Log/Dispatch/Conflicts.pm
+argv = --profile=$ROOT/perltidyrc
diff --git a/weaver.ini b/weaver.ini
new file mode 100644
index 0000000..90c76a6
--- /dev/null
+++ b/weaver.ini
@@ -0,0 +1,17 @@
+[@CorePrep]
+
+[Name]
+[Version]
+
+[Region / prelude]
+
+[Generic / SYNOPSIS]
+[Generic / DESCRIPTION]
+
+[Leftovers]
+
+[Region / postlude]
+
+[Authors]
+[Contributors]
+[Legal]