У вас есть какие-нибудь полезные скрипты awk и grep для разбора логов apache? [закрыто]


70

Я могу использовать анализаторы логов, но часто мне нужно анализировать последние веб-логи, чтобы увидеть, что происходит в данный момент.

Я иногда делаю вещи, как выяснить топ-10 ips, которые запрашивают определенный файл

cat foo.log | grep request_to_file_foo | awk '{print $1}' |  sort -n | uniq -c | sort -rn | head

Что у вас есть в вашем наборе инструментов?


1
У меня действительно было это большое красивое регулярное выражение, которое я написал вручную, чтобы проанализировать все мои пользовательские журналы apache до отдельных полей для отправки в базу данных. Я пинаю себя, что у меня его больше нет. Это был один лайнер; дал вам одну переменную для каждого элемента журнала - затем я вставлял в MySQL. Если я найду это, я отправлю это здесь.
Кайл Ходжсон

Ответы:


54

С помощью только файлов awk вы можете делать что угодно с файлами журналов apache. Файлы журнала Apache в основном разделены пробелами, и вы можете делать вид, что кавычки не существуют, и получать доступ к любой интересующей вас информации по номеру столбца. Единственный раз, когда это выходит из строя, это если у вас есть объединенный формат журнала и вы заинтересованы в пользовательских агентах, после чего вы должны использовать кавычки (") в качестве разделителя и выполнить отдельную команду awk. Далее будут показаны IP-адреса каждый пользователь, который запрашивает страницу индекса, отсортирован по количеству просмотров:

awk -F'[ "]+' '$7 == "/" { ipcount[$1]++ }
    END { for (i in ipcount) {
        printf "%15s - %d\n", i, ipcount[i] } }' logfile.log

7 $ - запрошенный URL. Вы можете добавить любые условия, которые вы хотите в начале. Замените '$ 7 == "/" любой информацией, которую вы хотите.

Если вы замените $ 1 в (ipcount [$ 1] ++), то вы можете сгруппировать результаты по другим критериям. Использование $ 7 покажет, какие страницы были доступны и как часто. Конечно, тогда вы захотите изменить условие в начале. Ниже будет показано, какие страницы были доступны пользователю с определенного IP:

awk -F'[ "]+' '$1 == "1.2.3.4" { pagecount[$7]++ }
    END { for (i in pagecount) {
        printf "%15s - %d\n", i, pagecount[i] } }' logfile.log

Вы также можете передать вывод через sort, чтобы получить результаты по порядку, либо как часть команды оболочки, либо также в самом скрипте awk:

awk -F'[ "]+' '$7 == "/" { ipcount[$1]++ }
    END { for (i in ipcount) {
        printf "%15s - %d\n", i, ipcount[i] | sort } }' logfile.log

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


Да, всегда странно видеть сумасшедшие длинные конвейеры cat / grep / awk. Как только вы попадаете в awk, этого обычно достаточно. Первые три пункта оригинального сообщения могут быть тривиально написаны как "awk" / request_to_file_foo / {print $ 1} 'foo.log ". awk может принимать файл в качестве входных данных и использовать регулярные выражения, чтобы знать, какие строки нужно учитывать.
Зак Томпсон

Элегантно и просто. Хороший.
Оливье Дюлак

Остерегайтесь пробелов в поле «authuser» (3-е), которое нарушает все, и я лично считаю, что это должно быть запрещено, чтобы мы могли это делать ;-)
Mandark

23

По причинам, которые я не могу себе представить, я никогда не видел, чтобы кто-то еще делал это, изменив формат файла журнала Apache на более легко разбираемую версию с информацией, которая действительно важна для вас.

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

Вот выдержка из конфигурации apache одного сервера:

# We don't want to log bots, they're our friends
BrowserMatch Pingdom.com robot

