Skip to content

Commit c059f81

Browse files
committed
core: impl field::Value for String (#2164)
## Motivation Currently, it is rather difficult to record `String`s as field values, even though `&str` is a primitive `Value` type. For example, this code does not currently compile: ```rust let my_string = String::from("hello world!"); tracing::debug!(my_string); ``` Instead, it is necessary to explicitly call `String::as_str` or a similar conversion method: ```rust let my_string = String::from("hello world!"); tracing::debug!(my_string = my_string.as_str()); ``` This is unfortunate, as it makes a fairly straightforward, commomplace task (recording a `String` as a field) unnecessarily verbose. ## Solution This branch adds an `impl Value for String` in `tracing-core`. The impl simply calls `String::as_str` and then calls `record_str`. Because `Value` takes an `&self`, and there are preexisting `impl<T: Value> Value` for `&T` and `&mut T`, the string is not consumed, and `&String` or `&mut String`s can also be used as `Value`s. I've also added tests validating that this actually works.
1 parent bf695ba commit c059f81

File tree

3 files changed

+43
-0
lines changed

3 files changed

+43
-0
lines changed

tracing-attributes/tests/fields.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ fn fn_clashy_expr_field2(s: &str) {
3131
let _ = s;
3232
}
3333

34+
#[instrument(fields(s = &s))]
35+
fn fn_string(s: String) {
36+
let _ = s;
37+
}
38+
3439
#[derive(Debug)]
3540
struct HasField {
3641
my_field: &'static str,
@@ -134,6 +139,14 @@ fn empty_field() {
134139
});
135140
}
136141

142+
#[test]
143+
fn string_field() {
144+
let span = span::mock().with_field(mock("s").with_value(&"hello world").only());
145+
run_test(span, || {
146+
fn_string(String::from("hello world"));
147+
});
148+
}
149+
137150
fn run_test<F: FnOnce() -> T, T>(span: NewSpan, fun: F) {
138151
let (subscriber, handle) = subscriber::mock()
139152
.new_span(span)

tracing-core/src/field.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ use crate::stdlib::{
116116
hash::{Hash, Hasher},
117117
num,
118118
ops::Range,
119+
string::String,
119120
};
120121

121122
use self::private::ValidLen;
@@ -596,6 +597,13 @@ where
596597
}
597598
}
598599

600+
impl crate::sealed::Sealed for String {}
601+
impl Value for String {
602+
fn record(&self, key: &Field, visitor: &mut dyn Visit) {
603+
visitor.record_str(key, self.as_str())
604+
}
605+
}
606+
599607
impl fmt::Debug for dyn Value {
600608
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
601609
// We are only going to be recording the field value, so we don't

tracing/tests/event.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,28 @@ fn option_ref_mut_values() {
470470
some_u64 = some_u64,
471471
none_u64 = none_u64
472472
);
473+
})
474+
}
475+
476+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
477+
#[test]
478+
fn string_field() {
479+
let (subscriber, handle) = subscriber::mock()
480+
.event(event::mock().with_fields(field::mock("my_string").with_value(&"hello").only()))
481+
.event(
482+
event::mock().with_fields(field::mock("my_string").with_value(&"hello world!").only()),
483+
)
484+
.done()
485+
.run_with_handle();
486+
with_default(subscriber, || {
487+
let mut my_string = String::from("hello");
488+
489+
tracing::event!(Level::INFO, my_string);
490+
491+
// the string is not moved by using it as a field!
492+
my_string.push_str(" world!");
493+
494+
tracing::event!(Level::INFO, my_string);
473495
});
474496

475497
handle.assert_finished();

0 commit comments

Comments
 (0)