Commande GREP ou autres pour parser des logs d'Apache.

Résolu/Fermé
pcsystemd Messages postés 691 Date d'inscription dimanche 27 novembre 2005 Statut Membre Dernière intervention 15 janvier 2024 - Modifié le 17 sept. 2021 à 16:49
mamiemando Messages postés 33120 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 21 mai 2024 - 23 sept. 2021 à 12:04
Bonjour,

J'ai fait un GREP sur le log "access.log" d'Apache de cette manière pour avoir les HITs (pages servies) en excluant les BOTs

cat access.log | egrep -v "Googlebot|bingbot|Applebot"  |wc -l


Je souhaiterais faire la même recherche mais cette fois sur une plage horaires.

Par exemple pour trouver le nombre d'entrées entre 17/Sep/2021:08:13:48 et 17/Sep/2021:16:11:01.

Contenu du fichier access.log
[17/Sep/2021:08:13:48 +0200] "GET /auto
--
--
[17/Sep/2021:16:11:00 +0200] "GET /tro


Une idée de la commande à saisir ou à utiliser?

Merci


Configuration: Linux / Brave


A voir également:

6 réponses

acefalo Messages postés 68 Date d'inscription vendredi 5 décembre 2014 Statut Membre Dernière intervention 27 février 2024 15
19 sept. 2021 à 23:23
Bonjour,

Si ton access.log est classé par heure d'accès, awk devrait faire l'affaire.

$4 = 4e colonne, séparateur par défaut = 'espace', && = opérateur AND
awk '$4 >= "[17/Sep/2021:08:13:48" && $4 <= "[17/Sep/2021:16:11:00"' access.log | wc -l


Exemple d'une entrée de log fonctionnant avec ce script :
127.0.0.1 - - [17/Sep/2021:08:13:48 -0300] "GET / HTTP/1.0" 200 2216


Tes entrées de log ont-elles cette forme? Si cela ne fonctionne pas, tu peux fournir une ligne complète de log anonymisée.

