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

Вопрос или проблема

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

tail -f /path/to/serverLog | grep "сервер запущен" ...(например, wget на сервере)?

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

Простым способом будет использование awk.

tail -f /path/to/serverLog | awk '
                    /Принтер горит!/ { system("shutdown -h now") }
                    /новый USB высокоскоростной/  { system("echo \"Новый USB\" | mail admin") }'

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

open(my $fd, "<", "/path/to/serverLog") or die "Не удается открыть журнал";
while(1) {
    if(eof $fd) {
        sleep 1;
        $fd->clearerr;
        next;
    }
    my $line = <$fd>;
    chomp($line);
    if($line =~ /Принтер горит!/) {
        system("shutdown -h now");
    } elsif($line =~ /новый USB высокоскоростной/) {
        system("echo \"Новый USB\" | mail admin");
    }
}

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

tail -F /path/to/serverLog | 
grep --line-buffered 'сервер запущен' | 
while read ; do my_command ; done

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

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

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

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

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

multitail -ex timeout "echo тайм-аут | wall" -l "ping 192.168.0.1"

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

Этот вопрос, похоже, уже был задан, но я думаю, что есть лучшее решение.

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

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

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

Вот текущий блог/учебник по swatch.

может сделать это сам

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

mylog() {
    echo >>/path/to/myscriptLog "$@"
}

while read line;do
    case "$line" in
        *"Принтер горит"* )
            mylog Останавливаю немедленно
            shutdown -h now
            ;;
        *DHCPREQUEST* )
            [[ "$line" =~ DHCPREQUEST\ for\ ([^\ ]*)\  ]]
            mylog Входящий или обновление для ${BASH_REMATCH[1]}
            $HOME/SomethingWithNewClient ${BASH_REMATCH[1]}
            ;;
        * )
            mylog "необработанная запись: $line"
            ;;
    esac
  done < <(tail -f /path/to/logfile)

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

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

Но для сервера с высокой нагрузкой, и поскольку мне нравится 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/^.*Новый вход.*от ip \([0-9.]\+\) .*$/NEW \1/p;
  s/^.*Auth.*ip \([0-9.]\+\) неудача./FAIL \1/p;
  ...
')

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

  1. Если конец журнала уже содержит “сервер запущен”.
  2. Автоматически завершить процесс tail, как только это будет найдено.

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

RELEASE=/tmp/${RANDOM}$$
(
  trap 'false' 1
  trap "rm -f ${RELEASE}" 0
  while ! [ -s ${RELEASE} ]; do sleep 3; done
  # Вы можете вставить код здесь, если хотите что-то сделать
  # как только grep будет успешным.
) & wait_pid=$!
tail --pid=${wait_pid} -F /path/to/serverLog \
| sed "1,10d" \
| grep "сервер запущен" > ${RELEASE}

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

Как только grep будет успешным, он:

  1. записывает вывод в ${RELEASE}, что
  2. прекратит процесс ${wait_pid} для
  3. выхода из tail

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

Я использую fail2ban для мониторинга и выполнения команды

Ответ или решение

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

1. Использование tail и grep

Один из наиболее простых и распространенных способов — использовать команду tail в сочетании с grep и конструкцией while read. Вот как это можно сделать:

tail -F /path/to/serverLog | grep --line-buffered 'server is up' | while read line; do
    # Замените my_command на команду, которую хотите выполнить
    my_command
done
  • tail -F следит за лог-файлом, даже если он будет переименован или изменен.
  • Опция --line-buffered у grep позволяет обеспечить немедленную обработку каждой строки, что важно для быстрого реагирования на события.
  • Команда my_command будет выполнена каждый раз, когда будет найдено искомое сообщение.

2. Использование awk

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

tail -f /path/to/serverLog | awk '
{
    if ($0 ~ /server is up/) {
        system("my_command")
    }
    # Можно добавить дополнительные условия для других команд
}'

Этот подход удобен для более сложных условий и обработки различного текста в логах.

3. Программа swatch

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

  1. Установите swatch, если он еще не установлен.
  2. Создайте конфигурационный файл, например swatch.config:
watchfor /server is up/
    exec = my_command
  1. Запустите swatch, указав ему на лог-файл и конфигурационный файл:
swatch -c swatch.config -t /path/to/serverLog

4. Использование multitail

Другим интересным инструментом является multitail, который также может выполнять команды при обнаружении текста в логах. Например, команду можно добавить, используя следующий синтаксис:

multitail -ex "server is up" "my_command" -f /path/to/serverLog

5. Программный подход на Perl или Python

Если вы хотите более гибкое решение, можно использовать язык сценариев, такой как Perl или Python, для написания селектора, который будет запускаться как демон:

Пример на Perl:

use strict;
use warnings;

open(my $fh, "<", "/path/to/serverLog") or die "Can't open log";
while (1) {
    while (my $line = <$fh>) {
        chomp($line);
        if ($line =~ /server is up/) {
            system("my_command");
        }
    }
    sleep(1);
}

Пример на Python:

import time
import subprocess

logfile = "/path/to/serverLog"

with open(logfile, 'r') as f:
    f.seek(0, 2)  # переместить указатель в конец файла
    while True:
        line = f.readline()
        if not line:
            time.sleep(1)  # пауза
            continue
        if "server is up" in line:
            subprocess.call(["my_command"])  # вызов команды

Заключение

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

Оцените материал
Добавить комментарий

Капча загружается...