mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-09 15:19:10 +02:00
* Reapply span attributes after creation to survive non-forwarding samplers Tracer.start_span builds the span from sampling_result.attributes, not the attributes kwarg — a custom Sampler that returns SamplingResult(RECORD_AND_SAMPLE) without forwarding attributes silently drops everything FastMCP passed at creation time. Reapply the same attributes immediately after span creation (guarded by is_recording()) so on_start hooks and samplers still see them, while the finished span is guaranteed to carry FastMCP's telemetry regardless of sampler behavior. * Restore only missing span attributes, not a blanket reapply Reapplying all attributes after span creation overwrote values a sampler deliberately set (e.g. a redacted mcp.method.name) and inflated dropped-attribute counts when the SDK's attribute limit was hit. Compare against the span's existing attributes and restore only the keys a non-forwarding sampler actually dropped, via a shared restore_missing_attributes() helper in fastmcp.telemetry. * Gate attribute restore on all-or-nothing, not per-key Restoring only missing keys reinserted attributes the SDK's bounded attribute map had already evicted under a low OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, evicting a different retained key and inflating dropped_attributes beyond what the sampler actually dropped. Gate on none of our attributes being present (plus dropped_attributes == 0) instead — the regression this exists to fix is a sampler dropping everything, and eviction under a limit always leaves some. Renamed restore_missing_attributes to restore_dropped_attributes to match. * Gate attribute restore on empty span, not per-key presence A sampler that intentionally supplies only its own attributes (e.g. to strip component names or resource URIs for privacy/cardinality control) left none of FastMCP's keys on the span, so the previous all-or-nothing gate treated it identically to a bare non-forwarding sampler and restored everything, defeating the filter. Key off the span having no attributes at all instead — a bare sampler leaves it empty, a filtering sampler doesn't. |
||
|---|---|---|
| .. | ||
| __init__.py | ||
| test_module.py | ||
| test_span_attributes.py | ||