Проблема: в некоторых строках есть определенная фраза с определенным идентификатором запроса.Любые строки для этого идентификатора запроса, которые находятся на заданном расстоянии от строки с фразой, должны быть пропущены, так же как и сама строка.
Мы не знаем, для какого идентификатора запроса это происходит, чтотакже может меняться через файл.
Сначала прочитайте, пока фраза не появится, сохраняя строки в буфере.Как только мы анализируем идентификатор запроса из этой строки, мы знаем, какой это.Затем обработайте накопленный буфер, печатая все строки для других идентификаторов запроса и только те, которые больше, чем расстояние пропуска для «выбранного».
Затем продолжайте собирать строки в буфере, пока фраза не будет найдена снова.Обработка буфера: выведите все строки для других идентификаторов и только те, которые находятся достаточно далеко от обеих фраз для выбранного идентификатора.
Приведенный ниже код был протестирован с использованием вариантов опубликованных данных.Он учитывает возможное смешивание строк с разными идентификаторами запроса;отбрасывание этого, предполагая, что строки с любым идентификатором запроса всегда находятся в блоке, значительно упростит код и ускорит его.
use warnings;
use strict;
use feature 'say';
my ($file, $skip_dist) = @ARGV;
die "Usage: $0 log-file [skip-distance]\n" if not $file;
$skip_dist //= 2; #/
my $trigger = qr{\[GET_REGION_INFO\]};
open my $fh, '<', $file or die "Can't open $file: $!";
my (@buf, $req_mark, $skip_idx, $next_req_cnt);
while (<$fh>) {
if (not $req_mark and /$trigger/) {
# Find the req_id of interest and save it into req_mark,
# then process the accumulated buffer
my ($req_id, $msg) = /:\s+(\*[0-9]+)\s+(.*)/;
$req_mark = $req_id;
# Find position of req_id which is skip_dist before the mark
# and print lines for req_mark before it (and all others)
my $del_idx = find_skip_start($req_mark, \@buf, $skip_dist);
for my $i (0..$#buf) {
if ($skip_idx and $i < $skip_idx) { print $buf[$i] }
else {
my ($req_id) = $buf[$i] =~ /:\s+(\*[0-9]+)/;
print $buf[$i] if $req_id ne $req_mark;
}
}
@buf = ();
$skip_idx = 0;
}
elsif (/$trigger/ or eof) {
# Process buffer collected between previous and this trigger,
# Or up to the end of file (the last line then need be added)
push @buf, $_ if eof;
my $skip_idx = (not eof)
? find_skip_start($req_mark, \@buf, $skip_dist)
: $#buf+1;
for my $i (0..$#buf) {
my ($req_id) = $buf[$i] =~ /:\s+(\*[0-9]+)/;
print $buf[$i]
if $req_id ne $req_mark
or (++$next_req_cnt > $skip_dist and $i < $skip_idx);
}
@buf = ();
$next_req_cnt = $skip_idx = 0;
# Check whether the request-id changed and update for next buffer
my ($req_id) = /:\s+(\*[0-9]+)/;
if ($req_id ne $req_mark) {
$req_mark = $req_id
}
}
else { push @buf, $_ }
}
sub find_skip_start {
my ($req_mark, $buf, $skip_dist) = @_;
my ($skip_idx, $prev_req_cnt);
for my $i (0..$#$buf) {
my ($req_id) = $buf->[$#$buf-$i] =~ /:\s+(\*[0-9]+)/;
if ( $req_id eq $req_mark and
(++$prev_req_cnt >= $skip_dist) )
{
$skip_idx = $#$buf-$i;
last;
}
}
return $skip_idx;
}
Есть ряд мест, где можно повысить эффективность.
Главный вопрос эффективности касается центральной проблемы размера буфера.Сколько данных можно собрать, пока мы не нажмем на курок?Что, если в файле всего несколько [GET_..]
строк, и в итоге мы накапливаем гигабайт (ы) данных?Тогда было бы лучше время от времени осветлять (печатать часть) буфер;но если данных никогда не бывает много, то частичное опустошение буфера сильно усложняет ситуацию, поскольку фраза может быть близка (сколько строк еще нужно сохранить?).
На этот вопрос нельзя ответить, не зная частоты этогофразу, а также частоту идентификатора запроса.Тогда одной оптимизацией было бы сначала заглянуть в файл, оценить эти частоты и принять решение по этому вопросу.Однако это не слишком надежно, поскольку нет никакой гарантии, что файл журнала в любом случае будет согласованным.
Приведенный выше код явно предполагает, что данных никогда не будет слишком много, и поэтому мы не будемвызвать проблемы, но было бы разумно добавить проверку и выписать часть буфера, если он становится слишком большим.
Для записи, когда я запускаю вышеупомянутую программу с образцами данных OP, получается:
2018/10/08 17:11:28 [debug] 8851#0: *2 Sent 8/8 bytes.
2018/10/08 17:11:28 [debug] 8851#0: *2 Session: Staging 8 bytes in thread buffer.
2018/10/08 17:11:33 [debug] 8851#0: *36 GET_REGION_INFO: Staging 99 bytes in thread buffer.
2018/10/08 17:11:33 [debug] 8851#0: *36 Sent 99/99 bytes.
2018/10/08 17:11:33 [debug] 8851#0: *36 Session: Staging 8 bytes in thread buffer.
2018/10/08 17:11:33 [debug] 8851#0: *36 Sent 8/8 bytes.
2018/10/08 17:11:38 [debug] 8851#0: *22 Receiving 8 bytes
2018/10/08 17:11:38 [debug] 8851#0: *22 Session: Staging 8 bytes in thread buffer.
В данном примере введите идентификатор запроса в строке с[GET..]
($req_mark
) равно *36
.
По умолчанию количество пропускаемых строк *36
(вокруг строки [GET..]
) установлено в два (2) для лучшего тестирования;это можно изменить при вызове.
Нет строк *36
перед строкой с [GET...]
(точнее, там, где была эта строка), так как в данных было только две, и они должным образомпропускаются;и первые две строки с *36
после строки [GET..]
(там, где была эта строка) опущены, затем остальная часть печатается.Это ожидаемый результат.
Когда я задаю расстояние пропуска (настройка $skip_dist
) 10
, вывод будет
2018/10/08 17:11:28 [debug] 8851#0: *2 Sent 8/8 bytes.
2018/10/08 17:11:28 [debug] 8851#0: *2 Session: Staging 8 bytes in thread buffer.
2018/10/08 17:11:38 [debug] 8851#0: *22 Receiving 8 bytes
2018/10/08 17:11:38 [debug] 8851#0: *22 Session: Staging 8 bytes in thread buffer.
, как и ожидалось: до 10 строк с *36
обе дои после строки с [GET..]
в этом примере данных, поэтому *36
строки не печатаются.
Исходное сообщение (при условии, что *36
является данным идентификатором запроса, представляющим интерес)
Сохраните эти *36
строки в буфере, и пока вы находитесь в этом регионе, проверьте эту фразу.Как только вы выйдете из этого региона, проверьте, была ли найдена фраза, и напечатайте соответственно
my $trigger = 'GET_REGION_INFO';
my $region_mark = '*36';
my (@buff, $drop_lines_mark);
while (<$fh>) {
my ($req_id, $msg) = /.*?:\s*(\*[0-9]+)\s+(.*)/;
if ($req_id eq $region_mark) {
push @buff, $_
$drop_lines_mark = $#buff if $msg =~ /$trigger/;
}
elsif (@buff) { # just left region of interest
if ($drop_lines_mark) {
for my $i (0..$#buff) {
print $buff[$i]
if $i < $drop_lines_mark-10
or $i > $drop_lines_mark+10;
}
}
else { print for @buff }
$drop_lines_mark = '';
@buff = ();
print; # don't forget the current line
}
else { print }
}
Непроверенный код.