问题描述
|
我需要将常规Perl代码的输出同时输出到屏幕和日志文件中。但是,问题在于该工具的运行时间可能为数小时。使用Capture :: Tiny \的tee,意味着仅在脚本终止后才将日志文件写入日志,这不是很有用。
为了进一步使事情复杂化,我需要从同一进程以及用system()调用的进程中捕获纯perl的输出。
最后,由于雇主的限制,它也需要在Win32上运行。
我还有什么其他选择?
解决方法
使用PerlIO :: Util。
刚刚在Strawberry Perl 5.12.1 32位下进行了测试,它可以完美运行,因此可以跨平台使用。以下代码完全符合您的期望。并且由于它修改了实际的STDOUT和STDERR文件句柄,因此对它们的任何写操作都会自动进行。
use strict;
use warnings;
use IO::Handle;
use PerlIO::Util;
use 5.012;
for (*STDOUT,*STDERR) {
$_->autoflush; $_->push_layer(tee => \">>stdout.txt\");
}
for (1..10) {
say $_;
warn $_ unless $_ % 2;
}
, 由于提出的解决方案都不令人满意,因此我独自坐下并解决了该问题:
捕获::微小::扩展
, 如果您的程序在Linux / Unix平台上运行,则可以使用tee命令。 Tee读取stdin并将其写入stdout和指定的文件。
例:
myprogram.pl 2>&1 |tee mylog.txt
唯一的警告是stdout和stderr将合并在同一文件中。
由于您使用的是Windows平台,因此可以搜索Google por tee.exe,也可以尝试使用以下简约的perl版本的tee:
$|=1;
if ( !$ARGV[0] ) {
print \"Usage: some_command \\| tee.pl [-a] logfile\\n\";
exit(1);
}
# Append mode
my $mode=\'>\';
if ($ARGV[0] eq \'-a\')
{
$mode=\'>>\';
shift;
}
my $LOGFILE=$ARGV[0];
while (<STDIN>) {
print;
open( OUT,\"$mode $LOGFILE\");
print OUT $_;
close OUT;
# Your logic here!
}
例:
perl myprogram.pl 2>&1 |perl tee.pl mylog.txt
如果您要进行系统调用,我真的会尽量避免修改源代码,以捕获越来越多的STDOUT和/或STDERR。
, 您可以使用IO :: Tee。
创建一个特殊的tee文件句柄。
编辑您的程序。将所有打印更改为stdout更改为该文件句柄的打印。
需要时,重新定义tee文件句柄以仅打印到stdout,或打印到2个或更多文件。
使用``而不是os system()捕获程序输出并将它们打印到特殊的文件句柄。
如果您不想使用任何模块,请创建自己的\“ myprint \”函数。它可以打印到stdout,如果启用了全局标志,也可以打印到日志文件。
sub myPrint
{
print @_;
if ($LOGMODE)
{
open(LOGFILE,\">>$logfile\");
print LOGFILE @_;
close LOGFILE;
}
}
, package Logger ;
# docs at the end ...
# capture conditionally the output of the command
# $objLogger->LogDebugMsg ( \"Running $cmd : \\n $cmd \" ) ;
# $objLogger->LogDebugMsg ( `$cmd 2>&1` ) ;
use lib \'.\' ; use strict ; use warnings ; use Carp qw(cluck);
our ( $MyBareName,$LibDir,$RunDir ) = () ;
BEGIN {
$RunDir = \'\' ;
$0 =~ m/^(.*)(\\\\|\\/)(.*)\\.([a-z]*)/;
$RunDir = $1 if defined $1 ;
push ( @INC,$RunDir) ;
#debug print join ( \' \',@INC ) ;
} #eof sub
use Timer ; use FileHandler ;
# the hash holding the vars
our $confHolder = () ;
# ===============================================================
# START OO
# the constructor
sub new {
my $self = shift;
#get the has containing all the settings
$confHolder = ${ shift @_ } ;
# Set the defaults ...
Initialize () ;
return bless({},$self);
} #eof new
BEGIN {
# strip the remote path and keep the bare name
$0=~m/^(.*)(\\\\|\\/)(.*)\\.([a-z]*)/;
my ( $MyBareName,$RunDir ) = () ;
$MyBareName = $3;
$RunDir= $1 ;
push ( @INC,$RunDir ) ;
} #eof BEGIN
sub AUTOLOAD {
my $self = shift ;
no strict \'refs\';
my $name = our $AUTOLOAD;
*$AUTOLOAD = sub {
my $msg = \"BOOM! BOOM! BOOM! \\n RunTime Error !!!\\nUndefined Function $name(@_)\\n\" ;
print \"$self,$msg\";
};
goto &$AUTOLOAD; # Restart the new routine.
}
sub DESTROY {
my $self = shift;
#debug print \"the DESTRUCTOR is called \\n\" ;
return ;
}
END {
close(STDOUT) || die \"can\'t close STDOUT: $! \\n\\n\" ;
close(STDERR) || die \"can\'t close STDERR: $! \\n\\n\" ;
}
# STOP OO
# =============================================================================
sub Initialize {
$confHolder = { Foo => \'Bar\',} unless ( $confHolder ) ;
# if the log dir does not exist create it
my $LogDir = \'\' ;
$LogDir = $confHolder->{\'LogDir\'} ;
# create the log file in the current directory if it is not specified
unless ( defined ( $LogDir )) {
$LogDir = $RunDir ;
}
use File::Path qw(mkpath);
if( defined ($LogDir) && !-d \"$LogDir\" ) {
mkpath(\"$LogDir\") ||
cluck ( \" Cannot create the \\$LogDir : $LogDir $! !!! \" ) ;
}
# START set default value if value not specified =========================
# Full debugging ....
$confHolder->{\'LogLevel\'} = 4
unless ( defined ( $confHolder->{\'LogLevel\'} ) ) ;
$confHolder->{\'PrintErrorMsgs\'} = 1
unless ( defined ( $confHolder->{\'PrintErrorMsgs\'} ) ) ;
$confHolder->{\'PrintDebugMsgs\'} = 1
unless ( defined ($confHolder->{\'PrintDebugMsgs\'})) ;
$confHolder->{\'PrintTraceMsgs\'} = 1
unless ( defined ( $confHolder->{\'PrintTraceMsgs\'} )) ;
$confHolder->{\'PrintWarningMsgs\'} = 1
unless ( defined ( $confHolder->{\'PrintWarningMsgs\'} ) ) ;
$confHolder->{\'LogMsgs\'} = 1
unless ( defined ( $confHolder->{\'LogMsgs\'} ) ) ;
$confHolder->{\'LogTimeToTextSeparator\'} = \'---\'
unless ( defined ( $confHolder->{\'LogTimeToTextSeparator\'} ) ) ;
#
# STOP set default value if value not specified =========================
} #eof sub Initialize
# =============================================================================
# START functions
# logs an warning message
sub LogErrorMsg {
my $self = shift ;
my $msg = \"@_\" ;
my $msgType = \"ERROR\" ;
# Do not print anything if the PrintWarningMsgs = 0
return if ( $confHolder->{\'LogMsgs\'} == 0 ) ;
# Do not print anything if the PrintWarningMsgs = 0
return if ( $confHolder->{\'PrintErrorMsgs\'} == 0 ) ;
$self->LogMsg( $msgType,\"$msg\" ) if ( $confHolder->{\'PrintErrorMsgs\'} == 1 ) ;
} #eof sub
# logs an warning message
sub LogWarningMsg {
my $self = shift ;
my $msg = \"@_\" ;
my $msgType = \'WARNING\' ;
# Do not print anything if the PrintWarningMsgs = 0
return if ( $confHolder->{\'LogMsgs\'} == 0 ) ;
# Do not print anything if the PrintWarningMsgs = 0
return if ( $confHolder->{\'PrintWarningMsgs\'} == 0 ) ;
$self->LogMsg( $msgType,\"$msg\" ) if ( $confHolder->{\'PrintWarningMsgs\'} == 1 ) ;
} #eof sub
# logs an info message
sub LogInfoMsg {
my $self = shift ;
my $msg = \"@_\" ;
my $msgType = \'INFO\' ;
# Do not print anything if the PrintWarningMsgs = 0
return if ( $confHolder->{\'LogMsgs\'} == 0 ) ;
# Do not print anything if the PrintWarningMsgs = 0
return if ( $confHolder->{\'PrintInfoMsgs\'} == 0 ) ;
$self->LogMsg( $msgType,\"$msg\" ) if ( $confHolder->{\'PrintInfoMsgs\'} == 1 ) ;
} #eof sub
# logs an trace message
sub LogTraceMsg {
my $self = shift ;
my $msg = \"@_\" ;
my $msgType = \'TRACE\' ;
my ($package,$filename,$line) = caller();
# Do not print anything if the PrintDebugMsgs = 0
return if ( $confHolder->{\'PrintTraceMsgs\'} == 0 ) ;
$msg = \"$msg : FROM Package: $package FileName: $filename Line: $line \" ;
# Do not print anything if the PrintWarningMsgs = 0
return if ( $confHolder->{\'LogMsgs\'} == 0 ) ;
# Do not print anything if the PrintWarningMsgs = 0
return if ( $confHolder->{\'PrintTraceMsgs\'} == 0 ) ;
$self->LogMsg( $msgType,\"$msg\" ) if ( $confHolder->{\'PrintTraceMsgs\'} == 1 ) ;
} #eof sub
# logs an Debug message
sub LogDebugMsg {
my $self = shift ;
my $msg = \"@_\" ;
my $msgType = \'DEBUG\' ;
# Do not print anything if the PrintWarningMsgs = 0
return if ( $confHolder->{\'LogMsgs\'} == 0 ) ;
# Do not print anything if the PrintWarningMsgs = 0
return if ( $confHolder->{\'PrintDebugMsgs\'} == 0 ) ;
$self->LogMsg( $msgType,\"$msg\" ) if ( $confHolder->{\'PrintDebugMsgs\'} == 1 ) ;
} #eof sub
sub GetLogFile {
my $self = shift ;
#debug print \"The log file is \" . $confHolder->{ \'LogFile\' } ;
my $LogFile = $confHolder->{ \'LogFile\' } ;
#if the log file is not defined we create one
unless ( $confHolder->{ \'LogFile\' } ) {
$LogFile = \"$0.log\" ;
}
return $LogFile ;
} #eof sub
sub BuildMsg {
my $self = shift ;
my $msgType = shift ;
my $objTimer= new Timer();
my $HumanReadableTime = $objTimer->GetHumanReadableTime();
my $LogTimeToTextSeparator = $confHolder->{\'LogTimeToTextSeparator\'} ;
my $msg = () ;
# PRINT TO STDOUT if
if ( $msgType eq \'WARNING\'
|| $msgType eq \'INFO\'
|| $msgType eq \'DEBUG\'
|| $msgType eq \'TRACE\' ) {
$msg = \" $HumanReadableTime $LogTimeToTextSeparator $msgType : @_ \\n\" ;
}
elsif ( $msgType eq \'ERROR\' ) {
$msg = \" $HumanReadableTime $LogTimeToTextSeparator $msgType : @_ \\n\" ;
}
else {
$msg = \" $HumanReadableTime $LogTimeToTextSeparator $msgType @_ \\n\" ;
}
return $msg ;
} #eof sub BuildMsg
sub LogMsg {
my $self = shift ;
my $msgType = shift ;
my $msg = $self->BuildMsg ( $msgType,@_ ) ;
my $LogFile = $self -> GetLogFile();
# Do not print anything if the LogLevel = 0
return if ( $confHolder->{\'LogLevel\'} == 0 ) ;
# PRINT TO STDOUT if
if (
$confHolder->{\'PrintMsgs\'} == 1
|| $confHolder->{\'PrintInfoMsgs\'} == 1
|| $confHolder->{\'PrintDebugMsgs\'} == 1
|| $confHolder->{\'PrintTraceMsgs\'} == 1
) {
print STDOUT $msg ;
}
elsif ( $confHolder->{\'PrintErrorMsgs\'} ) {
print STDERR $msg ;
}
if ( $confHolder->{\'LogToFile\'} == 1 ) {
my $LogFile = $self -> GetLogFile();
my $objFileHandler = new FileHandler();
$objFileHandler->AppendToFile( $LogFile,\"$msg\" );
} #eof if
#TODO: ADD DB LOGGING
} #eof LogMsg
# STOP functions
# =============================================================================
1;
__END__
=head1 NAME
Logger
=head1 SYNOPSIS
use Logger ;
=head1 DESCRIPTION
Provide a simple interface for dynamic logging. This is part of the bigger Morphus tool : google code morphus
Prints the following type of output :
2011.06.11-13:33:11 --- this is a simple message
2011.06.11-13:33:11 --- ERROR : This is an error message
2011.06.11-13:33:11 --- WARNING : This is a warning message
2011.06.11-13:33:11 --- INFO : This is a info message
2011.06.11-13:33:11 --- DEBUG : This is a debug message
2011.06.11-13:33:11 --- TRACE : This is a trace message : FROM Package: Morphus
FileName: E:\\Perl\\sfw\\morphus\\morphus.0.5.0.dev.ysg\\sfw\\perl\\morphus.pl Line: 52
=head2 EXPORT
=head1 SEE ALSO
perldoc perlvars
No mailing list for this module
=head1 AUTHOR
yordan.georgiev@gmail.com
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2011 Yordan Georgiev
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself,either Perl version 5.8.1 or,at your option,any later version of Perl 5 you may have available.
VersionHistory:
1.4.0 --- 2011.06.11 --- ysg --- Separated actions of building and printing msgs. Total refactoring. Beta .
1.3.0 --- 2011.06.09 --- ysg --- Added Initialize
1.2.0 --- 2011.06.07 --- ysg --- Added LogInfoErrorMsg print both to all possible
1.1.4 --- ysg --- added default values if conf values are not set
1.0.0 --- ysg --- Create basic methods
1.0.0 --- ysg --- Stolen shamelessly from several places of the Perl monks ...
=cut