Форум русскоязычного сообщества Ubuntu


Автор Тема: Как вывести кусок лог-файла за определенный промежуток времени?  (Прочитано 18914 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн vento1980

  • Автор темы
  • Новичок
  • *
  • Сообщений: 32
    • Просмотр профиля
Добрый день,

есть задача вывести записи лога за последние, скажем, 5 минут (в консоли).

Для примера, возьмем лог апача:
(Нажмите, чтобы показать/скрыть)

Лог состоит из строк. Строки лога снабжены временной меткой и каким-то сообщением.

Алгоритм видится такой:
1) cat log_file
2) передать вывод по конвейеру в awk:
3) в awk для каждой строки преобразовать строковую временную метку в кол-во секунд от 01.01.1970
4) если полученное значение больше чем systime() - 300 (5 мин), то делаем print строки лога иначе переход к следующей строке (systime возвращает текущее кол-во секунд от 01.01.1970)
5) далее выхлоп awk через конвеер мне нужно грепить по ключевым словам.

Затык на 3-м шаге алгоритма: как в awk преобразовать строку вида "22/Aug/2011:09:05:19 +0400" в кол-во секунд от 01.01.1970?

Рассмотрю другие способы решения задачи (например awk+perl). НО. Обязательно нужен конвейер в консоли.

Заранее благодарю!

Оффлайн oddworld

  • Активист
  • *
  • Сообщений: 404
    • Просмотр профиля
cat /var/log/syslog |grep 14:0можно и так, за 10 минут начиная с 14:00
нет ничего совершенного, и не будет...

Оффлайн vento1980

  • Автор темы
  • Новичок
  • *
  • Сообщений: 32
    • Просмотр профиля
Не. Не вариант.

Допустим сейчас 13:03. Нужно вывести за последние 10 мин. Что мне тогда указывать в грепе? 14:5? тогда записи с 13:0* не будут выведены.

Оффлайн shumtest

  • Активист
  • *
  • Сообщений: 731
  • Это вам просто кажется...
    • Просмотр профиля
    • Блог Шумомера
Если знаете регулярки, то греп становится очень гибким :)
Ну а если нет, тогда да - мучения с комбайнами обеспечены.

andrey_p

  • Гость
Подсказка:
date -d '22 Aug 2011 09:05:17' +%s
1313978717
А вообще-то если вам нужны манипуляции с датой и временем, то либо используйте любой (из четырех) скриптовый язык, либо готовьтесь к мозгободанию.

Оффлайн vento1980

  • Автор темы
  • Новичок
  • *
  • Сообщений: 32
    • Просмотр профиля
Про date -d '22 Aug 2011 09:05:17' +%s знаю. Только как его заиспользовать в построчной обработке awk непонятно. В качестве скрипта пытался использовать perl тогда вопрос по передачи через поток данных перлу, т.е. как обработать примерно такую конструкцию:
grep бла-бла-бла myfile.txt | perl -e 'какие-то функции по обработке stdin'
?

andrey_p

  • Гость
perl -ne или perl -pe (perldoc perlrun). Только зачем обязательно фильтр? Можно имя журнала передавать в качестве аргумента скрипта. Задача чуть больше чем для однострочника. Если Perl, то лучше использовать модуль DateTime (from CPAN).

Впрочем, если обязательно нужен конвейер, то можно и через конвейер. Вот набросок из "смеси", можно делать все в чистом шелл, или чистом awk/perl.

cat INPUTFILE | while read -r line; do                                       
  d=$(echo $line |
    grep -Eo '[0-9]{2}/[A-Z][a-z][a-z]/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2}' |
    sed 's|/| |g; s/:/ /' )
  d=$(date -d"$d" +%s)
  echo "$d $line"
done | awk -vd=1313978420 '$1>d' | perl -lane 'print "@F[1..$#F]"'

Оффлайн vento1980

  • Автор темы
  • Новичок
  • *
  • Сообщений: 32
    • Просмотр профиля
Если кому понадобится, навоял под свою задачу перловый скрипт для вывода записей апачевского лога за последние 10 минут (apache_log_filter.pl):
(Нажмите, чтобы показать/скрыть)

перловый скрипт юзал след. образом:
cat /var/apache2/logs/access_log | ~/apache_log_filter.pl | wc -l

