web-dev-qa-db-fra.com

Avez-vous des scripts awk et grep utiles pour analyser les journaux Apache?

Je peux utiliser des analyseurs de journaux, mais j'ai souvent besoin d'analyser les journaux Web récents pour voir ce qui se passe en ce moment.

Je fais parfois des choses comme pour comprendre les 10 meilleurs IPS qui demandent un certain fichier

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

Qu'avez-vous dans votre boîte à outils?

70
deadprogrammer

Vous pouvez faire à peu près n'importe quoi avec les fichiers journaux Apache avec awk seul. Les fichiers journaux Apache sont fondamentalement séparés par des espaces, et vous pouvez prétendre que les guillemets n'existent pas et accéder aux informations qui vous intéressent par numéro de colonne. La seule fois où cela tombe en panne, c'est si vous avez le format de journal combiné et que vous êtes intéressé par les agents utilisateurs, auquel cas vous devez utiliser des guillemets (") comme séparateur et exécuter une commande awk distincte. Ce qui suit vous montrera les adresses IP de chaque utilisateur qui demande la page d'index triée par le nombre de hits:

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

7 $ est l'URL demandée. Vous pouvez ajouter toutes les conditions que vous souhaitez au début. Remplacez le '$ 7 == "/" par les informations que vous souhaitez.

Si vous remplacez le $ 1 dans (ipcount [$ 1] ++), vous pouvez alors grouper les résultats selon d'autres critères. L'utilisation de 7 $ montrerait quelles pages ont été consultées et à quelle fréquence. Bien sûr, alors vous voudriez changer la condition au début. Ce qui suit montre quelles pages ont été consultées par un utilisateur à partir d'une adresse IP spécifique:

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

Vous pouvez également diriger la sortie via sort pour obtenir les résultats dans l'ordre, soit dans le cadre de la commande Shell, soit également dans le script awk lui-même:

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

Ce dernier serait utile si vous décidiez d'étendre le script awk pour imprimer d'autres informations. Tout dépend de ce que vous voulez savoir. Ceux-ci devraient servir de point de départ pour tout ce qui vous intéresse.

54
Mark

Une chose que je n'ai jamais vu faire, pour des raisons que je ne peux pas imaginer, est de changer le format du fichier journal Apache en une version plus facilement analysable avec les informations qui comptent vraiment pour vous.

Par exemple, nous n'utilisons jamais l'authentification de base HTTP, nous n'avons donc pas besoin de consigner ces champs. Je suis intéressé par la durée de traitement de chaque demande, nous allons donc l'ajouter. Pour un projet, nous voulons également savoir (sur notre équilibreur de charge) si des serveurs servent des demandes plus lentement que d'autres, nous enregistrons donc le nom du serveur auquel nous renvoyons des procurations.

Voici un extrait de la configuration Apache d'un serveur:

# 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

Ce que vous ne pouvez pas vraiment en dire, c'est qu'entre chaque champ se trouve un caractère de tabulation littéral (\ t). Cela signifie que si je veux faire une analyse en Python, peut-être afficher des états non-200 par exemple, je peux le faire:

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

Ou si je voulais faire "qui est le hotlinking d'images?" ce serait

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

Pour les comptages IP dans un journal d'accès, l'exemple précédent:

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

devient quelque chose comme ça:

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

