# Delta 0.1.2-pre.003 ## Base requise Livraison précédente validée : ```text 0.1.2-pre.002-fix.001 ``` La base de développement validée porte : ```text workspace.package.version = "0.1.2-pre.2.fix.1" Cargo.toml header version = 28 ``` 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 Introduire le runtime subscriber de Logging sans encore ouvrir le backend fichier/non bloquant : - ajouter `tracing-subscriber` avec la feature minimale `fmt` ; - installer une seule fois le subscriber global KSP ; - appliquer le takeover KSP et rendre silencieux les targets externes par défaut ; - mapper `LogFilterLevel` vers `LevelFilter` ; - appliquer un niveau KSP global puis les overrides par préfixe de target ; - introduire une première couche console stdout/stderr ; - intégrer les événements de lifecycle des spans `Off`, `NewAndClose` et `Full` ; - introduire `LoggingGuard`, `initialize()` et `reinitialize()` ; - permettre un démarrage sans sink puis une activation à chaud ; - vérifier le hot reload sans second subscriber global. La console reste volontairement synchrone dans cette tranche intermédiaire. `pre.004` la remplacera par un writer `tracing-appender` non bloquant et ajoutera fichier, guards, dropped-line counters et stripping ANSI avant toute stabilisation de `0.1.2`. ## Version Cargo `workspace.package.version` passe de : ```text 0.1.2-pre.2.fix.1 ``` à : ```text 0.1.2-pre.3 ``` L'identifiant de livraison est : ```text 0.1.2-pre.003 ``` Le header du `Cargo.toml` racine passe de version 28 à 29. ## Dépendance `tracing-subscriber` L'audit du 2026-08-14 confirme `tracing-subscriber 0.3.23` dans la génération `^0.3`. La dépendance est centralisée sous `[workspace.dependencies]` : ```toml tracing-subscriber = { version = "^0.3", default-features = false, features = ["fmt"] } ``` `ksp-logging-lib` la consomme avec : ```toml tracing-subscriber.workspace = true ``` La feature `fmt` fournit le formatter et entraîne les capacités `registry`/`std` nécessaires à la composition retenue. Ne sont pas activés par anticipation : - `env-filter` ; - `ansi` ; - `tracing-log` ; - `json` ; - `time` ; - `chrono` ; - `parking_lot`. `tracing-appender` reste absent jusqu'à `pre.004`. ## Takeover et filtering Le runtime utilise `tracing_subscriber::filter::Targets`. La construction est conceptuellement : ```text default unmatched targets = OFF ksp-* = LoggingSettings.default_filter target overrides = TargetFilter entries ``` Conséquences : - un événement `sqlx`, `hyper`, `rustls` ou autre target externe reste silencieux même à `ERROR` tant qu'aucune couche KSP ne le réémet explicitement ; - les crates KSP utilisent leur nom Cargo comme target ; - `ksp-logging-lib`, `ksp-store-lib`, etc. suivent le niveau global KSP ; - un `TargetFilter` plus spécifique peut relever ou abaisser le niveau d'une crate KSP donnée ; - aucune chaîne `RUST_LOG` ou `EnvFilter` n'est introduite. ## Console initiale `ConsoleSettings::stdout()` et `ConsoleSettings::stderr()` construisent une couche `fmt` avec : - target affiché ; - ANSI explicitement désactivé ; - lifecycle de span selon `SpanEvents` ; - filtering KSP `Targets`. Cette couche utilise encore directement `std::io::stdout` / `std::io::stderr`. Ce writer synchrone est uniquement la fondation de `pre.003`; il n'est pas le contrat final de la release. ## Spans runtime Le mapping retenu est : ```text SpanEvents::Off -> FmtSpan::NONE SpanEvents::NewAndClose -> FmtSpan::NEW | FmtSpan::CLOSE SpanEvents::Full -> FmtSpan::FULL ``` `NewAndClose` active ainsi la surface nécessaire aux diagnostics de début/fin et de temps busy/idle fournis par le formatter sans obliger les consumers à utiliser directement `tracing-subscriber`. ## Subscriber global La nouvelle API publique est : ```text ksp_logging_lib::LoggingGuard ksp_logging_lib::initialize(&LoggingSettings) -> Result ksp_logging_lib::reinitialize(&mut LoggingGuard, &LoggingSettings) -> Result<()> ``` `initialize()` : 1. valide/prépare les layers ; 2. crée une unique infrastructure `reload::Layer` ; 3. installe le subscriber global avec l'API fallible `tracing::subscriber::set_global_default` ; 4. retourne un `LoggingGuard` possédant le handle de reload et les settings actifs. Une seconde installation globale retourne : ```text logging.already_initialized ``` La cause `SetGlobalDefaultError` est conservée comme `source` Core. ## Hot reload La composition interne retenue est : ```text Registry -> reload::Layer -> Vec + Send + Sync>> ``` Le `Vec` peut être vide. Cela permet : ```text initialize(no sink) -> subscriber global installé mais silencieux reinitialize(console enabled) -> console activée sans second subscriber global ``` Le choix d'un `Vec` de layers boxed prépare directement `pre.004`, qui pourra ajouter ou retirer console/fichier sans changer la surface publique de reload. `reinitialize()` prépare d'abord complètement la nouvelle représentation. Une erreur de validation/préparation retourne avant le swap et conserve : - les settings actifs du `LoggingGuard` ; - les layers actuellement installés ; - le comportement de filtering en cours. Une erreur effective du handle `reload` retourne : ```text logging.reload_failed ``` et conserve sa cause externe via le contrat `source` Core. ## File settings pendant `pre.003` `FileSettings` reste dans la surface publique définie par `pre.002`, mais le backend fichier n'est pas encore construit dans cette tranche. `initialize()` / `reinitialize()` refusent donc temporairement une configuration avec `file = Some(...)` avec `logging.invalid_settings` et contexte `field = file` au lieu d'ignorer silencieusement la demande. Cette restriction transitoire disparaîtra lorsque le backend fichier réel sera introduit en `pre.004`. ## Erreurs ajoutées ```text logging.already_initialized logging.reload_failed ``` Elles s'ajoutent à : ```text logging.invalid_settings ``` Aucune connaissance Logging n'est ajoutée à Core. ## Tests ajoutés ### Unitaires runtime - mapping complet des niveaux KSP ; - silence des targets externes ; - default KSP `Info` ; - override `ksp-logging-lib = Trace` ; - mapping des événements de span ; - rejet temporaire du backend fichier avant `pre.004`. ### Intégration runtime global Un seul test global dans sa crate de test dédiée vérifie : 1. `initialize()` avec aucun sink ; 2. absence d'admission des callsites tant que Logging est désactivé ; 3. `reinitialize()` avec console active ; 4. activation `Trace` de `ksp-logging-lib` par override ; 5. maintien de `ksp-store-lib` à `Info` ; 6. maintien de `sqlx` à `Off` même pour `Error` ; 7. échec d'un reload demandant le backend fichier non encore disponible ; 8. conservation des anciens settings/filtering après cet échec ; 9. refus d'un deuxième `initialize()`. Le changement de filtering est observé via des fonctions contenant des callsites `tracing::enabled!` stables, afin de vérifier que le reload invalide correctement l'intérêt mis en cache. ## Fichiers ajoutés - `crates/ksp-logging-lib/src/runtime.rs` - `crates/ksp-logging-lib/unit_tests/runtime.rs` - `crates/ksp-logging-lib/tests/runtime.rs` - `deltas/0.1.2/pre.003.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/tests/public_api.rs` - `docs/plans/004-V0_1_2_LOGGING_FOUNDATION_PLAN.md` ## Fichiers supprimés Aucun. ## Validations exécutées pendant la préparation - revérification documentaire de `tracing-subscriber 0.3.23` et de ses features ; - contrôle TOML des manifests ; - contrôle des headers `file:` / `version:` ; - contrôle de la centralisation de `tracing-subscriber` sous `[workspace.dependencies]` ; - contrôle que `tracing-appender` reste absent ; - 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 la stack tracing restent dans `ksp-logging-lib` ; - contrôle du contenu du delta contre la base reconstruite `0.1.2-pre.2.fix.1`. ## 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.003`, passer à `0.1.2-pre.004` : - `tracing-appender` ; - console non bloquante ; - fichier Never/Hourly/Daily ; - `WorkerGuard` / `ErrorCounter` ; - stripping ANSI fichier ; - hot reload des sinks non bloquants et de leurs guards.