¿Tiene algún script útil awk y grep para analizar registros de apache? [cerrado]


70

Puedo usar analizadores de registros, pero a menudo necesito analizar registros web recientes para ver qué está sucediendo en este momento.

A veces hago cosas como averiguar los 10 mejores ips que solicitan un determinado archivo

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

¿Qué tienes en tu caja de herramientas?


1
De hecho, tenía esta gran expresión regular que había escrito a mano para analizar todos mis registros personalizados de Apache en campos individuales para enviarlos a una base de datos. Me estoy pateando a mí mismo porque ya no lo tengo. Era un trazador de líneas; le devolví una variable para cada elemento de registro; luego, me estaba insertando en MySQL. Si lo encuentro, lo publicaré aquí.
Kyle Hodgson

Respuestas:


54

Puede hacer casi cualquier cosa con los archivos de registro de apache con awk solo. Los archivos de registro de Apache están básicamente separados por espacios en blanco, y puede pretender que las comillas no existen y acceder a cualquier información que le interese por número de columna. El único momento en que esto se descompone es si tiene el formato de registro combinado y está interesado en agentes de usuario, momento en el que debe usar comillas (") como separador y ejecutar un comando awk separado. A continuación se muestran las IP de cada usuario que solicita la página de índice ordenada por el número de visitas:

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

$ 7 es la url solicitada. Puede agregar las condiciones que desee al principio. Reemplace el '$ 7 == "/" con la información que desee.

Si reemplaza $ 1 en (ipcount [$ 1] ++), puede agrupar los resultados por otros criterios. El uso de $ 7 mostraría a qué páginas se accedió y con qué frecuencia. Por supuesto, entonces querrás cambiar la condición al principio. A continuación se mostrará a qué páginas accedió un usuario desde una IP específica:

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

También puede canalizar la salida a través de ordenar para obtener los resultados en orden, ya sea como parte del comando de shell, o también en el script awk mismo:

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

Esto último sería útil si decidiera expandir el script awk para imprimir otra información. Todo es cuestión de lo que quieras descubrir. Estos deberían servir como punto de partida para lo que sea que le interese.


Yah, siempre parece extraño ver tuberías locas de gatos / grep / awk largos. Una vez que estás en awk, eso suele ser suficiente. Las primeras tres cláusulas de la publicación original podrían escribirse trivialmente como "awk '/ request_to_file_foo / {print $ 1}' foo.log". awk puede tomar un archivo como entrada y puede usar expresiones regulares para saber qué líneas deben preocuparse.
Zac Thompson

Elegante y simple Bueno.
Olivier Dulac

Cuidado, los espacios parecen permitidos en el campo "authuser" (3er), que rompe todo, y personalmente creo que debería estar prohibido, permitirnos hacer esto ;-)
Mandark

23

Una cosa que nunca he visto hacer a nadie más, por razones que no puedo imaginar, es cambiar el formato del archivo de registro de Apache a una versión más fácil de analizar con la información que realmente le importa.

Por ejemplo, nunca usamos autenticación básica HTTP, por lo que no necesitamos registrar esos campos. Yo estoy interesado en cuánto tiempo toma cada solicitud para servir, por lo que añadir que en. Para un proyecto, también queremos saber (en nuestro equilibrador de carga) si los servidores están sirviendo peticiones más lento que otros, así que registrar el nombre del servidor al que regresamos.

Aquí hay un extracto de la configuración de apache de un servidor:

# 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

Lo que realmente no puede deducir de esto es que entre cada campo hay un carácter de tabulación literal (\ t). Esto significa que si quiero hacer un análisis en Python, tal vez mostrar estados que no sean 200, por ejemplo, puedo hacer esto:

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

O si quisiera hacer '¿quién está conectando imágenes?' podría ser

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

Para los recuentos de IP en un registro de acceso, el ejemplo anterior:

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

se convierte en algo como esto:

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

Más fácil de leer y comprender, y mucho menos costoso desde el punto de vista computacional (sin expresiones regulares), lo que, en registros de 9 GB, hace una gran diferencia en cuánto tiempo lleva. Cuando esto se pone REALMENTE ordenado es si quieres hacer lo mismo para los agentes de usuario. Si sus registros están delimitados por espacios, debe hacer algunas coincidencias de expresiones regulares o buscar cadenas a mano. Con este formato, es simple:

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

Exactamente igual que el anterior. De hecho, cualquier resumen que desee hacer es esencialmente el mismo.

¿Por qué demonios gastaría la CPU de mi sistema en awk y grep cuando el corte haga exactamente lo que quiero órdenes de magnitud más rápido?


2
Sus ejemplos para el nuevo formato todavía son demasiado complicados: los recuentos de IP se convierten en cut -f 3 log | uniq -c | sort -nagentes de usuario cut -f 8 log | uniq -c | sort -n.
Creshal

