From 37f53f3080936e4922caa414bcef045c471f92e4 Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Fri, 14 Aug 2026 19:33:59 +0200 Subject: [PATCH] v0.1.2-pre.004-fix.002 --- Cargo.toml | 4 +- crates/ksp-logging-lib/src/runtime.rs | 15 +- deltas/0.1.2/pre.004-fix.002.md | 135 ++++++++++++++++++ .../004-V0_1_2_LOGGING_FOUNDATION_PLAN.md | 12 +- 4 files changed, 152 insertions(+), 14 deletions(-) create mode 100644 deltas/0.1.2/pre.004-fix.002.md diff --git a/Cargo.toml b/Cargo.toml index 34b6cc3..2f47005 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 32 +# version: 33 [workspace] resolver = "3" members = ["crates/ksp-core-lib", "crates/ksp-logging-lib"] [workspace.package] -version = "0.1.2-pre.4.fix.1" +version = "0.1.2-pre.4.fix.2" 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 55e615a..b9502e3 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: 4 +// version: 5 use tracing_subscriber::Layer; // rust-rules: trait-import use tracing_subscriber::layer::SubscriberExt; // rust-rules: trait-import @@ -194,8 +194,8 @@ fn prepare_runtime(settings: &crate::LoggingSettings) -> ksp_core_lib::Result PreparedOutput { return match console.output() { - crate::ConsoleOutput::Stdout => build_non_blocking_output(std::io::stdout(), "ksp-logging-console", settings), - crate::ConsoleOutput::Stderr => build_non_blocking_output(std::io::stderr(), "ksp-logging-console", settings), + crate::ConsoleOutput::Stdout => build_non_blocking_output(std::io::stdout(), "ksp-logging-console", settings, true), + crate::ConsoleOutput::Stderr => build_non_blocking_output(std::io::stderr(), "ksp-logging-console", settings, true), }; } @@ -216,23 +216,24 @@ fn build_file_output(file: &crate::FileSettings, settings: &crate::LoggingSettin }, }; let stripped_writer = crate::writer::StripAnsiWriter::new(appender); - return std::result::Result::Ok(build_non_blocking_output(stripped_writer, "ksp-logging-file", settings)); + return std::result::Result::Ok(build_non_blocking_output(stripped_writer, "ksp-logging-file", settings, false)); } -fn build_non_blocking_output(writer: W, thread_name: &str, settings: &crate::LoggingSettings) -> PreparedOutput +fn build_non_blocking_output(writer: W, thread_name: &str, settings: &crate::LoggingSettings, ansi_sanitization: bool) -> PreparedOutput where W: std::io::Write + std::marker::Send + 'static, { let (non_blocking, worker_guard) = tracing_appender::non_blocking::NonBlockingBuilder::default().lossy(true).thread_name(thread_name).finish(writer); let error_counter = non_blocking.error_counter(); - let layer = build_format_layer(non_blocking, settings); + let layer = build_format_layer(non_blocking, settings, ansi_sanitization); return PreparedOutput { layer, output: RuntimeOutput { _worker_guard: worker_guard, error_counter } }; } -fn build_format_layer(writer: tracing_appender::non_blocking::NonBlocking, settings: &crate::LoggingSettings) -> BoxedRuntimeLayer { +fn build_format_layer(writer: tracing_appender::non_blocking::NonBlocking, settings: &crate::LoggingSettings, ansi_sanitization: bool) -> BoxedRuntimeLayer { return tracing_subscriber::fmt::layer() .with_writer(writer) .with_ansi(false) + .with_ansi_sanitization(ansi_sanitization) .with_target(true) .with_file(true) .with_line_number(true) diff --git a/deltas/0.1.2/pre.004-fix.002.md b/deltas/0.1.2/pre.004-fix.002.md new file mode 100644 index 0000000..2594f19 --- /dev/null +++ b/deltas/0.1.2/pre.004-fix.002.md @@ -0,0 +1,135 @@ + + + +# Delta 0.1.2-pre.004-fix.002 + +## Base requise + +Livraison précédente : + +```text +0.1.2-pre.004-fix.001 +``` + +La base porte : + +```text +workspace.package.version = "0.1.2-pre.4.fix.1" +Cargo.toml header version = 32 +``` + +## Validation remontée + +Après application de `pre.004-fix.001`, un rebuild propre a donné : + +```text +cargo clean OK +cargo fmt --all OK +cargo check --workspace OK +cargo clippy --workspace --all-targets OK +cargo test --workspace ECHEC +``` + +Tous les tests sauf le test runtime global passent encore. L'échec reste strictement identique : + +```text +assertion failed: file_text.contains("file output marker") +``` + +La reproduction après `cargo clean` invalide donc l'hypothèse selon laquelle cet échec précis provenait de l'ordre de destruction corrigé par `pre.004-fix.001`. Ce lifecycle explicite est néanmoins conservé. + +## Cause réelle + +`tracing-subscriber 0.3.23` active par défaut la sanitization ANSI des valeurs dans `fmt::Layer`. Cette protection intervient pendant le formatage, donc avant l'appel au `MakeWriter`. + +Le sink fichier KSP était composé comme suit : + +```text +value containing ESC + -> fmt::Layer ANSI sanitization + -> NonBlocking + -> StripAnsiWriter + -> RollingFileAppender +``` + +Le `StripAnsiWriter` KSP ne recevait donc plus les octets ESC originaux à supprimer. Le test attend volontairement que : + +```text +file ESC[31moutput ESC[0m marker +``` + +devienne dans le fichier : + +```text +file output marker +``` + +La sanitization native et le stripping KSP sont deux politiques différentes : KSP veut supprimer les contrôles du fichier, pas les transformer avant son propre writer. + +## Correction + +Le runtime distingue désormais la politique du formatter selon le sink : + +```text +console + fmt::Layer.with_ansi(false) + fmt::Layer.with_ansi_sanitization(true) + -> NonBlocking console + +file + fmt::Layer.with_ansi(false) + fmt::Layer.with_ansi_sanitization(false) + -> NonBlocking + -> StripAnsiWriter + -> RollingFileAppender +``` + +La console conserve donc la protection native de `tracing-subscriber`. Le fichier laisse passer jusqu'au worker les séquences présentes dans les valeurs afin que `StripAnsiWriter` les supprime avant persistence. + +Le stripping reste hors du hot path : il est toujours exécuté derrière la queue non bloquante. + +Le test d'intégration runtime reste inchangé. Il continue à vérifier : + +- l'émission fichier après hot reload ; +- le retrait immédiat du sink et son drain ; +- la présence du target et du callsite ; +- l'absence de séquences ANSI ; +- le silence des targets externes. + +## Version technique + +Ce correctif modifie du Rust. La version workspace devient : + +```text +workspace.package.version = "0.1.2-pre.4.fix.2" +``` + +et l'en-tête du `Cargo.toml` racine devient : + +```text +# version: 33 +``` + +## Fichiers du delta + +```text +Cargo.toml +crates/ksp-logging-lib/src/runtime.rs +docs/plans/004-V0_1_2_LOGGING_FOUNDATION_PLAN.md +deltas/0.1.2/pre.004-fix.002.md +``` + +## Validations à exécuter + +```bash +cargo fmt --all +cargo check --workspace +cargo clippy --workspace --all-targets +cargo test --workspace +``` + +Si ces validations sont propres, la tranche suivante reste : + +```text +0.1.2-pre.005 — intégration + concurrence + saturation + audits +``` 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 046a69d..a137e0c 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`, étendu au runtime subscriber par `0.1.2-pre.003`/`pre.003-fix.001`, puis complété par les sorties non bloquantes de `0.1.2-pre.004` et leur lifecycle de retrait corrigé par `pre.004-fix.001`. +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`, étendu au runtime subscriber par `0.1.2-pre.003`/`pre.003-fix.001`, puis complété par les sorties non bloquantes de `0.1.2-pre.004`. `pre.004-fix.001` a rendu explicite l'ordre de retrait des layers et des `WorkerGuard`; `pre.004-fix.002` corrige ensuite l'interaction entre la sanitization ANSI native de `tracing-subscriber 0.3.23` et le stripping fichier KSP. -`pre.003-fix.001` a été validé dans l'environnement de développement avec `cargo fmt --all`, `cargo check --workspace`, `cargo clippy --workspace --all-targets` et `cargo test --workspace` propres sur la version Cargo `0.1.2-pre.3.fix.1`. `pre.004` ajoute `tracing-appender`, remplace la console synchrone par un writer non bloquant, introduit le fichier `Never/Hourly/Daily`, possède les `WorkerGuard`, expose des compteurs cumulés de dropped lines, déporte le stripping ANSI côté worker fichier et conserve le hot reload transactionnel des sinks. Les validations de `pre.004` ont montré un défaut de lifecycle lors du retrait immédiat du sink fichier : le fichier pouvait être lu avant que le worker n'ait observé un shutdown avec tous les anciens `NonBlocking` senders effectivement libérés. `pre.004-fix.001` retire donc explicitement les anciens layers du runtime, les détruit, puis détruit leurs `WorkerGuard`. +`pre.003-fix.001` a été validé dans l'environnement de développement avec `cargo fmt --all`, `cargo check --workspace`, `cargo clippy --workspace --all-targets` et `cargo test --workspace` propres sur la version Cargo `0.1.2-pre.3.fix.1`. `pre.004` ajoute `tracing-appender`, remplace la console synchrone par un writer non bloquant, introduit le fichier `Never/Hourly/Daily`, possède les `WorkerGuard`, expose des compteurs cumulés de dropped lines, déporte le stripping ANSI côté worker fichier et conserve le hot reload transactionnel des sinks. `pre.004-fix.001` a rendu explicite le retrait des anciens layers avant leurs `WorkerGuard`, mais la validation utilisateur après `cargo clean` a reproduit exactement le même échec du marqueur fichier. Cette seconde validation a invalidé l'hypothèse selon laquelle ce défaut précis provenait du drain. La cause réelle est la sanitization ANSI native de `tracing-subscriber 0.3.23`, activée par défaut dans `fmt::Layer` : elle transforme les séquences de contrôle présentes dans les valeurs avant qu'elles n'atteignent le writer. `pre.004-fix.002` la conserve pour la console mais la désactive sur le formatter fichier afin que le `StripAnsiWriter` KSP, placé côté worker, reçoive les séquences ANSI originales et les supprime réellement. Le lifecycle explicite de `fix.001` est conservé car il reste correct pour le retrait des sinks non bloquants. ## Base auditée @@ -534,6 +534,8 @@ La sortie fichier passe par un writer KSP de stripping ANSI avant persistence af 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. +`tracing-subscriber 0.3.23` active également une sanitization ANSI des valeurs dans `fmt::Layer` par défaut. Pour le sink fichier KSP, cette sanitization native est explicitement désactivée afin de ne pas convertir les séquences avant le `StripAnsiWriter`; le formatter continue de ne pas émettre lui-même de couleurs avec `with_ansi(false)`, puis le writer KSP supprime les contrôles côté worker. Pour la console, la sanitization native reste activée. + Le stripping n'est pas présenté comme un mécanisme de redaction de données. ## Lifecycle et ownership du guard @@ -978,7 +980,7 @@ Réalisé : - test d'intégration étendu pour couvrir fichier, erreur transactionnelle, flush lors du retrait du sink, takeover externe et stripping ANSI ; - tests unitaires ajoutés pour rotations, runtime disabled/non bloquant, statistiques et stripper ANSI. -Le premier test d'intégration `pre.004` a révélé que l'ordre de destruction n'était pas assez explicite lorsque le sink fichier était retiré immédiatement après une émission : `pre.004-fix.001` récupère désormais les anciens layers lors du swap et les détruit avant les `WorkerGuard`. Le test d'intégration existant reste le test de non-régression de cette garantie. +`pre.004-fix.001` récupère les anciens layers lors du swap et les détruit avant les `WorkerGuard`, ce qui rend le lifecycle de retrait explicite. La validation après ce fix a toutefois reproduit l'absence du marqueur fichier et a montré que ce lifecycle n'était pas la cause de cet échec précis. `pre.004-fix.002` corrige la cause réelle : le formatter fichier désactive la sanitization ANSI native afin que le stripper KSP puisse supprimer les séquences originales. Le test d'intégration existant reste inchangé et vérifie simultanément l'émission fichier, le retrait immédiat du sink, le drain du worker et le stripping attendu. La saturation déterministe des queues, les reloads concurrents et l'audit complet des dépendances restent à renforcer dans `pre.005`. @@ -1062,7 +1064,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. 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. Pour les changements de sinks, `pre.004-fix.001` utilise `Handle::modify` afin de récupérer le `Vec` retiré : les anciens layers sont détruits avant les `WorkerGuard` correspondants, ce qui fixe explicitement le lifecycle de drain des writers non bloquants. +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. Pour les changements de sinks, `pre.004-fix.001` utilise `Handle::modify` afin de récupérer le `Vec` retiré : les anciens layers sont détruits avant les `WorkerGuard` correspondants, ce qui fixe explicitement le lifecycle de drain des writers non bloquants. `pre.004-fix.002` distingue en outre la sanitization des valeurs : activée côté console, désactivée côté formatter fichier car le `StripAnsiWriter` KSP possède la responsabilité de suppression avant persistence. Restent à confirmer par les prereleases suivantes sans remettre en cause ce contrat :