Metadata-Version: 2.4
Name: zpp_logs
Version: 2.1.3
Summary: Module de gestion de logs
Home-page: https://github.com/ZephyrOff/zpp_logs
Author: ZephyrOff <contact@apajak.fr>
License: MIT License
Project-URL: Documentation, https://github.com/ZephyrOff/zpp_logs
Keywords: logs module zephyroff
Platform: ALL
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: psutil
Requires-Dist: impmagic
Requires-Dist: zpp_color
Requires-Dist: pyyaml
Requires-Dist: jinja2
Requires-Dist: requests
Requires-Dist: SQLAlchemy
Dynamic: author
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: keywords
Dynamic: license
Dynamic: license-file
Dynamic: platform
Dynamic: project-url
Dynamic: requires-dist
Dynamic: summary

# zpp_logs: Un Module de Logging Python Flexible et Puissant

`zpp_logs` est un module de logging Python conÃ§u pour offrir une flexibilitÃ© maximale dans la configuration et l'utilisation des journaux d'Ã©vÃ©nements. InspirÃ© par le module `logging` standard de Python, il introduit des fonctionnalitÃ©s avancÃ©es telles que la configuration via YAML, le formatage basÃ© sur Jinja2 avec des rÃ¨gles dynamiques, des filtres personnalisables et une gestion avancÃ©e des handlers.

## FonctionnalitÃ©s ClÃ©s

*   **Configuration Flexible** : Configurez l'intÃ©gralitÃ© de votre systÃ¨me de logging via un fichier YAML ou directement en Python.
*   **Niveaux de Log PersonnalisÃ©s** : Inclut un niveau `SUCCESS` (25) en plus des niveaux standards.
*   **Formatage AvancÃ© avec Jinja2** :
    *   Utilisez des templates Jinja2 pour dÃ©finir le format de vos messages de log.
    *   Fonctions Jinja2 personnalisÃ©es (`fg`, `attr`, `date`, `epoch`) pour un formatage riche (ex: couleurs dans la console).
    *   **RÃ¨gles de Formatage Dynamiques** : Appliquez des transformations conditionnelles aux champs de vos logs basÃ©es sur des expressions Jinja2, avec un comportement par dÃ©faut (`__default__`).
*   **Filtrage Puissant** : Filtrez les messages de log au niveau du handler en utilisant des expressions Jinja2.
*   **Handlers Multiples** :
    *   `ConsoleHandler` : Ã‰crit les logs dans la console (stdout/stderr).
    *   `FileHandler` : Ã‰crit les logs dans un fichier, avec rotation basÃ©e sur la taille (`maxBytes`, `backupCount`) et support du logging circulaire.
    *   `DatabaseHandler` : Enregistre les logs dans une base de donnÃ©es (SQLite, MySQL, etc.) avec mappage de colonnes personnalisable via Jinja2 et colonnes par dÃ©faut.
    *   `SMTPHandler` : Envoie les logs par e-mail via SMTP.
    *   `ResendHandler` : Envoie les logs par e-mail via l'API Resend.
*   **Modification Dynamique** : Modifiez les propriÃ©tÃ©s des formatters, handlers et loggers Ã  la volÃ©e aprÃ¨s leur crÃ©ation.

## Installation

1.  **Cloner le dÃ©pÃ´t** (ou crÃ©er la structure de fichiers) :
    ```bash
    mkdir zpp_logs
    # CrÃ©ez les fichiers .py Ã  l'intÃ©rieur de zpp_logs/
    # CrÃ©ez config.yaml et main.py Ã  la racine
    ```
2.  **Installer les dÃ©pendances** :
    ```bash
    pip install -r requirements.txt
    ```
    Contenu de `requirements.txt` :
    ```
    pyyaml
    jinja2
    colorama
    requests
    SQLAlchemy
    ```

## Concepts ClÃ©s

### Niveaux de Log

Les niveaux de log sont des entiers, avec des constantes prÃ©dÃ©finies :
`CRITICAL` (50), `ERROR` (40), `WARNING` (30), `SUCCESS` (25), `INFO` (20), `DEBUG` (10), `NOTSET` (0).

### Loggers

Les loggers sont les points d'entrÃ©e pour enregistrer les messages. Ils possÃ¨dent un nom et une liste de handlers.

#### Approche dynamique

##### 1. Instanciation d'un Logger
Un Logger a besoin d'un nom (name) et d'une liste de handlers pour Ãªtre crÃ©Ã©.

```python
from zpp_logs.core import Logger, CustomFormatter
from zpp_logs.handlers.console import ConsoleHandler
from zpp_logs.levels import DEBUG

# Ã‰tape 1: CrÃ©er un formateur
my_formatter = CustomFormatter("{{ levelname }}: {{ msg }}")

# Ã‰tape 2: CrÃ©er un ou plusieurs handlers
console_handler = ConsoleHandler(level=DEBUG, formatter=my_formatter)

# Ã‰tape 3: CrÃ©er le logger
logger = Logger(
    name="my_app",
    handlers=[console_handler]
)
```

##### 2. Ã‰mission de Logs
Une fois le logger crÃ©Ã©, utilisez ses mÃ©thodes de niveau pour Ã©mettre des messages.

```python
logger.debug("Information de dÃ©bogage dÃ©taillÃ©e.")
logger.info("DÃ©marrage du processus.")
logger.warning("Le disque est presque plein.")
logger.error("Impossible de contacter le service externe.")
logger.critical("Ã‰chec critique, arrÃªt de l'application.")
```


##### 3. Passer des DonnÃ©es SupplÃ©mentaires
Une fonctionnalitÃ© clÃ© est la capacitÃ© de passer des donnÃ©es structurÃ©es en utilisant des arguments clÃ©-valeur (**kwargs). Ces donnÃ©es sont ajoutÃ©es au record du log et deviennent disponibles dans votre CustomFormatter (Ã  la fois pour le formatage et pour les rÃ¨gles).

```python
# Le formateur peut utiliser 'user_id' et 'ip_address'
formatter = CustomFormatter("{{ msg }} (user: {{ user_id }}, ip: {{ ip_address }})")
logger.add_handler(ConsoleHandler(level=DEBUG, formatter=formatter))


# Passez les donnÃ©es en kwargs lors de l'appel de log
logger.info(
    "Tentative de connexion de l'utilisateur.",
    user_id="alice",
    ip_address="192.168.1.100"
)
# Sortie: Tentative de connexion de l'utilisateur. (user: alice, ip: 192.168.1.100)
```

#### Approche par instance de config

```python
from zpp_logs.core import LogManager

# Une seule instance de manager pour toute l'application
manager = LogManager('config.yaml')

# Obtenir le logger nommÃ© 'database'
db_logger = manager.get_logger('database')
db_logger.debug("Connexion Ã  la base de donnÃ©es...") # Ira dans app.log

# Obtenir le logger 'root'
root_logger = manager.get_logger('root')
root_logger.info("Message d'information gÃ©nÃ©ral.") # Ira dans la console

# Demander un logger qui n'est pas dans la config
# Le LogManager renverra la configuration du logger 'root' !
api_logger = manager.get_logger('external_api')
api_logger.warning("ProblÃ¨me avec l'API externe.") # Ira dans la console
```

