3

这个例子中:

$logger->debug({
    filter => \&Data::Dumper::Dumper,
    value  => $ref
});

我可以漂亮地打印我的参考文献而不是ARRAY(0xFFDFKDJ). 但是每次都打那么长的代码太无聊了。我只是想:

$logger->preprocessor({
    filter => \&Data::Dumper::Dumper,
    value  => $ref
});

$logger->debug( $ref, $ref2 );
$logger->info( $array );

$ref, $ref2, 和$array将被 Data::Dumper 转储。

有没有办法做到这一点?

UPD
在你的回答的帮助下,我做了补丁

现在你只需:

log4perl.appender.A1.layout=FallbackLayout
log4perl.appender.A1.layout.chain=PatternLayout
log4perl.appender.A1.layout.chain.ConversionPattern=%m%n
log4perl.appender.A1.warp_message = sub { $#_ = 2 if @_ > 3; \
                                       return @_; }
# OR
log4perl.appender.A1.warp_message = main::warp_my_message

sub warp_my_message {
    my( @chunks ) =  @_;

    use Data::Dump qw/ pp /;
    for my $msg ( @chunks ) {
        $msg =  pp $msg   if ref $msg;
    }

    return @chunks;
}

UPD2

或者你可以使用这个小模块

log4perl.appender.SomeAPP.warp_message  = Preprocess::Messages::msg_filter
log4perl.appender.SomeAPP.layout        = Preprocess::Messages

package Preprocess::Messages;

sub msg_filter {
    my @chunks =  @_;

    for my $msg ( @chunks ) {
        $msg =  pp $msg   if ref $msg;
    }

    return @chunks;
};

sub render {
    my $self =  shift;

    my $layout =  Log::Log4perl::Layout::PatternLayout->new(
        '%d %P %p> %c %F:%L %M%n  %m{indent=2}%n%n'
    );

    $_[-1] += 1; # increase level of the caller
    return $layout->render( join $Log::Log4perl::JOIN_MSG_ARRAY_CHAR, @{ shift() }, @_ );
}


sub new {
    my $class = shift;
    $class = ref ($class) || $class;

    return bless {}, $class;
}

1;

是的,当然你可以设置 'warp_message = 0' 并将 msg_filter 和 render 结合在一起。

log4perl.appender.SomeAPP.warp_message  = 0
log4perl.appender.SomeAPP.layout        = Preprocess::Messages

sub render {
    my($self, $message, $category, $priority, $caller_level) = @_;

    my $layout =  Log::Log4perl::Layout::PatternLayout->new(
        '%d %P %p> %c %F:%L %M%n  %m{indent=2}%n%n'
    );

    for my $item ( @{ $message } ) {
        $item =  pp $item   if ref $item;
    }

    $message =  join $Log::Log4perl::JOIN_MSG_ARRAY_CHAR, @$message;
    return $layout->render( $message, $category, $priority, $caller_level+1 );
}
4

1 回答 1

1

最简单的方法:使用warp_message

最简单的方法是创建一个自定义附加程序并设置warp_message参数,以便您可以获取传递给记录器的原始引用:

package DumpAppender;
use strict;
use warnings;

use Data::Dumper;

$Data::Dumper::Indent = 0;
$Data::Dumper::Terse  = 1;

sub new {
    bless {}, $_[0];
}

sub log {
    my($self, %params) = @_;
    print ref($_) ? Dumper($_) : $_ for @{ $params{message} };
    print "\n";
}

package main;   
use strict;
use warnings;

use Log::Log4perl;

Log::Log4perl->init(\q{
    log4perl.rootLogger=DEBUG,Dump
    log4perl.appender.Dump=DumpAppender
    log4perl.appender.Dump.layout=NoopLayout
    log4perl.appender.Dump.warp_message=0
});

my $logger = Log::Log4perl->get_logger;

$logger->debug(
    'This is a string, but this is a reference: ',
    { foo => 'bar' },
);

输出:

This is a string, but this is a reference: {'foo' => 'bar'}

不幸的是,如果您采用这种方法,您将不得不编写自己的代码来处理布局、打开文件等。除了只需要打印到屏幕的非常简单的项目外,我不会采用这种方法。

更好的方法:复合追加器

更好的方法是编写自己的复合 appender。复合追加器在以某种方式操作它们之后将消息转发到另一个追加器,例如过滤或缓存它们。使用这种方法,您可以只编写用于转储引用的代码,并让现有的附加程序完成繁重的工作。

下面展示了如何编写复合 appender。其中一些在Log::Log4perl::Appender的文档中进行了解释,但我从 Mike Schilli 的Log::Log4perl::Appender::Limit中复制了大部分内容:

package DumpAppender;   
use strict;
use warnings;

our @ISA = qw(Log::Log4perl::Appender);

use Data::Dumper;

$Data::Dumper::Indent = 0;
$Data::Dumper::Terse  = 1;

sub new {
    my ($class, %options) = @_;

    my $self = {
        appender => undef,
        %options
    };

    # Pass back the appender to be limited as a dependency to the configuration
    # file parser.
    push @{ $options{l4p_depends_on} }, $self->{appender};

    # Run our post_init method in the configurator after all appenders have been
    # defined to make sure the appenders we're connecting to really exist.
    push @{ $options{l4p_post_config_subs} }, sub { $self->post_init() };

    bless $self, $class;
}

sub log {
    my ($self, %params) = @_;

    # Adjust call stack so messages are reported with the correct caller and
    # file
    local $Log::Log4perl::caller_depth = $Log::Log4perl::caller_depth + 2;

    # Dump all references with Data::Dumper
    $_ = ref($_) ? Dumper($_) : $_ for @{ $params{message} };

    $self->{app}->SUPER::log(
        \%params,
        $params{log4p_category},
        $params{log4p_level}
    );
}

sub post_init {
    my ($self) = @_;

    if(! exists $self->{appender}) {
        die "No appender defined for " . __PACKAGE__;
    }

    my $appenders = Log::Log4perl->appenders();
    my $appender = Log::Log4perl->appenders()->{$self->{appender}};

    if(! defined $appender) {
        die "Appender $self->{appender} not defined (yet) when " .
            __PACKAGE__ . " needed it";
    }

    $self->{app} = $appender;
}

package main;

use strict;
use warnings;

use Log::Log4perl;

Log::Log4perl->init(\q{
    log4perl.rootLogger=DEBUG, Dump

    log4perl.appender.Dump=DumpAppender
    log4perl.appender.Dump.appender=SCREEN

    log4perl.appender.SCREEN=Log::Log4perl::Appender::Screen
    log4perl.appender.SCREEN.layout=PatternLayout
    log4perl.appender.SCREEN.layout.ConversionPattern=%d %p %m%n
});

my $logger = Log::Log4perl->get_logger;

$logger->debug(
    'This is a string, but this is a reference: ',
    { foo => 'bar' },
);

输出:

2015/09/14 13:38:47 DEBUG This is a string, but this is a reference: {'foo' => 'bar'}

请注意,如果您通过 API 而不是通过文件初始化 Log::Log4perl,则必须采取一些额外的步骤。这记录在Log::Log4perl::Appender 文档的复合附加程序部分中。

于 2015-09-14T19:45:28.493 回答