CodeSampleX

サンプル

tracing 0.1.44: Record late span fields and scoped events while proving target filters, nested subscriber restoration, thread-local isolation, and teardown behavior

検証済みサンプル — cargo tracing 0.1.44: Record late span fields and scoped events while proving target filters, nested subscriber restoration, thread-local…

sha256:f62f98bbd367198898e5a347730e84d546ee4fbc75f3b19e6ee3664c61af4d0e

このネットワークが提供するのは一つだけです。ビルドされるサンプル。サンドボックスで実行し、署名済みの受領証を保管します。等級はつけず、何も保証しません — 同じコードがあなたの環境でビルドされるかは測定していません。 合格した契約受領証を提出した異なる署名鍵の数です。1 なら作者だけ、2 以上なら他の誰かもビルドしています。鍵は自己生成で背後に登録された身元がないため、数えているのは人ではなく鍵です。 MIT-0

実行証拠

宣言された環境と署名済みの実行を分けてあります。このサンプルが何をどこで実行したかをそのまま確認できます。

証拠の基準
署名済みコントラクト合格
検証レシート
3
ビルドした署名鍵
3
宣言された環境 rust linux x64 rust rust cargo

検証実行環境

環境 コントラクト ステージ 実行日
rust 1 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · cargo@1
2026-08-17
rust 1 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · cargo@1
2026-08-18
rust 1 · linux alpine/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · cargo@1rust:1-alpine@sha256:a10e64dd139b…
2026-09-08

ケース

HOW
ゴール
Record late span fields and scoped events while proving target filters, nested subscriber restoration, thread-local isolation, and teardown behavior
パッケージ
シンボル
  • tracing::span!
  • tracing::event!
  • tracing::Span::record
  • tracing::subscriber::with_default
  • tracing_subscriber::filter::Targets
  • tracing_subscriber::Layer::with_filter
  • tracing_subscriber::layer::Context::event_scope
環境
rust
作成日
2026-08-17T01:09:31Z

コントラクト

  1. assert Span::record updates a field declared with tracing::field::Empty and the event observes the entered span scope
  2. assert a per-layer Targets filter captures the allowed target while excluding a higher-severity event from an unlisted target
  3. assert a nested with_default replaces rather than composes with the outer dispatcher, then restores the outer dispatcher when its closure returns
  4. assert a with_default dispatcher is thread-local and is not inherited by a thread spawned inside the scoped closure
  5. assert events emitted after the outer with_default closure returns are not delivered to either scoped capture

ファイル

  • Cargo.lock
  • Cargo.toml
  • csx.json
  • src/lib.rs

ソースアーティファクトをダウンロード (tar.gz)

ソース

Cargo.lock
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4

[[package]]
name = "cargo-tracing-api"
version = "0.1.0"
dependencies = [
 "tracing",
 "tracing-subscriber",
]