### Formatters

DÃ©finissent l'apparence des messages de log.
*   **`format_str`** : Une chaÃ®ne de template Jinja2 (ex: `"{{ date('%H:%M:%S') }} | {{ levelname }} | {{ msg }}"`).
*   **RÃ¨gles (`rules`)** : Un dictionnaire oÃ¹ les clÃ©s sont des expressions Jinja2 (Ã©valuÃ©es Ã  `True` ou `False`) et les valeurs sont des dictionnaires de champs Ã  modifier. La clÃ© `__default__` agit comme une clause `else`.

    ```yaml
    # Exemple de rÃ¨gles dans un formatter
    rules:
        "levelname == 'SUCCESS'":
          levelname: "{{ fg('green') }}SUCCESS{{ attr(0) }}"
          msg: "{{ fg('green') }}OpÃ©ration rÃ©ussie : {{ msg }}{{ attr(0) }}"
        "levelname == 'ERROR' and 'database' in msg":
          levelname: "{{ fg('yellow') }}DB_ERROR{{ attr(0) }}"
          msg: "{{ fg('yellow') }}ProblÃ¨me de base de donnÃ©es: {{ msg }}{{ attr(0) }}"
        __default__:
          levelname: "{{ fg('gray') }}DEFAULT{{ attr(0) }}"
          msg: "{{ fg('gray') }}Message par dÃ©faut: {{ msg }}{{ attr(0) }}"
    ```

### Handlers

Les handlers sont responsables de l'envoi des messages de log vers des destinations spÃ©cifiques (console, fichier, base de donnÃ©es, e-mail, etc.). Chaque handler peut Ãªtre configurÃ© indÃ©pendamment.

*   **`level`** : Le niveau minimum du message pour que le handler le traite. Un message avec un niveau infÃ©rieur Ã  celui du handler sera ignorÃ©. Peut Ãªtre une constante de `zpp_logs` (ex: `zpp_logs.INFO`) ou une chaÃ®ne de caractÃ¨re (ex: `INFO`).
*   **`ops`** : L'opÃ©rateur de comparaison du niveau. DÃ©finit comment le niveau du message est comparÃ© au `level` du handler.
    *   `">="` (par dÃ©faut) : Le handler traite les messages dont le niveau est supÃ©rieur ou Ã©gal Ã  son `level`.
    *   `">"` : Strictement supÃ©rieur.
    *   `"<="` : InfÃ©rieur ou Ã©gal.
    *   `"<"` : Strictement infÃ©rieur.
    *   `"=="` : Ã‰gal.
    *   `"!="` : DiffÃ©rent.
*   **`formatter`** : Le nom de l'instance du formatter Ã  utiliser pour ce handler, tel que dÃ©fini dans la section `formatters` du `config.yaml`.
*   **`filters`** : Une liste d'expressions Jinja2. Si une expression Ã©value Ã  `False` pour un message donnÃ©, ce message est filtrÃ© et n'est pas traitÃ© par le handler. Utile pour des filtrages complexes basÃ©s sur le contenu du message ou d'autres attributs du record de log.

### ConsoleHandler

Le `ConsoleHandler` est conÃ§u pour afficher les messages de log directement dans la console (sortie standard ou erreur standard).

#### FonctionnalitÃ©

Ce handler est l'un des plus simples. Il prend un message de log, le formate en utilisant le `CustomFormatter` fourni, et l'Ã©crit sur le flux de sortie spÃ©cifiÃ©, qui est par dÃ©faut `sys.stdout` (la sortie standard). Il est idÃ©al pour le dÃ©veloppement ou pour les applications en ligne de commande oÃ¹ une visibilitÃ© immÃ©diate des logs est nÃ©cessaire.

#### Options

| ParamÃ¨tre | Type | DÃ©faut | Description |
|-----------|------|---------|-------------|
| `level` | `int` | `NOTSET` | Le niveau de log minimum requis pour que le message soit traitÃ© par ce handler. Doit Ãªtre une constante de `zpp_logs.levels` (ex: `INFO`, `DEBUG`). |
| `formatter`| `CustomFormatter` | `None` | Une instance de `CustomFormatter` chargÃ©e de mettre en forme le message avant son affichage. |
| `filters` | `list` | `None` | Une liste de chaÃ®nes de caractÃ¨res. Chaque chaÃ®ne est une expression Jinja2 qui doit retourner `True` pour que le log soit traitÃ©. |
| `output` | `str` | `'sys.stdout'` | Le flux de sortie. Peut Ãªtre `'sys.stdout'` ou `'sys.stderr'`. |
| `ops` | `str` | `'>='` | L'opÃ©rateur de comparaison pour le niveau (`>=`, `==`, etc.). |
| `async_mode` | `bool` | `False` | Si `True`, les logs sont traitÃ©s dans un thread sÃ©parÃ© pour ne pas bloquer l'application principale. |

#### Usage

##### 1. Programmatic (dans un script Python)

Voici comment instancier et utiliser le `ConsoleHandler` directement dans votre code.

```python
from zpp_logs.core import Logger, CustomFormatter
from zpp_logs.handlers.console import ConsoleHandler
from zpp_logs.levels import INFO

# 1. CrÃ©er un formateur
formatter = CustomFormatter(
    format_str="{{ timestamp.strftime('%H:%M:%S') }} - {{ levelname }} - {{ msg }}"
)

# 2. CrÃ©er une instance du handler
console_handler = ConsoleHandler(level=INFO, formatter=formatter)

# 3. CrÃ©er un logger avec ce handler
logger = Logger(name="my_app", handlers=[console_handler])

# 4. Envoyer un message
logger.info("Ceci est un test pour la console.")
logger.debug("Ce message ne sera pas affichÃ© car son niveau est infÃ©rieur Ã  INFO.")
```

##### 2. DÃ©claratif (dans `config.yaml`)

Voici comment configurer le `ConsoleHandler` dans votre fichier `config.yaml`.

```yaml
# config.yaml

formatters:
  console_format:
    format: "{{ levelname }}: {{ msg }}"

handlers:
  # Nom de notre instance de handler
  console_out:
    # Classe Ã  utiliser
    class: zpp_logs.ConsoleHandler
    # Options du handler
    level: INFO
    formatter: console_format
    output: sys.stdout

loggers:
  root:
    # Associer le handler au logger
    handlers: [console_out]
```


### DatabaseHandler

Le `DatabaseHandler` enregistre les messages de log dans une table de base de donnÃ©es relationnelle.

#### FonctionnalitÃ©

Ce handler puissant utilise `SQLAlchemy` pour se connecter Ã  diffÃ©rentes bases de donnÃ©es (SQLite et MySQL sont supportÃ©s nativement) et y insÃ©rer les enregistrements de log. Il offre une grande flexibilitÃ© pour structurer les donnÃ©es de log.

