2011-01-17 2 views
8

Я хотел бы иметь модули, управляющие их протоколированием во время выполнения, но не имея всего ссылающегося на один монолитный файл конфигурации. Когда речь идет о процессах, работающих под разными разрешениями, я действительно не хочу иметь дело с каждым процессом, который должен иметь доступ к каждому журналу в системе, когда они только пишут подмножество из них.Log4perl: Как динамически загружать приложения во время выполнения?

Однако в руководстве Log4perl я не нашел документацию о том, как инициализировать дополнительные приложения из файла конфигурации во время выполнения. http://metacpan.org/pod/Log::Log4perl::Appender ссылается на метод add_appender, но работает с экземплярами объектов-экземпляров вместо conf-файлов. Он также не определяет объекты журнала и отношения logger-> appender.

Я пробовал иметь каждый пакет init из своего собственного 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 

Как я могу разобрать и добавить конфигурацию в текущей конфигурации?

Редактировать: Probalem с использованием переменной пакета заключается в том, что это просто роль Moose, потребляемая различными классами, в значительной степени просто MooseX :: Role :: Parameterized версия ответа Ether в Making self-logging modules with Log::Log4perl. Таким образом, мой регистратор заполняется в библиотеку, потребляющую его, и у меня нет глобальной переменной, с которой я могу работать каждый раз, когда я ее использую.

Хотя ..

Если я объявить глобальную переменную за пределами MooseX :: Роль :: Параметризованная роль блока, будет каждый класс, который потребляет роль будет использовать этот же переменный конф?

+0

Кто-нибудь? Я немного взглянул на источник для Log4perl, но я не могу быть первым, кто когда-либо хотел это сделать. Я пытаюсь сделать параметризацию Moose, которую я могу применить к классам, которые позволяют мне передать файл конфигурации в эту роль, и он просто делает правильную вещь, либо инициируя файл conf, либо добавляя конфигурацию конфига в текущую инициализированную Log4perl. – Oesor

+0

@Oesor: Пакет global по-прежнему глобальный, даже если он объявлен в роли Moose. Для использования ролей должно быть легко изменить решение (вместо механизма «BUILD» вы можете использовать «вокруг BUILDARGS» с очень похожим кодом). – bvr

+0

@Oesor: Обновленный код ниже на предыдущий комментарий. – bvr

ответ

5

Вы можете запомнить, какие конфигурационные файлы уже были загружены (хэш в коде ниже). Когда придет новый класс, вы можете перечитать все конфиги, объединить его вместе и снова инициализировать 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; 
+1

Это более или менее, как бы я это сделал. Вероятно, хорошая идея обернуть init в eval и удалить последний файл конфигурации, если он умирает в init. Ничего хуже, чем файл конфигурации с плохим протоколированием, приводящий к сбою несвязанных приложений. Было бы не сложно добавить значение флага в хэш% log_configs, чтобы указать плохой файл конфигурации. –

+1

@Mark Tozzi - обязательно правильная обработка ошибок должна быть добавлена, если такой подход должен использоваться в реальном приложении. Пожалуйста, рассмотрите фрагмент как доказательство концепции. Еще одним полезным дополнением может быть свойство для указания имени log config, по умолчанию для имени класса, а именно этого жестко запрограммированного образца (который btw не работает для классов с именами, таких как 'Foo :: Bar'). – bvr

5

В то время как я надеялся избежать этого, если я разбора конфигурационных файлов сам я могу получить доступ к конфигурации в Perl с помощью API документированной в http://search.cpan.org/perldoc?Log::Log4perl. А именно,

######################## 
    # 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); 

В то время как другой метод работает, есть слишком много предостережений для меня, чтобы быть комфортно, используя его (Gee я использовал эту библиотеку, зачем мой каротаж остановить?) - это просто слишком удивительно, на мой вкус ,

Вместо этого я думаю, что я посмотрю, не могу ли я получить из файла конфигурации в состояние Log :: Log4perl, просмотрев, как использовать Log::Log4perl::Config::PropertyConfigurator, которому делегируется -> init при анализе файла конфигурации необходим. Если я перейду через структуру данных, которая вернется, я смогу сравнить изменения с инициализацией на основе журнала за логгером и appender-by-appender и соответствующим образом изменить состояние инициализации, правильно обработать конфликты пространства имен и т. Д.

Смежные вопросы