it-swarm-ru.tech

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

У меня есть журнал сервера, который выводит определенную строку текста в свой файл журнала, когда сервер работает. Я хочу выполнить команду после запуска сервера и, следовательно, сделать что-то вроде следующего:

tail -f /path/to/serverLog | grep "server is up" ...(now, e.g., wget on server)?

Каков наилучший способ сделать это?

56
jonderry

Простой способ был бы awk.

tail -f /path/to/serverLog | awk '
                    /Printer is on fire!/ { system("shutdown -h now") }
                    /new USB high speed/  { system("echo \"New USB\" | mail admin") }'

И да, оба являются реальными сообщениями из журнала ядра. Perl может быть немного более элегантным в использовании для этого и может также заменить необходимость в tail. При использовании Perl это будет выглядеть примерно так:

open(my $fd, "<", "/path/to/serverLog") or die "Can't open log";
while(1) {
    if(eof $fd) {
        sleep 1;
        $fd->clearerr;
        next;
    }
    my $line = <$fd>;
    chomp($line);
    if($line =~ /Printer is on fire!/) {
        system("shutdown -h now");
    } elsif($line =~ /new USB high speed/) {
        system("echo \"New USB\" | mail admin");
    }
}
36
penguin359

Если вы ищете только одну возможность и хотите оставаться в основном в командной консоли вместо использования awk или Perl, вы можете сделать что-то вроде:

tail -F /path/to/serverLog | 
grep --line-buffered 'server is up' | 
while read ; do my_command ; done

... который будет работать my_command каждый раз, когда " сервер работает " появляется в файле журнала. Для нескольких возможностей вы можете удалить grep и ​​вместо этого использовать case внутри while.

Столица -F говорит tail следить за тем, чтобы файл журнала вращался; Т.е. если текущий файл переименовывается и его место занимает другой файл с таким же именем, tail переключится на новый файл.

--line-buffered опция говорит grep очищать свой буфер после каждой строки; в противном случае, my_command может быть не достигнуто своевременно (при условии, что в журналах есть строки разумного размера).

20
Jander

На этот вопрос, похоже, уже дан ответ, но я думаю, что есть лучшее решение.

Скорее, чем tail | whatever, Я думаю, что вы действительно хотите, это swatch. Swatch - это программа, разработанная специально для выполнения ваших задач, просмотра файла журнала и выполнения действий на основе строк журнала. С помощью tail|foo для этого потребуется активный терминал. Swatch, с другой стороны, работает как демон и всегда будет смотреть ваши логи. Swatch доступен во всех дистрибутивах Linux,

Я призываю вас попробовать это. Хотя вы можете вбить гвоздь обратной стороной отвертки, это не значит, что вы должны это делать.

Лучший 30-секундный учебник по образцу, который я смог найти, здесь .

14
bahamat

Странно, что никто не упомянул об утилите multitail, которая имеет эту функциональность "из коробки". Один из примеров использования:

Показать вывод команды ping и, если он отображает тайм-аут, отправить сообщение всем пользователям, которые в данный момент вошли в систему

multitail -ex timeout "echo timeout | wall" -l "ping 192.168.0.1"

Смотрите также другие примеры использования multitail.

11
php-coder

Баш мог сделать работу сам

Давайте посмотрим, насколько просто и читабельно это может быть:

mylog() {
    echo >>/path/to/myscriptLog "[email protected]"
}

while read line;do
    case "$line" in
        *"Printer on fire"* )
            mylog Halting immediately
            shutdown -h now
            ;;
        *DHCPREQUEST* )
            [[ "$line" =~ DHCPREQUEST\ for\ ([^\ ]*)\  ]]
            mylog Incomming or refresh for ${BASH_REMATCH[1]}
            $HOME/SomethingWithNewClient ${BASH_REMATCH[1]}
            ;;
        * )
            mylog "untrapped entry: $line"
            ;;
    esac
  done < <(tail -f /path/to/logfile)

Хотя вы не используете bash regex, это может остаться очень быстро!

Но bash + sed это очень эффективный и интересный тандем

Но для сервера с высокой нагрузкой, и, как мне нравится sed, потому что он очень быстрый и очень масштабируемый, я часто использую это:

while read event target lost ; do
    case $event in
        NEW )
            ip2int $target intTarget
            ((count[intTarget]++))
        ...

    esac
done < <(tail -f /path/logfile | sed -une '
  s/^.*New incom.*from ip \([0-9.]\+\) .*$/NEW \1/p;
  s/^.*Auth.*ip \([0-9.]\+\) failed./FAIL \1/p;
  ...
')
8
F. Hauri

Вот так я и начал это делать, но стал намного более изощренным. Пара вещей, которые нужно учитывать:

  1. Если хвост журнала уже содержит "сервер включен".
  2. Автоматически заканчивая процесс хвоста, как только он найден.

Я использую что-то вроде этого:

RELEASE=/tmp/${RANDOM}$$
(
  trap 'false' 1
  trap "rm -f ${RELEASE}" 0
  while ! [ -s ${RELEASE} ]; do sleep 3; done
  # You can put code here if you want to do something
  # once the grep succeeds.
) & wait_pid=$!
tail --pid=${wait_pid} -F /path/to/serverLog \
| sed "1,10d" \
| grep "server is up" > ${RELEASE}

Он работает, удерживая tail открытым до ${RELEASE} файл содержит данные.

Как только grep преуспеет в этом:

  1. записывает вывод в ${RELEASE} который будет
  2. прекратить ${wait_pid} обработать до
  3. выйти из tail

Примечание: sed может быть более сложным, чтобы фактически определить количество строк, которые tail произведет при запуске, и удалить это число. Но в целом это 10.

6
nix