tracing-record-hierarchical

Crates.iotracing-record-hierarchical
lib.rstracing-record-hierarchical
version0.1.1
sourcesrc
created_at2023-10-10 12:05:35.848474
updated_at2023-12-04 14:15:48.231052
descriptionRecord parent `tracing::Span` fields from inside child `tracing::Span`'s context.
homepagehttps://github.com/instrumentisto/tracing-record-hierarchical-rs
repositoryhttps://github.com/instrumentisto/tracing-record-hierarchical-rs
max_upload_size
id999055
size40,882
Kai Ren (tyranron)

documentation

https://docs.rs/tracing-record-hierarchical

README

tracing-record-hierarchical

crates.io Rust 1.70+ Unsafe Forbidden CI Rust docs

API Docs | Changelog

Record parent tracing::Span fields from inside child tracing::Span's context.

Motivation

Dealing with the complex relationship in the hierarchy of nested Spans in tracing may become quite cumbersome. When faced with the need to record a new value for a field of a Span higher in the tree, users have no choice but to refactor their code in some way to allow that. This includes:

  1. Extracting a Span::record out of the child Span:

    fn called_from_withing_a_span() {
        let id = 42;
        tracing::Span::current().record("id", id);
    
        tracing::info_span!("child").in_scope(|| {
            // ...
        })
    }
    

    Which doesn't work when:

    • There is another "layer" of Spans between them;
    • The value that needs to be recorded is computed in the function (you may still be able to work around by returning from in_scope closure);
    • The parent Span is in another crate you have no control of.
  2. Bringing the parent Span to the child:

    fn parent() {
        let parent_span = tracing::info_span!(
             "parent",
             id = tracing::field::Empty,
        );
        let _entered = parent_span.enter();
        child(parent_span.clone());
    }
    
    #[tracing::instrument(skip_all)]
    fn child(parent_span: tracing::Span) {
        let id = 42;
        parent_span.record("id", id);
    }
    

    We had to construct a parent Span using *_span! macro and pass it to the child.

Those workarounds are not ergonomic. Furthermore, in some cases cannot be used at all.

Overview

This crate adds a HierarchicalRecord Layer and a SpanExt trait with the record_hierarchical() method that can be used as a drop-in replacement for a Span::record.

Usage

Add the HierarchicalRecord Layer to your subscriber:

# use tracing_subscriber::prelude::*;
use tracing_record_hierarchical::HierarchicalRecord;

fn init_tracing() {
    tracing_subscriber::registry()
        .with(HierarchicalRecord::default())
        .init();
}

Whenever you're to use a Span::record to record a value to a parent Span's field, call the record_hierarchical() method instead, or a panicking must_record_hierarchical() version instead:

use tracing_record_hierarchical::SpanExt as _;

#[tracing::instrument(fields(foo = tracing::field::Empty))]
fn foo() {
    bar();
}

#[tracing::instrument]
fn bar() {
    tracing::Span::current()
        // This walks up the hierarchy of `Span`s from the `span` the method 
        // was called on (`current()` in this example) to the "root" `Span`. 
        // If some `Span` in the hierarchy has the `foo` field, the provided 
        // value will be recorded there. If none of the `Span`s in the 
        // hierarchy has this field, a panic will occur.
        .must_record_hierarchical("foo", 42);
}
#
# fn main() {
#     use tracing_subscriber::prelude::*;
#     use tracing_record_hierarchical::HierarchicalRecord;
# 
#     tracing_subscriber::registry().with(HierarchicalRecord::default()).init();
# 
#     foo();
# }

License

Copyright © 2023 Instrumentisto Team, https://github.com/instrumentisto

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Commit count: 11

cargo fmt