Skip to content

Commit af075d4

Browse files
Release 0.2.0: Fonctionnalités avancées
- Ajout du filtrage d'analyse (adresse IP et code de statut http). - Ajout de la possibilité de générer des graphiques camemberts.
1 parent 2789670 commit af075d4

19 files changed

+819
-157
lines changed

.github/workflows/documentation.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,7 @@ jobs:
3030
- name: Installation des dépendances
3131
run: |
3232
python -m pip install --upgrade pip
33-
pip install sphinx
34-
pip install sphinx_rtd_theme --break-system-packages
35-
pip install colorama
33+
pip install -r requirements.txt
3634
3735
# Étape 4 : Générer la documentation
3836
- name: Construction de la documentation (avec Sphinx)

.github/workflows/qualite.yaml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on:
44
pull_request:
55
branches:
66
- main
7-
- develop
7+
- 'release/**'
88

99
jobs:
1010
lint:
@@ -25,8 +25,7 @@ jobs:
2525
- name: Installation des dépendances
2626
run: |
2727
python -m pip install --upgrade pip
28-
pip install pylint
29-
pip install colorama
28+
pip install -r requirements.txt
3029
3130
# Étape 4 : Lancement de l'analyse
3231
- name: Analyse avec Pylint (note >= 9.0 requise)

.github/workflows/tests.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on:
44
pull_request:
55
branches:
66
- main
7-
- develop
7+
- 'release/**'
88

99
# Permissions (lecture uniquement)
1010
permissions:
@@ -33,6 +33,8 @@ jobs:
3333
run: |
3434
python -m pip install --upgrade pip
3535
pip install colorama
36+
pip install altair
37+
pip install pandas
3638
pip install pytest
3739
pip install pytest-cov
3840
pip install pytest-mock

README.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
Bienvenue dans le monde de LogBuster, l'outil ultime pour analyser, décortiquer et sauver vos logs Apache des griffes du chaos. Vous avez des logs qui traînent, qui sont indéchiffrables ou tout simplement encombrants ? Pas de panique, LogBuster est là pour les attraper, les analyser et vous offrir des statistiques claires et précises, comme jamais auparavant !
1616

