# Delta 0.1.2-pre.004 ## Base requise Livraison précédente validée : ```text 0.1.2-pre.003-fix.001 ``` La base de développement validée porte : ```text workspace.package.version = "0.1.2-pre.3.fix.1" Cargo.toml header version = 30 ``` Les validations remontées avant l'ouverture de cette tranche sont propres : ```bash cargo fmt --all cargo check --workspace cargo clippy --workspace --all-targets cargo test --workspace ``` ## Objectif Compléter le runtime Logging avec les sorties réellement retenues pour `0.1.2` : - ajouter `tracing-appender` ; - rendre console et fichier non bloquants pour le caller ; - posséder les `WorkerGuard` jusqu'au reload/shutdown approprié ; - exposer les dropped-line counters ; - activer le fichier `Never/Hourly/Daily` avec construction fallible ; - supprimer les séquences ANSI avant persistence ; - conserver le takeover et le hot reload transactionnel établis par `pre.003-fix.001`. ## Version Cargo `workspace.package.version` passe de : ```text 0.1.2-pre.3.fix.1 ``` à : ```text 0.1.2-pre.4 ``` L'identifiant de livraison est : ```text 0.1.2-pre.004 ``` Le header du `Cargo.toml` racine passe de version 30 à 31. ## Dépendance `tracing-appender` L'audit du 2026-08-14 confirme `tracing-appender 0.2.5`, publié le 2026-04-17, dans la génération `^0.2`. La dépendance est centralisée sous `[workspace.dependencies]` : ```toml tracing-appender = { version = "^0.2", default-features = false } ``` `ksp-logging-lib` la consomme avec : ```toml tracing-appender.workspace = true ``` Aucune feature optionnelle n'est activée. Le backend expose `NonBlockingBuilder`, `WorkerGuard`, `ErrorCounter` et `RollingFileAppender` sans feature supplémentaire. ## Console non bloquante La console n'utilise plus directement `stdout`/`stderr` dans le formatter. Chaque sink console construit : ```text Stdout | Stderr -> NonBlockingBuilder(lossy = true) -> fmt layer + WorkerGuard + ErrorCounter ``` Le mode lossy est explicite : lorsque la queue est saturée, un log peut être abandonné au lieu de bloquer le thread appelant. Le thread worker console est nommé : ```text ksp-logging-console ``` ## Fichier et rotation `FileSettings` est maintenant réellement consommé par le runtime. Le mapping est : ```text FileRotation::Never -> Rotation::NEVER FileRotation::Hourly -> Rotation::HOURLY FileRotation::Daily -> Rotation::DAILY ``` Le runtime utilise uniquement : ```text RollingFileAppender::builder() .rotation(...) .filename_prefix(...) .build(directory) ``` La forme builder retourne un `Result`; aucune API de construction qui panique n'est utilisée par KSP. Un échec retourne : ```text logging.file_output_initialization_failed ``` avec le directory, le file-name prefix et l'erreur `InitError` externe conservés dans le contrat Core. ## Stripping ANSI Le fichier est composé comme suit : ```text fmt layer -> NonBlocking queue -> StripAnsiWriter -> RollingFileAppender ``` Le stripping est donc effectué par le thread logging et non par le caller. `StripAnsiWriter` conserve un état entre les appels `Write` afin de retirer correctement une séquence terminal coupée entre plusieurs buffers. La première surface couvre : - CSI (`ESC [` ... final byte) ; - OSC terminé par BEL ou ST ; - autres chaînes terminal ESC de type DCS/SOS/PM/APC terminées par ST. Ce mécanisme est générique et n'introduit aucune dépendance Tauri. ## Formatter humain Console et fichier partagent le formatter humain KSP avec : - timestamp standard `tracing-subscriber` ; - niveau ; - target ; - champs/message ; - source file ; - line number ; - ANSI du formatter désactivé ; - lifecycle de spans selon `SpanEvents`. La ponctuation exacte du formatter reste hors contrat public. ## Ownership et reload `LoggingGuard` possède désormais les outputs actifs : ```text LoggingGuard ├── reload handle ├── current LoggingSettings ├── active console WorkerGuard/ErrorCounter ├── active file WorkerGuard/ErrorCounter └── cumulative retired dropped-line counters ``` `reinitialize()` : 1. valide les nouveaux settings ; 2. construit entièrement le nouveau file appender et tous les nouveaux non-blocking writers/guards ; 3. construit les nouveaux layers ; 4. remplace le `Vec` reloadable ; 5. mémorise les dropped lines des anciens sinks ; 6. remplace les outputs actifs ; 7. détruit les anciens `WorkerGuard`, provoquant leur flush borné par le backend. Une erreur avant le swap détruit uniquement les nouveaux outputs préparés et laisse l'ancienne configuration active. ## Dropped lines Nouvelle surface publique : ```text DroppedLines LoggingGuard::dropped_lines() -> DroppedLines ``` `DroppedLines` expose : ```text console() file() total() ``` Les valeurs sont cumulées pour toute la durée de vie du `LoggingGuard`, y compris après plusieurs hot reloads. Les `ErrorCounter` de `tracing-appender` ne sont pas exposés directement aux consumers. ## Tests ### Unitaires - mapping `Never/Hourly/Daily` ; - runtime sans sink ; - console préparée avec filter séparé, non-blocking output et guard ; - addition saturante des dropped-line counters ; - stripping CSI ; - stripping d'une CSI coupée entre deux writes ; - stripping OSC terminé par BEL/ST. ### Intégration runtime global Le test global vérifie désormais : 1. initialisation silencieuse sans sink ; 2. hot reload console non bloquante ; 3. takeover KSP et silence `sqlx` ; 4. erreur de création d'un file appender sur un chemin invalide ; 5. conservation des settings précédents après cet échec ; 6. hot reload vers un fichier `Never` ; 7. émission d'un message contenant des codes ANSI ; 8. retrait du sink fichier par reload, donc drop/flush de son guard ; 9. présence du message KSP dans le fichier ; 10. absence des codes ANSI persistés ; 11. absence du message externe `sqlx` ; 12. présence du target et de la source ; 13. lecture de la statistique cumulée ; 14. refus d'un second `initialize()`. La saturation déterministe avec une queue artificiellement petite est reportée à `pre.005`, où un writer de test injecté pourra être utilisé sans rendre la capacité de queue publique dans `LoggingSettings`. ## Fichiers ajoutés - `crates/ksp-logging-lib/src/writer.rs` - `crates/ksp-logging-lib/unit_tests/writer.rs` - `deltas/0.1.2/pre.004.md` ## Fichiers modifiés - `Cargo.toml` - `crates/ksp-logging-lib/Cargo.toml` - `crates/ksp-logging-lib/src/error.rs` - `crates/ksp-logging-lib/src/lib.rs` - `crates/ksp-logging-lib/src/runtime.rs` - `crates/ksp-logging-lib/unit_tests/runtime.rs` - `crates/ksp-logging-lib/tests/runtime.rs` - `crates/ksp-logging-lib/tests/public_api.rs` - `docs/plans/004-V0_1_2_LOGGING_FOUNDATION_PLAN.md` ## Validations exécutées pendant la préparation - revérification documentaire officielle de `tracing-appender 0.2.5` ; - vérification de la sémantique lossy de `NonBlockingBuilder` ; - vérification de `WorkerGuard` et `ErrorCounter::dropped_lines()` ; - vérification du builder fallible de `RollingFileAppender` ; - contrôle TOML des manifests ; - contrôle des headers `file:` / `version:` ; - contrôle de la centralisation de `tracing-appender` sous `[workspace.dependencies]` ; - contrôle que le code production ajouté n'utilise ni `unwrap`, ni `expect`, ni `panic`, ni opérateur `?`, ni `unsafe` ; - contrôle que les usages directs de `tracing-appender` restent dans `ksp-logging-lib`. ## Validations à exécuter dans le workspace ```bash cargo fmt --all cargo check --workspace cargo clippy --workspace --all-targets cargo test --workspace cargo tree -p ksp-logging-lib cargo tree -p ksp-logging-lib -d cargo tree -p ksp-logging-lib -e features ``` Aucune validation Cargo non exécutable dans l'environnement de préparation n'est déclarée réussie. ## Suite Après validation de `pre.004`, passer à `0.1.2-pre.005` : - concurrence/reloads répétés ; - saturation déterministe et dropped lines ; - audits de façade et usages directs de la stack tracing ; - audits Cargo/features/doublons ; - mesure grossière de l'overhead du reload/runtime ; - compléments de tests et documentation de crate avant la tranche finale.