v0.1.2-pre.001-fix.001

This commit is contained in:
2026-08-14 17:52:34 +02:00
parent 18ae0e4873
commit bd8401c0d0
5 changed files with 554 additions and 222 deletions

View File

@@ -0,0 +1,162 @@
<!-- file: deltas/0.1.2/pre.001-fix.001.md -->
<!-- version: 1 -->
# Delta 0.1.2-pre.001-fix.001
## Base requise
Livraison précédente :
```text
0.1.2-pre.001
```
Ce correctif est documentaire et corrige le plan de `pre.001` sans réécrire son delta historique.
## Objectif
Corriger le cadrage Logging avant validation du plan afin de fixer :
- le takeover complet du logging/tracing KSP par `ksp-logging-lib` ;
- le target KSP explicite égal au nom Cargo de la crate propriétaire ;
- le silence par défaut des targets tiers et la réémission explicite des informations utiles par le composant KSP propriétaire ;
- console et fichier non bloquants avec guards et compteurs de lignes abandonnées ;
- le stripping ANSI des fichiers ;
- un `initialize` global unique suivi d'un hot reload via `reinitialize` sans second subscriber global ;
- une surface de spans KSP synchrones et async avec diagnostic de durée `NEW/CLOSE`, `busy` et `idle` ;
- la responsabilité des données loggées au caller, sans détection/redaction automatique par Logging.
Aucun développement fonctionnel de `ksp-logging-lib` n'est introduit par ce fix.
## Décisions prises
### Takeover tracing
`ksp-logging-lib` devient le seul propriétaire KSP direct de la stack tracing et la seule façade autorisée pour les événements/spans KSP.
Le subscriber applique une politique de takeover :
```text
external targets = Off by default
ksp-* targets = configured KSP default
specific ksp-* = optional override
```
KSP ne renomme pas un événement tiers. Lorsqu'un détail provenant d'une dépendance externe est utile, la crate KSP propriétaire le réémet sous son propre target.
Exemple attendu pour Store : les logs SQLx natifs sont désactivés/silencieux ; les opérations SQL utiles sont journalisées explicitement par `ksp-store-lib`, typiquement au niveau `trace`.
### Targets
Les macros événements et spans exigent un target explicite correspondant au nom Cargo de la crate propriétaire. `domain`, `component` et autres fields restent des subdivisions structurées, pas des remplacements du target.
### Settings runtime
`LoggingSettings` reste propriétaire de Logging et indépendant de Config. Il couvre niveau KSP default, overrides de target, sorties console/fichier et politique d'événements de spans.
Une future `ksp-config-lib` pourra construire ces settings puis appeler la façade Logging.
### Non-blocking
Console et fichier utilisent des writers non bloquants avec leurs `WorkerGuard` possédés par `LoggingGuard`.
Le mode retenu privilégie l'absence de backpressure sur le hot path : une saturation peut abandonner des lignes. Les `ErrorCounter` sont conservés afin que ces pertes restent observables.
### Stripping ANSI
Les fichiers passent par un stripping ANSI générique avant persistence. Logging ne dépend pas de Tauri ; cette protection évite seulement de persister des séquences de terminal déjà présentes dans les données écrites.
### Initialisation et hot reload
`initialize(settings)` installe le subscriber global une seule fois et retourne `LoggingGuard`.
Après succès, `reinitialize(&mut guard, settings)` ou une méthode équivalente peut être appelée 0..N fois. Elle ne réinstalle pas le subscriber global ; elle modifie les filters/layers/sinks de l'infrastructure déjà installée.
Le reload vise une sémantique transactionnelle : une nouvelle configuration invalide ou impossible à construire laisse l'ancienne configuration active.
Le mécanisme interne exact (`tracing_subscriber::reload` ciblé ou routing KSP dynamique) sera choisi par implémentation/tests selon correction et overhead, sans modifier le contrat public.
### Spans sync/async et durée
`0.1.2` inclut désormais une surface de spans KSP par niveau, sans dépendance directe `tracing` dans les crates consommatrices.
Le code sync doit pouvoir exécuter un scope dans un span. Le code async doit instrumenter la `Future` elle-même et ne pas maintenir un enter guard à travers `.await`.
Les settings permettent au minimum `Off` et `NewAndClose`; `NEW | CLOSE` fournit des repères de début/fin et, lorsque les timestamps sont actifs, le close fournit `busy`/`idle`. Cette capacité sert au diagnostic rapide de latence/blocage et n'est pas présentée comme un benchmark de précision absolue.
### Contenu sensible
`ksp-logging-lib` n'essaie pas de détecter ou redacter automatiquement les données sensibles. La crate appelante est responsable du contenu qu'elle choisit de logger.
## Fichiers ajoutés
- `deltas/0.1.2/pre.001-fix.001.md`
## Fichiers modifiés
- `docs/plans/004-V0_1_2_LOGGING_FOUNDATION_PLAN.md`
- `docs/rules/RULES_DEPENDENCIES.md`
- `docs/architecture/003-COMPONENT_CONTRACTS.md`
- `docs/architecture/005-DEPENDENCY_GRAPH.md`
## Fichiers supprimés
Aucun.
## Version Cargo
Aucune modification de `Cargo.toml`.
Ce fix est limité à la documentation et respecte `VER-ID-008` : `workspace.package.version` reste donc :
```text
0.1.2-pre.1
```
L'identifiant de livraison est :
```text
0.1.2-pre.001-fix.001
```
## Validations exécutées
- vérification du delta `pre.001` fourni et des règles de version/delta/archive de la base `0.1.1` ;
- vérification de la documentation officielle `tracing` indiquant que le subscriber global ne peut être installé qu'une fois ;
- vérification de `tracing-subscriber::reload` pour le remplacement runtime d'une Layer/Filter ;
- vérification de l'avertissement officiel contre `Span::enter()` conservé à travers `.await` ;
- vérification de l'instrumentation de `Future` fournie par `tracing::Instrument` ;
- vérification de `FmtSpan::NEW | FmtSpan::CLOSE` et des champs `busy`/`idle` au close lorsque les timestamps sont actifs ;
- vérification du writer non bloquant, de `WorkerGuard` et `ErrorCounter` dans `tracing-appender 0.2.5` ;
- contrôle des headers `file:` / `version:` des fichiers livrés ;
- contrôle de l'absence de modification Cargo dans ce fix documentaire ;
- contrôle du contenu de l'archive selon `VER-ARCHIVE-004`.
## Validations non exécutées
Aucune validation Cargo n'est applicable à ce correctif documentaire et aucun code fonctionnel Logging n'existe encore dans la livraison.
Les commandes suivantes restent à exécuter dès que les tranches de développement les rendent applicables :
```bash
cargo fmt --all
cargo check --workspace
cargo test --workspace
cargo clippy --workspace --all-targets
cargo tree -p ksp-logging-lib
cargo tree -p ksp-logging-lib -d
cargo tree -p ksp-logging-lib -e features
```
## Questions ouvertes
Aucune question architecturale bloquante.
Restent à trancher par implémentation/tests dans les prereleases suivantes :
- mécanisme exact des macros spans/events préservant le callsite sans fuite de types `tracing` ;
- abstraction KSP exacte pour instrumenter les futures async ;
- composition reloadable interne la moins coûteuse ;
- API exacte d'observation des dropped lines.
Après validation de ce fix, la prochaine tranche reste `0.1.2-pre.002`.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/architecture/003-COMPONENT_CONTRACTS.md -->
<!-- version: 10 -->
<!-- version: 11 -->
# Contrats initiaux des composants KSP
@@ -80,11 +80,17 @@ Le retry d'un appel réseau identique reste une responsabilité transport, disti
`ksp-logging-lib` est la façade KSP unique pour le logging/tracing runtime. Elle importe/initialise directement `tracing`, `tracing-appender` et `tracing-subscriber` et peut dépendre de `ksp-core-lib` pour `Error` / `Result`.
Les crates comportementales KSP peuvent dépendre directement de `ksp-logging-lib` et utilisent sa façade pour `error`, `warn`, `info`, `debug` et `trace` avec target/domain/champs structurés selon l'API finale.
Les crates comportementales KSP utilisent sa façade pour leurs événements `error`, `warn`, `info`, `debug`, `trace` et pour leurs spans sync/async. Elles n'émettent pas leurs propres logs via une dépendance directe à la stack tracing.
Chaque émission KSP indique un target correspondant au nom Cargo de la crate propriétaire ; `domain`, `component` et les autres champs structurés décrivent les subdivisions fonctionnelles sans multiplier les targets.
Le subscriber KSP rend les targets tiers silencieux par défaut. Lorsqu'une information provenant d'une dépendance externe est nécessaire, la crate KSP qui possède l'opération la réémet explicitement sous son propre target ; Logging ne renomme pas les événements tiers.
Logging possède ses `LoggingSettings`, ses writers/guards et son lifecycle. Le subscriber global est installé une fois, puis la configuration peut être rechargée à chaud via la façade KSP sans dépendance vers Config.
`ksp-core-lib` n'a pas de dépendance logging requise. Les crates `*-api` purement déclaratives restent sans logging par défaut.
Une application Tauri peut exceptionnellement avoir une dépendance/framework tracing imposée par un plugin, sans définir une politique parallèle à `ksp-logging-lib`.
Une application Tauri peut exceptionnellement avoir une dépendance/framework tracing imposée par un plugin, sans définir une politique parallèle à `ksp-logging-lib`; les événements KSP restent émis via la façade KSP.
## Frontière materializer / store

