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

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

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

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

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

12 ответов

Решение

С помощью только файлов 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 для распечатки другой информации. Все дело в том, что вы хотите узнать. Они должны служить отправной точкой для всего, что вас интересует.

По причинам, которые я не могу себе представить, я никогда не видел, чтобы кто-то еще делал это, изменив формат файла журнала 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 делает то, что я хочу на порядок быстрее?

Забудьте про 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;

Вот скрипт для поиска главных 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

Источник

Для количества 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

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

Вот мой пример "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'

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

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

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

Я замечаю такие изменения, отслеживая страницы состояния сервера (через 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 или другой общий анализатор).

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

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

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

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

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

# $6 is the status code in my log file

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

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

В большинстве случаев я обычно читаю разделы журнала, основанные на времени, поэтому я написал следующий скрипт, используя 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
фи

Хотя это и не 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 }'

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

Другие вопросы по тегам