v0.1.2-pre.006
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# file: crates/ksp-logging-lib/Cargo.toml
|
||||
# version: 3
|
||||
# version: 4
|
||||
|
||||
[package]
|
||||
name = "ksp-logging-lib"
|
||||
@@ -13,5 +13,8 @@ tracing.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
tracing-appender.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
tokio.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: crates/ksp-logging-lib/README.md -->
|
||||
<!-- version: 1 -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# ksp-logging-lib
|
||||
|
||||
@@ -19,6 +19,8 @@ La crate possède :
|
||||
- les `WorkerGuard`, compteurs de lignes abandonnées, rotation fichier et stripping ANSI ;
|
||||
- l'instrumentation de scopes synchrones et de `Future` async.
|
||||
|
||||
L'API async de production reste indépendante de tout executor. Tokio est utilisé uniquement comme `dev-dependency` afin de valider `instrument(...)` sur un executor réel en mode current-thread et multi-thread ; il ne fait pas partie des dépendances runtime de la crate.
|
||||
|
||||
## Frontières
|
||||
|
||||
Une crate KSP comportementale qui journalise son activité dépend de `ksp-logging-lib` et n'utilise pas directement `tracing`, `tracing-subscriber` ou `tracing-appender`.
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<!-- file: crates/ksp-logging-lib/TODO.md -->
|
||||
<!-- version: 1 -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# TODO ksp-logging-lib
|
||||
|
||||
## À fermer avant la stable 0.1.2
|
||||
|
||||
- exécuter les validations Cargo complètes de `pre.005` puis de la tranche finale ;
|
||||
- exécuter explicitement le probe d'overhead ignoré et conserver son résultat dans le delta de validation approprié ;
|
||||
- vérifier une dernière fois le graphe de dépendances/features et l'absence de contournement de la façade KSP ;
|
||||
- synchroniser la documentation finale et le prompt `0.1.3 — ksp-config-lib`.
|
||||
- exécuter les validations Cargo complètes de `pre.006`, y compris les tests Tokio current-thread/multi-thread ;
|
||||
- vérifier que `cargo tree -p ksp-logging-lib -e normal` ne contient pas Tokio et que Tokio apparaît uniquement dans le graphe dev attendu ;
|
||||
- refaire l'audit final du graphe/features et de l'ownership de la stack tracing ;
|
||||
- après validation de la prerelease finale, préparer `rel.001`, publier `workspace.package.version = "0.1.2"` et taguer `v0.1.2` conformément aux règles de release.
|
||||
|
||||
## Capacités différées
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: crates/ksp-logging-lib/USAGE.md -->
|
||||
<!-- version: 1 -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# Utilisation de ksp-logging-lib
|
||||
|
||||
@@ -116,3 +116,15 @@ ksp_logging_lib::warn!(
|
||||
"logging queues dropped lines"
|
||||
);
|
||||
```
|
||||
|
||||
## Instrumentation async et executor
|
||||
|
||||
`instrument(span, future)` accepte une `Future` standard et ne dépend d'aucun executor particulier :
|
||||
|
||||
```rust
|
||||
let span = ksp_logging_lib::trace_span!(target: LOGGING_TARGET, "load_transactions");
|
||||
let result = ksp_logging_lib::instrument(span, async_operation()).await;
|
||||
```
|
||||
|
||||
La crate ne requiert pas Tokio en production. Tokio n'est présent qu'en `dev-dependency` pour valider la surface sur un executor réel, y compris après plusieurs suspensions et sur un runtime multi-thread. Un consumer peut donc utiliser l'executor adapté à son propre contexte sans que Logging lui en impose un.
|
||||
|
||||
|
||||
118
crates/ksp-logging-lib/tests/tokio_span.rs
Normal file
118
crates/ksp-logging-lib/tests/tokio_span.rs
Normal file
@@ -0,0 +1,118 @@
|
||||
// file: crates/ksp-logging-lib/tests/tokio_span.rs
|
||||
// version: 1
|
||||
|
||||
//! Integration tests for KSP span instrumentation on a real Tokio executor.
|
||||
|
||||
const TEST_TARGET: &str = "ksp-logging-lib";
|
||||
|
||||
#[derive(Clone)]
|
||||
struct CountingSubscriber {
|
||||
enters: std::sync::Arc<std::sync::atomic::AtomicU64>,
|
||||
exits: std::sync::Arc<std::sync::atomic::AtomicU64>,
|
||||
next_id: std::sync::Arc<std::sync::atomic::AtomicU64>,
|
||||
}
|
||||
|
||||
impl CountingSubscriber {
|
||||
fn new(enters: std::sync::Arc<std::sync::atomic::AtomicU64>, exits: std::sync::Arc<std::sync::atomic::AtomicU64>) -> Self {
|
||||
return Self { enters, exits, next_id: std::sync::Arc::new(std::sync::atomic::AtomicU64::new(1)) };
|
||||
}
|
||||
}
|
||||
|
||||
impl tracing::Subscriber for CountingSubscriber {
|
||||
fn enabled(&self, _metadata: &tracing::Metadata<'_>) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
fn new_span(&self, _span: &tracing::span::Attributes<'_>) -> tracing::span::Id {
|
||||
let id = self.next_id.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
return tracing::span::Id::from_u64(id);
|
||||
}
|
||||
|
||||
fn record(&self, _span: &tracing::span::Id, _values: &tracing::span::Record<'_>) {
|
||||
return;
|
||||
}
|
||||
|
||||
fn record_follows_from(&self, _span: &tracing::span::Id, _follows: &tracing::span::Id) {
|
||||
return;
|
||||
}
|
||||
|
||||
fn event(&self, _event: &tracing::Event<'_>) {
|
||||
return;
|
||||
}
|
||||
|
||||
fn enter(&self, _span: &tracing::span::Id) {
|
||||
self.enters.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
return;
|
||||
}
|
||||
|
||||
fn exit(&self, _span: &tracing::span::Id) {
|
||||
self.exits.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fn test_span(enters: std::sync::Arc<std::sync::atomic::AtomicU64>, exits: std::sync::Arc<std::sync::atomic::AtomicU64>) -> ksp_logging_lib::Span {
|
||||
let subscriber = CountingSubscriber::new(enters, exits);
|
||||
return tracing::subscriber::with_default(subscriber, || -> ksp_logging_lib::Span {
|
||||
return ksp_logging_lib::trace_span!(target: TEST_TARGET, "tokio_runtime_span", domain = "logging", executor = "tokio");
|
||||
});
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn instrumented_span_reenters_across_real_tokio_suspensions() {
|
||||
let enters = std::sync::Arc::new(std::sync::atomic::AtomicU64::new(0));
|
||||
let exits = std::sync::Arc::new(std::sync::atomic::AtomicU64::new(0));
|
||||
let span = test_span(std::sync::Arc::clone(&enters), std::sync::Arc::clone(&exits));
|
||||
let observed_enters = std::sync::Arc::clone(&enters);
|
||||
let future = ksp_logging_lib::instrument(span, async move {
|
||||
assert!(observed_enters.load(std::sync::atomic::Ordering::Relaxed) >= 1);
|
||||
tokio::task::yield_now().await;
|
||||
assert!(observed_enters.load(std::sync::atomic::Ordering::Relaxed) >= 2);
|
||||
tokio::task::yield_now().await;
|
||||
assert!(observed_enters.load(std::sync::atomic::Ordering::Relaxed) >= 3);
|
||||
return 42_u32;
|
||||
});
|
||||
let value = future.await;
|
||||
assert_eq!(value, 42_u32);
|
||||
let enter_count = enters.load(std::sync::atomic::Ordering::Relaxed);
|
||||
let exit_count = exits.load(std::sync::atomic::Ordering::Relaxed);
|
||||
assert!(enter_count >= 3);
|
||||
assert_eq!(enter_count, exit_count);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn instrumented_spans_are_usable_on_tokio_multithread_runtime() {
|
||||
let enters = std::sync::Arc::new(std::sync::atomic::AtomicU64::new(0));
|
||||
let exits = std::sync::Arc::new(std::sync::atomic::AtomicU64::new(0));
|
||||
let first_span = test_span(std::sync::Arc::clone(&enters), std::sync::Arc::clone(&exits));
|
||||
let second_span = test_span(std::sync::Arc::clone(&enters), std::sync::Arc::clone(&exits));
|
||||
let first_task = tokio::spawn(ksp_logging_lib::instrument(first_span, async {
|
||||
for _iteration in 0..32 {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
return 20_u32;
|
||||
}));
|
||||
let second_task = tokio::spawn(ksp_logging_lib::instrument(second_span, async {
|
||||
for _iteration in 0..32 {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
return 22_u32;
|
||||
}));
|
||||
let first_result = first_task.await;
|
||||
assert!(first_result.is_ok(), "first Tokio task must complete successfully");
|
||||
let first_value = match first_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let second_result = second_task.await;
|
||||
assert!(second_result.is_ok(), "second Tokio task must complete successfully");
|
||||
let second_value = match second_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
assert_eq!(first_value + second_value, 42_u32);
|
||||
let enter_count = enters.load(std::sync::atomic::Ordering::Relaxed);
|
||||
let exit_count = exits.load(std::sync::atomic::Ordering::Relaxed);
|
||||
assert!(enter_count >= 4);
|
||||
assert_eq!(enter_count, exit_count);
|
||||
}
|
||||
Reference in New Issue
Block a user