Skip to content

Commit 8aa62d8

Browse files
Feat: Ajout de l'analyse statistique de fichier de log Apache (#25)
- Ajout de la classe AnalyseurLogApache qui retourne des statistiques sur un fichier de log Apache - Modification de main.py pour récupérer l'analyse complète du fichier
1 parent 614520a commit 8aa62d8

2 files changed

Lines changed: 141 additions & 1 deletion

File tree

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
"""
2+
Module pour l'analyse statistique d'un fichier log Apache.
3+
"""
4+
5+
from collections import Counter
6+
from parse.fichier_log_apache import FichierLogApache
7+
8+
9+
class AnalyseurLogApache:
10+
"""
11+
Représente un analysateur pour faire une analyse statistique d'un fichier
12+
log Apache et créer des statistiques à partir de ce dernier.
13+
Attributes:
14+
fichier (FichierLogApache): Le fichier de log Apache à analyser.
15+
nombre_par_top (int): Le nombre maximal d'éléments à inclure dans
16+
les statistiques des classements (tops).
17+
"""
18+
19+
def __init__(self, fichier_log_apache: FichierLogApache, nombre_par_top: int = 3):
20+
"""
21+
Initialise un nouveau analysateur de fichier log Apache.
22+
Args:
23+
fichier_log_apache (FichierLogApache): Le fichier à analyser.
24+
nombre_par_top (int): Le nombre maximal d'éléments à inclure dans
25+
les statistiques des classements (tops). Par défaut, sa valeur est égale à ``3``.
26+
Raises:
27+
TypeError: Si l'argument ``fichier_log_apache`` n'est pas une instance de :class:`FichierLogApache`
28+
ou si l'argument ``nombre_par_top`` n'est pas un entier.
29+
ValueError: Si l'argument ``nombre_par_top`` est inférieur à ``0``.
30+
"""
31+
if not isinstance(fichier_log_apache, FichierLogApache):
32+
raise TypeError("La représentation du fichier doit être de type FichierLogApache.")
33+
if not isinstance(nombre_par_top, int) or isinstance(nombre_par_top, bool):
34+
raise TypeError("Le nombre par top doit être un entier.")
35+
if nombre_par_top < 0:
36+
raise ValueError("Le nombre par top doit être supérieur ou égale à 0.")
37+
self.fichier = fichier_log_apache
38+
self.nombre_par_top = nombre_par_top
39+
40+
def _get_repartition_elements(self,
41+
liste_elements: list,
42+
nom_elements: str,
43+
mode_top_classement: bool = False) -> list:
44+
"""
45+
Retourne le top 'n' des éléments qui apparaissent le plus dans la liste.
46+
Args:
47+
liste_elements (list): La liste des éléments.
48+
nom_elements (str): Le nom des éléments.
49+
mode_top_classement (bool): Indique si la méthode doit retourner ou non le top
50+
'n' des éléments les plus présents, où 'n' est égale à l'attribut
51+
:attr:`nombre_par_top`. Par défaut, ce mode est désactivé (valeur à ``False``).
52+
Returns:
53+
list: Une liste de dictionnaires contenant, pour chaque élément :
54+
- Sa valeur.
55+
- Son nombre total d'apparitions.
56+
- Son taux d'apparition dans la liste.
57+
La liste est triée dans l'ordre décroissant selon le nombre d'apparitions.
58+
Si ``mode_top_classement`` est défini à ``True``, elle contient au maximum
59+
:attr:`nombre_par_top` éléments, mais peut en contenir moins s'il y a moins
60+
de :attr:`nombre_par_top` éléments distincts.
61+
"""
62+
if not isinstance(liste_elements, list):
63+
raise TypeError("La liste des éléments doit être une instance du type list.")
64+
if not isinstance(nom_elements, str):
65+
raise TypeError("Le nom des éléments doit être une chaîne de caractères")
66+
if not isinstance(mode_top_classement, bool):
67+
raise TypeError("L'indication de l'activation du mode 'top_classement' doi être un booléen.")
68+
69+
total_elements = len(liste_elements)
70+
compteur_elements = Counter(liste_elements)
71+
top_elements = compteur_elements.most_common(self.nombre_par_top if mode_top_classement else None)
72+
return [
73+
{nom_elements: element, "total": total, "taux": total / total_elements * 100}
74+
for element, total in top_elements
75+
]
76+
77+
def get_analyse_complete(self) -> dict:
78+
"""
79+
Retourne l'analyse complète du fichier de log Apache.
80+
Returns:
81+
dict: L'analyse sous forme d'un dictionnaire.
82+
Le dictionnaire suit la structure suivante:
83+
- chemin: chemin du fichier
84+
- statistiques:
85+
- requetes:
86+
- top_urls: voir :meth:`get_top_urls`
87+
- repartition_code_statut_http: voir :meth:`get_total_par_code_statut_http`
88+
"""
89+
return {
90+
"chemin": self.fichier.chemin,
91+
"statistiques": {
92+
"total_entrees": self.get_total_entrees(),
93+
"requetes": {
94+
"top_urls": self.get_top_urls(),
95+
"repartition_code_statut_http": self.get_total_par_code_statut_http()
96+
}
97+
}
98+
}
99+
100+
def get_total_entrees(self) -> int:
101+
"""
102+
Retourne le nombre total d'entrées dans le fichier.
103+
Returns:
104+
int: Le nombre total d'entrées.
105+
"""
106+
return len(self.fichier.entrees)
107+
108+
def get_top_urls(self) -> list:
109+
"""
110+
Retourne le top :attr:`nombre_par_top` des urls les plus demandées.
111+
Returns:
112+
list: Une liste de dictionnaires où chaque clé contient :
113+
- url: L'URL demandée.
114+
- total: Le nombre total de fois où cette URL a été demandée.
115+
- taux: Le pourcentage de demandes correspondant à cette URL.
116+
La liste est triée dans l'ordre décroissant du nombre total d'apparitions.
117+
"""
118+
return self._get_repartition_elements(
119+
[entree.requete.url for entree in self.fichier.entrees],
120+
"url",
121+
True
122+
)
123+
124+
def get_total_par_code_statut_http(self) -> list:
125+
"""
126+
Retourne la répartition des réponses par code de statut htpp retourné.
127+
Returns:
128+
list: Une liste de dictionnaires où chaque clé contient :
129+
- code: Le code de statut http.
130+
- total: Le nombre total de fois où ce code a été demandée.
131+
- taux: Le pourcentage de demandes correspondant à ce code.
132+
La liste est triée dans l'ordre décroissant du nombre total d'apparitions.
133+
"""
134+
return self._get_repartition_elements(
135+
[entree.reponse.code_statut_http for entree in self.fichier.entrees],
136+
"code"
137+
)

app/main.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import colorama
66
from cli.parseur_arguments_cli import ParseurArgumentsCLI, ArgumentCLIException
77
from parse.parseur_log_apache import ParseurLogApache, FormatLogApacheInvalideException
8+
from analyse.analyseur_log_apache import AnalyseurLogApache
89

910

1011
if __name__ == "__main__":
@@ -27,8 +28,10 @@
2728
arguments_cli = parseur_cli.parse_args()
2829
# Analyse syntaxique du fichier log
2930
parseur_log = ParseurLogApache(arguments_cli.chemin_log)
30-
parseur_log.parse_fichier()
31+
fichier_log = parseur_log.parse_fichier()
3132
# Analyse statistique du fichier log
33+
analyseur_log = AnalyseurLogApache(fichier_log)
34+
analyse = analyseur_log.get_analyse_complete()
3235
# Exportation de l'analyse
3336
except ArgumentCLIException as ex:
3437
print(f"Erreur dans les arguments fournis !\n {ex}")

0 commit comments

Comments
 (0)