# Custom log format, for testing
#
#         date          proto   ipaddr  status  time    req     referer         user-agent
LogFormat "%{%F %T}t    %p      %a      %>s     %D      %r      %{Referer}i     %{User-agent}i" standard
CustomLog /var/log/apache2/access.log standard env=!robot

Из этого нельзя сказать, что между каждым полем находится буквальный символ табуляции (\ t). Это означает, что если я хочу провести некоторый анализ в Python, например, показать не-200 статусов, я могу сделать это:

for line in file("access.log"):
  line = line.split("\t")
  if line[3] != "200":
    print line

Или, если бы я хотел сделать, «кто ссылки горячие ссылки?» это было бы

if line[6] in ("","-") and "/images" in line[5]:

Для количества IP-адресов в журнале доступа, предыдущий пример:

grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" logfile | sort -n | uniq -c | sort -n

становится примерно так:

cut -f 3 log | uniq -c | sort -n

Легче читать и понимать, и намного дешевле в вычислительном отношении (без регулярных выражений), что при 9 ГБ журналах имеет огромное значение в том, сколько времени это займет. Когда это становится ДЕЙСТВИТЕЛЬНО аккуратно, если вы хотите сделать то же самое для User-agent. Если ваши журналы разделены пробелами, вы должны выполнить некоторое сопоставление с регулярным выражением или поиск строки вручную. С этим форматом все просто:

cut -f 8 log | uniq -c | sort -n

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

С какой стати я трачу центральный процессор моей системы на awk и grep, когда cut делает то, что я хочу на порядок быстрее?


2
Ваши примеры для нового формата на самом деле все еще слишком сложны - счетчики IP становятся cut -f 3 log | uniq -c | sort -nпользовательскими агентами cut -f 8 log | uniq -c | sort -n.
Creshal

Вы правы, это проще. Я обновил примеры, чтобы отразить это.
Дэн Удей

«cat file | grep string» бесполезен, почему бы не «grep string file»?
c4f4t0r

2
У меня нет оправданий, и я обновил пример соответственно.
Дэн Удей

15

Забудьте про awk и grep. Проверьте asql . Зачем писать нечитаемые сценарии, когда вы можете использовать sql-подобный синтаксис для запроса файла журнала. Например.

asql v0.6 - type 'help' for help.
asql> load /home/skx/hg/engaging/logs/access.log
Loading: /home/skx/hg/engaging/logs/access.log
sasql> select COUNT(id) FROM logs
46
asql> alias hits SELECT COUNT(id) FROM logs
ALIAS hits SELECT COUNT(id) FROM logs
asql> alias ips SELECT DISTINCT(source) FROM logs;
ALIAS ips SELECT DISTINCT(source) FROM logs;
asql> hits
46
asql> alias
ALIAS hits SELECT COUNT(id) FROM logs
ALIAS ips SELECT DISTINCT(source) FROM logs;

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

Я пытаюсь это в данный момент, время загрузки очень медленно (по крайней мере, в версии 0.9). Загрузка журнала размером 200 МБ занимает более пяти минут.
Aseques

Надо сказать, что после времени загрузки (это заняло около 15 минут) синтаксис этой программы великолепен, вы можете сортировать, считать и группировать по. Действительно мило.
Aseques

Apache HTTPD имеет метод, с помощью которого вы можете эффективно отправлять журналы в базу данных. Да, запись может занять много времени, но многопоточный прокси может сделать то, что нужно, в середине. В любом случае, это сделает запрос журналов в SQL-подобном синтаксисе намного быстрее. Также не требуется загрузка - сервер базы данных постоянно включен.
Nearora

6

Вот скрипт для поиска главных URL-адресов, лучших ссылок и лучших пользовательских агентов из последних N записей журнала

#!/bin/bash
# Usage
# ls-httpd type count
# Eg: 
# ls-httpd url 1000
# will find top URLs in the last 1000 access log entries
# ls-httpd ip 1000
# will find top IPs in the last 1000 access log entries
# ls-httpd agent 1000
# will find top user agents in the last 1000 access log entries

