Вопрос

Вот сценарий.У вас есть большое количество устаревших сценариев, все из которых используют общую библиотеку.Указанные сценарии используют оператор «печать» для диагностического вывода.Никакие изменения в сценариях не допускаются - они широко распространены, получили одобрение и давно покинули плодотворные долины надзора и контроля.

Теперь появилась новая потребность:журналирование теперь необходимо добавить в библиотеку.Это должно быть сделано автоматически и прозрачно, без необходимости изменения пользователями стандартной библиотеки своих сценариев.К общим методам библиотеки можно просто добавить вызовы журналирования;это самая легкая часть.Сложность заключается в том, что диагностические данные этих сценариев всегда отображались с помощью оператора print.Эти диагностические данные необходимо сохранить, но, что не менее важно, обработать.

В качестве примера такой обработки библиотека должна записывать только напечатанные строки, содержащие слова «предупреждение», «ошибка», «уведомление» или «внимание».Приведенный ниже чрезвычайно тривиальный и надуманный пример кода (tm) запишет некоторые из указанных результатов:

sub CheckPrintOutput
{
    my @output = @_; # args passed to print eventually find their way here.
    foreach my $value (@output) {
         Log->log($value) if $value =~ /warning|error|notice|attention/i;
    }
}

(Мне бы хотелось избежать таких вопросов, как «что на самом деле должно регистрироваться», «печать не должна использоваться для диагностики», «perl — отстой» или «этот пример имеет недостатки x, y и z»… это значительно упрощен для краткости и ясности.)

Основная проблема сводится к захвату и обработке данных, передаваемых на печать (или любой встроенной функции Perl, согласно этим рассуждениям).Является ли это возможным?Есть ли способ сделать это чисто?Существуют ли какие-либо модули журналирования, которые позволяют вам это делать?Или этого следует избегать, как чумы, и мне следует отказаться от когда-либо захвата и обработки печатной продукции?

Дополнительный:Это должно работать кроссплатформенно - как для Windows, так и для *nix.Процесс запуска сценариев должен оставаться прежним, как и выходные данные сценария.

Дополнительные дополнительные:Интересное предложение, сделанное в комментариях к ответу codelogic:

Вы можете создать подкласс http://perldoc.perl.org/IO/Handle.html и создайте свою собственную ручку файла, которая выполнит работу в журнале.— Камил Кисиэль

Это может сделать это, с двумя оговорками:

1) Мне нужен способ экспортировать эту функциональность всем, кто использует общую библиотеку.Это должно будет автоматически применяться к STDOUT и, возможно, также к STDERR.

2) IO::Handle в документации сказано, что вы не можете создать подкласс, и мои попытки до сих пор были бесплодны.Нужно ли что-то особенное, чтобы подкласс IO::Handle работал?Стандартное использование базы «IO::Handle», а затем переопределение методов new/print, похоже, ничего не дает.

Окончательное редактирование:Похоже, IO::Handle — это тупик, но Tie::Handle может это сделать.Спасибо за все предложения;они все очень хороши.Я собираюсь попробовать маршрут Tie::Handle.Если это вызовет проблемы, я вернусь!

Приложение:Обратите внимание: немного поработав с этим, я обнаружил, что Tie::Handle будет работать, если вы не сделаете ничего хитрого.Если вы используете какие-либо функции IO::Handle со своим привязанным STDOUT или STDERR, то, по сути, обеспечить их надежную работу - это непростая задача - я не смог найти способ заставить метод автоматической очистки IO::Handle работать на моем связанном ручка.Если бы я включил автопромывку до того, как привязал ручку, это сработало бы.Если вас это устраивает, маршрут Tie::Handle может быть приемлемым.

Это было полезно?

Решение

Существует ряд встроенных модулей, которые вы можете переопределить (см. перлсуб).Однако, print это один из встроенных модулей, который не работает таким образом.Трудности переопределения print подробно описаны здесь ветка Перлмонка.

Однако вы может

  1. Создать пакет
  2. Привяжите ручку
  3. Выберите этот дескриптор.

Несколько человек описали базовую структуру, но работает она примерно так:

package IO::Override;
use base qw<Tie::Handle>;
use Symbol qw<geniosym>;

sub TIEHANDLE { return bless geniosym, __PACKAGE__ }

sub PRINT { 
    shift;
    # You can do pretty much anything you want here. 
    # And it's printing to what was STDOUT at the start.
    # 
    print $OLD_STDOUT join( '', 'NOTICE: ', @_ );
}

tie *PRINTOUT, 'IO::Override';
our $OLD_STDOUT = select( *PRINTOUT );

Вы можете переопределить printf таким же образом:

sub PRINTF { 
    shift;
    # You can do pretty much anything you want here. 
    # And it's printing to what was STDOUT at the start.
    # 
    my $format = shift;
    print $OLD_STDOUT join( '', 'NOTICE: ', sprintf( $format, @_ ));
}

Видеть Галстук::Ручка для чего вы можете переопределить поведение STDOUT.

Другие советы

Вы можете использовать Perl выбирать для перенаправления STDOUT.

open my $fh, ">log.txt";
print "test1\n";
my $current_fh = select $fh;
print "test2\n";
select $current_fh;
print "test3\n";

Дескриптор файла может быть любым, даже каналом к ​​другому процессу, который обрабатывает ваши сообщения журнала.

PerlIO::tee в PerlIO::Утилита модуль, по-видимому, позволяет вам «направить» вывод дескриптора файла на несколько пунктов назначения (например,процессор журнала и STDOUT).

Много вариантов.Используйте select(), чтобы изменить дескриптор файла, на который печатаются значения по умолчанию.Или привязать STDOUT.Или открыть заново.Или примените к нему слой ввода-вывода.

Это не ответ на вашу проблему, но вы сможете использовать эту логику для собственного использования.Если нет, возможно, кому-то это будет полезно.

Перехват неверных заголовков до того, как они произойдут...

package PsychicSTDOUT;
use strict;

my $c = 0;
my $malformed_header = 0;
open(TRUE_STDOUT, '>', '/dev/stdout');
tie *STDOUT, __PACKAGE__, (*STDOUT);

sub TIEHANDLE {
    my $class = shift;
    my $handles = [@_];
    bless $handles, $class;
    return $handles;
}

sub PRINT {
    my $class = shift;
    if (!$c++ && @_[0] !~ /^content-type/i) {
        my (undef, $file, $line) = caller;
        print STDERR "Missing content-type in $file at line $line!!\n";
        $malformed_header = 1;
    }
    return 0 if ($malformed_header);
    return print TRUE_STDOUT @_;
}
1;

Применение:

use PsychicSTDOUT;
print "content-type: text/html\n\n"; #try commenting out this line
print "<html>\n";
print "</html>\n";

Вы можете запустить сценарий из сценария-оболочки, который захватывает стандартный вывод исходного сценария и записывает выходные данные в разумное место.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top