Module: Braintrust::Trace::SpanOrigin
- Included in:
- SpanExporter
- Defined in:
- lib/braintrust/trace/span_origin.rb
Overview
Span origin provenance decoration.
This is a behavior, not a type. Prepend it onto any exporter whose
export(span_data, timeout:) it can super into, and every exported
SpanData gains a braintrust.context_json attribute carrying span origin
(SDK name/version, instrumentation scope, environment).
Because it only ever touches the SpanData copies handed to this exporter, the enrichment is invisible to any other exporter sharing the same tracer provider - there is no global patch and nothing leaks onto a customer's other OTel traces.
Constant Summary collapse
- CONTEXT_JSON_ATTR_KEY =
"braintrust.context_json"
Class Method Summary collapse
- .attributes_with_origin(attributes, instrumentation_name:, environment:) ⇒ Object
-
.enrich(span_data, environment:) ⇒ OpenTelemetry::SDK::Trace::SpanData
Enrich a single SpanData with span origin provenance.
- .instrumentation_name(span) ⇒ Object
- .parse_context_json(raw) ⇒ Object
Instance Method Summary collapse
-
#export(span_data, timeout: nil) ⇒ Integer
Exporter behavior: enrich each SpanData with span origin before export.
Class Method Details
.attributes_with_origin(attributes, instrumentation_name:, environment:) ⇒ Object
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/braintrust/trace/span_origin.rb', line 55 def self.attributes_with_origin(attributes, instrumentation_name:, environment:) context = parse_context_json(attributes[CONTEXT_JSON_ATTR_KEY]) span_origin = context["span_origin"].is_a?(Hash) ? context["span_origin"] : {} span_origin_changed = false unless span_origin.key?("name") span_origin["name"] = "braintrust.sdk.ruby" span_origin_changed = true end unless span_origin.key?("version") span_origin["version"] = Braintrust::VERSION span_origin_changed = true end unless span_origin.key?("instrumentation") span_origin["instrumentation"] = {"name" => instrumentation_name} span_origin_changed = true end if environment && !span_origin.key?("environment") span_origin["environment"] = environment span_origin_changed = true end context_changed = context["span_origin"] != span_origin || span_origin_changed return attributes unless context_changed context["span_origin"] = span_origin attributes.merge(CONTEXT_JSON_ATTR_KEY => JSON.generate(context)) end |
.enrich(span_data, environment:) ⇒ OpenTelemetry::SDK::Trace::SpanData
Enrich a single SpanData with span origin provenance. Mutates the SpanData in place (replacing its frozen attributes hash with a new frozen hash - it never mutates the shared hash) and returns it.
40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/braintrust/trace/span_origin.rb', line 40 def self.enrich(span_data, environment:) attributes = span_data.attributes || {} enriched_attributes = attributes_with_origin( attributes, instrumentation_name: instrumentation_name(span_data), environment: environment ) return span_data if enriched_attributes.equal?(attributes) span_data.attributes = enriched_attributes.freeze span_data.total_recorded_attributes = enriched_attributes.length span_data end |
.instrumentation_name(span) ⇒ Object
93 94 95 96 97 98 99 100 101 102 |
# File 'lib/braintrust/trace/span_origin.rb', line 93 def self.instrumentation_name(span) if span.respond_to?(:instrumentation_scope) && span.instrumentation_scope&.respond_to?(:name) return span.instrumentation_scope.name end if span.respond_to?(:instrumentation_library) && span.instrumentation_library&.respond_to?(:name) return span.instrumentation_library.name end "braintrust-ruby" end |
.parse_context_json(raw) ⇒ Object
84 85 86 87 88 89 90 91 |
# File 'lib/braintrust/trace/span_origin.rb', line 84 def self.parse_context_json(raw) return {} unless raw.is_a?(String) && !raw.strip.empty? parsed = JSON.parse(raw) parsed.is_a?(Hash) ? parsed : {} rescue JSON::ParserError {} end |
Instance Method Details
#export(span_data, timeout: nil) ⇒ Integer
Exporter behavior: enrich each SpanData with span origin before export.
26 27 28 29 30 31 32 |
# File 'lib/braintrust/trace/span_origin.rb', line 26 def export(span_data, timeout: nil) # Environment is process-global and stable; read it once per batch # rather than once per span. It is cheap (ENV reads only). environment = Internal::Env.detect_environment enriched = span_data.map { |sd| SpanOrigin.enrich(sd, environment: environment) } super(enriched, timeout: timeout) end |