type=$1
length=$2

if [ "$3" == "" ]; then
  log_file="/var/log/httpd/example.com-access_log"
else
  log_file="$3"
fi

if [ "$type" = "ip" ]; then
  tail -n $length $log_file | grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" | sort -n | uniq -c | sort -n
elif [ "$type" = "agent" ]; then
  tail -n $length $log_file | awk -F\" '{print $6}'| sort -n | uniq -c | sort -n
elif [ "$type" = "url" ]; then
  tail -n $length $log_file | awk -F\" '{print $2}'| sort -n | uniq -c | sort -n
fi

Источник


4

для количества IP в журнале доступа:

cat log | grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" | sort -n | uniq -c | sort -n

Это немного некрасиво, но это работает. Я также использую следующее с netstat (чтобы увидеть активные соединения):

netstat -an | awk '{print $5}' | grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" | egrep -v "(`for i in \`ip addr | grep inet |grep eth0 | cut -d/ -f1 | awk '{print $2}'\`;do echo -n "$i|"| sed 's/\./\\\./g;';done`127\.|0\.0\.0)" | sort -n | uniq -c | sort -n

Они одни из моих любимых "лайнеров" :)


3

Составление списка общих вопросов было бы отличным показателем для ответов на этот вопрос. Мои общие вопросы:

  • почему изменился битрейт?
  • почему общее время отклика увеличивается?

Я замечаю такие изменения, отслеживая страницы состояния сервера (через mod_status) на предмет скорости обращения и приблизительного времени ответа для активных и недавно выполненных запросов (прекрасно понимая, что я пропускаю огромную кучу данных, но примеры достаточно хороши).

Я использую следующую директиву LogFormat (% T действительно полезен)

LogFormat "%h %l %u %t \"%r\" %>s %b 
    \"%{Referer}i\" \"%{User-Agent}i\" %T" custom

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

  • количество посещений за интервал (минуты или часы) для данного шаблона (IP-адрес или строка CGI или параметры и т. д.)
  • гистограммы приблизительного времени отклика (с использованием параметра% T)

Я обычно использую Perl, потому что в конце концов он становится достаточно сложным, чтобы быть полезным.


Примером, не относящимся к Perl, может быть быстрый удар в минуту для не-200 кодов состояния:

tail -9000 access_log | grep -v '" 200 ' | cut -d: -f2,3 | uniq -c

Да, я обманываю с этим grep, предполагая, что пробел-пробел-200-пробел совпадает только с http-кодами состояния .... может использовать awk или perl для изоляции поля, просто имейте в виду, что это может быть неточно.


Более сложным примером в Perl может быть визуализация изменения битрейта для шаблона.

В приведенном ниже сценарии есть что пережить, особенно если вы не знакомы с Perl.

  • читает stdin, так что вы можете использовать части ваших журналов, использовать tail (особенно с tail -f), с или без greps и другие фильтры ...
  • извлекает метки времени эпохи читов с помощью взлома регулярных выражений и использования Date :: Manip
  • Вы можете изменить его немного, чтобы извлечь время отклика или другие произвольные данные

код следует:

#!/usr/bin/perl
# script to show changes in hitrates for any regex pattern
# results displayed with arbitrary intervals
# and ascii indication of frequency
# gaps are also displayed properly
use Date::Manip;
use POSIX qw(strftime);
$pattern=shift || ".";
$ival=shift || 60;
$tick=shift || 10;
$minb=undef;
while (<>){
    next unless /$pattern/;
    $stamp="$1 $2" if m[(../.../....):(..:..:..)];
    $epoch = UnixDate(ParseDate($stamp),"%s");
    $bucket= int($epoch/$ival)*$ival;
    $minb=$bucket if $bucket<$minb || !defined($minb);
    $maxb=$bucket if $bucket>$maxb;
    $count{$bucket}++;
}
# loop thru the min/max range to expose any gaps
for($t=$minb;$t<=$maxb;$t+=$ival){
    printf "%s %s %4d %s\n",
            $t,
            strftime("%m/%d/%Y %H:%M:%S",localtime($t)),
            $count{$t}+0,
            substr("x"x100,0,$count{$t}/$tick
    );
}