Plus facile à lire et à comprendre, et beaucoup moins coûteux en termes de calcul (pas d'expression régulière), ce qui, sur des journaux de 9 Go, fait une énorme différence en termes de temps. Lorsque cela devient VRAIMENT net, c'est si vous voulez faire la même chose pour les agents utilisateurs. Si vos journaux sont séparés par des espaces, vous devez effectuer une correspondance d'expression régulière ou une recherche de chaîne à la main. Avec ce format, c'est simple:

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

Exactement les mêmes que ci-dessus. En fait, tout résumé que vous voulez faire est essentiellement exactement le même.

Pourquoi diable devrais-je dépenser le CPU de mon système en awk et grep quand cut fera exactement ce que je veux des ordres de grandeur plus rapidement?

24
Dan Udey

Oubliez awk et grep. Découvrez asql . Pourquoi écrire des scripts illisibles lorsque vous pouvez utiliser la syntaxe sql pour interroger le fichier journal. Par exemple.

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;
16
Vihang D

Voici un script pour trouver les meilleures URL, les meilleurs référents et les meilleurs useragents parmi les dernières entrées du journal 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

Source

6
anoopjohn

pour le nombre d'IP dans un journal d'accès:

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

C'est un peu moche, mais ça marche. J'utilise également ce qui suit avec netstat (pour voir les connexions actives):

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

Ils font partie de mes "one liners" préférés :)

5
f4nt

Voici mon exemple "sed", il lit le format par défaut des journaux Apache et le convertit en quelque chose de plus pratique pour le traitement automatique. La ligne entière est définie comme une expression régulière, les variables sont enregistrées et écrites dans la sortie avec '#' comme séparateur.

La notation simplifiée de l'entrée est:% s% s% s [% s] "% s"% s% s "% s" "% s"

Exemple de ligne d'entrée: xx.xx.xx.xx - - [29/Mar/2011: 12: 33: 02 +0200] "GET /index.html HTTP/1.0" 200 9443 "-" "Mozilla/4.0"

Exemple de ligne de sortie: 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'

Ressentez la puissance des expressions régulières :-)

3
Kris

Construire une liste de questions courantes serait un excellent index pour ces réponses à cette question. Mes questions courantes sont:

  • pourquoi le taux de réussite a-t-il changé?
  • pourquoi le temps de réponse global augmente-t-il? ".

Je remarque de tels changements en surveillant les pages d'état du serveur (via mod_status) pour le taux de réussite et le temps de réponse approximatif pour les demandes actives et récemment terminées (sachant très bien que je manque une énorme pile de données, mais les échantillons sont assez bons).

J'utilise la directive LogFormat suivante (le% T est vraiment utile)

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

Je recherche la cause à effet et ce qui s'est passé en premier ... généralement sur des sous-ensembles spécifiques de modèles dans mes journaux, j'ai donc besoin de connaître les éléments suivants pour tout modèle/expression régulière donné:

  • nombre de visites par intervalle (minute ou heure) pour un modèle donné (adresse IP ou chaîne ou paramètres cgi, etc.)
  • histogrammes du temps de réponse approximatif (en utilisant le paramètre% T)

J'utilise généralement Perl, car finalement il devient suffisamment complexe pour en valoir la peine.


Un exemple non Perl serait un taux de réussite rapide par minute pour les codes d'état non 200:

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

Oui, je triche avec ce grep, en supposant qu'un devis-espace-200-espace correspond uniquement aux codes de statut http .... pourrait utiliser awk ou Perl pour isoler le champ, gardez à l'esprit qu'il peut être inexact.


Un exemple plus complexe en Perl pourrait être de visualiser un changement de taux de réussite pour un motif.

Il y a beaucoup à mâcher dans le script ci-dessous, surtout si vous n'êtes pas familier avec Perl.

  • lit stdin pour que vous puissiez utiliser des parties de vos journaux, utiliser tail (en particulier avec tail -f), avec ou sans greps et autres filtrages ...
  • triche l'extraction d'horodatage Epoch avec le piratage d'une expression régulière et l'utilisation de Date :: Manip
  • vous ne pouvez le modifier que légèrement pour extraire le temps de réponse ou d'autres données arbitraires

le code suit:

#!/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 vous souhaitez simplement traiter des mesures standard, passez à la caisse

  • 'mergelog' pour rassembler tous vos journaux (si vous avez plusieurs apaches derrière un équilibreur de charge) et
  • webalizer (ou awstats ou autre analyseur commun).
