Requires Ruby SDK v0.6.0 or later.
on_span_export method that the SDK calls on each span after the span ends. The SDK runs customizers on every span it exports, including spans from integrations and spans you create with the OpenTelemetry API.
The Ruby SDK has no masking function, so span customizers are how you remove sensitive data before it leaves your application. To redact data after Braintrust receives it instead, see Protect sensitive data.
Register a customizer
To create a customizer and register it with the SDK:- Write the class. Define an
on_span_export(span)method. It receives each completed span and returns the span to export. In the method:- Read the attributes you want to change. Integrations store the span’s content as JSON strings in
braintrust.input_json(input),braintrust.output_json(output), andbraintrust.metadata(metadata). Braintrust parses these into the span’s fields. - Change the span and return it.
span.attributesis a writable hash, so you can replace or delete entries in place. You can also change other fields, such asspan.name. To leave a span unchanged, return it as is.
- Read the attributes you want to change. Integrations store the span’s content as JSON strings in
- Register an instance. Pass it in the
span_customizers:array ofBraintrust.init.
draft-reply span’s input shows {"to": "[EMAIL]"}. The customizer redacts spans from Ruby SDK integrations the same way, as long as you require the integration’s gem before you call Braintrust.init.
Add tags and metadata
To add metadata or tags to a span without losing the values the integration recorded:- Read the existing values.
braintrust.metadataholds the span’s whole metadata object as a JSON string, andbraintrust.tagsholds its tags as an array of strings. - Merge in your values. Add your keys to the parsed metadata and your tags to the existing list. Replacing either attribute drops what was there.
- Write the merged values back to
span.attributesand return the span.
environment metadata key and a production tag:
on_span_export raises an error, the SDK drops the whole export batch, as described in How customizers run. Choose the behavior that fits each customizer:
- Customizers that add data, like this one: Return the span unchanged when you can’t process it. This example does that when the existing metadata isn’t valid JSON.
- Customizers that redact data: Raise an error when you can’t redact a span, so the SDK drops the batch instead of sending unredacted data.
Chain customizers
To run more than one customizer:- Write each customizer separately, so each one handles one change and you can test and reuse it on its own.
- List them in
span_customizers:in the order you want them to run. Each customizer receives the span that the previous one returned.
RedactEmails, then AddEnvironment:
How customizers run
- Every exported span: The SDK runs customizers on every span it exports, including spans from integrations, evaluations, and spans you create with the OpenTelemetry API. If you set
filter_ai_spans: trueorBRAINTRUST_OTEL_FILTER_AI_SPANS=true, the SDK exports only spans whose name or attributes start withgen_ai.,braintrust.,llm.,ai., ortraceloop.. Customizers don’t run on the spans that this filter or yourspan_filter_funcsdrop. - Once per completed span: The SDK runs each customizer once per span, after the span ends, on the background export thread. Keep customizers fast, because slow customizers delay exports. Spans that your customizer creates, for example by calling an instrumented client, aren’t traced.
- Identity: Customizers can change anything except the trace ID, span ID, and parent span ID, which are read-only. To return a different span instead, build it from
span.to_span_data.dupso it keeps the same IDs, and return thatOpenTelemetry::SDK::Trace::SpanData.
Limitations
- Code only: You register customizers in code, with
Braintrust.init. There’s no environment variable for them, andbraintrust/setupandbraintrust execcan’t register them. - Objects, not blocks: Each entry in
span_customizers:must respond toon_span_export. Procs and lambdas don’t, soBraintrust.initraises anArgumentErrorfor them, and for any other object without the method. - No custom exporter: Customizers run in the SDK’s exporter. If you pass both
span_customizers:andexporter:toBraintrust.init, it raises anArgumentError. - Fixed at initialization:
Braintrust.initcopies thespan_customizers:array, so later changes to the array don’t apply. - Console output: Spans that
BRAINTRUST_ENABLE_TRACE_CONSOLE_LOGprints aren’t customized.
Next steps
- See all redaction options, including ingestion redaction, in Protect sensitive data.
- See
span_customizersin the API reference. - Set up Ruby SDK integrations for your AI libraries.