【问题标题】:Log4perl: How do I dynamically load appenders at runtime?Log4perl:如何在运行时动态加载附加程序?
【发布时间】:2011-01-17 17:18:25
【问题描述】:

我想让模块在运行时管理它们的日志记录,但不要让所有东西都引用一个单一的整体配置文件。当处理在不同权限下运行的进程时,我真的不想处理需要能够访问系统上每个日志的每个进程,而它们只写入其中的一个子集。

但是,我在 Log4perl 手册中没有找到太多关于如何在运行时从配置文件初始化附加附加程序的文档。 http://metacpan.org/pod/Log::Log4perl::Appender 引用了一个 add_appender 方法,但它适用于实例化的 appender 对象而不是 conf 文件。它也没有定义 logger 对象和 logger->appender 关系。

我尝试让每个包都从其自己的 conf 中初始化,但这只会在每次初始化时破坏现有配置。我想做的事情是这样的:

my $foo = Foo->new() ## Checks Log::Log4perl::initialized(), sees that it
                     ## hasn't been initalized yet, inits Log4perl from foo.conf
my $bar = Bar->new() ## Checks Log::Log4perl::initialized(), sees that it
                     ## has been initalized. Adds appenders and loggers defined
                     ## in bar.conf into the initialized configuration

如何解析配置并将其添加到当前配置中?

编辑:使用包变量的问题在于,这只是各种类使用的 Moose 角色,几乎只是 Making self-logging modules with Log::Log4perl 中 Ether 答案的 MooseX::Role::Parameterized 版本。因此,我的记录器正在组合到使用它的库中,并且我没有可以在每次使用它时处理的全局变量。

虽然..

如果我在 MooseX::Role::Parameterized 角色块之外声明一个全局变量,是否每个使用该角色的类都使用相同的 conf 变量?

【问题讨论】:

  • 有人吗?我已经浏览了一下 Log4perl 的源代码,但我不能成为第一个想要这样做的人。我正在尝试创建一个 Moose 参数化角色,我可以将其应用于允许我将配置文件传递给角色的类,并且它只是通过初始化 conf 文件或将 conf 文件配置附加到当前初始化的Log4perl。
  • @Oesor:即使在 Moose 角色中声明,包全局仍然是全局的。修改下面的解决方案以使用角色应该很容易(而不是 BUILD 机制,您可以使用非常相似的代码使用 around BUILDARGS)。
  • @Oesor:根据之前的评论更新了下面的代码。
  • 没意识到;认为在角色中使用 PACKAGE 的任何内容都将指代角色组成的包,而不是角色的包。
  • 其实——最后一个问题。这显然依赖于 Log4perl 的每次初始化都是通过记录器角色进行的。有什么方法可以检查 Log4perl 初始化的当前源,以便如果我在脚本中使用直接 Log::Log4perl->init('foobar.conf') 对其进行初始化,然后使用使用此记录器角色的库,我可以将库的 init 附加到当前的初始化中吗?

标签: perl logging log4perl


【解决方案1】:

您可以记住已经加载了哪些配置文件(下面代码中的%log_configs 哈希)。当新类到达时,您可以重新读取所有配置,将它们合并在一起并使用字符串引用参数再次初始化Log::Log4perl init

我通常更喜欢为每个应用程序配置一个日志,因为它更易于维护和重新加载功能。

package Logger;
use Moose::Role;
use Log::Log4perl;

our %log_configs = ();

around BUILDARGS => sub {
    my $orig  = shift;
    my $class = shift;

    my $config_name = lc($class) . '.conf';

    # if the config is not integrated yet
    if(! defined $log_configs{$config_name}) {
        $log_configs{$config_name} = 1;

        # reload all configs including new one
        my $config_text = '';
        for my $file (sort keys %log_configs) {
            $config_text .= "\n" . do {
                local $/;   # slurp
                unless(open my $fh, "<", $file) {
                    warn "$file could not be open\n";
                    '';
                }
                else {
                    <$fh>
                }
            };
        }

        # refresh config
        Log::Log4perl::init(\$config_text);
    }

    return $class->$orig(@_);
};


package Foo;
use Moose;
with 'Logger';
use Log::Log4perl ':easy';

sub BUILD {
    ERROR 'Foo reporting';
}


package Bar;
use Moose;
with 'Logger';
use Log::Log4perl ':easy';

sub BUILD {
    INFO 'Bar reporting';
}


package main;

my $foo = Foo->new;
my $bar = Bar->new;

【讨论】:

  • 这或多或少是我的做法。将 init 包装在 eval 中并删除最新的配置文件(如果它在 init 中死掉)可能是个好主意。没有什么比一个糟糕的日志配置文件导致无关的应用程序崩溃更糟糕的了。将标志值添加到 %log_configs 哈希以指示错误的配置文件并不难。
  • @Mark Tozzi - 如果要在实际应用中使用这种方法,当然应该添加适当的错误处理。请考虑将 sn-p 作为概念验证。另一个有用的添加可能是指定日志配置名称的属性,默认为类名,而不是这个硬编码示例(顺便说一句,它不适用于像 Foo::Bar 这样的命名空间类)。
【解决方案2】:

虽然我希望避免这种情况,但如果我自己解析配置文件,我可以通过http://search.cpan.org/perldoc?Log::Log4perl 中记录的 API 访问 perl 中的配置。即,

  ########################
  # Initialization section
  ########################
  use Log::Log4perl;
  use Log::Log4perl::Layout;
  use Log::Log4perl::Level;

     # Define a category logger
  my $log = Log::Log4perl->get_logger("Foo::Bar");

     # Define a layout
  my $layout = Log::Log4perl::Layout::PatternLayout->new("[%r] %F %L %m%n");

     # Define a file appender
  my $file_appender = Log::Log4perl::Appender->new(
                          "Log::Log4perl::Appender::File",
                          name      => "filelog",
                          filename  => "/tmp/my.log");

     # Define a stdout appender
  my $stdout_appender =  Log::Log4perl::Appender->new(
                          "Log::Log4perl::Appender::Screen",
                          name      => "screenlog",
                          stderr    => 0);

     # Have both appenders use the same layout (could be different)
  $stdout_appender->layout($layout);
  $file_appender->layout($layout);

  $log->add_appender($stdout_appender);
  $log->add_appender($file_appender);
  $log->level($INFO);

虽然另一种方法有效,但有太多注意事项让我无法舒适地使用它(天啊,我使用了这个库,为什么我的日志记录停止了?)——这对我的口味来说太令人惊讶了。

相反,我想通过查看如何使用Log::Log4perl::Config::PropertyConfigurator(在解析配置时由 ->init 委托)来查看我是否无法从配置文件进入 Log::Log4perl 状态需要文件。如果我检查返回的数据结构,我可以逐个记录器和逐个附加器比较初始化的更改,并适当地修改初始化状态,正确处理命名空间冲突等。

【讨论】:

    猜你喜欢
    • 2014-07-24
    • 2011-08-06
    • 1970-01-01
    • 1970-01-01
    • 2013-03-04
    • 2016-12-07
    • 2016-04-14
    • 2012-08-02
    • 1970-01-01
    相关资源
    最近更新 更多