Как я могу подключиться к печати 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, в соответствии с этими соображениями). Является ли это возможным? Есть ли способ сделать это чисто? Существуют ли какие-либо модули журналирования, которые имеют зацепки, чтобы позволить вам это сделать? Или это что-то, чего следует избегать, как чумы, и я должен отказаться от того, чтобы когда-либо захватывать и обрабатывать печатную продукцию?

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

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

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

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

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

2)IO :: Ручка Документация гласит, что вы не можете создать подкласс, и мои попытки до сих пор были бесплодными. Есть ли что-то особенное, что нужно для создания подклассов IO :: Handle? Стандартный «use base» IO :: Handle и последующее переопределение методов new / print, похоже, ничего не делают.

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

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

 Robert P23 дек. 2008 г., 01:01
Я могу изменить что угодно в общей библиотеке. Пользователь не должен запускать свои сценарии по-другому или передавать что-то новое в командной строке. Окончательный поток данных в STDOUT и STDERR должен быть таким же, как и раньше.
 Axeman23 дек. 2008 г., 00:51
И чтоявляются ты позволил поменять? Командные строки? Файлы параметров? Например, скажем, я сказал: «Да, вы можете зацепить печать», какова область того, что вы сможете сделать?
 user4451123 дек. 2008 г., 02:36
что происходит с исходным выводом? Можете ли вы отследить это и обработать оттуда?
 Robert P23 дек. 2008 г., 17:17
Исходный вывод обрабатывается другими программами. Они ожидают, что он останется таким же, как и раньше. Также, как упомянуто ниже в другом комментарии, мы также не хотим изменять среду - поэтому маскирование 'perl' с помощью другой программы, которая выполняет обработку журналов, будет проблематичным.

Ответы на вопрос(5)

Решение Вопроса

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

Тем не менее, выМожно

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

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

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, @_ ));
}

УвидетьTie :: Handle для чего все, что вы можете переопределить поведение STDOUT.

 Robert P23 дек. 2008 г., 17:24
Глядя на это больше. Я вижу - он привязан к одному дескриптору файла за раз, верно? Я скоро об этом расскажу и доложу.
 Axeman23 дек. 2008 г., 18:47
«Дескриптор файла» - это просто средство. Как только у нас есть поток, направленный туда, где мы можем указать поведение, мы можем записать столько реальных дескрипторов, сколько пожелаем, отфильтровать его так, как мы хотим - даже сделать вызовы базы данных, если это то, что мы хотели сделать.
 Robert P23 дек. 2008 г., 19:41
Фантастически, это именно то, на что я надеялся: это влияет только на STDOUT (и, вероятно, тоже на STDERR), прозрачный для внешней стороны. Спасибо!
 Robert P23 дек. 2008 г., 19:55
Как насчет переназначения глобуса STDOUT вместо select ()? Мы сохранили его в модуле ... для всех целей и задач наш новый связанный дескриптор должен быть STDOUT. Кажется, что-то там, но не за пределами работоспособности. Знаете ли вы о каких-либо серьезных проблемах с этим?
 Robert P23 дек. 2008 г., 17:08
О, это выглядит хорошо. Изменит ли это поведение печати, если они используют print для записи в дескрипторы файлов или просто в STDOUT?
 Robert P23 дек. 2008 г., 19:57
«Мы сохранили его в модуле» == STDOUT.

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

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

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

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

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

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

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

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

 user4209223 дек. 2008 г., 00:38
Вы можете открыть дескриптор файла как канал к другому процессу. Сделайте там запись, затем вы сможете распечатать на стандартный вывод изнутри.
 codelogic23 дек. 2008 г., 00:39
Вы можете сделать так, чтобы конвейерный процесс перенаправлял свой STDIN в STDOUT в дополнение к его обработке, таким образом текущее поведение не будет затронуто. Я не знаю ни одного модуля, который абстрагирует эту функциональность.
 Kamil Kisiel23 дек. 2008 г., 00:58
Вы можете подклассperldoc.perl.org/IO/Handle.html и создайте свой собственный дескриптор файла, который будет выполнять регистрацию.
 codelogic23 дек. 2008 г., 01:30
@ Роберт: Нет, вам не нужно вручную раскошелиться. Perl позаботится о создании другого процесса и установлении соединения между дескриптором файла и STDIN другого процесса. Это не блокирует.
 Robert P23 дек. 2008 г., 00:32
select - это хорошо, но нам нужно обработать и эти данные. Существует ли тип дескриптора файла, который дает хуки для оценки и обработки данных, передаваемых в него?
 Robert P23 дек. 2008 г., 01:09
re: codelogic-я имел в виду, что вы создадите канал, а затем запустите другой процесс с помощью fork. Если бы не форк, что бы вы сделали, чтобы начать процесс? QX? Re: Камил: Интересно. Это потребует, чтобы они включили этот подкласс IO :: Handle, верно? Может ли он быть реэкспортирован автоматически?
 Robert P23 дек. 2008 г., 00:33
Кроме того, мы не можем изменить текущее поведение этих сценариев, поэтому выходные данные также должны оставаться в STDOUT.
 Robert P23 дек. 2008 г., 00:44
Интересная идея ... это было бы с чем-то вроде fork ()? Есть и другое требование ... это должно работать на разных платформах: windows и * nix. Насколько я понимаю, форк не работает в Windows. Или вы говорите о другом механизме трубопровода?
 codelogic23 дек. 2008 г., 00:56
Труба не вилка. Он создает новый процесс и предоставляет вам дескриптор его STDIN. Подробнее об этом @troubleshooters.com/codecorn/littperl/perlfile.htm#Piping

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

 Robert P23 дек. 2008 г., 00:41
К сожалению, это нарушает требование «скрипт должен выполняться так же, как и раньше». Пользователь не должен делать что-то другое, чтобы получить эту информацию регистрации.
 brian d foy23 дек. 2008 г., 07:56
Это ничего не нарушает. Ничто не говорит о том, что то, что выполняется при вводе perl, является настоящим perl.
 Robert P23 дек. 2008 г., 17:14
Достаточно верно с точки зрения общей реализации; скрипт-обертка является одним из возможных решений. Тем не менее, запрос пользователей на запуск другого сценария просто для запуска их сценария не является решением, которое будет работать в этом сценарии - командная строка, perl distro и консольный вывод должны оставаться неизменными.
 Robert P23 дек. 2008 г., 18:31
Но в зависимости от вашего, это может быть. :)
 Robert P23 дек. 2008 г., 17:15
И это должно влиять ТОЛЬКО на сценарии, которые используют эту конкретную библиотеку. Если бы я изменил то, что оболочка считала «perl», это затронуло бы другие сценарии, которым не нужна общая библиотека. Это также не является приемлемым решением для этого приложения.

Ваш ответ на вопрос