Il peut fonctionner de deux maniÃ¨res :
1.  **Mode Automatique :** Si vous ne fournissez pas de modÃ¨le SQLAlchemy, le handler crÃ©era automatiquement une table avec des colonnes par dÃ©faut (`id`, `timestamp`, `level`, `logger_name`, `message`) ou selon un mapping que vous spÃ©cifiez.
2.  **Mode ModÃ¨le :** Vous pouvez fournir votre propre classe de modÃ¨le SQLAlchemy. Le handler utilisera la table dÃ©finie par ce modÃ¨le pour y insÃ©rer les logs.

**DÃ©pendances :** Ce handler requiert `SQLAlchemy`. Pour MySQL, vous aurez Ã©galement besoin de `PyMySQL` (`pip install sqlalchemy pymysql`).

#### Options

| ParamÃ¨tre | Type | DÃ©faut | Description |
|-----------|------|---------|-------------|
| `level` | `int` | `NOTSET` | Le niveau de log minimum requis. |
| `formatter`| `CustomFormatter`| `None` | Formateur (moins crucial ici car les donnÃ©es sont mappÃ©es, mais toujours utilisÃ© pour les rÃ¨gles). |
| `connector` | `dict` | `None` | **Requis.** Dictionnaire de connexion Ã  la base de donnÃ©es. |
| `columns` | `dict` | `None` | Optionnel. Mappe les noms de colonnes de la table aux expressions Jinja2 Ã  extraire du log record. |
| `model` | `str` or `DeclarativeMeta`| `None`| Optionnel. Le modÃ¨le SQLAlchemy Ã  utiliser (soit la classe, soit le chemin d'importation).|
| `ops` | `str` | `'>='` | OpÃ©rateur de comparaison pour le niveau. |
| `async_mode` | `bool` | `False` | Si `True`, l'insertion en base de donnÃ©es se fait dans un thread sÃ©parÃ©. |

##### DÃ©tails du `connector`

Le dictionnaire `connector` doit contenir :
- `engine`: `'sqlite'` ou `'mysql'`.
- Pour SQLite : `filename` (chemin du fichier) et `table` (nom de la table).
- Pour MySQL : `host`, `user`, `password`, `database`.

##### DÃ©tails du `columns`

Ce dictionnaire est la clÃ© de la flexibilitÃ©. La clÃ© est le nom de la colonne dans la base de donnÃ©es, la valeur est une expression Jinja2.

Exemple : `{'user_id': 'user.id', 'request_path': 'request.path'}`

#### Usage

##### 1. Programmatic (Mode Automatique)

```python
from zpp_logs.core import Logger, CustomFormatter
from zpp_logs.handlers.database import DatabaseHandler
from zpp_logs.levels import INFO

# 1. DÃ©finir le connecteur pour une base SQLite
db_connector = {
    'engine': 'sqlite',
    'filename': 'logs/app_logs.db',
    'table': 'activity_logs'
}

# 2. DÃ©finir le mapping des colonnes
# On veut enregistrer le timestamp, le niveau, le message et un 'user_id' personnalisÃ©
column_mapping = {
    'timestamp': 'timestamp',
    'level': 'levelname',
    'message': 'msg',
    'user_id': 'user_id' # 'user_id' sera passÃ© en kwarg
}

# 3. CrÃ©er une instance du handler
db_handler = DatabaseHandler(
    level=INFO,
    formatter=CustomFormatter(""), # Le formateur est moins important ici
    connector=db_connector,
    columns=column_mapping
)

# 4. CrÃ©er un logger
logger = Logger(name="db_app", handlers=[db_handler])

# 5. Envoyer un log avec un champ personnalisÃ©
logger.info("L'utilisateur a changÃ© son mot de passe.", user_id=123)
logger.warning("Tentative de connexion Ã©chouÃ©e.", user_id=456)
```

##### 2. DÃ©claratif (dans `config.yaml`)

```yaml
# config.yaml

formatters:
  # Un formateur vide est suffisant, car le mapping se fait dans le handler
  db_format:
    format: ""

handlers:
  log_to_db:
    class: zpp_logs.DatabaseHandler
    level: INFO
    formatter: db_format
    # Configuration du connecteur
    connector:
      engine: sqlite
      filename: "config_logs.db"
      table: "system_events"
    # Configuration du mapping
    columns:
      timestamp: timestamp
      level: levelname
      message: msg
      logger: name

loggers:
  root:
    handlers: [log_to_db]
```

### FileHandler

Le `FileHandler` Ã©crit les messages de log dans un fichier sur le systÃ¨me de fichiers.

#### FonctionnalitÃ©

Ce handler est utilisÃ© pour la persistance des logs. Il Ã©crit les messages formatÃ©s dans un fichier spÃ©cifiÃ©. Il supporte Ã©galement des fonctionnalitÃ©s avancÃ©es comme la rotation de fichiers (log rotation) basÃ©e sur la taille, ce qui permet de gÃ©rer l'espace disque utilisÃ© par les logs.

- **Rotation Standard :** Quand la taille maximale est atteinte, le fichier de log actuel est renommÃ© (ex: `app.log` -> `app.log.1`) et un nouveau fichier vide est crÃ©Ã©.
- **Rotation Circulaire :** Un mode spÃ©cial oÃ¹, au lieu de crÃ©er de nouveaux fichiers, la ligne la plus ancienne du fichier est supprimÃ©e pour faire de la place.

#### Options

| ParamÃ¨tre | Type | DÃ©faut | Description |
|-----------|------|---------|-------------|
| `level` | `int` | `NOTSET` | Le niveau de log minimum requis. |
| `formatter`| `CustomFormatter`| `None` | L'instance `CustomFormatter` pour la mise en forme. |
| `filters` | `list` | `None` | Filtres Jinja2 pour un contrÃ´le fin. |
| `filename` | `str` | `None` | **Requis.** Le chemin vers le fichier de log. Peut inclure des variables Jinja2. |
| `maxBytes` | `int` | `0` | La taille maximale en octets que le fichier de log peut atteindre avant la rotation. Si `0`, la rotation est dÃ©sactivÃ©e. |
| `backupCount`| `int` | `0` | Le nombre de fichiers de sauvegarde Ã  conserver. Si `> 0`, la rotation standard est utilisÃ©e. Si `0` et `maxBytes > 0`, la rotation circulaire est utilisÃ©e. |
| `ops` | `str` | `'>='` | L'opÃ©rateur de comparaison pour le niveau. |
| `async_mode` | `bool` | `False` | Si `True`, l'Ã©criture des logs se fait dans un thread sÃ©parÃ©. |

#### Usage

##### 1. Programmatic (dans un script Python)

Voici comment configurer un `FileHandler` avec rotation.

```python
from zpp_logs.core import Logger, CustomFormatter
from zpp_logs.handlers.file import FileHandler
from zpp_logs.levels import DEBUG

# 1. CrÃ©er un formateur dÃ©taillÃ©
formatter = CustomFormatter(
    format_str="{{ timestamp.isoformat() }} | {{ levelname }} | {{ msg }}"
)

# 2. CrÃ©er une instance du handler avec rotation
# Rotation aprÃ¨s 1 MB, conserve 3 fichiers de backup (app.log.1, app.log.2, app.log.3)
file_handler = FileHandler(
    level=DEBUG,
    formatter=formatter,
    filename='app.log',
    maxBytes=1024 * 1024,  # 1 MB
    backupCount=3
)

# 3. CrÃ©er un logger avec ce handler
logger = Logger(name="file_app", handlers=[file_handler])

# 4. Envoyer des messages
logger.info("L'application a dÃ©marrÃ©.")
logger.debug("Ceci est une information de diagnostic.")
```

##### 2. DÃ©claratif (dans `config.yaml`)

Voici comment configurer le `FileHandler` dans votre fichier `config.yaml`.

```yaml
# config.yaml

formatters:
  file_format:
    format: "{{ timestamp }} | {{ name }} | {{ levelname }} | {{ msg }}"

handlers:
  # Nom de notre instance de handler
  log_to_file:
    # Classe Ã  utiliser
    class: zpp_logs.FileHandler
    # Options du handler
    level: DEBUG
    formatter: file_format
    filename: config_app.log
    maxBytes: 512000  # 500 KB
    backupCount: 5
    encoding: utf-8

loggers:
  root:
    handlers: [log_to_file]
```


### ResendHandler

Le `ResendHandler` envoie des emails de log en utilisant l'API du service [Resend](https://resend.com).

#### FonctionnalitÃ©

Ce handler est une alternative moderne au `SMTPHandler`. Il s'intÃ¨gre avec Resend, une plateforme d'envoi d'emails transactionnels pour les dÃ©veloppeurs. Il est idÃ©al pour envoyer des alertes critiques de maniÃ¨re fiable sans avoir Ã  gÃ©rer son propre serveur SMTP.

Le handler envoie une requÃªte POST Ã  l'API de Resend. Le sujet de l'email peut Ãªtre un template Jinja2, et le corps de l'email (`html`) est le message brut (`msg`) du log.

**DÃ©pendances :** Ce handler requiert la librairie `requests` (`pip install requests`).

#### Options


| ParamÃ¨tre     | Type              | DÃ©faut   | Description                                                                                                    |
| ------------- | ----------------- | -------- | -------------------------------------------------------------------------------------------------------------- |
| `level`       | `int`             | `NOTSET` | Le niveau de log minimum (ex: `ERROR`).                                                                        |
| `formatter`   | `CustomFormatter` | `None`   | Formateur (le corps de l'email est `msg` brut, mais le formateur peut Ãªtre utile pour les rÃ¨gles et le sujet). |
| `host`        | `str`             | `None`   | **Requis.** L'adresse du serveur SMTP.                                                                         |
| `port`        | `int`             | `None`   | **Requis.** Le port du serveur SMTP (ex: 587 pour TLS).                                                        |
| `username`    | `str`             | `None`   | **Requis.** Le nom d'utilisateur pour s'authentifier auprÃ¨s du serveur SMTP.                                   |
| `password`    | `str`             | `None`   | **Requis.** Le mot de passe pour l'authentification.                                                           |
| `fromaddr`    | `str`             | `None`   | **Requis.** L'adresse email de l'expÃ©diteur.                                                                   |
| `toaddrs`     | `list`            | `None`   | **Requis.** Une liste d'adresses email de destinataires.                                                       |
| `subject`     | `str`             | `None`   | **Requis.** Le sujet de l'email. Peut contenir des expressions Jinja2.                                         |
| `ops`         | `str`             | `'>='`   | L'opÃ©rateur de comparaison pour le niveau.                                                                     |
| `async_mode`  | `bool`            | `False`  | Si `True`, l'envoi de l'email se fait dans un thread sÃ©parÃ© pour ne pas bloquer l'application.                 |
| `insecure`    | `bool`            | `False`  | Si `True`, connexion au serveur sans TLS                                                                       |
| `cc`          | `list`            | `None`   | Personne en copie des mails                                                                                    |
| `bcc`         | `list`            | `None`   | Personne en copie des mails                                                                                    |
| `attachments` | `list`            | `None`   | Listes des fichiers Ã  joindre au mail                                                                          |

#### Usage

##### 1. Programmatic (dans un script Python)

**Note :** Ne jamais coder en dur votre clÃ© d'API. Utilisez des variables d'environnement.

```python
import os
from zpp_logs.core import Logger, CustomFormatter
from zpp_logs.handlers.resend import ResendHandler
from zpp_logs.levels import ERROR

# 1. CrÃ©er une instance du handler
# La clÃ© d'API est rÃ©cupÃ©rÃ©e depuis les variables d'environnement
resend_handler = ResendHandler(
    level=ERROR,
    formatter=CustomFormatter(""), # Corps de l'email = msg
    api_key=os.environ.get('RESEND_API_KEY'),
    fromaddr="onboarding@resend.dev", # Adresse d'exemple fournie par Resend
    to=["votre_email@exemple.com"],
    subject="[ERREUR] Un problÃ¨me est survenu sur {{ name }}"
)

# 2. CrÃ©er un logger avec ce handler
logger = Logger(name="user_service", handlers=[resend_handler])

# 3. Envoyer un log qui dÃ©clenchera l'email via Resend
logger.error("Impossible de mettre Ã  jour le profil de l'utilisateur #500. Erreur de base de donnÃ©es.")
```

##### 2. DÃ©claratif (dans `config.yaml`)

**Attention :** Stocker des clÃ©s d'API en clair dans des fichiers de configuration est une mauvaise pratique.

```yaml
# config.yaml

formatters:
  resend_format:
    format: ""

handlers:
  send_resend_alert:
    class: zpp_logs.ResendHandler
    level: ERROR
    formatter: resend_format
    # --- ParamÃ¨tres Resend ---
    # IdÃ©alement, utilisez un placeholder qui est remplacÃ© au dÃ©marrage de l'app
    api_key: "RE_VOTRE_CLÃ‰_API_Ici"
    fromaddr: "app@votre-domaine-verifie.com"
    to:
      - "devops@exemple.com"
    subject: "Erreur dÃ©tectÃ©e dans le service {{ name }}"

loggers:
  user_service: # Logger spÃ©cifique
    handlers: [send_resend_alert]
  root: # Logger racine
    handlers: [] # Ne pas envoyer d'email pour les logs gÃ©nÃ©raux
```

### SMTPHandler

Le `SMTPHandler` envoie les messages de log par email via un serveur SMTP.

#### FonctionnalitÃ©

Ce handler est particuliÃ¨rement utile pour les notifications d'Ã©vÃ©nements critiques. Lorsqu'un log atteint un certain niveau de sÃ©vÃ©ritÃ© (typiquement `ERROR` ou `CRITICAL`), ce handler peut envoyer un email Ã  une liste de destinataires pour une alerte immÃ©diate.

Le sujet de l'email peut Ãªtre une chaÃ®ne de caractÃ¨res formatÃ©e avec Jinja2, permettant de crÃ©er des sujets dynamiques. Le corps de l'email est le message brut du log (`msg`). La connexion au serveur SMTP se fait via TLS pour plus de sÃ©curitÃ©.

#### Options

| ParamÃ¨tre | Type | DÃ©faut | Description |
|-----------|------|---------|-------------|
| `level` | `int` | `NOTSET` | Le niveau de log minimum (ex: `ERROR`). |
| `formatter`| `CustomFormatter`| `None` | Formateur (le corps de l'email est `msg` brut, mais le formateur peut Ãªtre utile pour les rÃ¨gles et le sujet). |
| `host` | `str` | `None` | **Requis.** L'adresse du serveur SMTP. |
| `port` | `int` | `None` | **Requis.** Le port du serveur SMTP (ex: 587 pour TLS). |
| `username` | `str` | `None` | **Requis.** Le nom d'utilisateur pour s'authentifier auprÃ¨s du serveur SMTP. |
| `password` | `str` | `None` | **Requis.** Le mot de passe pour l'authentification. |
| `fromaddr` | `str` | `None` | **Requis.** L'adresse email de l'expÃ©diteur. |
| `toaddrs` | `list` | `None` | **Requis.** Une liste d'adresses email de destinataires. |
| `subject` | `str` | `None` | **Requis.** Le sujet de l'email. Peut contenir des expressions Jinja2. |
| `ops` | `str` | `'>='` | L'opÃ©rateur de comparaison pour le niveau. |
| `async_mode` | `bool` | `False` | Si `True`, l'envoi de l'email se fait dans un thread sÃ©parÃ© pour ne pas bloquer l'application. |

#### Usage

##### 1. Programmatic (dans un script Python)

**Note :** Pour des raisons de sÃ©curitÃ©, Ã©vitez de coder en dur les identifiants. Utilisez des variables d'environnement ou un gestionnaire de secrets.

```python
import os
from zpp_logs.core import Logger, CustomFormatter
from zpp_logs.handlers.smtp import SMTPHandler
from zpp_logs.levels import CRITICAL

# 1. CrÃ©er une instance du handler avec les informations du serveur SMTP
# (Ici, nous supposons que les identifiants sont dans des variables d'environnement)
smtp_handler = SMTPHandler(
    level=CRITICAL,
    formatter=CustomFormatter(""), # Le corps est le message brut
    host="smtp.exemple.com",
    port=587,
    username=os.environ.get('SMTP_USER'),
    password=os.environ.get('SMTP_PASS'),
    fromaddr="noreply@monapp.com",
    toaddrs=["admin@exemple.com", "dev-on-call@exemple.com"],
    subject="[ALERTE CRITIQUE] Erreur dans {{ name }}"
)

# 2. CrÃ©er un logger avec ce handler
logger = Logger(name="payment_gateway", handlers=[smtp_handler])

# 3. Envoyer un log critique qui dÃ©clenchera l'envoi d'un email
logger.critical("Ã‰chec du traitement du paiement pour la transaction #12345.")
```

##### 2. DÃ©claratif (dans `config.yaml`)

**Attention :** Stocker des mots de passe en clair dans des fichiers de configuration est une mauvaise pratique de sÃ©curitÃ©. PrÃ©fÃ©rez des mÃ©canismes d'injection de secrets. Cet exemple est Ã  titre illustratif.

```yaml
# config.yaml

formatters:
  email_format:
    format: "" # Non utilisÃ© pour le corps de l'email

handlers:
  send_alert_email:
    class: zpp_logs.SMTPHandler
    level: CRITICAL
    formatter: email_format
    # --- ParamÃ¨tres SMTP ---
    host: smtp.exemple.com
    port: 587
    # IdÃ©alement, utilisez des 'placeholders' que vous remplacez au dÃ©marrage
    username: "MON_USER_SMTP"
    password: "MON_MOT_DE_PASSE_SMTP"
    fromaddr: "alert@system.com"
    toaddrs:
      - "admin-equipe-a@exemple.com"
      - "admin-equipe-b@exemple.com"
    subject: "ALERTE: {{ levelname }} dans le logger '{{ name }}'"

loggers:
  root:
    handlers: [send_alert_email] # Attacher le handler au logger
```


### WebhookHandler

Le `WebhookHandler` envoie les messages de log Ã  une URL de webhook via une requÃªte HTTP POST.

#### FonctionnalitÃ©

Ce handler est extrÃªmement versatile et permet de s'intÃ©grer avec une multitude de services tiers (Slack, Discord, IFTTT, Zapier, etc.) ou avec vos propres endpoints d'API. Il envoie une charge utile (payload) JSON personnalisable Ã  une URL spÃ©cifiÃ©e.

Il supporte plusieurs mÃ©thodes d'authentification pour sÃ©curiser les appels :
- **Bearer Token :** Authentification via un jeton dans l'en-tÃªte `Authorization`.
- **Basic Auth :** Authentification HTTP Basic avec un nom d'utilisateur et un mot de passe.
- **Custom Token :** Authentification via un jeton secret passÃ© dans l'en-tÃªte `X-Webhook-Token`.
- **Aucune :** Si aucune mÃ©thode n'est spÃ©cifiÃ©e, la requÃªte est envoyÃ©e sans authentification.

**DÃ©pendances :** Ce handler requiert la librairie `requests` (`pip install requests`).

#### Options

| ParamÃ¨tre | Type | DÃ©faut | Description |
|-----------|------|---------|-------------|
| `level` | `int` | `NOTSET` | Le niveau de log minimum requis pour dÃ©clencher l'envoi. |
| `formatter`| `CustomFormatter`| `None` | Formateur (utile pour les rÃ¨gles, moins pour le formatage direct). |
| `url` | `str` | `None` | **Requis.** L'URL du webhook Ã  appeler. |
| `data` | `dict` | `{}` | **Requis.** Un dictionnaire dÃ©finissant la structure du JSON Ã  envoyer. Les valeurs sont des templates Jinja2. |
| `bearer` | `str` | `None` | Le jeton (token) Ã  utiliser pour l'authentification de type "Bearer". |
| `basic` | `dict` | `None` | Un dictionnaire avec les clÃ©s `user` and `pass` pour l'authentification "Basic". |
| `token` | `str` | `None` | Le jeton Ã  passer dans l'en-tÃªte `X-Webhook-Token` pour une authentification personnalisÃ©e. |
| `ssl_verify`|`bool` | `True` | Si `False`, la vÃ©rification du certificat SSL de l'URL du webhook sera dÃ©sactivÃ©e. Ã€ utiliser avec prudence. |
| `ops` | `str` | `'>='` | L'opÃ©rateur de comparaison pour le niveau. |
| `async_mode` | `bool` | `False` | Si `True`, l'appel au webhook se fait dans un thread sÃ©parÃ©. |

#### Usage

##### 1. Programmatic (dans un script Python)

```python
import os
from zpp_logs.core import Logger, CustomFormatter
from zpp_logs.handlers.webhook import WebhookHandler
from zpp_logs.levels import WARNING

# 1. DÃ©finir la structure de la charge utile (payload)
json_payload_template = {
    "content": "Alerte de niveau {{ levelname }} sur le service {{ name }}",
    "embeds": [{
        "title": "DÃ©tails du Log",
        "description": "{{ msg }}",
        "color": 16711680 # Rouge pour les erreurs (exemple pour Discord)
    }],
    "extra_data": {
        "request_id": "{{ request_id | default('N/A') }}"
    }
}

# 2. CrÃ©er une instance du handler
webhook_handler = WebhookHandler(
    level=WARNING,
    formatter=CustomFormatter(""), # Pas besoin de formateur de chaÃ®ne ici
    url=os.environ.get("DISCORD_WEBHOOK_URL"),
    data=json_payload_template
    # Aucune authentification nÃ©cessaire pour les webhooks Discord
)

# 3. CrÃ©er un logger avec ce handler
logger = Logger(name="api_gateway", handlers=[webhook_handler])

# 4. Envoyer un log
logger.warning(
    "Le temps de rÃ©ponse de l'API externe a dÃ©passÃ© le seuil.",
    request_id="a7b2c9x4"
)
```

##### 2. DÃ©claratif (dans `config.yaml`)

C'est ici que ce handler brille par sa lisibilitÃ©.

######## Exemple 1 : Authentification Bearer

```yaml
formatters:
  standard: # Le formateur peut Ãªtre vide si non nÃ©cessaire
    format: ""

handlers:
  send_to_my_api:
    class: zpp_logs.WebhookHandler
    level: INFO
    formatter: standard
    url: "https://api.mon-service.com/v1/log"
    # --- Authentification ---
    bearer: "VOTRE_JETON_API_SECRET"
    # --- DonnÃ©es JSON ---
    data:
      application: "backend-worker"
      source: "production"
      status: "{{ levelname }}"
      message: "{{ msg }}"
      user_context: "{{ user_id | default('system') }}"

loggers:
  root:
    handlers: [send_to_my_api]
```

######## Exemple 2 : Authentification Basic

```yaml
handlers:
  send_to_legacy_system:
    class: zpp_logs.WebhookHandler
    level: ERROR
    formatter: standard
    url: "https://legacy.interne/api/log"
    # --- Authentification ---
    basic:
      user: "api_user"
      pass: "S3cr3tP4ssw0rd"
    # --- DonnÃ©es JSON ---
    data:
      severity: "{{ levelno }}"
      text: "{{ msg }}"

loggers:
  legacy_connector:
    handlers: [send_to_legacy_system]
```

######## Exemple 3 : Authentification par Jeton PersonnalisÃ© (`X-Webhook-Token`)

```yaml
handlers:
  send_to_private_webhook:
    class: zpp_logs.WebhookHandler
    level: INFO
    formatter: standard
    url: "https://mon-service.privÃ©/webhook"
    # --- Authentification ---
    token: "VOTRE_TOKEN_SECRET_PARTAGÃ‰"
    # --- DonnÃ©es JSON ---
    data:
      source: "{{ name }}"
      log_level: "{{ levelname }}"
      log_message: "{{ msg }}"

loggers:
  private_app:
    handlers: [send_to_private_webhook]
```



### Journalisation Asynchrone

Pour les handlers qui peuvent Ãªtre lents (comme l'envoi d'e-mails avec `SMTPHandler` ou l'Ã©criture dans une base de donnÃ©es distante), `zpp_logs` offre un mode de journalisation asynchrone. Lorsqu'il est activÃ©, le handler s'exÃ©cute dans un thread d'arriÃ¨re-plan, ce qui empÃªche votre application principale de se bloquer.

Pour activer le mode asynchrone pour un handler, ajoutez simplement `async_mode: true` Ã  sa configuration dans votre fichier `config.yaml`.

**Exemple : Rendre le `SMTPHandler` asynchrone**

```yaml
# Exemple de SMTPHandler asynchrone dans config.yaml
handlers:
    email_critical_async:
        class: zpp_logs.SMTPHandler
        level: zpp_logs.CRITICAL
        async_mode: true  # Active le mode asynchrone
        formatter: standard
        host: smtp.your-email-provider.com
        port: 587
        username: your_email@example.com
        password: your_email_password
        fromaddr: "no-reply@your-app.com"
        toaddrs: ["admin@your-app.com"]
        subject: "ALERTE CRITIQUE (Async): {{ levelname }} dans {{ name }}"
```

C'est tout ! Ce handler enverra maintenant les e-mails en arriÃ¨re-plan sans ralentir votre application. Cette fonctionnalitÃ© peut Ãªtre appliquÃ©e Ã  n'importe quel handler.

De mÃªme, lors de la crÃ©ation d'un handler en Python, vous pouvez l'activer en passant `async_mode=True` Ã  son constructeur.

**Exemple : Rendre le `ConsoleHandler` asynchrone en Python**

```python
# Exemple de ConsoleHandler asynchrone en Python
mon_handler_console = ConsoleHandler(
    level=INFO,
    formatter=mon_formatter,
    async_mode=True  # Active le mode asynchrone
)

logger = Logger(name="mon_logger", handlers=[mon_handler_console])
logger.info("Ce message sera traitÃ© en arriÃ¨re-plan.")
print("Ce print s'exÃ©cute immÃ©diatement.")
```

### 2. Configuration Programmatique

La configuration programmatique vous permet de construire et de gÃ©rer votre systÃ¨me de logging directement dans votre code Python, offrant une flexibilitÃ© maximale et un contrÃ´le prÃ©cis sur chaque composant.

#### Imports NÃ©cessaires

Pour commencer, importez les classes et constantes essentielles :

```python
from zpp_logs import (
    Logger, CustomFormatter, ConsoleHandler, FileHandler, DatabaseHandler,
    SMTPHandler, ResendHandler,
    DEBUG, INFO, WARNING, ERROR, CRITICAL, SUCCESS
)
import sys
import os
from sqlalchemy import create_engine, text # Pour la vÃ©rification de la base de donnÃ©es
```

#### 2.1. CrÃ©ation d'un Formatter

Un formatter dÃ©finit l'apparence de vos messages de log. Vous pouvez spÃ©cifier un format de base avec `format_str` (supportant Jinja2) et ajouter des rÃ¨gles de formatage dynamiques.

```python
# CrÃ©ation d'un formatter de base
programmatic_formatter = CustomFormatter(
    format_str="[PROG] {{ date('%H:%M:%S') }} | {{ levelname }} | {{ msg }}"
)

# Ajout de rÃ¨gles dynamiques au formatter
# Ces rÃ¨gles modifient l'apparence des champs 'levelname' ou 'msg' en fonction de conditions
programmatic_formatter.set_rule(
    "levelname == 'INFO'",
    {"levelname": "{{ fg('cyan') }}INFO{{ attr(0) }}"}
)
programmatic_formatter.set_rule(
    "levelname == 'WARNING'",
    {"msg": "{{ fg('yellow') }}WARNING: {{ msg }}{{ attr(0) }}"}
)
programmatic_formatter.set_rule(
    "__default__",
    {"levelname": "{{ fg('magenta') }}PROG_DEFAULT{{ attr(0) }}"}
)
```

#### 2.2. CrÃ©ation des Handlers

Les handlers dirigent les messages de log formatÃ©s vers diffÃ©rentes destinations. Chaque handler est configurÃ© avec un niveau minimum, un opÃ©rateur de comparaison, un formatter et des filtres optionnels.

##### ConsoleHandler

Envoie les logs Ã  la console (stdout ou stderr).

```python
# ConsoleHandler: envoie les logs INFO et supÃ©rieurs Ã  la sortie standard
programmatic_console_handler = ConsoleHandler(
    level=INFO,
    formatter=programmatic_formatter,
    output=sys.stdout
)
```

##### FileHandler

Ã‰crit les logs dans un fichier, avec des options de rotation avancÃ©es.

```python
# FileHandler: Ã©crit les logs DEBUG et supÃ©rieurs dans un fichier avec rotation
programmatic_file_handler = FileHandler(
    level=DEBUG,
    formatter=programmatic_formatter,
    filename="logs/programmatic_app.log",
    maxBytes=512,
    backupCount=1
)
```

##### DatabaseHandler

Enregistre les logs dans une base de donnÃ©es. Supporte SQLite, MySQL, etc., avec mappage de colonnes personnalisable.

```python
# DatabaseHandler: enregistre les logs INFO et supÃ©rieurs dans une base SQLite
# Assurez-vous que le fichier DB n'existe pas pour un test propre
if os.path.exists("logs/programmatic_db.db"): os.remove("logs/programmatic_db.db")
programmatic_db_handler = DatabaseHandler(
    level=INFO,
    formatter=programmatic_formatter,
    connector={
        "engine": "sqlite",
        "filename": "logs/programmatic_db.db",
        "table": "prog_logs"
    },
    columns={
        "timestamp": "date('%Y-%m-%d %H:%M:%S')",
        "level": "levelname",
        "message": "msg"
    }
)
```

##### SMTPHandler

Envoie les logs par e-mail via un serveur SMTP.

```python
# SMTPHandler: envoie les logs CRITICAL et supÃ©rieurs par e-mail
programmatic_smtp_handler = SMTPHandler(
    level=CRITICAL,
    formatter=programmatic_formatter,
    host="smtp.mailtrap.io",
    port=2525,
    username="your_mailtrap_username",
    password="your_mailtrap_password",
    fromaddr="programmatic@example.com",
    toaddrs=["admin@programmatic.com"],
    subject="[PROG] ALERTE CRITIQUE: {{ levelname }} de {{ name }}"
)
```

##### ResendHandler

Envoie les logs par e-mail via l'API Resend.

```python
# ResendHandler: envoie les logs ERROR et supÃ©rieurs via l'API Resend
programmatic_resend_handler = ResendHandler(
    level=ERROR,
    formatter=programmatic_formatter,
    api_key="re_YOUR_RESEND_API_KEY",
    fromaddr="onboarding@programmatic.dev",
    to=["dev@programmatic.dev"],
    subject="[PROG] ERREUR: {{ levelname }} de {{ name }}"
)
```

#### 2.3. CrÃ©ation du Logger

Un logger est le point d'entrÃ©e pour enregistrer vos messages. Il regroupe un ensemble de handlers.

```python
# CrÃ©ation d'un logger et association des handlers
programmatic_logger = Logger(name="programmatic_logger", handlers=[
    programmatic_console_handler,
    programmatic_file_handler,
    programmatic_db_handler,
    programmatic_smtp_handler,
    programmatic_resend_handler
])
```

#### 2.4. Utilisation du Logger

Une fois configurÃ©, utilisez le logger pour enregistrer vos messages.

```python
# Enregistrement de messages de diffÃ©rents niveaux
programmatic_logger.info("Ceci est un message info du logger programmatique.")
programmatic_logger.warning("Ceci est un message d'avertissement du logger programmatique.")
programmatic_logger.error("Ceci est un message d'erreur du logger programmatique.")
programmatic_logger.info("Ce message contient des informations secrÃ¨tes du logger programmatique.") # Devrait Ãªtre filtrÃ© par le ConsoleHandler
programmatic_logger.critical("Ceci est un message critique qui devrait envoyer un e-mail.")

# Exemple de vÃ©rification: lecture des logs depuis la base de donnÃ©es
prog_db_engine = create_engine("sqlite:///logs/programmatic_db.db")
with prog_db_engine.connect() as conn:
    result = conn.execute(text("SELECT timestamp, level, message FROM prog_logs ORDER BY timestamp DESC LIMIT 3"))
    print("\n--- Derniers 3 logs de la DB programmatique ---")
    for row in result:
        print(f"Timestamp: {row.timestamp}, Level: {row.level}, Message: {row.message}")
```


## Modification Dynamique

Les objets `Logger`, `CustomFormatter` et les instances de `BaseHandler` (et ses sous-classes) exposent des mÃ©thodes pour modifier leur comportement aprÃ¨s leur crÃ©ation.

### Modification des Formatters

```python
# Supposons 'my_formatter' est une instance de CustomFormatter
# my_formatter = CustomFormatter(...)

# Ajouter/Modifier une rÃ¨gle
my_formatter.set_rule(
    "levelname == 'ERROR'",
    {"levelname": "{{ fg('red') }}DYNAMIC_ERROR{{ attr(0) }}"}
)

# Supprimer une rÃ¨gle
my_formatter.delete_rule("levelname == 'WARNING'")

# Les logs suivants utiliseront les rÃ¨gles modifiÃ©es
# programmatic_logger.error("Un message d'erreur dynamique.")
```

### Modification des Handlers

```python
# Supposons 'my_handler' est une instance de ConsoleHandler, FileHandler, etc.
# my_handler = ConsoleHandler(...)

# Changer le niveau
my_handler.set_level(DEBUG)

# Changer l'opÃ©rateur de comparaison
my_handler.set_ops('==') # N'acceptera que les messages de niveau DEBUG

# Ajouter un filtre
my_handler.add_filter("'nouvel_element' not in msg")

# Supprimer un filtre
my_handler.remove_filter("'secret' not in msg")

# Changer le formatter (si le handler a Ã©tÃ© crÃ©Ã© avec un formatter)
# my_handler.set_formatter(un_autre_formatter)
```

### Modification des Loggers

```python
# Supposons 'my_logger' est une instance de Logger
# my_logger = Logger(...)

# Ajouter un handler
my_logger.add_handler(programmatic_file_handler)

# Supprimer un handler
my_logger.remove_handler(programmatic_console_handler)
```

## Fonctions jinja2 Ã©tendues

*   **Fonctions Jinja2 personnalisÃ©es** : Ces fonctions peuvent Ãªtre utilisÃ©es directement dans votre chaÃ®ne de formatage ou dans les rÃ¨gles. Elles permettent d'accÃ©der Ã  des informations contextuelles trÃ¨s riches.

    *   **Formatage et Attributs :**
        *   `fg(color_name)` : Applique une couleur de premier plan au texte suivant. `color_name` peut Ãªtre `black`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`, ou des noms spÃ©cifiques comme `deep_sky_blue_3a` (cyan), `medium_purple_4` (magenta), `grey_46` (blanc).
            *   Exemple : "{{ fg('green') }}Mon message en vert{{ attr(0) }}"
        *   `bg(color_name)` : Applique une couleur d'arriÃ¨re-plan au texte suivant. Utilise les mÃªmes `color_name` que `fg()`.
            *   Exemple : "{{ bg('blue') }}Texte sur fond bleu{{ attr(0) }}"
        *   `attr(code)` : Applique des attributs de texte. Le code `0` rÃ©initialise tous les styles (couleur, gras, etc.).
            *   Exemple : "{{ fg('red') }}Texte rouge{{ attr(0) }} Texte normal"

    *   **Date et Heure :**
        *   `date(format_str)` : Formate la date et l'heure actuelles. `format_str` suit les codes de formatage de `strftime` de Python (ex: `%Y-%m-%d %H:%M:%S`).
            *   Exemple : "Log du {{ date('%Y-%m-%d %H:%M') }}"
        *   `epoch()` : Renvoie le timestamp Unix actuel (nombre de secondes depuis l'Ã©poque).
            *   Exemple : "Timestamp: {{ epoch() }}"

    *   **Informations sur le Code Source :**
        *   `exc_info()` : Renvoie les informations sur l'exception courante (type, valeur, traceback). Utile dans les blocs `try...except`.
            *   Exemple : "Exception: {{ exc_info() }}"
        *   `filename()` : Renvoie le nom du fichier Python oÃ¹ l'appel de log a Ã©tÃ© effectuÃ©.
            *   Exemple : "Fichier: {{ filename() }}"
        *   `filepath()` : Renvoie le chemin absolu du rÃ©pertoire contenant le fichier Python oÃ¹ l'appel de log a Ã©tÃ© effectuÃ©.
            *   Exemple : "Chemin du fichier: {{ filepath() }}"
        *   `lineno()` : Renvoie le numÃ©ro de ligne dans le fichier Python oÃ¹ l'appel de log a Ã©tÃ© effectuÃ©.
            *   Exemple : "Ligne: {{ lineno() }}"
        *   `functname()` : Renvoie le nom de la fonction ou mÃ©thode Python oÃ¹ l'appel de log a Ã©tÃ© effectuÃ©.
            *   Exemple : "Fonction: {{ functname() }}"

    *   **Informations sur le SystÃ¨me de Fichiers / Chemin :**
        *   `path()` : Renvoie le chemin absolu du rÃ©pertoire de travail actuel.
            *   Exemple : "CWD: {{ path() }}"

    *   **Informations sur le Processus :**
        *   `process()` : Renvoie le nom du processus Python actuel.
            *   Exemple : "Processus: {{ process() }}"
        *   `processid()` : Renvoie l'ID du processus Python actuel.
            *   Exemple : "PID: {{ processid() }}"

    *   **Informations sur l'Utilisateur :**
        *   `username()` : Renvoie le nom d'utilisateur du systÃ¨me.
            *   Exemple : "Utilisateur: {{ username() }}"
        *   `uid()` : Renvoie l'ID utilisateur (UID) du systÃ¨me.
            *   Exemple : "UID: {{ uid() }}"

    *   **Informations sur le SystÃ¨me d'Exploitation :**
        *   `os_name()` : Renvoie le nom du systÃ¨me d'exploitation (ex: `Windows`, `Linux`, `Darwin`).
            *   Exemple : "OS: {{ os_name() }}"
        *   `os_version()` : Renvoie la version dÃ©taillÃ©e du systÃ¨me d'exploitation.
            *   Exemple : "Version OS: {{ os_version() }}"
        *   `os_release()` : Renvoie la version du systÃ¨me d'exploitation (ex: `10`, `20.04`).
            *   Exemple : "Release OS: {{ os_release() }}"
        *   `platform()` : Renvoie une chaÃ®ne d'identification de la plateforme (ex: `Windows-10-10.0.19045-SP0`).
            *   Exemple : "Plateforme: {{ platform() }}"
        *   `os_archi()` : Renvoie l'architecture du systÃ¨me d'exploitation (ex: `64bit`).
            *   Exemple : "Architecture OS: {{ os_archi() }}"

    *   **Informations sur la MÃ©moire (RAM) :**
        *   `mem_total()` : MÃ©moire RAM totale du systÃ¨me.
        *   `mem_available()` : MÃ©moire RAM disponible.
        *   `mem_used()` : MÃ©moire RAM utilisÃ©e.
        *   `mem_free()` : MÃ©moire RAM libre.
        *   `mem_percent()` : Pourcentage de mÃ©moire RAM utilisÃ©e.
            *   Exemple : "RAM: {{ mem_used() }} / {{ mem_total() }} ({{ mem_percent() }}%)"

    *   **Informations sur la MÃ©moire Swap :**
        *   `swap_total()` : MÃ©moire Swap totale.
        *   `swap_used()` : MÃ©moire Swap utilisÃ©e.
        *   `swap_free()` : MÃ©moire Swap libre.
        *   `swap_percent()` : Pourcentage de mÃ©moire Swap utilisÃ©e.
            *   Exemple : "Swap: {{ swap_used() }} / {{ swap_total() }} ({{ swap_percent() }}%)"

    *   **Informations sur le CPU :**
        *   `cpu_count()` : Nombre de cÅ“urs physiques du CPU.
        *   `cpu_logical_count()` : Nombre de cÅ“urs logiques (incluant les threads) du CPU.
        *   `cpu_percent()` : Pourcentage d'utilisation du CPU (sur un court intervalle).
            *   Exemple : "CPU: {{ cpu_percent() }}% ({{ cpu_logical_count() }} cÅ“urs)"

    *   **Informations sur le Disque Actuel (du fichier de log) :**
        *   `current_disk_device()` : Nom du pÃ©riphÃ©rique de disque (ex: `C:\`).
        *   `current_disk_mountpoint()` : Point de montage du disque.
        *   `current_disk_fstype()` : Type de systÃ¨me de fichiers (ex: `NTFS`).
        *   `current_disk_total()` : Taille totale du disque.
        *   `current_disk_used()` : Espace utilisÃ© sur le disque.
        *   `current_disk_free()` : Espace libre sur le disque.
        *   `current_disk_percent()` : Pourcentage d'utilisation du disque.
            *   Exemple : "Disque ({{ current_disk_device() }}): {{ current_disk_used() }} / {{ current_disk_total() }} ({{ current_disk_percent() }}%)"

    *   **Fonctions Utilitaires :**
        *   `re_match(regex_pattern, value)` : Tente de faire correspondre `regex_pattern` au dÃ©but de `value`. Renvoie un objet match si trouvÃ©, `None` sinon.
            *   Exemple : "Match: {{ re_match('^(Error|Warning)', levelname) is not none }}"


## ExtensibilitÃ©

Le module est conÃ§u pour Ãªtre facilement extensible :
*   **Nouveaux Handlers** : CrÃ©ez de nouvelles classes hÃ©ritant de `BaseHandler` et implÃ©mentez la mÃ©thode `emit`. Ajoutez-les Ã  `_handler_class_map`.
*   **Nouvelles Fonctions Jinja2** : DÃ©finissez de nouvelles fonctions Python et ajoutez-les aux `globals` des environnements Jinja2 (`env`, `filter_env`, `filename_env`, etc.) oÃ¹ vous souhaitez les utiliser.
*   **Nouveaux Types de Colonnes DB** : Ã‰tendez `DatabaseHandler` pour supporter plus de types de colonnes SQLAlchemy.

---
