Подтвердить что ты не робот

Как я могу подключиться к печати на Perl?

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

Теперь появилась новая потребность: теперь в библиотеку нужно добавить журнал. Это должно выполняться автоматически и прозрачно, без необходимости использования стандартной библиотеки для изменения их скриптов. Общие методы библиотеки могут просто добавлять к ним вызовы протоколирования; это легкая часть. Жесткая часть заключается в том, что диагностический вывод из этих сценариев всегда отображался с помощью инструкции "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 sucks" или "этот пример имеет недостатки xy и z".. Это значительно упрощается для краткости и ясности.)

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

Дополнительно: для этого нужно использовать межплатформенные окна и * nix. Процесс запуска скриптов должен оставаться таким же, как и вывод из script.

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

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

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

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

2) документация IO:: Handle говорит, что вы не можете подклассифицировать ее, и мои попытки до сих пор были бесплодны. Есть ли что-то особенное, что необходимо для того, чтобы сделать sublclassing IO:: Handle? Стандартная "база использования" IO:: Handle ", а затем переопределение новых/методов печати ничего не делает.

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

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

4b9b3361

Ответ 1

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

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

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

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

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 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 printing to what was STDOUT at the start.
    # 
    my $format = shift;
    print $OLD_STDOUT join( '', 'NOTICE: ', sprintf( $format, @_ ));
}

См. Tie::Handle за все, что вы можете отменить поведение STDOUT.

Ответ 2

Вы можете использовать Perl select для перенаправления 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:: Util, похоже, позволяет вам "выводить" вывод дескриптора файла нескольким адресатам (например, обработчик журналов и STDOUT).

Ответ 3

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

Ответ 4

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

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

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";

Ответ 5

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