Skip to content

Commit 844eaf7

Browse files
committed
Attach client-level span fields
This isn't quite what we want because the fields are still buried in a "self" field, despite numerous attempts to flatten them. Opened tokio-rs/tracing#3124 to track a feature request (or similar). We have options but most are rather error prone. Perhaps the best way is our own attribute macro that would expand to `#[tracing::instrument]` with appropriate defaults to attach client-level fields like `az.namespace`. We could also write a helper function or two to just wrap the whole thing, which would also give us better control over the error or return value as well, like attaching `error.type` per our guidelines.
1 parent bb6ece4 commit 844eaf7

File tree

5 files changed

+82
-19
lines changed

5 files changed

+82
-19
lines changed

.cargo/config.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[build]
2+
rustflags = ["--cfg", "tracing_unstable"]

Cargo.lock

Lines changed: 36 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ publish = false
77
[dependencies]
88
serde = { version = "1.0.213", features = ["derive"] }
99
tokio = { version = "1.41.0", features = ["sync", "time"] }
10-
tracing = "0.1.40"
10+
tracing = { version = "0.1.40", features = ["valuable"] }
1111
url = "2.5.2"
12+
valuable = { version = "0.1.0", features = ["derive"] }
1213

1314
[dev-dependencies]
1415
clap = { version = "4.5.20", features = ["derive"] }

src/clients.rs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@ use std::{collections::HashMap, fmt, sync::Arc, time::Duration};
99
use tokio::{sync::Mutex, time::sleep};
1010
use tracing::{info_span, Instrument};
1111
use url::Url;
12+
use valuable::{Fields, NamedField, NamedValues, Structable, Valuable};
1213

1314
pub struct ExampleClient {
1415
endpoint: Url,
1516
models: Arc<Mutex<HashMap<String, Model>>>,
1617
}
1718

1819
impl ExampleClient {
20+
const NAMESPACE: &str = "Microsoft.Example";
21+
1922
pub fn new(endpoint: impl AsRef<str>) -> Result<Self> {
2023
Ok(Self {
2124
endpoint: Url::parse(endpoint.as_ref())?,
@@ -30,10 +33,11 @@ impl ExampleClient {
3033
pub async fn get_model(&self, name: &str) -> Result<Model> {
3134
let mut span = tracing::Span::current();
3235
if span
33-
.field("client")
34-
.is_none_or(|name| name.name() == "ExampleClient")
36+
.field(AZ_CLIENT_FIELD.name())
37+
.is_none_or(|name| name.name() == stringify!(ExampleClient))
3538
{
36-
span = info_span!(target: "ExampleClient::get_model", "get_model", client = "ExampleClient");
39+
span =
40+
info_span!(target: "ExampleClient::get_model", "get_model", self = self.as_value());
3741
}
3842
async move {
3943
sleep(Duration::from_millis(100)).await;
@@ -54,10 +58,10 @@ impl ExampleClient {
5458
pub async fn create_or_update_model(&self, model: Model) -> Result<Model> {
5559
let mut span = tracing::Span::current();
5660
if span
57-
.field("client")
58-
.is_none_or(|name| name.name() == "ExampleClient")
61+
.field(AZ_CLIENT_FIELD.name())
62+
.is_none_or(|name| name.name() == stringify!(ExampleClient))
5963
{
60-
span = info_span!(target: "ExampleClient::create_or_update_model", "create_or_update_model", client = "ExampleClient");
64+
span = info_span!(target: "ExampleClient::create_or_update_model", "create_or_update_model", self = self.as_value());
6165
}
6266
async move {
6367
sleep(Duration::from_millis(300)).await;
@@ -75,6 +79,10 @@ impl ExampleClient {
7579
tracing::error!(name: "create_or_update_model", target: "ExampleClient::create_or_update_model", error = %err)
7680
)
7781
}
82+
83+
pub(crate) fn as_value(&self) -> valuable::Value<'_> {
84+
tracing::field::valuable(self)
85+
}
7886
}
7987

8088
impl fmt::Debug for ExampleClient {
@@ -84,3 +92,29 @@ impl fmt::Debug for ExampleClient {
8492
.finish()
8593
}
8694
}
95+
96+
impl Valuable for ExampleClient {
97+
fn as_value(&self) -> valuable::Value<'_> {
98+
valuable::Value::Structable(self)
99+
}
100+
101+
fn visit(&self, visit: &mut dyn valuable::Visit) {
102+
visit.visit_named_fields(&NamedValues::new(
103+
FIELDS,
104+
&[
105+
stringify!(ExampleClient).as_value(),
106+
Self::NAMESPACE.as_value(),
107+
],
108+
));
109+
}
110+
}
111+
112+
pub(crate) const AZ_CLIENT_FIELD: NamedField = NamedField::new("az.client");
113+
pub(crate) const AZ_NAMESPACE_FIELD: NamedField = NamedField::new("az.namespace");
114+
static FIELDS: &[NamedField<'static>] = &[AZ_CLIENT_FIELD, AZ_NAMESPACE_FIELD];
115+
116+
impl Structable for ExampleClient {
117+
fn definition(&self) -> valuable::StructDef<'_> {
118+
valuable::StructDef::new_static(stringify!(ExampleClient), Fields::Named(FIELDS))
119+
}
120+
}

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ pub trait ExampleClientExt: private::Sealed {
1818

1919
impl ExampleClientExt for ExampleClient {
2020
#[tracing::instrument(
21-
name = "rotate",
21+
"rotate",
2222
target = "ExampleClient::rotate",
2323
skip_all,
24-
fields(client = "ExampleClient")
24+
fields(self = self.as_value())
2525
)]
2626
async fn rotate<S: Into<Secret>>(&self, name: &str, secret: S) -> Result<Model> {
2727
let mut m = self.get_model(name).await?;

0 commit comments

Comments
 (0)