Tienes razón, eso es más simple. He actualizado los ejemplos para reflejar eso.
Dan Udey

"cat file | grep string" es inútil, ¿por qué no "grep string file"?
c4f4t0r

2
No tengo excusa, y he actualizado el ejemplo en consecuencia.
Dan Udey

15

Olvídate de awk y grep. Echa un vistazo a asql . ¿Por qué escribir scripts ilegibles cuando puede usar la sintaxis tipo sql para consultar el archivo de registro? P.ej.

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;

Interesante, pero podría tener problemas si sus registros son particularmente grandes, creo. Además, ¿qué tan bien hace frente a los formatos de registro personalizados?
Vagnerr 05 de

Lo estoy intentando en este momento, el tiempo de carga es muy lento (al menos en la versión 0.9). Cargar un registro de 200Mb lleva más de cinco minutos ..
aseques

Debo decir que después del tiempo de carga (tomó alrededor de 15 minutos) la sintaxis de este programa es excelente, puede ordenar, contar y agrupar por. Muy agradable.
aseques

Apache HTTPD tiene un método con el que puede enviar efectivamente los registros a una base de datos. Sí, las escrituras pueden tomar mucho tiempo, pero un proxy roscado puede hacer lo correcto en el medio. De todos modos, eso hará que la consulta de registros en una sintaxis tipo SQL sea mucho más rápida. No hay carga involucrada también: el servidor de base de datos está perpetuamente "ENCENDIDO".
casi

6

Aquí hay una secuencia de comandos para encontrar las URL principales, los principales referentes y los mejores agentes de uso de las entradas de registro 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

Fuente


4

para los recuentos de IP en un registro de acceso:

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

Es un poco feo, pero funciona. También uso lo siguiente con netstat (para ver las conexiones activas):

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

Son algunos de mis favoritos "one liners" :)


3

Crear una lista de preguntas comunes sería un gran índice para estas respuestas a esta pregunta. Mis preguntas comunes son:

  • ¿Por qué cambió el hitrate?
  • ¿Por qué aumenta el tiempo de respuesta general? '.

Noto tales cambios al monitorear las páginas de estado del servidor (a través de mod_status) para obtener una tasa de hit y un tiempo de respuesta aproximado para solicitudes activas y recientemente completadas (sabiendo muy bien que extraño una gran cantidad de datos, pero las muestras son lo suficientemente buenas).

Uso la siguiente directiva LogFormat (% T es realmente útil)

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

Estoy buscando causa-efecto y lo que sucedió primero ... generalmente sobre subconjuntos específicos de patrones en mis registros, por lo que necesito saber lo siguiente para cualquier patrón / expresión regular dada:

  • Recuentos por intervalo (minuto u hora) para un patrón dado (dirección IP o cadena cgi o parámetros, etc.)
  • histogramas de tiempo de respuesta aproximado (usando el parámetro% T)

Generalmente uso perl, porque eventualmente se vuelve lo suficientemente complejo como para que valga la pena.


Un ejemplo que no es Perl sería una tasa de éxito rápida por minuto para códigos de estado que no sean 200:

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

Sí, estoy haciendo trampa con ese grep, suponiendo que un espacio de cita-espacio-200 coincide solo con códigos de estado http ... podría usar awk o perl para aislar el campo, solo tenga en cuenta que podría ser inexacto.


Un ejemplo más complejo en perl podría ser visualizar un cambio en la tasa de aciertos para un patrón.

Hay mucho para masticar en el siguiente script, especialmente si no eres familiar con Perl.

  • lee stdin para que pueda usar partes de sus registros, use tail (especialmente con tail -f), con o sin greps y otros filtros ...
  • trucos de extracción de marca de tiempo de época con el truco de una expresión regular y el uso de Fecha :: Manip
  • puede modificarlo solo ligeramente para extraer el tiempo de respuesta u otros datos arbitrarios

código sigue:

#!/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
    );
}

Si solo desea procesar métricas estándar, finalice la compra

  • 'mergelog' para juntar todos sus registros (si tiene múltiples apaches detrás de un equilibrador de carga) y
  • webalizer (o awstats u otro analizador común).

3

Aquí mi ejemplo 'sed', lee el formato predeterminado de los registros de apache y lo convierte en algo más conveniente para el procesamiento automático. Toda la línea se define como expresión regular, las variables se guardan y se escriben en la salida con '#' como separador.

La notación simplificada de la entrada es:% s% s% s [% s] "% s"% s% s "% s" "% s"

Ejemplo de línea de entrada: xx.xx.xx.xx - - [29 / Mar / 2011: 12: 33: 02 +0200] "GET /index.html HTTP / 1.0" 200 9443 "-" "Mozilla / 4.0"

