From 488d8ae0a801fed9a0f4bac3a3235e6d79a4859f Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Fri, 14 Aug 2026 18:29:56 +0200 Subject: [PATCH] v0.1.2-pre.003-fix.001 --- Cargo.toml | 4 +- crates/ksp-logging-lib/src/runtime.rs | 5 +- crates/ksp-logging-lib/unit_tests/runtime.rs | 19 ++- deltas/0.1.2/pre.003-fix.001.md | 144 ++++++++++++++++++ .../004-V0_1_2_LOGGING_FOUNDATION_PLAN.md | 14 +- 5 files changed, 174 insertions(+), 12 deletions(-) create mode 100644 deltas/0.1.2/pre.003-fix.001.md diff --git a/Cargo.toml b/Cargo.toml index 65a6807..6e97005 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 29 +# version: 30 [workspace] resolver = "3" members = ["crates/ksp-core-lib", "crates/ksp-logging-lib"] [workspace.package] -version = "0.1.2-pre.3" +version = "0.1.2-pre.3.fix.1" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/crates/ksp-logging-lib/src/runtime.rs b/crates/ksp-logging-lib/src/runtime.rs index 9abfc65..3e84caa 100644 --- a/crates/ksp-logging-lib/src/runtime.rs +++ b/crates/ksp-logging-lib/src/runtime.rs @@ -1,5 +1,5 @@ // file: crates/ksp-logging-lib/src/runtime.rs -// version: 1 +// version: 2 use tracing_subscriber::Layer; // rust-rules: trait-import use tracing_subscriber::layer::SubscriberExt; // rust-rules: trait-import @@ -72,6 +72,8 @@ fn prepare_runtime_layers(settings: &crate::LoggingSettings) -> ksp_core_lib::Re } let mut layers = RuntimeLayers::new(); if let std::option::Option::Some(console) = settings.console() { + let takeover_filter: BoxedRuntimeLayer = std::boxed::Box::new(build_target_filter(settings)); + layers.push(takeover_filter); layers.push(build_console_layer(console, settings)); } return std::result::Result::Ok(layers); @@ -87,7 +89,6 @@ fn build_console_layer(console: &crate::ConsoleSettings, settings: &crate::Loggi .with_ansi(false) .with_target(true) .with_span_events(map_span_events(settings.span_events())) - .with_filter(build_target_filter(settings)) .boxed(); return layer; } diff --git a/crates/ksp-logging-lib/unit_tests/runtime.rs b/crates/ksp-logging-lib/unit_tests/runtime.rs index 34fa2bc..00fc347 100644 --- a/crates/ksp-logging-lib/unit_tests/runtime.rs +++ b/crates/ksp-logging-lib/unit_tests/runtime.rs @@ -1,5 +1,5 @@ // file: crates/ksp-logging-lib/unit_tests/runtime.rs -// version: 1 +// version: 2 #[test] fn level_mapping_covers_all_ksp_levels() { @@ -54,3 +54,20 @@ fn file_output_is_rejected_until_file_runtime_is_introduced() { }; assert_eq!(error.code(), crate::ERROR_CODE_INVALID_SETTINGS); } + +#[test] +fn console_runtime_keeps_takeover_filter_separate_from_formatter() { + let settings = crate::LoggingSettings::new( + crate::LogFilterLevel::Info, + crate::SpanEvents::Off, + std::option::Option::Some(crate::ConsoleSettings::stdout()), + std::option::Option::None, + ); + let result = super::prepare_runtime_layers(&settings); + assert!(result.is_ok()); + let layers = match result { + std::result::Result::Ok(layers) => layers, + std::result::Result::Err(_) => return, + }; + assert_eq!(layers.len(), 2); +} diff --git a/deltas/0.1.2/pre.003-fix.001.md b/deltas/0.1.2/pre.003-fix.001.md new file mode 100644 index 0000000..ed1aa68 --- /dev/null +++ b/deltas/0.1.2/pre.003-fix.001.md @@ -0,0 +1,144 @@ + + + +# Delta 0.1.2-pre.003-fix.001 + +## Base requise + +Livraison précédente : + +```text +0.1.2-pre.003 +``` + +La base porte : + +```text +workspace.package.version = "0.1.2-pre.3" +Cargo.toml header version = 29 +``` + +## Motif du correctif + +Les validations remontées pour `pre.003` sont : + +```text +cargo fmt --all OK +cargo check --workspace OK +cargo clippy --workspace --all-targets OK +cargo test --workspace ECHEC +``` + +Le test d'intégration : + +```text +global_runtime_supports_takeover_hot_reload_and_single_initialization +``` + +panique pendant le premier `reinitialize()` activant la console : + +```text +a `Filtered` layer was used, but it had no `FilterId`; was it registered with the subscriber? +``` + +## Cause + +`pre.003` construisait le sink console sous cette forme conceptuelle : + +```text +fmt layer + .with_filter(Targets) + -> Filtered +``` + +Ce `Filtered` était ensuite boxed dans le `Vec>>` placé derrière `tracing_subscriber::reload::Layer`. + +Au démarrage sans sink, le `Vec` initial était vide. Le premier hot reload construisait donc un nouveau `Filtered` après l'installation du subscriber global puis remplaçait le `Vec` via `Handle::reload`. Or un per-layer `Filtered` a besoin que son `FilterId` soit enregistré lors de son attachement au subscriber. La documentation de `tracing-subscriber 0.3.23` indique explicitement que `Handle::reload` ne doit pas être utilisé pour remplacer directement un `Filtered`. + +Le panic n'indique donc pas un défaut du contrat public KSP de hot reload, mais une composition interne incorrecte des layers de `pre.003`. + +## Correction + +Le runtime conserve : + +```text +reload::Layer>>> +``` + +mais la composition devient : + +```text +Vec reloadable +├── Targets global takeover filter +└── fmt console layer +``` + +au lieu de : + +```text +Vec reloadable +└── Filtered +``` + +`Targets` est utilisé comme layer de filtrage global. Le layer `fmt` n'appelle plus `with_filter`. + +Conséquences : + +- aucun nouveau `Filtered` n'est injecté par `Handle::reload` ; +- aucun `FilterId` tardif n'est nécessaire ; +- le takeover reste global : les targets externes restent `OFF` ; +- les niveaux KSP et overrides par préfixe restent inchangés ; +- le `Vec` complet peut toujours être remplacé pour activer/désactiver des sinks à chaud ; +- l'API publique `initialize` / `reinitialize` / `LoggingGuard` ne change pas ; +- `pre.004` peut toujours ajouter le backend fichier au même runtime reloadable. + +Une configuration sans sink conserve un `Vec` vide, donc le logging reste effectivement désactivé jusqu'à un `reinitialize()` qui ajoute une sortie. + +## Tests + +Le test d'intégration déjà présent qui a révélé la régression reste le test de non-régression principal : + +```text +global_runtime_supports_takeover_hot_reload_and_single_initialization +``` + +Un test unitaire supplémentaire vérifie que la console prépare deux layers distincts : le takeover filter global et le formatter. + +## Version technique + +Ce correctif modifie du Rust. Conformément à la règle KSP de signal technique, la version workspace devient : + +```text +workspace.package.version = "0.1.2-pre.3.fix.1" +``` + +et l'en-tête du `Cargo.toml` racine devient : + +```text +# version: 30 +``` + +## Fichiers du delta + +```text +Cargo.toml +crates/ksp-logging-lib/src/runtime.rs +crates/ksp-logging-lib/unit_tests/runtime.rs +docs/plans/004-V0_1_2_LOGGING_FOUNDATION_PLAN.md +deltas/0.1.2/pre.003-fix.001.md +``` + +## Validations à exécuter + +```bash +cargo fmt --all +cargo check --workspace +cargo test --workspace +cargo clippy --workspace --all-targets +``` + +Si ces validations sont propres, la tranche suivante reste : + +```text +0.1.2-pre.004 — non-blocking console/file + guards + ANSI + reload sinks +``` diff --git a/docs/plans/004-V0_1_2_LOGGING_FOUNDATION_PLAN.md b/docs/plans/004-V0_1_2_LOGGING_FOUNDATION_PLAN.md index 2c9f4ad..0eba974 100644 --- a/docs/plans/004-V0_1_2_LOGGING_FOUNDATION_PLAN.md +++ b/docs/plans/004-V0_1_2_LOGGING_FOUNDATION_PLAN.md @@ -1,13 +1,13 @@ - + # Plan KSP 0.1.2 — Logging foundation ## Statut -Plan actif de `0.1.2`, établi par `0.1.2-pre.001`, corrigé par `0.1.2-pre.001-fix.001`, concrétisé par la façade de `0.1.2-pre.002` puis étendu au runtime subscriber par `0.1.2-pre.003`. +Plan actif de `0.1.2`, établi par `0.1.2-pre.001`, corrigé par `0.1.2-pre.001-fix.001`, concrétisé par la façade de `0.1.2-pre.002` puis étendu au runtime subscriber par `0.1.2-pre.003` et son correctif `0.1.2-pre.003-fix.001`. -`pre.002-fix.001` a été validé dans l'environnement de développement avec `cargo fmt`, `cargo check`, `cargo clippy --workspace --all-targets` et `cargo test --workspace` propres sur la version Cargo `0.1.2-pre.2.fix.1`. `pre.003` ajoute le subscriber global, le takeover/filtering KSP, la console initiale et le hot reload des layers. Les writers non bloquants, le fichier, les guards et le stripping ANSI restent réservés à `pre.004`. +`pre.002-fix.001` a été validé dans l'environnement de développement avec `cargo fmt`, `cargo check`, `cargo clippy --workspace --all-targets` et `cargo test --workspace` propres sur la version Cargo `0.1.2-pre.2.fix.1`. Pour `pre.003`, `cargo fmt`, `cargo check` et `cargo clippy --workspace --all-targets` sont propres, mais `cargo test --workspace` a révélé un panic de `tracing-subscriber` lors du premier hot reload avec console : un `Filtered` nouvellement injecté dans le `Vec` reloadable ne possédait pas de `FilterId` enregistré. `pre.003-fix.001` sépare donc le `Targets` global du formatter afin qu'aucun `Filtered` ne soit remplacé par `Handle::reload`. Les writers non bloquants, le fichier, les guards et le stripping ANSI restent réservés à `pre.004`. ## Base auditée @@ -926,7 +926,7 @@ Objectifs : ### `0.1.2-pre.003` — subscriber + takeover + console + reload foundation -Statut : implémenté dans la tranche `pre.003`, sous réserve des validations Cargo à exécuter dans l'environnement de développement. +Statut : implémenté dans `pre.003`, puis corrigé par `pre.003-fix.001` après détection d'un panic de reload dans le test d'intégration global. Réalisé : @@ -943,7 +943,7 @@ Réalisé : La console de `pre.003` utilise encore directement `stdout`/`stderr` comme writer synchrone. C'est un état transitoire volontaire : `pre.004` remplace ces writers par `tracing-appender::non_blocking`, introduit les `WorkerGuard`/`ErrorCounter`, puis ajoute le fichier et le stripping ANSI. La release stable `0.1.2` ne sera pas déclarée conforme tant que ce remplacement n'est pas terminé. -Le runtime reloadable est un `Vec>>` placé derrière une unique `reload::Layer`. Cette composition permet de changer à chaud le nombre et le type des sinks tout en conservant un seul subscriber global et prépare directement l'ajout du layer fichier de `pre.004`. +Le runtime reloadable reste un `Vec>>` placé derrière une unique `reload::Layer`. En revanche, les sinks reloadables ne doivent pas encapsuler leur `Targets` via `Layer::with_filter`, car cela crée un `Filtered` dont le `FilterId` est enregistré lors de son attachement initial au subscriber et ne peut pas être remplacé directement avec `Handle::reload`. `pre.003-fix.001` place donc `Targets` comme layer de filtrage global distinct dans le même `Vec`, à côté du layer `fmt` non filtré. Le `Vec` entier reste remplaçable à chaud, mais aucun `Filtered` nouvellement construit n'a besoin d'être enregistré après l'installation globale du subscriber. Cette composition conserve le takeover externe, permet de changer à chaud le nombre et le type des sinks et prépare l'ajout du layer fichier de `pre.004`. ### `0.1.2-pre.004` — non-blocking console/fichier + guards + ANSI + reload sinks @@ -1038,7 +1038,7 @@ Les deux questions d'API propres à `pre.002` sont résolues : 1. les macros KSP délèguent aux macros `tracing` au point d'appel via un bridge interne caché et exigent un `target:` explicite ; 2. la surface span publique est `Span::in_scope(...)` pour le synchrone et `instrument(span, future)` pour l'async, avec type de future retourné opaque. -La composition de reload est désormais fixée pour cette release à un `Vec` de layers boxed derrière une `reload::Layer`, ce qui autorise l'activation/désactivation des sinks et le remplacement de leurs paramètres sans second subscriber global. +La composition de reload est désormais fixée pour cette release à un `Vec` de layers boxed derrière une `reload::Layer`, ce qui autorise l'activation/désactivation des sinks et le remplacement de leurs paramètres sans second subscriber global. Le takeover `Targets` est un layer global distinct dans ce `Vec`; les layers de sortie reloadables ne doivent pas être des `Filtered` remplacés directement par `Handle::reload`. Restent à confirmer par les prereleases suivantes sans remettre en cause ce contrat : @@ -1046,4 +1046,4 @@ Restent à confirmer par les prereleases suivantes sans remettre en cause ce con 2. le détail visuel exact du formatter humain, sans transformer sa ponctuation en contrat public ; 3. le comportement de flush/rotation et le swap transactionnel des `WorkerGuard` lorsque les sinks non bloquants seront introduits. -La prochaine action après validation de `pre.003` est `0.1.2-pre.004` : `tracing-appender`, console/fichier non bloquants, guards, rotation, stripping ANSI et reload des sinks. +La prochaine action après validation de `pre.003-fix.001` est `0.1.2-pre.004` : `tracing-appender`, console/fichier non bloquants, guards, rotation, stripping ANSI et reload des sinks.