Если вы просто хотите обработать стандартные метрики, оформите заказ

  • 'mergelog', чтобы собрать все ваши журналы (если у вас есть несколько apache за балансировщиком нагрузки) и
  • webalizer (или awstats или другой общий анализатор).

3

Вот мой пример «sed», он читает формат логов apache по умолчанию и преобразует его в нечто более удобное для автоматической обработки. Вся строка определена как регулярное выражение, переменные сохраняются и записываются в вывод с «#» в качестве разделителя.

Упрощенная запись ввода:% s% s% s [% s] "% s"% s% s "% s" "% s"

Пример строки ввода: xx.xx.xx.xx - - [29 / Mar / 2011: 12: 33: 02 +0200] "GET /index.html HTTP / 1.0" 200 9443 "-" "Mozilla / 4.0"

Пример строки выходных данных: xx.xx.xx.xx # - # - # 29 / Mar / 2011: 12: 33: 02 + 0200 # GET /index.html HTTP / 1.0 # 200 # 9443 # - # Mozilla / 4.0

cat access.log | \ 
  sed 's/^\(.*\) \(.*\) \(.*\) \[\(.*\)\] \"\(.*\)\" \(.*\) \(.*\) \"\(.*\)\" \"\(.*\)\"$/\1#\2#\3#\4#\5#\6#\7#\8#\9/g'

Почувствуйте силу регулярных выражений :-)


Это сделало обработку с AWK бризом. Искал быстрый способ настроить общий разделитель, и это прибило его.
Citricguy

Я почувствовал силу регулярных выражений и просто хотел передать мой собственный твик, который вырезает «HTML / 1.1» и разделяет протокол (вероятно, не в соответствии со стандартами) в свою собственную область. Наслаждайтесь: `` `cat access.log | sed 's /^(.*) (. *) (. *) [(. *)] \ "([[: alpha:]] \ +) (. *) HTTP \ / 1 \ .1 \" ( . *) (. *) \ "(. *) \" \ "(. *) \" $ / \ 1 # \ 2 # \ 3 # \ 4 # \ 5 # \ 6 # \ 7 # \ 8 # \ 9 # \ 10 / g '`` `
Джош Рамбут

2

Я часто использую awk, следя за файлом. Каждую ночь я доставляю себе веб-отчет для каждого сервера. В зависимости от вашего файла журнала и вашего LogFormat вам нужно отредактировать некоторые из этих лайнеров, чтобы они работали на вас.

Вот простой пример:

Если я хочу подключить журналы на моем сервере только для кодов состояния 404/500, я бы сделал это:

# $6 is the status code in my log file

tail -f ${APACHE_LOG} |  awk  '$8 ~ /(404|500)/ {print $6}'

<snip>

echo ""
#echo  "Hits by source IP:"
echo "======================================================================"

awk '{print $2}' "$1" | grep -ivE "(127.0.0.1|192.168.100.)" | sort | uniq -c | sort -rn | head -25

echo ""
echo ""
#echo "The 25 most popular pages:"
echo "======================================================================"

awk '{print $6}' "$1" | grep -ivE '(mod_status|favico|crossdomain|alive.txt)' | grep -ivE '(.gif|.jpg|.png)' | \
 sed 's/\/$//g' | sort | \
 uniq -c | sort -rn | head -25

echo ""    
echo ""
echo "The 25 most popular pages (no js or css):"
echo "======================================================================"

awk '{print $6}' "$1" | grep -ivE '(mod_status|favico|crossdomain|alive.txt)' | grep -ivE '(.gif|.jpg|.png|.js|.css)' | \
 sed 's/\/$//g' | sort | \
   uniq -c | sort -rn | head -25

   echo ""