Ejemplo de línea de salida: 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'

Siente el poder de las expresiones regulares :-)


Esto hizo que el procesamiento con AWK sea muy sencillo. Estaba buscando una forma rápida de configurar un deliminador común y esto lo logró.
Citricguy

He sentido el poder de la expresión regular y solo quería transmitir mi propio ajuste, que corta el "HTML / 1.1" y separa el protocolo (de una manera que probablemente no cumple con los estándares) en su propio campo. Disfruta: `` `cat access.log | sed 's /^(.*) (. *) (. *) [(. *)] \ "([[: alpha:]] \ +) (. *) HTTP \ / 1 \ .1 \" ( . *) (. *) \ "(. *) \" \ "(. *) \" $ / \ 1 # \ 2 # \ 3 # \ 4 # \ 5 # \ 6 # \ 7 # \ 8 # \ 9 # \ 10 / g '' ``
Josh Rumbut

2

Utilizo mucho awk siguiendo o capturando el archivo. Cada noche me entrego un informe web para cada servidor. Dependiendo de su archivo de registro y su LogFormat, necesitará editar algunos de los revestimientos para que funcionen para usted.

Aquí hay un ejemplo simple:

Si quiero seguir los registros en mi servidor por solo códigos de estado 404/500, haría esto:

# $6 is the status code in my log file

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

<recorte>

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

Quién está conectando en caliente tus imágenes:

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

1

Lo que suelo hacer la mayor parte del tiempo es leer secciones de un registro en función del tiempo, así que escribí el siguiente script usando sed para extraer el período que me interesa, funciona en cada archivo de registro que he encontrado a través y puede manejar los registros archivados también.

#! / bin / bash
# Este script debe devolver un conjunto de líneas entre 2 valores, el propósito principal es buscar un archivo de registro entre 2 veces
Uso de #Script: archivo "iniciar" "detener" logship.sh

# Si el archivo contiene cualquier "/" en el rango de fechas, las siguientes 2 líneas agregan el carácter de escape para que la búsqueda se pueda realizar para esos caracteres
inicio = $ (echo "$ 1" | sed 's / \ // \\\ // g')
stop = $ (echo "$ 2" | sed 's / \ // \\\ // g')

comprimido = $ (echo "$ 3" | grep -c "gz $") # figura si el archivo está comprimido o no

if ["$ zipped" == "1"]; luego # Si el archivo está comprimido, páselo a través de zcat antes de sed
        zcat $ 3 | sed -n "/ $ start /, / $ stop / p";
más
        sed -n "/ $ start /, / $ stop / p" $ 3; # si no está comprimido simplemente ejecuta sed
fi

1

Si bien no es sed o awk, hay dos cosas que he encontrado útiles para manejar archivos de registro de apache y icecast.

AWStats tiene una secuencia de comandos muy útil llamada logresolvemerge.pl que combinará múltiples archivos de registro comprimidos o sin comprimir, duplicados y los clasificará por marca de tiempo. También puede realizar búsquedas de DNS y configurarse para ejecutar multiproceso. Es particularmente útil cuando se usa con awstats porque awstats no puede agregar líneas de registro con marcas de tiempo más antiguas que la base de datos actual, por lo que todo debe agregarse en orden, pero eso es muy fácil ya que simplemente arroja todo en logresolvemerge.pl y todo sale bien.

sed y awk son bastante malos en el manejo de fechas porque generalmente los tratan como cadenas. awk tiene algunas funciones de hora y fecha, pero no son suficientes. Por ejemplo, extraer un rango de líneas entre dos marcas de tiempo es difícil si esas marcas de tiempo exactas no aparecen en el archivo (incluso si los valores entre ellas sí lo hacen): el ejemplo de Chris tiene exactamente este problema. Para lidiar con eso, escribí un script PHP que informa los rangos de marca de tiempo del archivo de registro y también puedo extraer un fragmento por rango de marca de tiempo, usando cualquier formato de fecha u hora que desee (no necesita coincidir con el formato de marca de tiempo del archivo de registro).

Para mantener este tema, aquí hay un par de funciones útiles: Obtenga el número total de bytes servidos desde el registro de apache o icecast:

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

Obtenga el número total de segundos conectados desde un registro de Icecast:

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

+1 para el registro simple de apache de suma de bytes con awk
rymo

0

Al recuperar este viejo hilo, después de renunciar a asql para archivos de registro grandes, busqué una solución, también en serverfault, descubrí que wtop aquí es una herramienta de código abierto, que es capaz de hacer monitoreo en vivo o procesar registros y obtener estadísticas (arriba N), muy flexible y potente, el lugar oficial está aquí

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.