17+
Le document de réflexion est disponible [ici](https://drive.google.com/file/d/1j_F8MizttT8etW3joW23XR5ZI5k-uNqB/view?usp=sharing).
18+
1719
## 📋 Table des matières
1820

1921
- [👻 Fonctionnalités](#-fonctionnalités)
@@ -28,6 +30,8 @@ Bienvenue dans le monde de LogBuster, l'outil ultime pour analyser, décortiquer
2830

2931
- 📄 Parsing avancé de logs Apache.
3032
- 📉 Extraire des statistiques clés.
33+
- 🥧 Génération de graphiques camemberts.
34+
- 🧽 Filtrer les analyses.
3135
- 🗂️ Ranger les données par catégorie.
3236
- 🧹 Indiquer les erreurs de format avec précision.
3337
- 🚚 Exporter les données en JSON.
@@ -39,7 +43,7 @@ Bienvenue dans le monde de LogBuster, l'outil ultime pour analyser, décortiquer
3943
git clone https://github.com/AnthonyGuillauma/code_source
4044
cd code_source
4145
python -m venv .venv
42-
source .venv/bin/activate # Activation de l'environnement virtuel sous Bash
46+
source .venv/bin/activate
4347
pip install -r requirements.txt
4448
```
4549

@@ -48,17 +52,20 @@ pip install -r requirements.txt
4852
git clone https://github.com/AnthonyGuillauma/code_source
4953
cd code_source
5054
python -m venv .venv
51-
.venv\Scripts\activate # Activation de l'environnement virtuel sous Windows
55+
.venv\Scripts\activate
5256
pip install -r requirements.txt
5357
```
5458

5559
## 🛠️ Utilisation de base
5660

5761
```
58-
python app/main.py chemin_log [-s SORTIE]
62+
python app/main.py chemin_log [-s SORTIE] [-i IP] [-c CODE_STATUT_HTTP] [--camembert CAMEMBERT]
5963
```
6064
- `chemin_log` : Le chemin vers le fichier de log Apache à analyser.
6165
- `-s SORTIE` (optionnel) : Le chemin où sauvegarder les résultats de l'analyse. Si non spécifié, les résultats seront sauvegardés dans un fichier `analyse-log-apache.json`.
66+
- `-i IP` (optionnel) : Le filtre à appliquer sur les adresses IP des entrées du fichier de log. Uniquement les entrées avec cette adresse IP seront analysées.
67+
- `-c CODE_STATUT_HTTP` (optionnel) : Le filtre à appliquer sur les code de statut http des entrées du fichier de log. Uniquement les entrées avec ce code de statut http seront analysées.
68+
- `--camembert CAMEMBERT` (optionnel) : Active la génération de graphiques camemberts dans lors de l'analyse pour les statistiques compatibles (plus d'infos [ici](https://anthonyguillauma.github.io/code_source/#o-o-format-de-l-analyse)).
6269

6370
## ⚠️ Précautions
6471

app/analyse/analyseur_log_apache.py

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
Module pour l'analyse statistique d'un fichier log Apache.
33
"""
44

5+
from os.path import abspath
56
from collections import Counter
67
from parse.fichier_log_apache import FichierLogApache
8+
from analyse.filtre_log_apache import FiltreLogApache
79

810

911
class AnalyseurLogApache:
@@ -17,24 +19,29 @@ class AnalyseurLogApache:
1719
les statistiques des classements (tops).
1820
"""
1921

20-
def __init__(self, fichier_log_apache: FichierLogApache, nombre_par_top: int = 3):
22+
def __init__(self,
23+
fichier_log_apache: FichierLogApache,
24+
filtre: FiltreLogApache,
25+
nombre_par_top: int = 3):
2126
"""
2227
Initialise un nouveau analysateur de fichier log Apache.
2328
2429
Args:
2530
fichier_log_apache (FichierLogApache): Le fichier à analyser.
31+
filtre (FiltreLogApache): Le filtre à appliquer dans l'analyse. Si une entrée ne
32+
passe pas le filtre, elle ne sera pas pris en compte dans l'analyse.
2633
nombre_par_top (int): Le nombre maximal d'éléments à inclure dans
2734
les statistiques des classements (tops). Par défaut, sa valeur est égale à ``3``.
2835
2936
Raises:
30-
TypeError: Si l'argument ``fichier_log_apache`` n'est pas une instance
31-
de :class:`FichierLogApache`
32-
ou si l'argument ``nombre_par_top`` n'est pas un entier.
37+
TypeError: Les paramètres ne sont pas du type attendu.
3338
ValueError: Si l'argument ``nombre_par_top`` est inférieur à ``0``.
3439
"""
3540
# Vérification du type des paramètres
3641
if not isinstance(fichier_log_apache, FichierLogApache):
3742
raise TypeError("La représentation du fichier doit être de type FichierLogApache.")
43+
if not isinstance(filtre, FiltreLogApache):
44+
raise TypeError("Le filtre à appliquer aux entrées doit être de type FiltreLogApache.")
3845
if not isinstance(nombre_par_top, int) or isinstance(nombre_par_top, bool):
3946
raise TypeError("Le nombre par top doit être un entier.")
4047
# Vérification de la valeur du paramètre
@@ -43,8 +50,22 @@ def __init__(self, fichier_log_apache: FichierLogApache, nombre_par_top: int = 3
4350

4451
# Ajout des données
4552
self.fichier = fichier_log_apache
53+
self.filtre = filtre
4654
self.nombre_par_top = nombre_par_top
4755

56+
def _get_entrees_passent_filtre(self) -> list:
57+
"""
58+
Retourne les entrées qui passent le filtre.
59+
60+
Returns:
61+
list: La liste des entrées qui passent le filtre.
62+
"""
63+
entrees_valides = []
64+
for entree in self.fichier.entrees:
65+
if self.filtre.entree_passe_filtre(entree):
66+
entrees_valides.append(entree)
67+
return entrees_valides
68+
4869
def _get_repartition_elements(self,
4970
liste_elements: list,
5071
nom_elements: str,
@@ -93,22 +114,29 @@ def get_analyse_complete(self) -> dict:
93114
Retourne l'analyse complète du fichier de log Apache.
94115
95116
L'analyse suit la structure suivante :
96-
- chemin: chemin du fichier
117+
- chemin: chemin absolu du fichier
118+
- total_entrees: voir :meth:`get_total_entrees`
119+
- filtre: filtre appliqué à l'analyse
97120
- statistiques:
98-
- requetes:
99-
- top_urls: voir :meth:`get_top_urls`
100-
- repartition_code_statut_http:
101-
voir :meth:`get_total_par_code_statut_http`
121+
- total_entrees_filtre: voir :meth:`get_total_entrees_filtre`
122+
- requetes:
123+
- top_urls: voir :meth:`get_top_urls`
124+
- reponses:
125+
- repartition_code_statut_http: voir :meth:`get_total_par_code_statut_http`
102126
103127
Returns:
104128
dict: L'analyse sous forme d'un dictionnaire.
105129
"""
106130
return {
107-
"chemin": self.fichier.chemin,
131+
"chemin": abspath(self.fichier.chemin),
132+
"total_entrees": self.get_total_entrees(),
133+
"filtre": self.filtre.get_dict_filtre(),
108134
"statistiques": {
109-
"total_entrees": self.get_total_entrees(),
135+
"total_entrees_filtre": self.get_total_entrees_filtre(),
110136
"requetes": {
111137
"top_urls": self.get_top_urls(),
138+
},
139+
"reponses": {
112140
"repartition_code_statut_http": self.get_total_par_code_statut_http()
113141
}
114142
}
@@ -123,9 +151,19 @@ def get_total_entrees(self) -> int:
123151
"""
124152
return len(self.fichier.entrees)
125153

154+
def get_total_entrees_filtre(self) -> int:
155+
"""
156+
Retourne le nombre d'entrées qui ont passées le filtre dans le fichier.
157+
158+
Returns:
159+
int: Le nombre total d'entrées.
160+
"""
161+
return len(self._get_entrees_passent_filtre())
162+
126163
def get_top_urls(self) -> list:
127164
"""
128165
Retourne le top :attr:`nombre_par_top` des urls les plus demandées.
166+
Les entrées prisent en compte sont uniquement celles qui ont passées le filtre.
129167
130168
Returns:
131169
list: Une liste de dictionnaires où chaque clé contient :
@@ -136,14 +174,15 @@ def get_top_urls(self) -> list:
136174
La liste est triée dans l'ordre décroissant du nombre total d'apparitions.
137175
"""
138176
return self._get_repartition_elements(
139-
[entree.requete.url for entree in self.fichier.entrees],
177+
[entree.requete.url for entree in self._get_entrees_passent_filtre()],
140178
"url",
141179
True
142180
)
143181

144182
def get_total_par_code_statut_http(self) -> list:
145183
"""
146-
Retourne la répartition des réponses par code de statut htpp retourné.
184+
Retourne la répartition des réponses par code de statut http retourné.
185+
Les entrées prisent en compte sont uniquement celles qui ont passées le filtre.
147186
148187
Returns:
149188
list: Une liste de dictionnaires où chaque clé contient :
@@ -154,6 +193,21 @@ def get_total_par_code_statut_http(self) -> list:
154193
La liste est triée dans l'ordre décroissant du nombre total d'apparitions.
155194
"""
156195
return self._get_repartition_elements(
157-
[entree.reponse.code_statut_http for entree in self.fichier.entrees],
196+
[entree.reponse.code_statut_http for entree in self._get_entrees_passent_filtre()],
158197
"code"
159198
)
199+
200+
def get_total_par_code_statut_http_camembert(self) -> list:
201+
"""
202+
Retourne la répartition des réponses par code de statut http retourné sous
203+
un format utilisable par un camembert.
204+
Les entrées prisent en compte sont uniquement celles qui ont passées le filtre.
205+
206+
Returns:
207+
list: Une liste de liste de deux éléments où l'index 0 est le code et l'index 1
208+
son total d'apparition.
209+
"""
210+
return [
211+
[stat["code"], stat["total"]]
212+
for stat in self.get_total_par_code_statut_http()
213+
]

app/analyse/filtre_log_apache.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
"""
2+
Module pour les filtres lors d'une analyse d'un fichier log Apache.
3+
"""
4+
5+
from typing import Optional
6+
from parse.entree_log_apache import EntreeLogApache
7+
8+
9+
class FiltreLogApache:
10+
"""
11+
Représente le filtre à appliquer lors d'une analyse d'un fichier de log Apache.
12+
13+
Attributes:
14+
adresse_ip (Optional[str]): L'adresse IP que doit avoir une entrée pour
15+
pouvoir passer le filtre. Si sa valeur est ``None``, ce filtre ne sera
16+
pas appliqué.
17+
code_statut_http (Optional[int]): Le code de statut http que doit avoir une entrée
18+
pour pouvoir passer le filtre. Si sa valeur est ``None``, ce filtre ne sera
19+
pas appliqué.
20+
"""
21+
22+
def __init__(self, filtre_adresse_ip: Optional[str], filtre_code_statut_http: Optional[int]):
23+
"""
24+
Initalise le filtre à appliquer lors d'une analyse.
25+
26+
Args:
27+
filtre_adresse_ip (Optional[str]): L'adresse IP que doit avoir une entrée pour
28+
pouvoir passer le filtre. Si sa valeur est ``None``, cette vérification ne sera
29+
pas appliqué.
30+
filtre_code_statut_http (Optional[int]): Le code de statut http que doit
31+
avoir une entrée pour pouvoir passer le filtre. Si sa valeur est ``None``,
32+
cette vérification ne sera pas appliqué.
33+
34+
Raises:
35+
TypeError: Les paramètres ne sont pas du type attendu.
36+
"""
37+
# Vérification des paramètres
38+
if filtre_adresse_ip is not None and not isinstance(filtre_adresse_ip, str):
39+
raise TypeError("L'adresse IP dans un filtre doit être une chaîne de caractère.")
40+
if (filtre_code_statut_http is not None
41+
and not isinstance(filtre_code_statut_http, int)
42+
or isinstance(filtre_code_statut_http, bool)):
43+
raise TypeError("Un code de statut http dans un filtre doit être un entier.")
44+
45+
# Ajout des filtres
46+
self.adresse_ip = filtre_adresse_ip
47+
self.code_statut_http = filtre_code_statut_http
48+
49+
def entree_passe_filtre(self, entree: EntreeLogApache) -> bool:
50+
"""
51+
Indique si l'entrée passée en paramètre passe le filtre.
52+
53+
Args:
54+
entree (EntreeLogApache): L'entrée à vérifier.
55+
56+
Returns:
57+
bool: True si l'entrée passe le filtre, False sinon.
58+
59+
Raises:
60+
TypeError: L'``entrée`` n'est pas de type :class:`EntreeLogApache`
61+
"""
62+
# Vérification du paramètre
63+
if not isinstance(entree, EntreeLogApache):
64+
raise TypeError("L'entrée à vérifier pour le filtre doit être de type EntreeLogApache")
65+
66+
# Vérification que l'entrée passe le filtre
67+
# Application du filtre sur l'adresse IP si activé
68+
if self.adresse_ip is not None:
69+
if self.adresse_ip != entree.client.adresse_ip:
70+
return False
71+
# Application du filtre sur le code de statut http si activé
72+
if self.code_statut_http is not None:
73+
if self.code_statut_http != entree.reponse.code_statut_http:
74+
return False
75+
76+
return True
77+
78+
def get_dict_filtre(self) -> dict:
79+
"""
80+
Retourne le filtre sous forme d'un dictionnaire.
81+
Les clés représentent le champs d'une entrée et leur valeur la valeur
82+
que doit avoir ce champs. Si la valeur d'un filtre est ``None``, cela signifie que
83+
cette vérification n'est pas activé.
84+
85+
Returns:
86+
dict: Les filtres sous forme d'un dictionnaire.
87+
"""
88+
return {
89+
"adresse_ip": self.adresse_ip,
90+
"code_statut_http": self.code_statut_http
91+
}

0 commit comments

Comments
 (0)