📝docs(docs): add documentation for README.md

add documentation in README.md, usage.md, architecture.md, update python version based on the version used in development
This commit is contained in:
2026-09-02 11:52:27 +02:00
parent a43afb9df7
commit bfa5fa600b
6 changed files with 340 additions and 10 deletions
+83
View File
@@ -0,0 +1,83 @@
# logwatcher
Logwatcher est un outil sur-mesure d'analyse et de tri automatique des logs LAME MDC pour le support N2.
Les techniciens N2 reçoivent quotidiennement des compte-rendus de suivi
d'imports NOSYMAG contenant des centaines de lignes d'erreurs. La plupart
ne les concernent pas. Logwatcher analyse ces compte-rendus, identifie
les erreurs pertinentes pour le support N2, et génère trois rapports
(n2, autres, tout) prêts à être transmis.
---
## 📦 Installation
### Prérequis
- Python >= 3.14
- `uv` (recommandé) ou `pip`
### Procédure
#### uv
```bash
uv pip install -e ".[dev]"
```
#### pip
```bash
pip install -e ".[dev]"
```
---
## 🚀 Commandes
### Analyser un fichier de logs
```bash
logwatcher --input-files chemin/vers/CR_fichier.txt
```
### Analyser tous les logs d'un répertoire
```bash
logwatcher --input-dir chemin/vers/dossier
```
### Afficher la version
```bash
logwatcher --version
```
Le guide complet se trouve dans [usage](docs/usage.md).
---
## 🏗️ Architecture
Le détail de l'architecture se trouve dans [architecture](docs/architecture.md).
---
## 🧪 Tests
Les tests se trouvent dans `tests/`. Le projet est couvert à ~95% par les tests unitaires et d'intégration.
### Lancer tous les tests
```bash
pytest
```
### Lancer un test spécifique (ex: `test_parser.py`)
```bash
pytest tests/test_parser.py
```
### Générer un rapport de couverture
```bash
pytest --cov=logwatcher --cov-report=term-missing
```
---
## 📄 Licence
MIT — voir [LICENSE](LICENSE).
+105
View File
@@ -0,0 +1,105 @@
# Architecture
## Vue d'ensemble
Logwatcher est un outil en ligne de commande Python qui analyse des fichiers
de logs LAME MDC, identifie les erreurs pertinentes pour le support N2, et
génère des rapports texte.
Le traitement suit un pipeline linéaire :
```text
fichiers de logs
│
▼
parser.py lit et parse les fichiers en LogEntry
│
▼
classifier.py classe chaque entrée : N2 ou hors N2
│
▼
reporter.py génère les trois rapports (all, n2, other)
│
▼
rapports .log
```
## Structure du package
```text
src/logwatcher/
├── __init__.py # expose __version__
├── __main__.py # point d'entrée : python -m logwatcher
├── cli.py # interface en ligne de commande (Typer)
├── config.py # constantes : chemins, formats
├── logging_config.py # configuration du logging
├── models.py # LogEntry : structure de données
├── parser.py # lecture et parsing des fichiers
├── classifier.py # classification N2 / hors N2
└── reporter.py # génération des rapports texte
```
### Modules
#### cli.py — point d'entrée
* Définit `app = typer.Typer()` et la commande principale.
* Valide les arguments (`--input-files` / `--input-dir` mutuellement exclusifs).
* Orchestre le pipeline : parsing → classification → rapports.
* Gère les erreurs utilisateur (`BadParameter`, code de sortie 2) et
* les erreurs d'exécution (code de sortie 1).
* Expose `--version` (eager, affiche et quitte).
#### models.py — LogEntry
Dataclass immuable (par convention) représentant une ligne de log parsée.
Champs principaux :
* `server_ip`, `mdc_server_name` : origine du log
* `store_name` : magasin concerné
* `start_time`, error_time : horodatages
* `error_message` : message d'erreur complet
* `error_name` : nom du pattern N2 correspondant (rempli par le classifier)
* `raw_line` : ligne brute d'origine
* `ligne` : numéro de ligne dans le fichier source
LogEntry est créée uniquement par le parser. Le classifier l'enrichit
(remplit `error_name`). Le modèle lui-même ne contient pas de logique métier.
[`parser.py`]() — lecture et parsing
* `LOG_PATTERN` : expression régulière du format d'une ligne LAME MDC.
* `parse_log_file()` : lit un fichier, retourne list[LogEntry].
* Les lignes non conformes (système, en-têtes, corrompues) sont ignorées silencieusement.
#### classifier.py — classification
* N2_PATTERNS : dictionnaire des motifs d'erreurs pertinents pour le N2, fournis par les techniciens.
* classify_log_entries() : sépare les entrées en deux listes (relevant, irrelevant) et remplit entry.error_name pour les entrées pertinentes.
#### reporter.py — rapports
* Templates texte (`string.Template`) : `BASE_TEMPLATE`, `N2_SUPPORT_TEMPLATE`, `OTHER_TEMPLATE`, `ERROR_TEMPLATE`.
* `build_reports()` : construit les trois rapports (`dict n2, other, all`).
* `write_log_report()` : écrit les rapports sur disque en Windows-1252.
* `_get_period()` : calcule les bornes min/max des error_time.
#### logging_config.py — logging
* `setup_logging()` : configure le logger racine.
* Handler console (`INFO`, ou `DEBUG` si verbose) + handler fichier.
* Le fichier de log `logwatcher.log` est horodaté et écrit dans le dossier de sortie.
#### config.py — constantes
* Chemins (`OUTPUT_PATH`, `FIXTURE_PATH` ...).
* Formats de date (`DATETIME_FORMAT`).
## Dépendances
| Package | Rôle |
|---------|------|
| `typer` | Interface en ligne de commande |
| `pytest` | Tests (dev) |
| `pytest-cov` | Couverture (dev) |
| `ruff` | Linting/formatage (dev) |
| `mypy` | Typage statique (dev) |
+142
View File
@@ -0,0 +1,142 @@
# Guide d'utilisation
## Introduction
Logwatcher analyse les comptes-rendus de suivi d'imports NOSYMAG (fichiers
`CR_*.txt` et autres logs LAME MDC) pour identifier les erreurs pertinentes
pour le support N2. Il génère trois rapports : un pour les erreurs N2, un
pour les erreurs hors périmètre N2, et un rapport complet.
---
## Entrées
### Analyser un fichier de logs
```bash
logwatcher --input-files chemin/vers/CR_fichier.txt
```
### Analyser plusieurs fichiers de logs
L'option `--input-files` accepte plusieurs fichiers. Répéter l'option pour
chaque fichier :
```bash
logwatcher --input-files fichier1.txt --input-files fichier2.txt
```
### Analyser tous les logs d'un répertoire
```bash
logwatcher --input-dir chemin/vers/dossier
```
L'option `--input-dir` analyse tous les fichiers `.log`, `.txt` ou sans
extension contenus dans le répertoire. Les autres fichiers sont ignorés.
> ### Règle d'exclusivité
> `--input-files` et `--input-dir` sont mutuellement exclusifs. Fournir
les deux options lève une erreur et le traitement est interrompu. Ne fournir
aucune des deux lève également une erreur.
## Sorties
### Emplacement des rapports
Par défaut, les rapports sont écrits dans le répertoire `output/` du dossier
courant. Un autre emplacement peut être fourni avec `--output-dir` :
```bash
logwatcher --input-dir chemin/vers/dossier --output-dir chemin/vers/sortie
```
### Les rapports générés
Chaque analyse produit trois rapports différents : `all.log`, `n2.log` et `other.log`.
- `all.log` : Toutes les erreurs analysées, N2 et hors N2.
- `n2.log` : Uniquement les erreurs pertinentes pour le support N2.
- `other.log` : Uniquement les erreurs hors périmètre N2
#### Contenu d'un rapport
Chaque rapport contient un en-tête avec la période analysée, le nombre de
fichiers lus et le nombre total d'erreurs, suivi du détail des erreurs.
Un rapport se présente ainsi :
```text
RAPPORT D'ANALYSE DE LOGS
=========================
Période : 01/09/2026 11:07:53 -> 01/09/2026 15:04:18
Fichier(s) lu(s) : 1
Nombre total d'erreur(s) : 3
=================================
ERREURS SUPPORT N2
=================================
Nombre d'erreurs : 2
[1] BL_AUTO_BESTSELLER_REJECTED
Magasin: DUMAS-DELAGE
MDC: MDC_340
Serveur: 192.168.13.23
Heure: 28/07/2026 08:05:27
Raison: Erreur : 1099_2622609667_DESADV : / 00098/000 ... ne correspond
```
## Options
- `--output-dir` : Répertoire de sortie des rapports. Par défaut : `output/`.
```bash
logwatcher --input-dir logs/ --output-dir rapports/2026-09-01/
```
- `--version` : Affiche la version du package et quitte.
```bash
logwatcher --version
# logwatcher version: 0.0.1
```
## Cas particuliers
### Fichier vide
Un fichier vide est lu sans erreur. Les trois rapports sont générés avec `Nombre total d'erreur(s) : 0`.
### Répertoire vide
Un répertoire vide est traité sans erreur. Les trois rapports sont générés
avec `Fichier(s) lu(s) : 0 et Nombre total d'erreur(s) : 0`.
### Logs non reconnus
Les lignes qui ne correspondent pas au format LAME MDC (lignes système,
en-têtes de répertoire, lignes corrompues) sont ignorées silencieusement
et ne produisent pas d'entrée dans les rapports.
### Encodage des fichiers
Les fichiers d'entrée sont lus en Windows-1252 (encodage natif des logs
LAME MDC). Les rapports générés sont également écrits en Windows-1252.
## Exemple complet
Analyser tous les logs d'un répertoire et écrire les rapports dans un
dossier dédié :
```bash
logwatcher --input-dir logs/2026-09-01/ --output-dir rapports/2026-09-01/
```
Après exécution, les fichiers suivants sont créés dans `rapports/2026-09-01/` :
```text
all.log # toutes les erreurs
n2.log # erreurs pertinentes pour le N2
other.log # erreurs hors périmètre N2
```
Le rapport `n2.log` peut ensuite être transmis aux techniciens N2, et
`other.log` aux équipes concernées par les autres erreurs.
+1 -1
View File
@@ -3,7 +3,7 @@ name = "logwatcher"
version = "0.0.1"
description = "Trier, classifier et extraire automatiquement les entrées pertinentes d'un fichier de logs applicatif."
readme = "README.md"
requires-python = ">=3.10"
requires-python = ">=3.14"
dependencies = [
"typer>=0.27.2",
]
View File
+9 -9
View File
@@ -128,7 +128,7 @@ def test_render_target_report_relevant(
relevant = get_only_relevant_log_entries[0]
target_report = _render_target_report(relevant, N2_SUPPORT_TEMPLATE)
assert "ERREURS SUPPORT N2" in target_report
assert target_report.find("Nombre d'erreurs\t : " + str(len(relevant))) != -1
assert target_report.find("Nombre d'erreur(s)\t : " + str(len(relevant))) != -1
assert _render_entries(relevant) in target_report
@@ -139,7 +139,7 @@ def test_render_target_report_irrelevant(
irrelevant = get_only_irrelevant_log_entries[1]
target_report = _render_target_report(irrelevant, OTHER_TEMPLATE)
assert "AUTRES ERREURS" in target_report
assert target_report.find("Nombre d'erreurs\t : " + str(len(irrelevant))) != -1
assert target_report.find("Nombre d'erreur(s)\t : " + str(len(irrelevant))) != -1
assert _render_entries(irrelevant) in target_report
@@ -165,10 +165,10 @@ def test_build_reports(
assert "all" in reports and "n2" in reports and "other" in reports
assert reports["all"].find(f"Période\t : {start_date} -> {end_date}") != -1
assert reports["all"].find(f"Fichiers lu\t : {nb_files}") != -1
assert reports["all"].find(f"Fichier(s) lu(s)\t : {nb_files}") != -1
assert (
reports["all"].find(
f"Nombre total d'erreurs\t: {len(get_all_log_entries[0] + get_all_log_entries[1])}"
f"Nombre total d'erreur(s)\t: {len(get_all_log_entries[0] + get_all_log_entries[1])}"
)
!= -1
)
@@ -181,12 +181,12 @@ def test_build_reports(
and "AUTRES ERREURS" in reports["other"]
)
assert (
reports["n2"].find(f"Nombre total d'erreurs\t: {len(get_all_log_entries[0])}")
reports["n2"].find(f"Nombre total d'erreur(s)\t: {len(get_all_log_entries[0])}")
!= -1
)
assert (
reports["other"].find(
f"Nombre total d'erreurs\t: {len(get_all_log_entries[1])}"
f"Nombre total d'erreur(s)\t: {len(get_all_log_entries[1])}"
)
!= -1
)
@@ -224,21 +224,21 @@ def _test_write_log_report(
assert reports["all"] == all_file.read_text(encoding="windows-1252")
assert (
all_file.read_text(encoding="windows-1252").find(
f"Nombre total d'erreurs\t: {nb_errors['all']}"
f"Nombre total d'erreur(s)\t: {nb_errors['all']}"
)
!= -1
)
assert reports["n2"] == n2_file.read_text(encoding="windows-1252")
assert (
n2_file.read_text(encoding="windows-1252").find(
f"Nombre total d'erreurs\t: {nb_errors['n2']}"
f"Nombre total d'erreur(s)\t: {nb_errors['n2']}"
)
!= -1
)
assert reports["other"] == other_file.read_text(encoding="windows-1252")
assert (
other_file.read_text(encoding="windows-1252").find(
f"Nombre total d'erreurs\t: {nb_errors['other']}"
f"Nombre total d'erreur(s)\t: {nb_errors['other']}"
)
!= -1
)