View File

@@ -1,5 +1,5 @@
<!-- file: docs/architecture/005-DEPENDENCY_GRAPH.md -->
<!-- version: 7 -->
<!-- version: 8 -->
# Graphe de dépendances KSP
@@ -100,9 +100,11 @@ runtime crate
-> ksp-logging-lib
```
`ksp-logging-lib` est la seule façade KSP propriétaire de l'initialisation/configuration tracing. Une crate runtime ne dépend normalement pas directement de `tracing`.
`ksp-logging-lib` est la seule façade KSP propriétaire de l'initialisation/configuration tracing. Une crate runtime KSP n'utilise pas directement `tracing`, `tracing-subscriber` ou `tracing-appender` pour émettre ses propres événements/spans.
Exception technique possible : une application/framework, notamment Tauri, peut devoir intégrer un plugin tracing. Cette adaptation ne crée pas une seconde politique de logging parallèle.
Le subscriber global KSP est installé une seule fois puis sa configuration peut être rechargée à chaud par `ksp-logging-lib`. Les targets externes sont silencieux par défaut ; les informations tierces utiles sont réémises par le composant KSP propriétaire sous son propre target.
Exception technique possible : une application/framework, notamment Tauri, peut devoir intégrer un plugin tracing. Cette adaptation ne crée pas une seconde politique de logging parallèle et les événements KSP restent émis via la façade KSP.
`ksp-core-lib` et les crates `*-api` purement déclaratives n'ont pas de dépendance logging obligatoire.

View File

@@ -1,13 +1,13 @@
<!-- file: docs/plans/004-V0_1_2_LOGGING_FOUNDATION_PLAN.md -->
<!-- version: 1 -->
<!-- version: 2 -->
# Plan KSP 0.1.2 — Logging foundation
## Statut
Plan actif de `0.1.2`, établi par `0.1.2-pre.001`.
Plan actif de `0.1.2`, établi par `0.1.2-pre.001` et corrigé par `0.1.2-pre.001-fix.001`.
`pre.001` reste une tranche de brainstorming, audit et planification. Aucun développement fonctionnel de `ksp-logging-lib` n'est commencé avant validation de ce plan.
`pre.001` et son fix restent une tranche de brainstorming, audit et planification. Aucun développement fonctionnel de `ksp-logging-lib` n'est commencé avant validation de ce plan corrigé.
## Base auditée
@@ -31,20 +31,22 @@ Dans le workflow KSP, l'archive fournie provient directement du tag Gitea corres
## Mission bornée
`0.1.2` introduit `ksp-logging-lib` comme façade KSP commune de logging/tracing et propriétaire de la politique runtime correspondante.
`0.1.2` introduit `ksp-logging-lib` comme façade KSP commune de logging/tracing et propriétaire exclusif de la politique runtime correspondante.
La release doit fournir :
1. une surface d'émission KSP pour `error`, `warn`, `info`, `debug` et `trace` ;
2. des settings runtime propres à Logging, indépendants de Config ;
3. une initialisation globale déterministe ;
4. une sortie console ;
5. une sortie fichier optionnelle avec rotation bornée et writer non bloquant ;
6. un filtrage global et par préfixe de `target` ;
7. un lifecycle explicite conservant les guards nécessaires aux writers non bloquants ;
8. un contrat d'erreurs basé sur `ksp-core-lib` ;
9. une politique documentée contre les fuites de secrets ;
10. les tests de callsite, settings, filtering, initialisation, fichiers et façade publique nécessaires à la stabilisation.
2. une surface KSP de spans pour instrumenter des scopes synchrones et des `Future` async sans dépendance `tracing` directe chez les consumers ;
3. des settings runtime propres à Logging, indépendants de Config, permettant de choisir niveau, console, fichier et paramètres associés ;
4. une installation globale unique du subscriber KSP ;
5. une reconfiguration à chaud après initialisation, sans redémarrer le processus, worker ou service appelant ;
6. une sortie console non bloquante ;
7. une sortie fichier optionnelle non bloquante avec rotation bornée ;
8. un filtrage KSP global et par target, avec silence par défaut des targets externes ;
9. un lifecycle explicite conservant les guards nécessaires aux writers non bloquants et au reload ;
10. un stripping ANSI des sorties fichier afin de ne pas persister des séquences de terminal injectées par un framework ou une couche d'adaptation ;
11. un contrat d'erreurs basé sur `ksp-core-lib` ;
12. les tests de callsite, spans sync/async, settings, filtering, takeover, initialisation, hot reload, console, fichiers et façade publique nécessaires à la stabilisation.
La release ne lit aucun document JSON/TOML et n'introduit aucune dépendance vers Config ou une couche supérieure.
@@ -62,13 +64,20 @@ ksp-logging-lib
Règles :
- `ksp-logging-lib` est le seul propriétaire KSP de l'initialisation et de la politique de subscriber/appender ;
- `ksp-logging-lib` est le seul propriétaire KSP direct de `tracing`, `tracing-subscriber`, `tracing-appender`, de l'installation du subscriber et de la politique de logging ;
- les crates KSP comportementales émettent leurs événements et spans exclusivement via la façade de `ksp-logging-lib` ;
- une crate KSP runtime ne dépend pas directement de `tracing`, `tracing-subscriber` ou `tracing-appender` pour produire ses propres logs ;
- `ksp-core-lib` ne dépend pas de Logging ;
- `ksp-logging-lib` ne dépend pas de `ksp-config-lib` ;
- `ksp-logging-lib` ne dépend pas de Wallet, Transport, Program, Store, workers, jobs, pipelines ou applications ;
- les crates comportementales KSP peuvent plus tard dépendre directement de `ksp-logging-lib` ;
- les crates `*-api` purement déclaratives restent sans Logging par défaut ;
- une future intégration Tauri imposée par un plugin reste un adaptateur d'application et ne redéfinit pas la politique KSP.
- les événements produits directement par les dépendances externes sont silencieux par défaut au niveau du subscriber KSP ;
- lorsqu'une information provenant d'une dépendance externe est utile, la crate KSP propriétaire de l'opération la réémet explicitement sous son propre target KSP au niveau approprié ;
- KSP ne renomme pas ou ne réécrit pas les targets d'événements tiers après émission ;
- lorsqu'une dépendance externe offre son propre mécanisme pour désactiver ses logs verbeux, la crate KSP propriétaire doit l'utiliser en plus du filtering central lorsqu'il est pertinent ;
- une future intégration Tauri imposée par un plugin reste un adaptateur d'application et ne redéfinit pas la politique KSP ; les logs KSP écrits par l'application restent émis via `ksp-logging-lib`.
Exemple de politique future pour Store : les logs SQLx natifs sont désactivés/silencieux ; `ksp-store-lib` réémet uniquement les opérations utiles avec `target = "ksp-store-lib"`, typiquement au niveau `trace` pour les détails de requêtes.
## Audit de la stack `tracing` au 2026-08-14
@@ -154,7 +163,7 @@ sera utilisé pour vérifier la résolution et l'unification réelles.
## Instrumentation : macros pour préserver le callsite
### Décision
### Événements
Les cinq points d'émission publics seront des macros KSP :
@@ -168,37 +177,74 @@ ksp_logging_lib::trace!
Une fonction wrapper qui appellerait elle-même `tracing::info!` ou équivalent déplacerait les métadonnées source vers le wrapper. Les événements ne seront donc pas émis par des fonctions publiques de ce type.
Les macros KSP resteront minces et délégueront aux macros `tracing` au point d'appel. Leur implémentation exacte — wrapper `macro_rules!` transparent ou réexport suffisamment direct — sera choisie par le plus petit mécanisme qui réussit les tests de callsite sans exposer une seconde API `tracing` aux consommateurs.
Les macros KSP restent minces et délèguent aux primitives `tracing` au point d'appel. Leur implémentation exacte doit préserver le callsite sans obliger le consumer à dépendre directement de `tracing`.
### Syntaxe
### Target obligatoire
La surface doit conserver une syntaxe structurée proche de `tracing` plutôt que créer une grammaire KSP parallèle.
Chaque émission KSP comportementale fournit explicitement un `target:` correspondant au nom Cargo de la crate propriétaire, par exemple :
```text
ksp-store-lib
ksp-wallet-lib
ksp-onchain-transport-lib
```
Une constante crate-level peut être utilisée si elle respecte les contraintes compile-time des macros retenues ; le mécanisme exact sera fixé par tests.
Exemple conceptuel :
```rust
ksp_logging_lib::info!(
target: "ksp.wallet",
domain = "wallet",
component = "repository",
slot = slot,
"wallet state updated"
ksp_logging_lib::trace!(
target: crate::TRACING_TARGET,
domain = "store",
component = "postgres",
operation = "load_transactions",
"executing store query"
);
```
Les tests publics fixeront uniquement la syntaxe réellement retenue avant stabilisation.
Le target implicite généré depuis le module Rust n'est pas une surface KSP valide pour les crates comportementales.
### Callsite à tester
### Spans
Un test d'intégration extérieur au `src` doit capturer les `Metadata` d'un événement émis par une macro KSP et vérifier au minimum :
`0.1.2` doit également exposer une surface KSP de spans avec les niveaux usuels, conceptuellement :
```text
ksp_logging_lib::error_span!
ksp_logging_lib::warn_span!
ksp_logging_lib::info_span!
ksp_logging_lib::debug_span!
ksp_logging_lib::trace_span!
```
Les spans suivent la même règle de target explicite que les événements et doivent préserver leur callsite réel.
La façade ne doit pas obliger les consumers à importer `tracing::Span` ou `tracing::Instrument`. Une abstraction KSP de span et/ou des helpers KSP d'instrumentation seront retenus par l'implémentation la plus petite qui conserve cette isolation.
Pour le code synchrone, la surface doit permettre d'associer un scope d'exécution au span sans déplacer le callsite.
Pour le code async, la surface doit instrumenter la `Future` elle-même. Un guard issu de `Span::enter()` ne doit pas être maintenu à travers un `.await`, car cela produit des traces incorrectes lorsque l'exécution change de tâche/thread ou que la future yield.
### Mesure de durée
Le formatter doit pouvoir synthétiser au besoin les événements de lifecycle `NEW` et `CLOSE` des spans. Avec les timestamps actifs, `CLOSE` fournit le temps `busy` et `idle` calculé par `tracing-subscriber`; `NEW | CLOSE` donne rapidement un repère de début, de fin et de durée d'activité du span.
Cette capacité est destinée au diagnostic rapide de latence et de blocage, y compris pour des futures async. Elle n'est pas présentée comme un benchmark micro/nanoseconde de précision absolue.
Le contrôle exact de l'émission des événements de lifecycle appartient aux settings Logging afin de ne pas créer de bruit lorsqu'aucun diagnostic de timing n'est demandé.
### Callsites à tester
Les tests d'intégration extérieurs au `src` doivent capturer les `Metadata` d'événements et de spans émis par la façade KSP et vérifier au minimum :
- le fichier source est celui du consommateur/test et non un fichier de `ksp-logging-lib` ;
- le module path correspond au consommateur ;
- le numéro de ligne correspond à l'invocation ;
- le target implicite reste celui du callsite/module lorsque le caller n'en fournit pas ;
- un `target:` explicite est conservé tel quel.
- le `target:` explicite est conservé tel quel ;
- les macros refusent ou ne documentent pas une voie KSP sans target explicite ;
- la surface async instrumente correctement une future sans conserver un enter guard à travers un `.await`.
La macro n'est pas considérée stabilisée avant réussite de ce test.
La façade n'est pas considérée stabilisée avant réussite de ces tests.
## Niveaux KSP
@@ -234,14 +280,22 @@ Aucun type `tracing::Level` ou `tracing_subscriber::filter::LevelFilter` n'est e
### `target`
`target` reste la métadonnée native de routage/filtering de `tracing`.
`target` est la métadonnée native de routage/filtering de `tracing`, mais KSP impose une convention plus stricte :
Politique initiale :
- toute crate KSP comportementale émet avec un `target:` explicite ;
- ce target correspond au nom Cargo de la crate propriétaire de l'événement ;
- il n'est pas utilisé pour représenter chaque sous-module ou opération ;
- les sous-domaines et composants sont décrits par des champs structurés ;
- les targets externes ne sont pas des targets KSP et restent silencieux par défaut.
- sans override, conserver le target naturel correspondant au module/callsite Rust ;
- permettre un `target:` statique explicite lorsque le caller a besoin d'une identité de routage durable ;
- ne pas imposer dès `0.1.2` une taxonomie globale de targets pour toutes les futures crates ;
- filtrer les targets par préfixe.
Exemple :
```text
target = "ksp-store-lib"
domain = "store"
component = "postgres"
operation = "load_transactions"
```
### `domain`
@@ -257,33 +311,50 @@ Il n'est ni obligatoire ni déduit du nom de crate.
### Autres champs
Les callers peuvent émettre d'autres champs structurés utiles au contexte : slot, signature publique, Program ID public, opération, état, compteur, durée, etc., sous réserve de la politique secrets.
Les callers peuvent émettre d'autres champs structurés utiles au contexte : slot, signature publique, Program ID public, opération, état, compteur, durée, etc.
Aucune liste fermée de champs n'est introduite.
Aucune liste fermée de champs n'est introduite. Le contenu demandé à logger appartient à la responsabilité de la crate appelante ; `ksp-logging-lib` ne tente pas d'interpréter ou de classifier arbitrairement les valeurs reçues.
## Filtering retenu
### Takeover KSP
La première règle du subscriber est de supprimer le bruit externe :
```text
global/external default = Off
KSP owned targets = configured KSP default level
specific KSP target = optional override
```
La famille KSP est identifiée par la convention de targets correspondant aux noms de crates `ksp-*`.
Exemple conceptuel :
```text
external default = Off
ksp-* = Info
ksp-store-lib = Trace
ksp-wallet-lib = Debug
```
Ainsi, un événement direct `sqlx`, `hyper`, `rustls` ou autre ne devient pas visible simplement parce que son niveau est `info` ou `debug`.
Lorsqu'un détail SQL est utile, Store le réémet explicitement sous `ksp-store-lib`; Logging ne transforme jamais `sqlx` en `ksp-store-lib`.
### Première surface
Le filtering initial comprend :
1. un niveau global/default ;
2. zéro ou plusieurs overrides par préfixe de target.
1. un niveau KSP default ;
2. zéro ou plusieurs overrides par target/préfixe KSP ;
3. un niveau global externe fixé à `Off` par la politique de takeover.
Exemple conceptuel :
```text
default = Info
ksp_program_lib = Debug
ksp_program_lib::decoder = Trace
noisy_dependency = Off
```
La sémantique s'appuiera sur `tracing_subscriber::filter::Targets`.
La sémantique de niveau/target pourra s'appuyer sur `tracing_subscriber::filter::Targets` tant que les tests confirment qu'elle satisfait le hot reload retenu.
### Pourquoi pas `EnvFilter`
`EnvFilter` permet une syntaxe et un filtrage plus dynamiques, notamment par span/champs, mais active des dépendances/features supplémentaires (`matchers`, `regex-automata`, `once_cell`, etc.).
`EnvFilter` apporte une syntaxe et un filtrage plus dynamiques, notamment par span/champs, mais active des dépendances/features supplémentaires (`matchers`, `regex-automata`, `once_cell`, etc.).
Aucun besoin de cette puissance n'est démontré pour la fondation `0.1.2`.
@@ -292,9 +363,9 @@ Conséquences :
- pas de parsing de `RUST_LOG` par Logging ;
- pas de directive textuelle `EnvFilter` dans les settings ;
- pas de filtre sur `domain`/`component` en tant que champs ;
- pas de reload dynamique du filtre dans cette release.
- le hot reload modifie la représentation typée KSP et les couches/filters internes, pas une chaîne `RUST_LOG`.
Config pourra ultérieurement convertir sa propre représentation validée vers les settings typés de Logging sans rendre `EnvFilter` public.
Config pourra ultérieurement convertir sa propre représentation validée vers les settings typés de Logging.
## Settings runtime
@@ -307,19 +378,21 @@ Ils ne :
- lisent aucun fichier ;
- ne consultent aucune variable d'environnement ;
- ne connaissent aucun profil Config ;
- ne contiennent aucun secret ;
- n'exposent aucun type `tracing*` public.
`ksp-config-lib` pourra plus tard lire/résoudre ses documents puis construire explicitement un `LoggingSettings` et appeler `initialize` ou `reinitialize`.
Les structures publiques utilisent des champs privés avec constructeurs/getters/builder methods explicites, conformément à la direction déjà utilisée dans Core.
### Surface conceptuelle
La surface de `pre.002`/`pre.003` doit rester proche de :
La surface doit rester proche de :
```text
LoggingSettings
default_filter: LogFilterLevel
target_filters: Vec<TargetFilter>
span_events: SpanEvents
console: Option<ConsoleSettings>
file: Option<FileSettings>
@@ -327,6 +400,11 @@ TargetFilter
target_prefix: String
level: LogFilterLevel
SpanEvents
Off
NewAndClose
Full
ConsoleSettings
output: ConsoleOutput
@@ -345,43 +423,49 @@ FileRotation
Daily
```
Les noms exacts peuvent être ajustés pendant l'implémentation uniquement pour respecter l'ergonomie Rust et les règles du dépôt ; les responsabilités ne doivent pas dériver sans correction du plan.
`SpanEvents::NewAndClose` correspond au diagnostic de timing demandé ; `Full` reste disponible si l'observation des transitions enter/exit est utile pour un diagnostic plus détaillé. Les noms exacts peuvent être ajustés pendant l'implémentation sans modifier les responsabilités.
### Defaults
Aucun `Default` implicite de `LoggingSettings` n'est requis dans la première surface.
Le caller choisit explicitement son niveau global et ses sorties. Cela évite qu'une application obtienne silencieusement une politique de production non décidée.
Le caller choisit explicitement son niveau KSP global et ses sorties. Le niveau global des targets externes reste `Off` par politique et n'est pas rendu configurable dans cette première surface.
`ConsoleSettings` pourra offrir des constructeurs explicites `stdout()` / `stderr()` si cela simplifie l'API sans ambiguïté.
### Validation
`initialize()` doit refuser au minimum :
`initialize()` et `reinitialize()` doivent refuser au minimum :
- une configuration sans aucune sortie active ;
- un préfixe de target vide lorsqu'il est fourni comme override ;
- un préfixe de target vide ;
- un override de target qui ne correspond pas à la convention KSP retenue ;
- un préfixe de fichier vide si le backend retenu ne peut pas le traiter sans ambiguïté.
Les autres invariants seront ajoutés uniquement s'ils correspondent à une erreur réelle du backend ou à une ambiguïté de contrat.
## Console
La première surface retient une sortie console synchrone via le writer standard choisi :
La sortie console est construite avec un writer `tracing-appender` non bloquant, comme la sortie fichier.
```text
Stdout
Stderr
Stdout | Stderr
-> NonBlocking
-> WorkerGuard
```
Raisons :
Objectifs :
- aucune thread dédiée n'est nécessaire pour le terminal dans la première fondation ;
- l'ownership de `WorkerGuard` reste alors réservé à la sortie fichier non bloquante ;
- l'émission ordinaire d'un log KSP ne doit pas attendre l'I/O terminal ;
- le `WorkerGuard` console est possédé par le lifecycle KSP jusqu'au shutdown/reload approprié ;
- stdout reste disponible pour les applications qui le souhaitent ;
- stderr permet aux futurs exécutables de ne pas mélanger diagnostics et sortie de données sur stdout.
Le format initial est un format humain unique, sans JSON ni ANSI obligatoire. Il doit inclure au minimum :
Le mode de queue doit privilégier le non-blocage du caller : lorsque la queue est saturée, les lignes peuvent être abandonnées plutôt que d'appliquer une backpressure au hot path.
Le compteur `ErrorCounter` correspondant est conservé par le runtime afin que les pertes puissent être observées/inspectées ; l'API publique exacte de cette statistique sera fixée pendant l'implémentation.
Le format initial est un format humain unique. Il doit inclure au minimum :
- timestamp fourni par le formatter standard retenu ;
- niveau ;
@@ -389,7 +473,7 @@ Le format initial est un format humain unique, sans JSON ni ANSI obligatoire. Il
- message/champs structurés ;
- source file/line lorsque la configuration `fmt` retenue le permet sans dépendance supplémentaire.
Le texte exact d'une ligne n'est pas un protocole de sérialisation stable. Les tests doivent contrôler les informations nécessaires, pas figer inutilement tous les espaces/ponctuations du formatter externe.
Le texte exact d'une ligne n'est pas un protocole de sérialisation stable. Les tests contrôlent les informations nécessaires, pas tous les espaces/ponctuations du formatter externe.
## Fichiers, rotation et writer non bloquant
@@ -413,8 +497,6 @@ Sont différés :
- multiples routes fichier indépendantes ;
- formats fichier distincts par route.
Une limite de rétention pourra être ajoutée ultérieurement si l'exploitation réelle le justifie ; elle ne fait pas partie du contrat `0.1.2`.
### Construction sans panic
L'implémentation doit utiliser la forme builder de `RollingFileAppender` qui retourne un `Result` en cas d'échec d'initialisation, et non une API qui panique.
@@ -425,23 +507,25 @@ L'erreur externe utile est enveloppée dans `ksp_core_lib::Error` et conservée
La sortie fichier utilise `tracing_appender::non_blocking::NonBlockingBuilder` afin de déplacer les écritures ordinaires hors du thread appelant.
Le mode retenu pour la première surface est **non-lossy** :
La politique retenue est explicitement **non bloquante pour le caller**. Lorsque la queue est pleine, les lignes peuvent être abandonnées au lieu d'appliquer une backpressure susceptible de bloquer un worker critique.
```text
lossy(false)
```
Ainsi, une saturation de la file applique de la backpressure au lieu de supprimer silencieusement des événements. Cette décision évite d'introduire immédiatement un compteur de lignes perdues et rend le comportement de fiabilité explicite.
Le runtime conserve l'`ErrorCounter` fourni par le writer afin que le nombre de lignes abandonnées reste observable.
La taille de file reste celle du backend tant qu'un besoin réel ne justifie pas de l'exposer dans `LoggingSettings`.
Si les mesures futures démontrent que cette backpressure est inacceptable pour certains workloads, un mode lossy explicite et observable pourra être ajouté dans une release ultérieure ; il ne doit pas être activé silencieusement.
### Stripping ANSI fichier
La sortie fichier passe par un writer KSP de stripping ANSI avant persistence afin de supprimer les séquences de terminal déjà présentes dans les données écrites.
Cette responsabilité reste générique : `ksp-logging-lib` ne dépend pas de Tauri. Elle évite simplement que des séquences ANSI injectées par une couche d'application/framework se retrouvent persistées dans les fichiers.
Le stripping n'est pas présenté comme un mécanisme de redaction de données.
## Lifecycle et ownership du guard
`tracing-appender` retourne un `WorkerGuard` dont la durée de vie contrôle le flush de la file non bloquante.
`tracing-appender` retourne des `WorkerGuard` dont la durée de vie contrôle le flush des files non bloquantes.
KSP possédera ce guard dans un type public de lifecycle :
KSP possède ces guards et les handles de reload dans un type public de lifecycle :
```text
LoggingGuard
@@ -449,48 +533,97 @@ LoggingGuard
Contrat :
- `initialize(&LoggingSettings) -> ksp_core_lib::Result<LoggingGuard>` ;
- si seule la console est active, `LoggingGuard` reste un objet valide sans worker fichier ;
- si le fichier est actif, le guard interne est conservé pendant toute la durée de vie de `LoggingGuard` ;
- l'appelant conserve `LoggingGuard` jusqu'à la fin ordonnée du processus ;
- sa destruction effectue le flush/shutdown fourni par le `WorkerGuard` ;
- aucun `WorkerGuard` n'est stocké dans une variable locale d'initialisation qui serait détruite au retour de `initialize()` ;
- aucune fuite volontaire (`mem::forget`, global mutable caché, etc.) n'est utilisée pour prolonger sa durée de vie.
- `initialize(&LoggingSettings) -> ksp_core_lib::Result<LoggingGuard>` est appelé au plus une fois par processus ;
- console et fichier peuvent chacun posséder leur `WorkerGuard` ;
- `LoggingGuard` conserve les guards actifs, les compteurs de dropped lines et l'état nécessaire au hot reload ;
- l'appelant conserve `LoggingGuard` pendant toute la durée de vie du logging ;
- un reload construit d'abord les nouveaux sinks/filters, puis bascule vers eux, puis flush/détruit les anciens guards ;
- aucun `WorkerGuard` n'est détruit à la fin d'une fonction d'initialisation ou de reconfiguration avant que son sink ne soit effectivement retiré ;
- aucune fuite volontaire (`mem::forget`) n'est utilisée pour prolonger artificiellement la durée de vie.
Le subscriber global de `tracing` n'est pas désinstallable/restartable comme un service ordinaire. Le lifecycle de `0.1.2` est donc volontairement à sens unique :
Le subscriber global reste installé jusqu'à la fin du processus. Le lifecycle KSP devient :
```text
uninitialized -> initialized -> process shutdown
uninitialized
-> initialized(settings A)
-> reinitialized(settings B)
-> reinitialized(settings C)
-> process shutdown
```
Une destruction anticipée de `LoggingGuard` est considérée comme une fin du backend fichier, pas comme une possibilité de réinitialiser Logging ensuite.
La destruction finale de `LoggingGuard` termine/flushe les backends non bloquants mais ne rend pas possible une seconde installation globale.
## Initialisation globale
## Initialisation globale et hot reload
### API
### `initialize`
La fonction publique principale retenue est conceptuellement :
La fonction publique principale reste conceptuellement :
```rust
pub fn initialize(settings: &LoggingSettings) -> ksp_core_lib::Result<LoggingGuard>
```
Elle reste synchrone : installer un subscriber et construire des writers locaux ne constitue pas un workflow I/O async justifiant artificiellement une API `async`.
Elle reste synchrone : installer un subscriber et construire des writers locaux ne justifie pas artificiellement une API `async`.
### Réinitialisation
Le subscriber global est installé une seule fois avec une API fallible. Un deuxième appel à `initialize()` échoue avec une erreur KSP déterministe, que le premier guard soit encore détenu ou non.
L'installation globale doit utiliser une API fallible (`try_init`/équivalent), jamais `init()` lorsqu'elle peut paniquer.
### `reinitialize`
Comportement :
Après un `initialize()` réussi, la configuration peut être remplacée à chaud 0..N fois.
- premier appel valide avec subscriber global libre : succès ;
- second appel après succès : erreur KSP déterministe ;
- appel lorsqu'un autre subscriber global a déjà été installé : erreur KSP ;
- aucune tentative silencieuse de remplacer le subscriber existant ;
- aucune comparaison complexe de settings pour rendre un second appel « idempotent » ;
- aucune reconfiguration globale dynamique dans `0.1.2`.
La forme publique préférée est liée au guard, conceptuellement :
Les tests globaux doivent éviter de se gêner entre eux, par exemple en isolant la validation de l'initialisation globale dans un test/processus dédié et en testant les composants internes avec des subscribers locaux lorsque possible.
```rust
pub fn reinitialize(
guard: &mut LoggingGuard,
settings: &LoggingSettings,
) -> ksp_core_lib::Result<()>
```
ou une méthode équivalente sur `LoggingGuard` si l'ergonomie finale est meilleure.
Cette forme impose naturellement la précondition : un reload n'est possible que si l'initialisation a déjà réussi.
`reinitialize()` ne tente **jamais** d'appeler une seconde fois `set_global_default`/`try_init`. Le subscriber global installé par `initialize()` contient une infrastructure reloadable ; `reinitialize()` modifie ses filters/layers/sinks actifs.
Le mécanisme exact pourra utiliser `tracing_subscriber::reload` pour les éléments appropriés ou une couche KSP dynamique plus spécialisée si les mesures montrent un overhead inférieur. Le contrat public de hot reload ne dépend pas de ce choix interne.
### Sémantique transactionnelle
Le reload vise la propriété suivante :
```text
success -> nouvelle configuration active
error -> ancienne configuration reste active
```
Ordre conceptuel :
1. valider les nouveaux `LoggingSettings` ;
2. construire les nouveaux writers/non-blocking/guards nécessaires ;
3. préparer les nouveaux filters/layers ;
4. basculer la configuration active ;
5. flush puis détruire les anciens guards devenus inutiles.
Une erreur de création du nouveau fichier, writer ou filter ne doit pas couper le logging déjà opérationnel.
`reinitialize()` peut prendre un verrou et attendre un flush ponctuel : cette opération de contrôle n'appartient pas au hot path. En revanche, les appels `error!`/`warn!`/`info!`/`debug!`/`trace!` et l'instrumentation ordinaire restent non bloquants vis-à-vis des I/O.
### Cas d'usage attendu
Une future `ksp-config-lib` pourra recharger son document puis effectuer :
```text
new config
-> new LoggingSettings
-> ksp-logging-lib::reinitialize(...)
```
sans redémarrer un worker/service uniquement pour passer `ksp-store-lib` de `Info` à `Debug`/`Trace`, activer/désactiver le fichier ou modifier ses paramètres.
### Tests globaux
Les tests globaux doivent éviter de se gêner entre eux, par exemple en isolant l'installation globale dans des processus de test dédiés et en testant les composants internes avec des subscribers locaux lorsque possible.
## Erreurs Logging
@@ -513,6 +646,8 @@ Premiers codes conceptuels :
```text
logging.invalid_settings
logging.already_initialized
logging.not_initialized
logging.reload_failed
logging.file_output_initialization_failed
```
@@ -523,47 +658,21 @@ Règles :
- les constantes `ErrorCode` sont définies dans `ksp-logging-lib` ;
- Core n'ajoute aucune variante/connaissance Logging ;
- les causes externes utiles sont conservées avec `Error::with_source(...)` lorsqu'elles satisfont `Error + Send + Sync + 'static` ;
- le contexte n'embarque aucun secret ;
- aucun `unwrap`, `expect`, `panic` production ni opérateur `?` n'est introduit.
## Politique secrets et données sensibles
## Responsabilité du contenu loggé
### Valeurs interdites dans les logs
`ksp-logging-lib` transporte et formate les événements que les callers lui demandent d'émettre. Elle n'est pas responsable de déterminer si une valeur arbitraire passée dans un champ `Debug`/`Display` constitue un secret ou une donnée sensible.
Ne jamais émettre en clair :
Conséquences :
- clé privée ou bytes secrets de keypair ;
- seed/seed phrase/mnemonic ;
- password/passphrase/PIN ;
- token API, bearer token, session token ou cookie d'authentification ;
- header `Authorization` complet ;
- clé de chiffrement ou secret de signature ;
- DSN/URL de connexion contenant des credentials ;
- contenu futur de `KS_SECRET_*` / `KB_SECRET_*` ou équivalent sensible ;
- tout objet `Debug` susceptible d'embarquer indirectement l'une de ces valeurs.
- aucune détection automatique de secrets ;
- aucune redaction automatique ;
- aucun scanner de noms de champs ;
- aucun helper universel présenté comme barrière de sécurité ;
- la crate appelante reste responsable de ne pas transmettre de données qu'elle ne souhaite pas journaliser.
Les adresses publiques, signatures publiques, Program IDs, slots et identifiants publics peuvent être loggés lorsqu'ils sont utiles.
### Settings Logging
`LoggingSettings` ne possède aucun champ de secret. Une future Config peut lui transmettre des chemins, niveaux, targets et paramètres de sortie, mais jamais les secrets de son propre document.
### Responsabilité des callers
La façade ne peut pas détecter de façon fiable qu'un arbitraire `Debug`/`Display` contient un secret.
La règle est donc :
- omission par défaut ;
- redaction explicite par le caller avant émission si la présence d'un champ est réellement nécessaire ;
- ne jamais logger un objet de configuration complet uniquement par commodité ;
- ne pas considérer le filtrage de niveau comme une protection de secret.
### Helper de redaction
Aucun helper générique de redaction n'est ajouté dans `0.1.2`.
Un helper donnant une impression de protection automatique sans contrôler le contenu des objets serait trompeur. Une future nécessité concrète pourra introduire un type/helper explicitement borné.
`LoggingSettings` ne contient que des paramètres de logging (niveaux, targets, sorties, chemins/rotation, spans) ; cela ne constitue pas une politique de secret mais simplement son contrat fonctionnel.
## Format, spans et compatibilité `log`
@@ -580,20 +689,24 @@ Sont hors de la première surface :
### Spans
La crate est propriétaire de la stack tracing, mais `0.1.2` ne crée pas encore une façade publique KSP pour :
Les spans font désormais partie de la fondation `0.1.2`.
- `span!` ;
- `#[instrument]` ;
- OpenTelemetry ;
- contexte distribué.
La surface doit couvrir :
Les cinq macros d'événements suffisent à la première fondation. Une surface spans sera ajoutée uniquement au premier besoin concret d'une crate async/runtime supérieure.
- création de spans par niveau avec target KSP explicite ;
- champs structurés `domain`, `component` et autres fields ;
- exécution synchrone dans un span ;
- instrumentation correcte des `Future` async ;
- événements de lifecycle optionnels, au minimum `NEW | CLOSE`, permettant d'obtenir rapidement timestamp de début, timestamp de fermeture et temps `busy`/`idle` ;
- callsite réel préservé.
`#[instrument]` n'est pas requis par `0.1.2` : la surface macros/helpers KSP suffit et évite d'activer `tracing-attributes` sans besoin.
### Compatibilité `log`
La feature `tracing-log` de `tracing-subscriber` n'est pas activée maintenant.
La feature `tracing-log` de `tracing-subscriber` n'est pas activée.
KSP ne possède encore aucune dépendance runtime supérieure démontrant un besoin de récupérer des événements provenant de la crate `log`. Cette compatibilité pourra être activée ultérieurement dans Logging si un transport/provider concret l'exige.
Cette décision participe au takeover : KSP ne cherche pas à aspirer automatiquement le bruit de dépendances instrumentées avec la crate `log`. Une information utile est réémise explicitement par la crate KSP propriétaire sous son propre target.
## API publique candidate
@@ -606,8 +719,16 @@ ksp_logging_lib::info!
ksp_logging_lib::debug!
ksp_logging_lib::trace!
ksp_logging_lib::error_span!
ksp_logging_lib::warn_span!
ksp_logging_lib::info_span!
ksp_logging_lib::debug_span!
ksp_logging_lib::trace_span!
ksp_logging_lib::LogFilterLevel
ksp_logging_lib::TargetFilter
ksp_logging_lib::SpanEvents
ksp_logging_lib::<KSP span abstraction/helpers>
ksp_logging_lib::ConsoleOutput
ksp_logging_lib::ConsoleSettings
ksp_logging_lib::FileRotation
@@ -615,10 +736,13 @@ ksp_logging_lib::FileSettings
ksp_logging_lib::LoggingSettings
ksp_logging_lib::LoggingGuard
ksp_logging_lib::initialize
ksp_logging_lib::reinitialize
ksp_logging_lib::<ErrorCode constants owned by Logging>
```
Les noms exacts de l'abstraction de span et de l'API d'instrumentation async sont figés pendant l'implémentation après tests de callsite/type leakage.
Les modules d'implémentation restent privés. Les types externes `tracing*` ne deviennent pas le contrat public KSP.
Arborescence candidate :
@@ -634,12 +758,15 @@ crates/ksp-logging-lib/
│ ├── error.rs
│ ├── macros.rs
│ ├── settings.rs
│ ├── span.rs
│ └── runtime.rs
├── unit_tests/
│ ├── settings.rs
│ ├── span.rs
│ └── runtime.rs
└── tests/
├── callsite.rs
├── reload.rs
└── public_api.rs
```
@@ -647,55 +774,78 @@ Cette arborescence reste ajustable si une séparation plus petite suffit. Aucun
## Tests prévus
### Façade/macros
### Façade/macros événements
- les cinq macros sont accessibles depuis crate-root ;
- message simple ;
- champs structurés ;
- target explicite ;
- target explicite obligatoire selon la surface retenue ;
- target égal au nom de crate dans les fixtures KSP ;
- callsite file/module/line réel ;
- target implicite du caller ;
- absence de dépendance directe `tracing` dans une crate consommatrice de test si un fixture workspace est nécessaire.
- absence de dépendance directe `tracing` dans une crate consommatrice de test.
### Spans sync/async
- les cinq macros de span ou la surface finale équivalente sont accessibles depuis crate-root ;
- callsite span file/module/line réel ;
- target explicite conservé ;
- scope synchrone correctement associé au span ;
- future async instrumentée par la façade KSP ;
- aucun enter guard conservé à travers `.await` dans l'API recommandée ;
- `SpanEvents::NewAndClose` produit les événements lifecycle attendus ;
- `CLOSE` contient les temps `busy`/`idle` lorsque les timestamps sont actifs ;
- `SpanEvents::Off` ne produit pas de bruit lifecycle synthétique.
### Settings
- constructeurs/getters ;
- niveaux `Off/Error/Warn/Info/Debug/Trace` ;
- target filters ;
- span events ;
- console stdout/stderr ;
- fichier Never/Hourly/Daily ;
- validation sans sortie ;
- validation des chaînes vides retenues.
- validation des chaînes vides retenues ;
- rejet des overrides de targets externes si cette validation est conservée.
### Filtering
### Filtering et takeover
- niveau default ;
- override de target exact ;
- override par préfixe ;
- target explicitement Off ;
- priorité d'un préfixe plus spécifique selon la sémantique `Targets` retenue.
- targets externes silencieux par défaut ;
- target `ksp-*` au niveau KSP default ;
- override d'un target KSP exact ;
- override par préfixe KSP ;
- target KSP explicitement `Off` ;
- priorité d'un préfixe plus spécifique selon la sémantique retenue ;
- une émission tierce n'est jamais renommée en target KSP ;
- une réémission KSP explicite apparaît uniquement sous le target de la crate propriétaire.
### Runtime
- initialisation valide ;
- initialisation refusée ;
- subscriber déjà occupé ;
- console reçoit les événements attendus ;
- file output écrit les événements attendus ;
- deuxième `initialize` refusé ;
- subscriber global déjà occupé ;
- console non bloquante reçoit les événements attendus ;
- file output non bloquant écrit les événements attendus ;
- rotation choisie est transmise au backend ;
- stripping ANSI du fichier ;
- erreurs d'initialisation fichier remontent sous `ksp_core_lib::Error` avec code Logging ;
- guard conservé jusqu'au flush final.
- guards console/fichier conservés jusqu'au flush final ;
- dropped-line counters récupérables/observables selon l'API finale.
### Secrets
### Hot reload
Aucun test ne peut prouver qu'un caller futur ne loggera jamais un secret arbitraire.
Les validations testables portent sur :
- l'absence de champs sensibles dans `LoggingSettings` ;
- l'absence de dump automatique des settings/configurations ;
- les exemples/docs qui utilisent uniquement des valeurs publiques ;
- l'absence de helper présenté comme redaction automatique universelle.
- `reinitialize` nécessite un `LoggingGuard` valide ;
- changement de niveau KSP à chaud ;
- changement d'override pour un target précis ;
- activation/désactivation console ;
- activation/désactivation fichier ;
- changement de chemin/rotation fichier lorsque supporté ;
- changement de `SpanEvents` à chaud ;
- aucun second `set_global_default` ;
- une reconfiguration invalide conserve l'ancienne configuration active ;
- les anciens guards sont flushés après bascule et pas avant ;
- émissions concurrentes pendant le reload sans panic/deadlock ;
- coût du mécanisme reload mesuré suffisamment pour détecter une régression grossière du hot path.
## Audits de dépendances
@@ -709,9 +859,9 @@ cargo tree -p ksp-logging-lib -e features
Contrôles particuliers :
- pas de `tracing-attributes` tant que `attributes` n'est pas nécessaire ;
- pas de `tracing-attributes` : les spans KSP de `0.1.2` n'exigent pas `#[instrument]` ;
- pas de `nu-ansi-term` par activation KSP de `ansi` ;
- pas de `tracing-log` par default feature ;
- pas de `tracing-log` par default feature afin de ne pas aspirer automatiquement les logs tiers ;
- pas de stack `env-filter`/regex ;
- pas de stack JSON/Serde ;
- pas de dépendance Config ;
@@ -749,11 +899,11 @@ Objectifs :
- auditer la base `0.1.1` ;
- auditer la stack tracing actuelle ;
- fixer ownership, macros/callsites, settings, filtering, console, fichier, lifecycle, erreurs et secrets ;
- fixer takeover, targets KSP, settings, filtering, console/fichier non bloquants, stripping ANSI, lifecycle, hot reload et spans sync/async ;
- dimensionner la suite ;
- ne pas créer encore la crate fonctionnelle ni ajouter de dépendance inutilisée.
### `0.1.2-pre.002` — crate + settings + façade d'émission
### `0.1.2-pre.002` — crate + settings + façade événements/spans
Objectifs :
@@ -761,44 +911,46 @@ Objectifs :
- dépendre de `ksp-core-lib` ;
- revérifier puis ajouter `tracing` au workspace ;
- implémenter les types de settings indépendants de Config ;
- implémenter les cinq macros KSP ;
- ajouter les premiers tests publics de façade ;
- établir le test de callsite, en ajoutant `tracing-subscriber` à cette tranche seulement si le test l'utilise réellement ; sinon le fixer au début de `pre.003` avant de considérer les macros stabilisées.
- implémenter les cinq macros événements et la surface de spans KSP ;
- établir les tests de callsite événements/spans ;
- établir l'instrumentation async sans exposer `tracing::Instrument` aux consumers.
### `0.1.2-pre.003` — subscriber + console + filtering
### `0.1.2-pre.003` — subscriber + takeover + console + reload foundation
Objectifs :
- revérifier puis ajouter `tracing-subscriber` si ce n'est pas déjà fait ;
- revérifier puis ajouter `tracing-subscriber` ;
- implémenter le mapping `LogFilterLevel` ;
- implémenter `Targets` default + overrides ;
- implémenter le silence externe + default KSP + overrides ;
- implémenter la couche console ;
- installer le subscriber global avec API fallible ;
- stabiliser le comportement de réinitialisation ;
- achever les tests de callsite avant toute poursuite vers le fichier.
- introduire l'infrastructure reloadable et les premiers tests de hot reload ;
- intégrer les événements lifecycle de spans `Off/NewAndClose/Full`.
### `0.1.2-pre.004` — fichier + non-blocking + lifecycle
### `0.1.2-pre.004` — non-blocking console/fichier + guards + ANSI + reload sinks
Objectifs :
- revérifier puis ajouter `tracing-appender` ;
- passer console et fichier sur writers non bloquants ;
- implémenter `FileSettings` et Never/Hourly/Daily ;
- utiliser le builder fallible du file appender ;
- utiliser le non-blocking en mode non-lossy ;
- introduire/achever `LoggingGuard` ;
- tester flush/lifetime et erreurs de fichier ;
- vérifier le graphe/features après unification de la stack complète.
- conserver les `WorkerGuard` et `ErrorCounter` ;
- implémenter le stripping ANSI fichier ;
- permettre le remplacement à chaud des sinks/settings concernés ;
- tester flush/lifetime, saturation et erreurs de fichier.
### `0.1.2-pre.005` — intégration + tests + audits
### `0.1.2-pre.005` — intégration + concurrence + tests + audits
Objectifs :
- compléter les tests settings/filtering/runtime/façade ;
- vérifier la politique secrets dans docs/exemples ;
- compléter les tests settings/filtering/takeover/spans/runtime/reload ;
- tester les reloads concurrents et l'absence de deadlock/panic ;
- vérifier que les crates de fixture n'utilisent pas directement `tracing` ;
- auditer les réexports crate-root ;
- auditer la documentation de crate (`README.md`, `TODO.md`, `USAGE.md`) ;
- exécuter les audits Cargo/features/doublons et scripts réellement présents ;
- corriger les écarts sans ouvrir Config.
- mesurer l'overhead grossier de la stratégie reload afin d'éviter une architecture inutilement coûteuse.
### `0.1.2-pre.006` — validation finale + docs + cleanup + prompt 0.1.3
@@ -809,7 +961,7 @@ Objectifs :
- consolider la documentation durable ;
- synchroniser roadmap/index/changelog général s'il existe alors ;
- nettoyer/archive uniquement ce que les règles demandent ;
- préparer le prompt final `0.1.3 — ksp-config-lib` ;
- préparer le prompt final `0.1.3 — ksp-config-lib` en documentant la conversion Config -> `LoggingSettings` et le hot reload ;
- préparer la livraison finale avant `rel.001` et tag stable.
Le découpage reste souple. Une tranche trop large est scindée ; une tranche devenue inutile est supprimée par correction explicite du plan.
@@ -818,7 +970,8 @@ Le découpage reste souple. Une tranche trop large est scindée ; une tranche de
- `ksp-config-lib` et documents/profils Config ;
- parsing JSON/TOML de configuration ;
- Tauri ;
- watcher de fichiers de configuration : Logging fournit `reinitialize`, Config décidera plus tard quand l'appeler ;
- Tauri comme dépendance de `ksp-logging-lib` ;
- wallet/keypair/signer ;
- RPC/WS/providers ;
- Program decoding/execution ;
@@ -829,41 +982,48 @@ Le découpage reste souple. Une tranche trop large est scindée ; une tranche de
- trading/ML ;
- OpenTelemetry/export réseau ;
- observabilité distribuée ;
- public span API/`#[instrument]` ;
- hot reload dynamique des filtres ;
- `#[instrument]` public dans cette release ;
- JSON log format ;
- field-based/domain filtering ;
- rotation par taille/compression ;
- multi-route avancé ;
- politique de rétention complexe ;
- compatibilité `log` tant qu'aucun besoin concret ne l'impose.
- compatibilité automatique avec la crate `log`/capture des logs tiers.
## Critères de sortie de `0.1.2`
La release peut être stabilisée lorsque :
- `ksp-logging-lib` est la façade runtime unique KSP ;
- les cinq macros KSP fonctionnent sans dépendance `tracing` directe chez les consumers ;
- les tests prouvent le callsite réel ;
- `ksp-logging-lib` est la façade runtime unique KSP et seule propriétaire directe de la stack tracing ;
- les crates comportementales de test utilisent exclusivement la façade KSP pour événements et spans ;
- les cinq macros événements fonctionnent avec target KSP explicite et callsite réel ;
- la surface de spans sync/async fonctionne sans exposer une dépendance `tracing` directe aux consumers ;
- `NEW | CLOSE` permet d'obtenir rapidement début/fin et temps busy/idle lorsque demandé ;
- les settings sont indépendants de Config ;
- le filtering default/target est déterministe ;
- console et fichier optionnel sont validés ;
- le file writer non bloquant est non-lossy et son guard est possédé explicitement ;
- la répétition d'initialisation retourne une erreur KSP au lieu de paniquer ;
- les targets externes sont silencieux par défaut et les détails utiles sont réémis explicitement par leur propriétaire KSP ;
- console et fichier optionnel sont non bloquants et leurs guards sont possédés explicitement ;
- la saturation des queues ne bloque pas le hot path et les lignes abandonnées restent observables ;
- le fichier supprime les séquences ANSI avant persistence ;
- `initialize` installe le subscriber une seule fois ;
- `reinitialize` permet de modifier à chaud niveaux, filters et sorties retenues sans second subscriber global ;
- une erreur de reconfiguration conserve l'ancienne configuration active ;
- les reloads concurrents sont testés sans panic/deadlock ;
- les erreurs utilisent Core sans dépendance inverse ;
- aucune fuite automatique de secret n'est introduite par la façade/settings ;
- le graphe/features respecte le besoin minimal retenu ;
- les validations workspace et audits présents sont propres ;
- la documentation finale et le prompt `0.1.3` sont prêts.
## Questions ouvertes après `pre.001`
## Questions ouvertes après `pre.001-fix.001`
Aucune question architecturale bloquante ne justifie du développement supplémentaire dans `pre.001`.
Aucune question architecturale bloquante ne justifie du développement fonctionnel dans `pre.001`.
Restent volontairement à confirmer par implémentation/tests :
Restent à confirmer par implémentation/tests sans remettre en cause le contrat :
1. le mécanisme Rust exact des macros KSP événements/spans qui préserve le callsite et masque les types `tracing` ;
2. la forme exacte de l'abstraction KSP permettant d'instrumenter proprement les futures async ;
3. la composition interne la moins coûteuse pour le hot reload (reload de filters/layers ciblés ou routing dynamique KSP), tout en conservant un seul subscriber global ;
4. l'API publique exacte d'observation des dropped lines ;
5. le détail visuel exact du formatter humain, sans transformer sa ponctuation en contrat public.
1. le mécanisme Rust exact des macros KSP (wrapper transparent ou réexport) qui préserve le callsite avec la plus petite surface ;
2. le détail visuel exact du formatter humain, sans transformer sa ponctuation en contrat public ;
3. l'utilité future d'une rétention bornée, d'ANSI, JSON, `tracing-log`, `EnvFilter`, spans ou reload, tous explicitement hors de la première surface tant qu'un besoin concret n'apparaît pas.
La prochaine action après validation de ce plan est `0.1.2-pre.002`, pas l'ouverture de Config.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/rules/RULES_DEPENDENCIES.md -->
<!-- version: 8 -->
<!-- version: 9 -->
# Règles des dépendances KSP
@@ -43,12 +43,14 @@ Elles complètent les règles Rust générales et le graphe de `docs/architectur
## Logging
- **DEP-LOG-001** — `ksp-logging-lib` est le propriétaire KSP direct de `tracing`, `tracing-appender`, `tracing-subscriber` et de l'initialisation/configuration logging.
- **DEP-LOG-001** — `ksp-logging-lib` est le propriétaire KSP direct de `tracing`, `tracing-appender`, `tracing-subscriber` et de l'initialisation/configuration runtime logging.
- **DEP-LOG-002** — `ksp-logging-lib` peut dépendre de `ksp-core-lib` pour le contrat commun `Error` / `Result`.
- **DEP-LOG-003** — `ksp-core-lib` ne dépend pas de `ksp-logging-lib` dans l'architecture actuelle.
- **DEP-LOG-004** — Les crates KSP comportant du runtime peuvent dépendre directement de `ksp-logging-lib` et ne dépendent normalement pas directement de `tracing`.
- **DEP-LOG-004** — Les crates KSP comportant du runtime dépendent de `ksp-logging-lib` lorsqu'elles instrumentent leur comportement et n'utilisent pas directement `tracing`, `tracing-subscriber` ou `tracing-appender` pour émettre leurs propres événements/spans.
- **DEP-LOG-005** — Les crates `*-api` purement déclaratives n'ajoutent pas une dépendance logging sans comportement réel à instrumenter.
- **DEP-LOG-006** — Une application/framework peut exceptionnellement intégrer directement un plugin/dépendance tracing imposé par son framework, notamment Tauri, sans créer une seconde politique de logging parallèle à `ksp-logging-lib`.
- **DEP-LOG-006** — Le subscriber KSP rend silencieux par défaut les targets externes ; une information tierce utile est réémise explicitement par la crate KSP propriétaire sous son propre target au lieu de renommer/réécrire l'événement tiers.
- **DEP-LOG-007** — Une application/framework peut exceptionnellement intégrer directement un plugin/dépendance tracing imposé par son framework, notamment Tauri, sans créer une seconde politique de logging parallèle à `ksp-logging-lib`; les événements KSP restent émis via la façade KSP.
- **DEP-LOG-008** — `ksp-logging-lib` possède ses settings runtime et son hot reload ; `ksp-config-lib` peut plus tard construire ces settings et demander une reconfiguration sans créer de dépendance inverse Logging -> Config.
## Program / Execution