3
ericslaw

Qui relie vos images à chaud:

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

J'utilise beaucoup awk en suivant ou en catant le fichier. Chaque soir, je me remets un rapport web pour chaque serveur. En fonction de votre fichier journal et de votre LogFormat, vous devrez modifier certains des liners pour travailler pour vous ...

Voici un exemple simple:

Si je veux tailler les journaux sur mon serveur pour seulement les codes d'état 404/500, je ferais ceci:

# $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
Michael Steinfeld

Bien qu'il ne soit pas sed ou awk, il y a deux choses que j'ai trouvées utiles pour gérer les fichiers journaux Apache et icecast.

AWStats possède un script très utile appelé logresolvemerge.pl qui combinera plusieurs fichiers journaux compressés ou non compressés, supprimera les dupes et les triera par horodatage. Il peut également effectuer des recherches DNS et être configuré pour s'exécuter en multithread. Il est particulièrement utile lors de l'utilisation avec awstats car awstats ne peut pas ajouter de lignes de journal avec des horodatages plus anciens que la base de données actuelle, donc tout doit être ajouté dans l'ordre, mais c'est très facile car vous jetez tout tout à logresolvemerge.pl et tout ressort bien.

sed et awk sont assez mauvais pour gérer les dates car ils les traitent généralement comme des chaînes. awk a des fonctions d'heure et de date, mais elles ne sont pas à la hauteur. Par exemple, extraire une plage de lignes entre deux horodatages est difficile si ces horodatages exacts ne se produisent pas dans le fichier (même si des valeurs entre eux le font) - l'exemple de Chris a exactement ce problème. Pour y faire face, j'ai écrit n PHP qui rapporte les plages d'horodatage du fichier journal et peut également extraire un morceau par plage d'horodatage, en utilisant n'importe quel format de date ou d'heure que vous aimez (il n'a pas besoin de correspondre au format d'horodatage du fichier journal).

Pour garder cela sur le sujet, voici quelques astuces utiles: Obtenez le nombre total d'octets servis à partir du journal Apache ou icecast:

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

Obtenez le nombre total de secondes connectées à partir d'un journal icecast:

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

La chose que j'ai tendance à faire la plupart du temps est de lire des sections d'un journal en fonction du temps, j'ai donc écrit le script suivant en utilisant sed pour extraire la période qui m'intéresse, cela fonctionne sur chaque fichier journal que je suis venu et peut également gérer les journaux archivés.

 #!/bin/bash 
 # Ce script doit renvoyer un ensemble de lignes entre 2 valeurs, le but principal est de rechercher un fichier journal entre 2 fois 
 # Utilisation du script: logship.sh "start" "stop" file 
 
 # Si le fichier contient un "/" dans la plage de dates, les 2 lignes suivantes ajoutent le caractère d'échappement afin que la recherche puisse être effectuée pour ceux caractères 
 start = $ (echo "$ 1" | sed 's/\ // \\\ // g') 
 stop = $ (echo "$ 2" | sed 's/\// /\\\//g')[.____. diplomatique.____. diplomatique zippé=$(echo "$ 3" | grep -c "gz $") #figure si le fichier est compressé ou non 
 
 si ["$ zippé" == "1"]; puis #Si le fichier est compressé, passez-le par zcat avant sed 
 zcat $ 3 | sed -n "/$start/,/$stop/p";;.____.[else
 sed -n"/$ start /,/$ stop/p "$ 3; #si ce n'est pas zippé, lancez sed 
 fi 
1
Chris

Récupérer cet ancien thread, après avoir abandonné asql pour les gros fichiers journaux, a recherché une solution againg, également dans serverfault, j'ai trouvé à propos de wtop ici c'est un outil open source, capable de faire de la surveillance en direct ou traiter les journaux et obtenir des statistiques (top N), très flexible et puissant, l'endroit officiel est ici

0
aseques