Скрипт потестил на Linux-е (Linux 2.6.38-12-generic #51-Ubuntu SMP Wed Sep 28 14:25:20 UTC 2011 i686 i686 i386 GNU/Linux) и соляре (SunOS 5.10 Generic_144488-09 sun4v sparc SUNW,Sun-Fire-T200).

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

Оффлайн xeon_greg

  • Активист
  • *
  • Сообщений: 981
    • Просмотр профиля
какова цель такого изврщения, почему именно 10 минут? на серверах с разной загруженностью это может быть 100 записей, а может быть и 10000, и парсить здоровенный лог ради выгребания всего 10 минут.... не лучше ли использовать tail -n xxx..  и выгребать с хвоста нужное кол-во записей, в комбинации с grep можно получать что хочешь..

Оффлайн vento1980

  • Автор темы
  • Новичок
  • *
  • Сообщений: 32
    • Просмотр профиля
Неплохая идея: сначала оценить примерное количество записей из большого лога кот. требуется для обработки а далее выделить те что попадают во временной интервал. Т.е. получится что-то типа:
tail -6000 /var/apache2/logs/access_log | ~/apache_log_filter.pl | wc -lСадомазо организовано для мониторинга состояния (в данном случае апача): есть система мониторинга, кот. опрашивает лог каждые 10 мин. и если определенных записей не найдено (или, наоборот, найдено), то генерится инцидент (см. ITSM).

Оффлайн xeon_greg

  • Активист
  • *
  • Сообщений: 981
    • Просмотр профиля
Неплохая идея: сначала оценить примерное количество записей из большого лога кот. требуется для обработки а далее выделить те что попадают во временной интервал. Т.е. получится что-то типа:
tail -6000 /var/apache2/logs/access_log | ~/apache_log_filter.pl | wc -lСадомазо организовано для мониторинга состояния (в данном случае апача): есть система мониторинга, кот. опрашивает лог каждые 10 мин. и если определенных записей не найдено (или, наоборот, найдено), то генерится инцидент (см. ITSM).
ну как один и вариантов. уж лучше перебора всего лога. только
Цитировать
tail -n6000 ...


Оффлайн wl

  • Старожил
  • *
  • Сообщений: 1393
    • Просмотр профиля
1. В природе есть gnome log viewer.

2. Где-то в недрах вот этого блога был любопытный скрипт, позволяющий выполнять SQL-запросы на текстовых файлах.
Ссылку на скрипт я найти что-то не могу, но сам он вот:

#!/usr/bin/python

# SELECT - ad-hoc SQL queries on text files
#
# (c) 2008 Markus Bertheau
#
# Licensed under the GPL Version 3 (see LICENSE)
#
# Usage:
# SELECT all FROM file.txt
# SELECT response_code, count\(1\) FROM access_log WHERE method = "'GET'" GROUP BY response_code ORDER BY 2 DESC

import sys
try:
    import sqlite3
except ImportError:
    from pysqlite2 import dbapi2 as sqlite3
import re

file_types = {
    'access_log_combined':
                    r"^(?P<remote_host>[^ ]+) "
                  + r"(?P<remote_logname>([^\"]+|\"([^\"]|\\\")*\")) "
                  + r"(?P<remote_user>([^\"]+|\"([^\"]|\\\")*\")) "
                  + r"\[(?P<request_time>\d{2}/\w{3}/\d{4}(:\d{2}){3} [+-]\d{4})\] "
                  + r"\"(?P<request_method>(\w+)) +"
                  + r"(?P<request_uri>[^ ]+) "
                  + r"(?P<request_protocol>HTTP/\d\.\d) *\" "
                  + r"(?P<response_code>\d{3}) "
                  + r"(?P<response_length>(\d+|-)) "
                  + r"\"(?P<referer>[^\"]*)\" "
                  + r"\"(?P<user_agent>([^\"]|\\\")*)\"$"
}

def parse_cmdline():
    """
    Parse the command line into field list, filename and rest.
    The command line looks like this:
    SELECT field1, field2 FROM table      WHERE ...
           (fieldlist   )      (filename) (sqlargs )
    """
    lowersysargv = [s.lower() for s in sys.argv[1:]]
    if not 'from' in lowersysargv:
        sys.stderr.write('Syntax error: FROM missing\n')
        sys.exit(1)

    fromindex = lowersysargv.index('from')

    fieldlist = sys.argv[1:fromindex + 1]
    if len(fieldlist) == 0:
        sys.stderr.write('Syntax error: field list empty\n')
        sys.exit(1)

    try:
        filename = sys.argv[fromindex + 2]
    except IndexError:
        sys.stderr.write('Syntax error: no file specified after FROM\n')
        sys.exit(1)

    # collect all other arguments after the table list
    sqlArgs = sys.argv[fromindex + 3:]

    return (fieldlist, filename, sqlArgs)

def read_file(filename):
    """
    open file (stdin if filename == -), detect table type, create database and
    table and load data into table. return db
    """
# init database
    db = sqlite3.connect(':memory:')
    cur = db.cursor()

# read file into database
    if filename == '-':
        fd = sys.stdin
    else:
        fd = open(filename)

    firstline = fd.readline()

    table_type = detect_table_type(firstline)
    create_table(cur, table_type[0])

    insert_record(cur, table_type, firstline)
    for line in fd.readlines():
        insert_record(cur, table_type, line)
    db.commit()

    return db

def main():
    (fieldlist, filename, sqlArgs) = parse_cmdline()
    db = read_file(filename)
    cur = db.cursor()

# create query
    sqlFieldList = ' '.join([fieldfilter(s) for s in fieldlist])

# order by
# limit and offset

    sqlQuery = 'SELECT %s FROM A %s' % (sqlFieldList, ' '.join(sqlArgs))
    cur.execute(sqlQuery)
    curlen = db.cursor()
    sqlWidthQuery = 'SELECT %s FROM (%s)' % (", ".join(['max(length("%s"))' % s[0]
                                                        for s in cur.description]),
                                             sqlQuery)
    curlen.execute(sqlWidthQuery)
    headers = map(lambda x: x[0], cur.description)
    headerlengths = curlen.fetchone()
    headers = [(header[0], max(len(header[0]), headerlengths[header[1]])) for header in zip(headers, range(len(headers)))]

    firstline = []
    for (header, length) in headers:
        firstline.append(" %s%s " % (header, " " * (length - len(header))))
    print "|".join(firstline)

    secondline = []
    for (header, length) in headers:
        secondline.append('-' * (length + 2))
    print "+".join(secondline)

    for row in cur:
        dataline = []
        for field in zip(headers, row):
            dataline.append(" %s%s " % (field[1], " " * (field[0][1] - len(str(field[1])))))
        print "|".join(dataline)


def fieldfilter(s):
    """
    convert command line field list entries to sql field list entries
    """
    if s.lower() == 'all':
        return '*'
    return '%s' % s

def create_table(cur, fieldnames):
    sql = 'CREATE TABLE A (%s)' % ','.join(['"%s"' % s for s in fieldnames])
    cur.execute(sql)

def insert_record(cur, table_type, line):
# silently ignore empty lines
    try:
        values = table_type[1](line)
    except:
        sys.stderr.write(line)
        # ignore non-matching lines
        return
    sql = 'INSERT INTO A (%s) VALUES (%s)' % (','.join(['"%s"' % s for s in values.keys()]),
                                              ','.join(['?'] * len(values)))
    cur.execute(sql, values.values())

def detect_table_type(line):
    """
    detect table type.
    returns tuple(tuple of column names,
                  function to convert line into dict {field_name: value, ...})
    """
    for (name, reg) in file_types.items():
        try:
            rec = re.compile(reg)
            rem = rec.match(line)
            fieldnames = rem.groupdict().keys()
            # sort field names by field appearance in line
            fieldnames.sort(lambda x, y: cmp(rem.start(x), rem.start(y)))
            return (fieldnames, lambda x: rec.match(x).groupdict())
        except:
            continue
    # default to splitting by space and numerical indexes
    fieldnames = range(1, len(line.split()) + 1)
    return (fieldnames, lambda x: dict(zip(fieldnames, x.split())))

if __name__ == '__main__':
    main()


Не доделан, конечно, немного, но можно и допилить :)

