Я могу использовать анализаторы журналов, но часто мне нужно проанализировать последние веб-журналы, чтобы увидеть, что происходит в данный момент.
Иногда я пытаюсь вычислить 10 первых IP-адресов, которые запрашивают определенный файл.
cat foo.log | grep request_to_file_foo | awk '{print $1}' | sort -n | uniq -c | sort -rn | head
Что у вас есть в вашем ящике для инструментов?
Вы можете делать практически все, что угодно, с файлами журнала apache, используя только awk. Файлы журнала 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
Это одни из моих любимых «однострочников» :)
Составление списка общих вопросов было бы отличным указателем для ответа на этот вопрос. Мои общие вопросы:
Я замечаю такие изменения, отслеживая страницы состояния сервера (через mod_status) на предмет скорости и приблизительного времени ответа для активных и недавно завершенных запросов (прекрасно зная, что я пропускаю огромную кучу данных, но образцы достаточно хороши).
Я использую следующую директиву LogFormat (% T действительно полезен)
LogFormat "%h %l %u %t \"%r\" %>s %b
\"%{Referer}i\" \"%{User-Agent}i\" %T" custom
Я ищу причинно-следственные связи и то, что произошло первым ... обычно в отношении определенных подмножеств шаблонов в моих журналах, поэтому мне нужно знать следующее для любого данного шаблона / регулярного выражения:
Обычно я использую perl, потому что со временем он становится достаточно сложным, чтобы иметь смысл.
Примером без Perl будет быстрая скорость попадания в минуту для кодов статуса, отличных от 200:
tail -9000 access_log | grep -v '" 200 ' | cut -d: -f2,3 | uniq -c
Да, я жульничаю с этим grep, предполагая, что кавычка-пробел-200-пробел соответствует только кодам статуса http .... можно использовать awk или perl для изоляции поля, просто имейте в виду, что это может быть неточно.
Более сложным примером в Perl может быть визуализация изменения скорости попадания для шаблона.
В приведенном ниже скрипте есть что разжевать, особенно если вы не знакомы с perl.
код следующий:
#!/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
);
}
Если вы просто хотите обрабатывать стандартные метрики, оформляйте заказ
Вот мой пример "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 / март / 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, отслеживая или отслеживая файл. Каждую ночь я отправляю себе веб-отчет для каждого сервера. В зависимости от вашего файла журнала и вашего 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
</ snip>
Кто делает горячие ссылки на ваши изображения:
awk -F\" '($2 ~ /\.(jpg|gif)/ && $4 !~ /^http:\/\/www\.mydomain\.com/){print $4}' access_log | sort | uniq -c | sort
То, что я обычно делаю большую часть времени, - это чтение разделов журнала в зависимости от времени, поэтому я написал следующий сценарий, используя sed, чтобы извлечь интересующий меня период, он работает с каждым файлом журнала, который я получил поперек и может также обрабатывать архивные журналы.
#!/bin/bash #This script should return a set of lines between 2 values, the main purpose is for searching a log file between 2 times #Script usage: logship.sh "start" "stop" file #If the file contains any "/" in the date range the following 2 lines add the escape character so that the search can be performed for those characters start=$(echo "$1" | sed 's/\//\\\//g') stop=$(echo "$2" | sed 's/\//\\\//g') zipped=$(echo "$3" | grep -c "gz$") #figures out if the file is zipped or not if [ "$zipped" == "1" ]; then #If the file is zipped then pass it through zcat before sed zcat $3 | sed -n "/$start/,/$stop/p"; else sed -n "/$start/,/$stop/p" $3; #if it's not zipped just run sed fi
Хотя это и не sed или awk, есть две вещи, которые я нашел полезными для обработки файлов журналов apache и icecast.
AWStats имеет очень полезный скрипт под названием logresolvemerge.pl который объединит несколько сжатых или несжатых файлов журнала, удалит дублированные файлы и отсортирует их по отметкам времени. Он также может выполнять поиск DNS и быть настроен для работы в многопоточном режиме. Это особенно полезно при использовании с awstats, потому что awstats не может добавлять строки журнала с отметками времени старше, чем текущая база данных, поэтому все должны быть добавлены по порядку, но это очень просто, поскольку вы просто бросаете все на logresolvemerge.pl, и все это выглядит красиво.
sed и awk довольно плохо обрабатывают даты, потому что обычно они обрабатывают их как строки. В awk есть несколько функций времени и даты, но их не так много. Например, извлечь диапазон строк между двумя отметками времени сложно, если эти точные отметки времени не встречаются в файле (даже если значения между ними присутствуют) - пример Криса имеет именно эту проблему. Чтобы разобраться с этим, я написал сценарий PHP который сообщает о диапазонах временных меток файла журнала, а также может извлекать фрагмент по диапазону меток времени, используя любой формат даты или времени, который вам нравится (он не должен соответствовать формату метки времени файла журнала).
Чтобы сохранить эту тему, вот несколько полезных awkisms: Получите общее количество байтов, обслуженных из журнала apache или icecast:
cat access.log | awk '{ sum += $10 } END { print sum }'
Получите общее количество секунд, подключенных из журнала icecast:
cat access.log | awk '{ sum += $13 } END { print sum }'
Восстановив этот старый поток, после отказа от asql для больших файлов журнала, я снова искал решение, также в serverfault, я нашел о wtop Вот это инструмент с открытым исходным кодом, который способен вести мониторинг в реальном времени или обрабатывать журналы и получать статистику (первые N), очень гибкий и мощный, официальное место Вот