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


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

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

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

Что у вас есть на панели инструментов?


63
2018-05-21 22:14


Источник


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


Ответы:


Вы можете делать почти что угодно с файлами журнала 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 in (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

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

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

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


53
2018-06-03 02:21



Да, всегда кажется странным видеть сумасшедшие длинные конвейеры cat / grep / awk. Когда вы находитесь в awk, этого обычно достаточно. Первые три статьи исходного сообщения могут быть тривиально записаны как «awk» / request_to_file_foo / {print $ 1} «foo.log». awk может взять файл как входной сигнал и может использовать регулярное выражение, чтобы узнать, какие строки нужно заботиться. - Zac Thompson
Элегантный и простой. Хорошо. - Olivier Dulac
Опасайтесь, пробелы, кажется, разрешены в поле «authuser» (3), что ломает все, и я лично считаю, что это должно быть запрещено, чтобы позволить нам это делать ;-) - Mandark


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

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

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


23
2018-06-05 21:46



Ваши примеры для нового формата на самом деле все еще сложнее - количество IP-адресов становится cut -f 3 log | uniq -c | sort -n, пользовательские агенты cut -f 8 log | uniq -c | sort -n, - Creshal
Вы правы, это проще. Я обновил примеры, чтобы отразить это. - Dan Udey
«cat file | grep string» бесполезен, почему бы не «grep string file»? - c4f4t0r
У меня нет оправдания и соответственно обновил этот пример. - Dan Udey


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

14
2018-06-05 02:22



Интересно, но вы можете столкнуться с проблемами, если ваши журналы будут особенно большими, я бы подумал. Также насколько хорошо он справляется с пользовательскими форматами журналов? - Vagnerr
Я пытаюсь в данный момент, время загрузки настолько медленное (по крайней мере, в версии 0.9). Загрузка 200-мегабайтного журнала занимает более пяти минут. - Aseques
Я должен сказать, что после времени загрузки (это заняло около 15 минут) синтаксис этой программы велик, вы можете сортировать, подсчитывать и группировать. Действительно мило. - Aseques
Apache HTTPD имеет метод, с помощью которого вы можете эффективно отправлять журналы в базу данных. Да, записи могут занять много времени, но прокси-сервер с потоками может сделать только правильную вещь, зажатую посередине. Во всяком случае, это сделает журналы запросов в синтаксисе SQL намного быстрее. Не задействована также загрузка - сервер базы данных постоянно «ВКЛ». - nearora


Вот сценарий, чтобы найти верхние 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

Источник


6
2017-11-27 14:03





для подсчета 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

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


4
2018-05-22 02:19





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

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

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

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

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

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

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

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


Пример non-perl был бы быстрым хитом в минуту для кодов статуса не-200:

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

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


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

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

  • читает stdin, поэтому вы можете использовать части своих журналов, использовать хвост (особенно с хвостом -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', чтобы собрать все ваши журналы (если у вас есть несколько апачей за балансиром нагрузки) и
  • webalizer (или awstats или другой общий анализатор).

3
2018-06-05 21:34





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

Упрощенная нотация ввода: % s% s% s [% s] "% s"% s% s "% s" "% s"

Пример строки ввода: xx.xx.xx.xx - - [29 / Март / 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'

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


3
2018-03-29 15:40



Это сделало обработку с 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 '`` ` - Josh Rumbut


Я использую awk много, хвост или cat'ing файла. Каждую ночь я предоставляю себе веб-отчет для каждого сервера. В зависимости от вашего файла журнала и вашего 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
2017-11-19 03:19





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

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

2
2018-06-05 14:05





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

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

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

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

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

Получите общее количество секунд, связанных с журналом icecast:

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

1
2017-09-19 13:01



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


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

#! / Bin / Баш
# Этот сценарий должен возвращать набор строк между двумя значениями, основная цель - поиск файла журнала в 2 раза
#Script использование: 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 $") # показывает, если файл застрял или нет

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

0
2018-05-28 22:28