Si ton log à cette forme (exemple, débute par [17/Sep):
[17/Sep/2021:08:13:48 +0200] "GET /auto

Tu peux utiliser awk de cette manière :
awk '$1 >= "[17/Sep/2021:08:13:48" && $1 <= "[17/Sep/2021:16:11:00"' access.log | wc -l


Sources :
Log Samples from Apache
https://www.ossec.net/docs/log_samples/apache/apache.html

grep an accesslog(apache) file within a specific time period
https://gist.github.com/sheeplogh/3421464
1
mamiemando Messages postés 33120 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 21 mai 2024 7 753
20 sept. 2021 à 09:58
Bonjour

L'idée d'utiliser
awk
est bonne, mais la solution proposée ne marchera pas en l'état.

En effet,
awk
considérera les dates comme des chaînes qu'il comparera selon l'ordre lexicographique (donc, caractère par caractère, conformément au code ASCII de chaque caractère). Si cela se passerait bien pour les valeurs numériques, ça ne sera malheureusement pas le cas avec les mois (typiquement Jan arrive avant Dec et Dec arrive Sep).

Il faut donc pour chaque ligne convertir le mois sous forme numérique avant de réaliser la comparaison, ce qui peut se faire un
awk
ou avec un langage comme
python
.

Bonne chance
0
acefalo Messages postés 68 Date d'inscription vendredi 5 décembre 2014 Statut Membre Dernière intervention 27 février 2024 15 > mamiemando Messages postés 33120 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 21 mai 2024
20 sept. 2021 à 17:25
Bonjour,

J'y avait pensé que cela ne fonctionnerait pas avec les chevauchements de mois, j'aurais dû le spécifier. Dans ma tête je faisais une distinction entre plage horaire (qui se situait dans la même journée ou à tout le moins dans un intervalle assez rapproché dans le temps) et période (peu importe la journée ou le mois). Entre le 31 août et le 1er septembre, c'est assez rapproché dans le temps et mon script ne fonctionnera pas dans ce cas là et avec tous les chevauchements de mois.

Pour les cas simples où nous voulons seulement les accès dans la même journée (je pense que cela fonctionne aussi pour le mois au complet), penses-tu que mon script à d'autres problèmes ? J'aime ta solution et celle de dubcek. Votre maîtrise de
python
et de
awk
vous permet plus d'expressivité.
0
mamiemando Messages postés 33120 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 21 mai 2024 7 753 > acefalo Messages postés 68 Date d'inscription vendredi 5 décembre 2014 Statut Membre Dernière intervention 27 février 2024
21 sept. 2021 à 15:36
Entre le 31 août et le 1er septembre, c'est assez rapproché dans le temps et mon script ne fonctionnera pas dans ce cas là et avec tous les chevauchements de mois.

En effet.

Pour les cas simples où nous voulons seulement les accès dans la même journée (je pense que cela fonctionne aussi pour le mois au complet),

Tout à fait, et c'est ce problème que dubcek et moi résolvons avec nos deux propositions.

Penses-tu que mon script à d'autres problèmes ?


Oui :
  • Ton inégalité nécessite que le log ne s'étale pas sur plusieurs années. En effet, ton critère de filtrage extrairait
    18/Sep/2021
    si on fourni les bornes
    17/Sep/2020
    et
    19/Sep/2020
    . Pourtant, mes deux bornes sont bien dans le même mois. Dans le cas général, ton script marche que si les deux bornes sont dans le même jour.
  • Note que si l'année apparaissait avant le mois, ton script marcherait mieux : le seul problème resterait celui de la gestion des mois, ce qui imposerait que les deux bornes soient dans le même mois. Ce serait donc toujours imparfait, mais les contraintes sur les deux bornes seraient moindre.


La solution de dubcek et la mienne ne font pas d'hypothèse sur le formatage des dates ou la durée du log. La solution de dubcek a l'avantage d'être concise et de ne nécessiter qu'
awk
.

La mienne à l'inconvénient de nécessiter d'avoir Python3 sur la machine. Elle apporte en contrepartie quelques avantages,
  • Isoler le champ associé à la date : contrairement à ta solution et celle de dubcek, celle que je propose permet de s'affranchir de l'index auquel se situe la date ($1 dans l'exemple de départ, $4 sur mon serveur apache). Dans mon script c'est réalisé, grâce à l'expression régulière
    RE_APACHE
    (qui dépend du log).
  • Simplicité / expressivité : Le type
    datetime
    permet de manipuler facilement des dates et d'exprimer n'importe quelle critère de filtrage, en particulier comparer des dates ou d'utiliser des opérateurs ensemblistes, supportés nativement.
  • Modularité / réutilisabilité : Seules deux informations sont importantes pour répondre au problème posé : (i) comment isoler le timestamp et (ii) quel est son format de ce timestamp. Le module
    datetime
    permet de facilement s'adapter si le format de la date évolue (voir
    parse_date
    ). Cela évite d'avoir à écrire une expression rationnelle et de spécifier comment extraire chaque attribut de la date. Ceci dit, c'est parce qu'ici on a "de la chance", la date est délimitée par des crochets, ce qui n'est pas toujours vrai dans un fichier de log. Quoi qu'il en soit, si tu peux paramétrer le code selon le paramètre d'extraction et le paramètre de formatage (respectivement
    RE_APACHE
    et
    parse_date
    dans mon code, le code devient indépendant des conventions choisies dans log. Cela permettrait ainsi de continuer à utiliser le même code pour d'autres formats de logs.


Bonne chance
0
mamiemando Messages postés 33120 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 21 mai 2024 7 753
Modifié le 20 sept. 2021 à 10:47
Bonjour,

Voici comment tu pourrais faire en
python3
:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import datetime, re, sys

RE_APACHE = re.compile(".*\[(.*)\].*")

if len(sys.argv) != 4:
    print(f"usage: {sys.argv[0]} input_log date_min date_max", sys.stderr)
    sys.exit(1)

def parse_date(date):
    return datetime.datetime.strptime(date, "%d/%b/%Y:%H:%M:%S %z")

filename = sys.argv[1]
date_min = parse_date(sys.argv[2])
date_max = parse_date(sys.argv[3])
assert date_min <= date_max
    
with open(filename) as f:
    for line in f:
        m = RE_APACHE.match(line)
        if not m:
            continue
        date = parse_date(m.group(1))
        if date_min <= date <= date_max:
            print(line.strip())


Sauve ça dans un fichier (disons
toto.py
)

En gros, on cherche les lignes qui contiennent un timestamp (donc, qui ont un truc entre crochets) avec l'expression régulière
RE_APACHE
. Note que l'expression régulière permet de s'affranchir de la position du timestamp dans la ligne, on fait uniquement l'hypothèse qu'il est entre crochet.

Pour chaque ligne du log, si la date extraite est comprise entre les deux autres dates passées en paramètres du programme, on écrit la ligne (sinon on l'ignore).

Pour lancer le programme, lance dans ton terminal :

python3 toto.py apache.log "20/Sep/2021:07:57:00 +0000" "20/Sep/2021:07:58:00 +0000"


Bonne chance
1
dubcek Messages postés 18724 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 15 mai 2024 5 615
20 sept. 2021 à 11:17
hello
on convertit les dates en secondes
on convertit la chaine pour être compatible avec date -d ...
JJ/Mmm/AAAA:HH:MM:SS devient JJ-Mmm-AAAA HH:MM:SS

d1="17/Sep/2021:08:13:48"; t1=${d1////-}; t1=${t1/:/ }; t1=$(date -d "$t1" +%s)
d2="17/Sep/2021:16:11:01"; t2=${d2////-}; t2=${t2/:/ }; t2=$(date -d "$t2" +%s)
awk -F "[][]|[ ]" -v t1=$t1 -v t2=$t2 '{x=$2; gsub("/", "-", x); sub(":", " ", x); "date -d \"" x "\" +%s" | getline t}; t >= t1 && t <=t2 {print $0}' access.log
1
dubcek Messages postés 18724 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 15 mai 2024 5 615
Modifié le 22 sept. 2021 à 15:57
vérifier qu'il ne manque pas de "
awk -F "[][]|[ ]" -v t1=$t1 -v t2=$t2 '{x=$2; gsub("/", "-", x); sub(":", " ", x); "date -d \"" x "\" +%s" | getline t}; t >= t1 && t <=t2  {print $0}'  access.log


ou essayer avec gawk
1

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
pcsystemd Messages postés 691 Date d'inscription dimanche 27 novembre 2005 Statut Membre Dernière intervention 15 janvier 2024 22
22 sept. 2021 à 14:45
Merci a tous pour votre temps et vos solutions.

@mamiemando
Je n'ai pas la version 3 de Python du coup je vais voir si cela fonctionne avec ma version 2.7.9!

@dubcek
J'ai l'erreur suivante :

d1="21/Sep/2021:13:13:48"; t1=${d1////-}; t1=${t1/:/ }; t1=$(date -d "$t1" +%s)
d2="21/Sep/2021:14:01:01"; t2=${d2////-}; t2=${t2/:/ }; t2=$(date -d "$t2" +%s)
awk -F "[][]|[ ]" -v t1=$t1 -v t2=$t2 '{x=$2; gsub("/", "-", x); sub(":", " ", x); "date -d \"" x "\" +%s" | getline t}; t >= t1 && t <=t2 {print $0}' access.log
/bin/sh: 1: Syntax error: Unterminated quoted string


0
mamiemando Messages postés 33120 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 21 mai 2024 7 753
23 sept. 2021 à 12:04
En python 2 il faudrait faire quelques adaptations et python2 c'est un peu vieille école, donc je ne saurais que trop te conseiller d'installer un interpréteur python3 (e.g.
sudo apt install python3
).

Pour migrer le script en python2 il faudrait faire quelques adaptations mineures :
  • print(msg)
    devient
    print msg
    ou il faut importer la fonction
    print
    depuis
    from __future__
    ;
  • même remarque pour
    with
  • le test de la forme
    L <= x <= U
    doit être réécrit en une clause d'opérations binaires (ici
    L <= x
    and
    x <= U
    )


Bonne chance
0
pcsystemd Messages postés 691 Date d'inscription dimanche 27 novembre 2005 Statut Membre Dernière intervention 15 janvier 2024 22
22 sept. 2021 à 17:40
Ok merci. Je vais tester.
0