我有一组日志和调试功能,我想在多个模块/对象中使用它们。我希望能够使用命令行开关全局打开/关闭它们。
以下代码执行此操作,但是,我希望能够省略软件包名称并将所有内容保存在单个文件中。
更具体地说,我想将日志记录函数名称导入到每个模块中,以便可以在没有任何包名称限定的情况下调用它们(类似于C ++ use namespace;
指令)并且我希望能够从使用它们的脚本全局启用/禁用它们,如下面的示例代码所示。
另一件事 - 我不认为我完全理解为什么以下代码有效。
#! /usr/bin/perl -w
use strict;
use Getopt::Long;
{
package LogFuncs;
use threads;
use Time::HiRes qw( gettimeofday );
# provide tcpdump style time stamp
sub _gf_time {
my ( $seconds, $microseconds ) = gettimeofday();
my @time = localtime($seconds);
return sprintf( "%02d:%02d:%02d.%06ld",
$time[2], $time[1], $time[0], $microseconds );
}
sub logerr;
sub compile {
my %params = @_;
*logerr = $params{do_logging}
? sub {
my $msg = shift;
warn _gf_time() . " Thread " . threads->tid() . ": $msg\n";
}
: sub { };
}
}
{
package FooObj;
sub new {
my $class = shift;
bless {}, $class;
};
sub foo_work {
my $self = shift;
# do some foo work
LogFuncs::logerr($self);
}
}
{
package BarObj;
sub new {
my $class = shift;
my $data = { fooObj => FooObj->new() };
bless $data, $class;
}
sub bar_work {
my $self = shift;
$self->{fooObj}->foo_work();
LogFuncs::logerr($self);
}
}
my $do_logging = 0;
GetOptions(
"do_logging" => \$do_logging,
);
LogFuncs::compile(do_logging => $do_logging);
my $bar = BarObj->new();
LogFuncs::logerr("Created $bar");
$bar->bar_work();
答案 0 :(得分:6)
如果您想将所有内容保存在同一个文件中,为什么不将记录器放在文件范围词汇的顶部。
GetOptions( do_logging => \$do_logging );
my $logerr = $do_logging ? sub {logging_code_here()} : sub {};
$logerr
现在可用于同一文件中该点之后定义的任何包中。
但是,构建日志记录调用通常更快,如下所示:
my $logerr = sub { logging_code_here() };
$logerr->("some string $some_var") if $do_logging;
这样就可以避免子程序调用,并且如果日志记录关闭,则不需要计算$ logerr的字符串参数。
您还可以设置日志记录级别:
$logerr->("starting loop") if $do_logging;
for (@big_array) {
$logerr->("processing $_") if $do_logging > 1;
...
}
编辑:虽然我不认为这是最好的做法,根据你的评论,这是你可能正在寻找的(一个pragma):
use 5.010;
use warnings;
use strict;
BEGIN { # compile time
$INC{'log.pm'}++; # block 'require log;'
package log;
sub is_active {(caller 1)[10]{log}} # test the hints hash
sub import {
$^H{log} = 1; # set the hints hash for 'log'
my $logmsg = (caller).'::logmsg'; # name of caller's sub
no strict 'refs';
*$logmsg = sub {print "logging: @_\n" if is_active} # install sub
unless *{$logmsg}{CODE}; # unless we did already
}
sub unimport {
$^H{log} = 0; # unset the hints hash
}
}
package MyPkg;
use log;
logmsg 'hello, world!';
{
no log;
logmsg 'nope, not here';
}
logmsg 'back again';
答案 1 :(得分:2)
如果需要修补某些日志记录方法(或者在开发人员环境中有条件地执行其他操作而不是生产),可以将代码放入包的import()
方法中并使用{{{}中的参数1}}行启用/禁用它:
use
答案 2 :(得分:2)
下面是您的代码,没有用于日志记录功能的显式包。我已经将LogFuncs :: compile重命名为logerr:setup更加明确,并且不会污染主命名空间。 perl中的所有东西都有一个包,默认情况下它只是 main 。我认为之前的代码,包含日志记录功能的包实际上更简洁,并且可以轻松地拆分成单独的模块,因此可以更容易地重用它。
至于不了解代码是如何工作的,我不知道如何解决这个问题,但这只是因为你没有发现任何你不理解的细节。我假设你的意思是包裹。 Perl包可以被视为命名空间或类。对于LogFuncs包,您将其用作命名空间,并在其中创建的subs作为静态类方法进行访问(使用其他更传统的OO语言中的某些表示法可能会对其有所启发)这个)。包FooObj和BarObj通常通过提供构造函数 new()而更传统地用作对象,并期望使用对象表示法( - > sub)调用其中的subs,自动将对象本身作为sub /方法的第一个参数。将sub定义为静态类方法或对象方法的唯一方法是它是否期望将对象作为第一个参数接收。因此,通过正确地确定第一个传递的参数是什么并采取相应的行动,一点点的深思熟虑和仔细的编程可以产生适用于两者的子。
#! /usr/bin/perl -w
use strict;
use Getopt::Long;
use threads;
use Time::HiRes qw( gettimeofday );
# provide tcpdump style time stamp
sub _gf_time {
my ( $seconds, $microseconds ) = gettimeofday();
my @time = localtime($seconds);
return sprintf( "%02d:%02d:%02d.%06ld",
$time[2], $time[1], $time[0], $microseconds );
}
sub logerr;
sub logerr_setup {
my %params = @_;
*logerr = $params{do_logging}
? sub {
my $msg = shift;
warn _gf_time() . " Thread " . threads->tid() . ": $msg\n";
}
: sub { };
}
{
package FooObj;
sub new {
my $class = shift;
bless {}, $class;
};
sub foo_work {
my $self = shift;
# do some foo work
main::logerr($self);
}
}
{
package BarObj;
sub new {
my $class = shift;
my $data = { fooObj => FooObj->new() };
bless $data, $class;
}
sub bar_work {
my $self = shift;
$self->{fooObj}->foo_work();
main::logerr($self);
}
}
my $do_logging = 0;
GetOptions(
"do_logging" => \$do_logging,
);
logerr_setup(do_logging => $do_logging);
my $bar = BarObj->new();
logerr("Created $bar");
$bar->bar_work();