У меня есть сценарий perl, который записывает сообщения в STDOUT и STDERR (с помощью операторов print / croak), но я также перенаправляю STDOUT и STDERR в файл журнала:
File::Tee::tee STDOUT, ">>", "$logFile" ;
File::Tee::tee STDERR, ">>", "$logFile" ;
Теперь в выходном файле журнала сообщения от STDOUT и STDERR отображаются не по порядку. Кроме того, фактический вывод на терминал также не работает. Я попытался очистить буферы (как рекомендовано здесь: https://perl.plover.com/FAQs/Buffering.html), но это не помогает:
select(STDERR) ;
$| = 1 ;
select(STDOUT) ;
$| = 1 ;
Кто-нибудь знает, что я должен сделать, чтобы просмотреть вывод по порядку (я также пробовал дополнительно очистить дескриптор файла, соответствующий $ logfile, но он все тот же)?
РЕДАКТИРОВАТЬ:
Спасибо всем, кто ответил. Многие дискуссии по этому поводу закончились комментариями, поэтому я собираюсь перечислить несколько вещей, которые я пробовал, основываясь на отзывах всех вас.
- Я уже промывал STDOUT и STDERR до того, как использовал File :: Tee. Как подозревал @jimtut, виновником действительно был File :: Tee - его удаление восстановило порядок на консоли. Но я действительно хотел перенаправить STDOUT и STDERR.
- @mob предложил вместо этого использовать IO :: Tee, но я не совсем понял, как заставить это работать так, как я хочу в моем коде.
- @briandfoy указал, что не существует надежного способа гарантировать, что 2 отдельных дескриптора файлов будут отображаться в правильном порядке в реальном времени, а также предложил использовать процедуру регистрации, которая является единственным местом, которое может писать в STDOUT / STDERR. @zimd далее указал, что File :: Tee использует fork, что является сутью проблемы, поскольку 2 процесса не могут гарантировать какой-либо порядок вывода.
- Поскольку виноват File :: Tee, я попытался удалить это из кода. Я обновил функцию регистратора для печати в STDOUT / STDERR, а также для дополнительной печати в дескриптор файла $ log. Далее, чтобы записать предупреждения в журнал, я сделал следующее:
sub warning_handler {
my $msg = $_[0] ;
print STDERR $msg ;
print $log $msg if defined $log ;
}
$SIG{__WARN__} = \&warning_handler ;
Это отлично сработало для всего кода под моим контролем. Теперь все печаталось в порядке и на консоли, и в файле журнала. Однако я понял, что не могу использовать это решение, поскольку я также звонил в чьи-то пакеты perl для некоторых функций, и, очевидно, я не мог перехватить печать / кваканье и т. Д., Которые писали в STDOUT / STDERR в пакете «с полки». Так что прямо сейчас у меня нет хорошего решения. Однако я подозреваю, что если я найду способ перехвата STDOUT / STDERR в perl, я смогу получить то, что мне нужно.
EDIT2: я добавил свой собственный ответ, который, вероятно, является наиболее близким к решению проблемы, изменяя решение моба с использованием IO :: Tee вместо File :: Tee, но даже это пропускает некоторые сообщения (хотя он исправляет порядок).
РЕДАКТИРОВАТЬ3: Наконец-то нашел `` решение ''
use IO::Tee ;
use Capture::Tiny qw(capture);
...
...
select(STDERR) ;
$| = 1 ;
select(STDOUT) ;
$| = 1 ;
open (my $log, ">", $logfilename) ;
*REALSTDOUT = *STDOUT ;
*REALSTDERR = *STDERR ;
*STDOUT = IO::Tee->new(\*REALSTDOUT, $log);
*STDERR = IO::Tee->new(\*REALSTDERR, $log);
# Regular Perl code here which sends output to STDOUT/STDERR
...
...
# system calls / calls to .so needs to be catpured
&log_streams(sub { &some_func_which_calls_shared_object() ; }) ;
sub log_streams {
my ($cr, @args) = @_; # code reference, with its arguments
my ($out, $err, $exit) = capture { $cr->(@args) };
if ($out) {
print STDOUT $out;
}
if ($err) {
print STDERR $err;
}
}
Использование IO :: Tee гарантирует, что все сгенерированные perl выходные данные на консоль также попадут в файл журнала, и это происходит немедленно, тем самым обновляя журнал и консоль в реальном времени. Поскольку IO :: Tee меняет значение дескрипторов файлов STDOUT / STDERR, чтобы теперь ссылаться на дескрипторы teed, он может перехватывать stdio только из операторов perl, он пропускает системные вызовы, поскольку они обходят дескрипторы STDOUT / STDERR perl. Таким образом, мы фиксируем вывод системного вызова, а затем используем процедуру log_streams, чтобы перенаправить его потокам STDOUT / STDERR, которые теперь имеют псевдоним. Это создает задержку в выводе сгенерированного системного вызова вывода, отображаемого в журнале / терминале, но нет задержки для вывода, сгенерированного perl, то есть лучшего из обоих миров. Обратите внимание, что порядок stderr и stdout, сгенерированный вызовом подпрограммы some_func_which_calls_shared_object, не сохраняется, поскольку в подпрограмме log_streams мы сначала печатаем в STDOUT, а затем в STDERR - до тех пор, пока системный вызов является атомарным и мало что делает в с точки зрения чередования сообщений stdout / stderr мы должны быть в порядке. Оцените решения от briandfoy, mob и zimd, ответы которых я объединил, чтобы прийти к этому решению! Никогда не думал, что для решения этой, казалось бы, очень простой проблемы потребуется детальное рассмотрение.
tee
и просто печатаете на консоли, это в порядке? Я предполагаю, что это так, и виноватtee
. - person jimtut   schedule 14.02.2020myscript.pl >> logfile 2>>&1
- person jimtut   schedule 14.02.2020select(STDERR) ; $| = 1 ; select(STDOUT) ; $| = 1 ;
перед звонкомtee
? Быстрый взгляд на код показывает, что это должно сработать. - person ikegami   schedule 15.02.2020