[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"

[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"

[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"

[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"

[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
 "unicode-ident",
]

[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
 "proc-macro2",
]

[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
 "lazy_static",
]

[[package]]
name = "syn"
version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
 "proc-macro2",
 "quote",
 "unicode-ident",
]

[[package]]
name = "thread_local"
version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070"
dependencies = [
 "cfg-if",
]

[[package]]
name = "tracing"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
 "pin-project-lite",
 "tracing-attributes",
 "tracing-core",
]

[[package]]
name = "tracing-attributes"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "tracing-core"
version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [
 "once_cell",
]

[[package]]
name = "tracing-subscriber"
version = "0.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
dependencies = [
 "sharded-slab",
 "thread_local",
 "tracing-core",
]

[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
Cargo.toml
[package]
name = "cargo-tracing-api"
version = "0.1.0"
edition = "2021"

[dependencies]
tracing = "=0.1.44"
tracing-subscriber = { version = "=0.3.23", default-features = false, features = ["registry"] }

csx.json
{"case":{"believed":"A subscriber installed with with_default is often assumed to compose with an existing default and propagate to threads spawned inside its closure.","caseId":"case:sha256:7cc6a428fa2a0b7782dbab1a2f523fa8dbd4f78620b782d8dde18bd640f8c159","contract":["assert Span::record updates a field declared with tracing::field::Empty and the event observes the entered span scope","assert a per-layer Targets filter captures the allowed target while excluding a higher-severity event from an unlisted target","assert a nested with_default replaces rather than composes with the outer dispatcher, then restores the outer dispatcher when its closure returns","assert a with_default dispatcher is thread-local and is not inherited by a thread spawned inside the scoped closure","assert events emitted after the outer with_default closure returns are not delivered to either scoped capture"],"goal":"Record late span fields and scoped events while proving target filters, nested subscriber restoration, thread-local isolation, and teardown behavior","kind":"HOW","packages":["pkg:cargo/tracing@0.1.44","pkg:cargo/tracing-subscriber@0.3.23"],"schemaVersion":1,"symbols":["tracing::span!","tracing::event!","tracing::Span::record","tracing::subscriber::with_default","tracing_subscriber::filter::Targets","tracing_subscriber::Layer::with_filter","tracing_subscriber::layer::Context::event_scope"]},"contractCommand":["cargo","test","--locked","--offline"],"environment":{"arch":"x64","ecosystem":"cargo","executionContext":"rust","language":"rust","os":"linux","packageManager":"cargo","runtime":"rust","schemaVersion":1},"license":"MIT-0","packages":["pkg:cargo/tracing@0.1.44","pkg:cargo/tracing-subscriber@0.3.23"],"schemaVersion":1,"symbols":["tracing::span!","tracing::event!","tracing::Span::record","tracing::subscriber::with_default","tracing_subscriber::filter::Targets","tracing_subscriber::Layer::with_filter","tracing_subscriber::layer::Context::event_scope"],"verifierAdapter":"cargo@1"}
src/lib.rs
//! A deterministic contract for scoped `tracing` subscribers.

#[cfg(test)]
mod tests {
    use std::collections::{BTreeMap, HashMap};
    use std::fmt;
    use std::sync::{Arc, Mutex};

    use tracing::field::{Field, Visit};
    use tracing::span::{Attributes, Id, Record};
    use tracing::{Event, Level, Subscriber};
    use tracing_subscriber::filter::{LevelFilter, Targets};
    use tracing_subscriber::layer::{Context, SubscriberExt};
    use tracing_subscriber::registry::LookupSpan;
    use tracing_subscriber::{Layer, Registry};

    #[derive(Debug, Default)]
    struct Capture {
        spans: HashMap<u64, SpanSnapshot>,
        events: Vec<EventSnapshot>,
    }

    #[derive(Debug, PartialEq, Eq)]
    struct SpanSnapshot {
        name: &'static str,
        target: &'static str,
        fields: BTreeMap<String, String>,
    }

    #[derive(Debug, PartialEq, Eq)]
    struct EventSnapshot {
        target: &'static str,
        level: &'static str,
        fields: BTreeMap<String, String>,
        scope: Vec<&'static str>,
    }

    #[derive(Clone)]
    struct CaptureLayer {
        capture: Arc<Mutex<Capture>>,
    }

    impl CaptureLayer {
        fn new(capture: Arc<Mutex<Capture>>) -> Self {
            Self { capture }
        }
    }

    impl<S> Layer<S> for CaptureLayer
    where
        S: Subscriber + for<'lookup> LookupSpan<'lookup>,
    {
        fn on_new_span(&self, attributes: &Attributes<'_>, id: &Id, _ctx: Context<'_, S>) {
            let metadata = attributes.metadata();
            let mut fields = BTreeMap::new();
            attributes.record(&mut FieldVisitor(&mut fields));
            self.capture.lock().unwrap().spans.insert(
                id.clone().into_u64(),
                SpanSnapshot {
                    name: metadata.name(),
                    target: metadata.target(),
                    fields,
                },
            );
        }

        fn on_record(&self, id: &Id, values: &Record<'_>, _ctx: Context<'_, S>) {
            let mut capture = self.capture.lock().unwrap();
            let span = capture
                .spans
                .get_mut(&id.clone().into_u64())
                .expect("recorded span was created by this subscriber");
            values.record(&mut FieldVisitor(&mut span.fields));
        }

        fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
            let metadata = event.metadata();
            let mut fields = BTreeMap::new();
            event.record(&mut FieldVisitor(&mut fields));
            let scope = ctx
                .event_scope(event)
                .map(|scope| {
                    scope
                        .from_root()
                        .map(|span| span.metadata().name())
                        .collect()
                })
                .unwrap_or_default();
            self.capture.lock().unwrap().events.push(EventSnapshot {
                target: metadata.target(),
                level: metadata.level().as_str(),
                fields,
                scope,
            });
        }
    }

    struct FieldVisitor<'a>(&'a mut BTreeMap<String, String>);

    impl Visit for FieldVisitor<'_> {
        fn record_str(&mut self, field: &Field, value: &str) {
            self.0.insert(field.name().into(), value.into());
        }

        fn record_u64(&mut self, field: &Field, value: u64) {
            self.0.insert(field.name().into(), value.to_string());
        }

        fn record_bool(&mut self, field: &Field, value: bool) {
            self.0.insert(field.name().into(), value.to_string());
        }

        fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
            self.0.insert(field.name().into(), format!("{value:?}"));
        }
    }

    #[test]
    fn scoped_subscribers_replace_restore_filter_and_do_not_cross_threads() {
        let outer_capture = Arc::new(Mutex::new(Capture::default()));
        let inner_capture = Arc::new(Mutex::new(Capture::default()));

        let outer = Registry::default().with(CaptureLayer::new(outer_capture.clone()));
        let targets = Targets::new()
            .with_default(LevelFilter::OFF)
            .with_target("sample::allowed", LevelFilter::INFO);
        let inner =
            Registry::default().with(CaptureLayer::new(inner_capture.clone()).with_filter(targets));

        tracing::subscriber::with_default(outer, || {
            tracing::event!(target: "sample::outer", Level::INFO, phase = "before");

            tracing::subscriber::with_default(inner, || {
                let request = tracing::span!(
                    target: "sample::allowed",
                    Level::INFO,
                    "request",
                    request_id = tracing::field::Empty,
                    status = "started"
                );
                request.record("request_id", &"req-42");

                let _entered = request.enter();
                tracing::event!(
                    target: "sample::allowed",
                    Level::INFO,
                    user_id = 7_u64,
                    action = "accepted"
                );
                tracing::event!(
                    target: "sample::blocked",
                    Level::ERROR,
                    secret = true,
                    action = "must-not-escape"
                );

                // `with_default` installs a thread-local default. A child thread does
                // not inherit it, even while the scoped closure is still running.
                std::thread::spawn(|| {
                    tracing::event!(
                        target: "sample::allowed",
                        Level::INFO,
                        worker = true
                    );
                })
                .join()
                .unwrap();
            });

            // Dropping the inner scope restores the previous (outer) dispatcher.
            tracing::event!(target: "sample::outer", Level::INFO, phase = "after");
        });

        // Dropping the outer scope restores the process default; neither capture
        // receives events emitted after teardown.
        tracing::event!(target: "sample::outer", Level::INFO, phase = "outside");

        let inner = inner_capture.lock().unwrap();
        assert_eq!(
            inner.events.len(),
            1,
            "blocked and child-thread events stay out"
        );
        assert_eq!(inner.events[0].target, "sample::allowed");
        assert_eq!(inner.events[0].level, "INFO");
        assert_eq!(inner.events[0].fields.get("user_id"), Some(&"7".into()));
        assert_eq!(
            inner.events[0].fields.get("action"),
            Some(&"accepted".into())
        );
        assert_eq!(inner.events[0].scope, vec!["request"]);

        assert_eq!(inner.spans.len(), 1);
        let span = inner.spans.values().next().unwrap();
        assert_eq!(span.name, "request");
        assert_eq!(span.target, "sample::allowed");
        assert_eq!(span.fields.get("status"), Some(&"started".into()));
        assert_eq!(span.fields.get("request_id"), Some(&"req-42".into()));
        drop(inner);

        let outer = outer_capture.lock().unwrap();
        assert_eq!(
            outer.events.len(),
            2,
            "inner scope replaces, then restores outer"
        );
        assert_eq!(outer.events[0].fields.get("phase"), Some(&"before".into()));
        assert_eq!(outer.events[1].fields.get("phase"), Some(&"after".into()));
        assert!(outer.spans.is_empty());
    }
}

オリジンシーダー

csx-seed