Обсуждение аналогичных утилит на Stackoverflow: http://stackoverflow.com/questions/778081/sql-query-engine-for-text-files-on-linux

Да, а апач не умеет сразу в СУБД свои логи класть?
« Последнее редактирование: 01 Ноября 2011, 15:56:21 от wl »
На свете феньки есть такие, брат Горацио, которых лохи просто не секут. (Шекспир, "Гамлет", вольный перевод)

Оффлайн xeon_greg

  • Активист
  • *
  • Сообщений: 981
    • Просмотр профиля
ну раз уж пошла такая пляска, зачем скрипты, есть готовый инструмент asql, и да умеет складывать логи в sql, есть модуль..

Оффлайн unimix

  • Активист
  • *
  • Сообщений: 537
    • Просмотр профиля
Садомазо организовано для мониторинга состояния (в данном случае апача): есть система мониторинга, кот. опрашивает лог каждые 10 мин. и если определенных записей не найдено (или, наоборот, найдено), то генерится инцидент (см. ITSM).

А что, ротации лог-фалов апача на сервере не ведётся? Если есть ротация, то необходимо ещё и это учитывать.

Ну и ещё один простенький пример для конвеера:
Код: (bash) [Выделить]
#! /bin/bash

TIME=`date -d '-10minute' +%s`

while read line
do
time=`echo $line | cut -f4 -d' ' | sed -e 's/^\[//' -e 's/\// /g' -e 's/\:/ /'`
time=`date -d "$time" +%s`
if [ $time -gt $TIME ]
then
echo $line
fi
done

Оффлайн vento1980

  • Автор темы
  • Новичок
  • *
  • Сообщений: 32
    • Просмотр профиля
Увы, конструкция time=`date -d "$time" +%s` на соляре не работает.

С ротацией логов надо думать.

В скриптец хочу добавить обработку часового пояса. Вроде как mktime можно заставить считать unixtime для определенной зоны.
« Последнее редактирование: 02 Ноября 2011, 12:43:17 от vento1980 »

 

Страница сгенерирована за 0.035 секунд. Запросов: 25.