#echo "The 25 most common referrer URLs:"
echo "======================================================================"

awk '{print $11}' "$1" | \
 grep -vE "(^"-"$|/www.$host|/$host)" | \
 sort | uniq -c | sort -rn | head -25

echo ""

#echo "Longest running requests"
echo "======================================================================"

awk  '{print $10,$6}' "$1" | grep -ivE '(.gif|.jpg|.png|.css|.js)'  | awk '{secs=0.000001*$1;req=$2;printf("%.2f minutes req time for %s\n", secs / 60,req )}' | sort -rn | head -50

exit 0

</ snip>


2

Кто горячо связывает ваши изображения:

awk -F\" '($2 ~ /\.(jpg|gif)/ && $4 !~ /^http:\/\/www\.mydomain\.com/){print $4}' access_log | sort | uniq -c | sort

1

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

#! / Bin / Баш
# Этот скрипт должен возвращать набор строк между 2 значениями, основная цель - поиск в файле журнала 2 раза
# Использование скрипта: logship.sh «start» «stop» file

# Если файл содержит любые символы "/" в диапазоне дат, в следующих 2 строках добавляется escape-символ, чтобы можно было выполнить поиск этих символов.
start = $ (echo "$ 1" | sed 's / \ // \\\ // g')
stop = $ (echo "$ 2" | sed 's / \ // \\\ // g')

zipped = $ (echo "$ 3" | grep -c "gz $") # определяет, был ли файл упакован в архив или нет

if ["$ zipped" == "1"]; затем # Если файл заархивирован, передайте его через zcat
        zcat $ 3 | sed -n "/ $ start /, / $ stop / p";
еще
        sed -n "/ $ start /, / $ stop / p" $ 3; # если он не застегнут, просто запустите sed
фи

1

Хотя это и не sed или awk, есть две вещи, которые я нашел полезными для работы с файлами журналов apache и icecast.

AWStats имеет очень полезный скрипт logresolvemerge.pl , который объединяет несколько сжатых или несжатых файлов журнала, удаляет дубликаты и сортирует по отметке времени. Он также может выполнять поиск DNS и быть настроен для запуска многопоточных. Это особенно полезно при использовании с awstats, потому что awstats не может добавлять строки журнала с временными метками, которые старше текущей базы данных, поэтому все должны быть добавлены по порядку, но это очень просто, поскольку вы просто добавляете все в logresolvemerge.pl, и все это красиво появляется.

sed и awk довольно плохо справляются с датами, потому что они обычно воспринимают их как строки. У awk есть некоторые функции времени и даты, но их не так много. Например, извлечь диапазон строк между двумя временными метками сложно, если эти точные временные метки не встречаются в файле (даже если значения между ними есть) - пример Криса имеет именно эту проблему. Чтобы справиться с этим, я написал PHP-скрипт, который сообщает о диапазонах меток времени файла журнала, а также может извлекать порцию по диапазону меток времени, используя любой формат даты или времени, который вам нравится (он не должен совпадать с форматом метки времени файла журнала).

Чтобы сохранить это в теме, вот несколько полезных акизм: Получите общее количество байтов, обслуживаемых из журнала Apache или Icecast:

cat access.log | awk '{ sum += $10 } END { print sum }'

Получить общее количество секунд, подключенных из журнала Icecast:

cat access.log | awk '{ sum += $13 } END { print sum }'

+1 для простого байт-суммирования apache log с awk
rymo

0

При восстановлении этого старого потока, после отказа от asql для больших файлов журналов, искал решение, которое также вызывало ошибку сервера. Я обнаружил, что здесь wtop - это инструмент с открытым исходным кодом, который способен выполнять мониторинг в реальном времени или журналы процессов и получать статистику (top N), очень гибкий и мощный, официальное место здесь

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.