#spans #tracing #tracing-subscriber #record #parent #subscriber #refactoring

tracing-record-hierarchical

Record parent tracing::Span fields from inside child tracing::Span’s context

2 releases

0.1.1 Dec 4, 2023
0.1.0 Oct 10, 2023

#351 in Profiling

Download history 19/week @ 2023-12-22 21/week @ 2023-12-29 1/week @ 2024-01-05 14/week @ 2024-01-19 40/week @ 2024-01-26 22/week @ 2024-02-02 99/week @ 2024-02-09 134/week @ 2024-02-16 111/week @ 2024-02-23 85/week @ 2024-03-01 77/week @ 2024-03-08 77/week @ 2024-03-15 56/week @ 2024-03-22 122/week @ 2024-03-29 90/week @ 2024-04-05

355 downloads per month

MIT/Apache

30KB
508 lines

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.

Dependencies

~1.4–2MB
~36K SLoC