summaryrefslogtreecommitdiff
path: root/lib/Log/Dispatch/File.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Log/Dispatch/File.pm')
-rw-r--r--lib/Log/Dispatch/File.pm285
1 files changed, 285 insertions, 0 deletions
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