Skip to content

[FEATURE]: Public API for removing tags from a Span (4.x lost this capability) #19152

Description

@jhaenchen

Motivation

I maintain a TraceProcessor that acts as an allowlist-based redaction filter: it walks each span's tags, keeps the ones on a known-safe allowlist, transforms a few (e.g. redacts message bodies while keeping the command name), and drops everything else. This is a common shape for teams that need to guarantee no unexpected tag ever ships to Datadog — a defense-in-depth layer against an integration or a well-meaning set_tag() call leaking PII/PHI.

Under ddtrace 2.x / 3.x this was straightforward: the processor read span._meta, computed the sanitized dict, and assigned it back. _meta was a single-underscore internal attribute, but the pattern was stable and widely used.

In 4.x the Span was restructured (data moved into a native SpanData base + context) and _meta is no longer accessible on the Span. The public API kept the additive tag operations — get_tag, set_tag, get_tags, set_tags — but there is no public tag removal. set_tag(k, None) doesn't remove; it stores None. The only working call I've found is span._remove_attribute(k), which is defined on the internal SpanData base.

Ask

A supported public method for tag removal on Span. Something like:

span.remove_tag(key: str) -> None

and matching remove_metric(key) / remove_struct_tag(key) for the parallel storage. Alternatively, blessing _remove_attribute (etc.) as stable via docs would achieve the same practical outcome for consumers.

Current workaround

Falling back to the private methods:

  • span._remove_attribute(key) — tag removal
  • span._has_meta_structs() / span._get_meta_structs() / span._remove_struct_tag(key) — the same, for struct-typed metadata

These work in 4.11.x. It's a code smell, and it puts consumers on a manual-review cadence for every ddtrace bump — which is the opposite of what a public API should require.

Alternatives considered

  • Redact at write time (subclass Span.set_tag) — inverts the design, wraps a hot path, and creates surface area for accidental bypass.
  • Drop the whole trace when an unexpected tag appears — nuclear; auto-instrumentation adds runtime tags on every span, so this would drop most traces in practice.
  • Skip 4.x — not viable long-term; 4.8.2 is the earliest release with the fix for CVE-2026-50271 (baggage-header DoS).

Happy to send a PR if there's alignment on the surface.

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