74 votes

Avez-vous des scripts awk et grep scripts pour analyser les journaux d'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 déterminer le top 10 des adresses IP 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 ?

62voto

Vicent Marti Points 2942

Vous pouvez faire à peu près tout ce que vous voulez avec les fichiers journaux d'Apache en utilisant uniquement awk. Les fichiers journaux Apache sont fondamentalement séparés par des espaces, et vous pouvez faire comme si les guillemets n'existaient pas, et accéder à toutes les informations qui vous intéressent par numéro de colonne. Le seul cas où cela ne fonctionne pas est si vous avez le format de journal combiné et que vous êtes intéressé par les agents utilisateurs, auquel cas vous devez utiliser les guillemets (") comme séparateur et exécuter une commande awk séparée. Ce qui suit vous montrera les adresses IP de chaque utilisateur qui demande la page d'index, triées par le nombre d'occurrences :

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 voulez au début. Remplacez '$7 == "/" par l'information que vous voulez.

Si vous remplacez le $1 par (ipcount[$1]++), vous pouvez alors regrouper les résultats selon d'autres critères. L'utilisation de $7 permettrait de voir quelles pages ont été consultées et à quelle fréquence. Bien sûr, il faut alors modifier la condition au début. L'exemple suivant 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 faire passer la sortie par sort pour obtenir les résultats dans l'ordre, soit dans le cadre de la commande Shell, soit dans le Shell awk lui-même :

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

Cette dernière serait utile si vous décidiez d'étendre l'awk script pour imprimer d'autres informations. Tout dépend de ce que vous voulez découvrir. Celles-ci devraient servir de point de départ pour tout ce qui vous intéresse.

26voto

Dan Udey Points 1460

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

Par exemple, nous n'utilisons jamais l'authentification de base HTTP, nous n'avons donc pas besoin d'enregistrer ces champs. I Je suis intéressés par le temps que prend chaque requête pour être servie, nous allons donc l'ajouter. Pour un projet, nous voulons également savoir (sur notre équilibreur de charge) si certains serveurs servent les demandes plus lentement que d'autres, nous enregistrons donc le nom du serveur auquel nous renvoyons par proxy.

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 voir, 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, par exemple montrer les statuts non-200, 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 fait du hotlinking ?", ce serait

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

Pour le décompte des IP dans un journal des 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 de regex), ce qui, sur des journaux de 9 Go, fait une énorme différence en termes de temps. Si vous voulez faire la même chose pour les agents utilisateurs, cela devient VRAIMENT intéressant. Si vos journaux sont délimités par des espaces, vous devez effectuer une correspondance par 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 la même chose que ci-dessus. En fait, tout résumé que vous voulez faire est essentiellement identique.

Pourquoi diable dépenserais-je l'unité centrale de mon système pour awk et grep alors que cut fera exactement ce que je veux plusieurs fois plus vite ?

18voto

Jason Navarrete Points 3873

Oubliez awk et grep. Découvrez asql . Pourquoi écrire des scripts illisibles quand on peut utiliser une syntaxe de type sql pour interroger le fichier journal. Ex.

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;

8voto

anoopjohn Points 201

Voici un script pour trouver les meilleures urls, les meilleurs référents et les meilleurs useragents à partir des N entrées de journal récentes.

#!/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 :

5voto

Stephen L Points 293

Pour les comptes 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

Ce sont quelques-uns de mes "one liners" préférés :)

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X