Skip to content

core: Make dyn Any + 'static a primitive, recordable type #905

@davidbarsky

Description

@davidbarsky

Feature Request

Crates

  • tracing-core

Motivation

  • More easily record structured key/value pairs using tracing_core::Value without needing to take a public dependency on serde 1.0.

Proposal

David:

Implement tracing_core::Value to be implemented on T: dyn std::any::Any + 'static. This would allow layers to opt-in to handle formatting of arbitrary types without requiring crates such as hyperium/http to take a public dependency on tracing_core::Value. I haven't really thought through the ecosystem-wide implications of supporting this, but I suspect tracing-subscriber could support a registry of formatters for arbitrary types, such that subscribers and layers won't need to take an explicit dependency on a given formatter.

Eliza, continuing in #819:

Then, we could add a Visit::record_any method to the Visit trait, where the visitor can just downcast the recorded type to whatever it wants (and, eventually, when we introduce ValueSet indexing, ValueSet::get_any). We would probably add a tracing::field::any helper function, and/or a new sigil, for recording something as an Any without having to cast it as a dyn Any.

This does have one significant downside: currently, all the record methods fall through to fmt::Debug. This means that if a subscriber does not have special handling for a particular type, it can just print it. However, an Any trait object is not known to implement fmt::Debug. This means that if you don't have a particular type to downcast to, you can't really do anything of value with an Any.

We could hack around that limitation by making the Any value type be T: Any + Debug, and recording it by wrapping it in a special struct that stores a dyn Any and a closure that downcasts the Any to its original type so that it can be formatted with Debug. But, this is a bit awkward.

This would also, critically, not allow us to record arbitrary Serialize types: only known ones. If the subscriber knows specific Any types it wants to serialize, it could downcast them and use their Serialize implementations. However, it cannot say "i will record anything that serde can serialize".

To address the "I can only record types that I know how to downcast" problem, Layers and Subscribers would be encouraged to provide a registry of formatters + downcasters for std::any::Any that the end-user (e.g., the person creating a binary executable) would register when creating their subscriber or layer.

Alternatives

Don't do this. Based off Eliza's response, I worry that supporting dyn Any + 'static is strictly worse than something like Value::from_serde or the current practice of extensions traits on Spans that allow users to directly write to a Subscriber's extensions.

  • Value::from_serde, or something equivalent to Value::from_serde, seems like a more general purpose and correct solution than dyn Any + 'static because it doesn't require Layer or Subscriber-specific knowledge of types.
  • While Subscriber/Layer-specific extension traits are Layer/Subscriber-specific, this approach doesn't introduce a potential source of complexity and poor interoperability between Subscribers to tracing_core.
  • dyn Any + 'static could prevent the usage of non-'static references, requiring a clone to record a value. This is something that tracing has avoided when recording other values and wouldn't hit if tracing_core::Value supported something serde-style serializers.

On the other hand, a registry of formatting/downcasting functions for std::any::Any could serve nice compliment for specialized, associative collections like http::Request and friends, in that it provides a handy escape hatch for types/crates that either can't or won't take a public dependency on tracing_core::Value. That said, I'm not sure this benefit is worth the downsides because Serde's support for implementing Serialize/Deserialize on foreign types would handle this usecase nicely.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions