Requires Go SDK v0.16.0 or later.
The Go 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 function. It receives each completed span as an OpenTelemetry
sdktrace.ReadOnlySpanand returns the span to export. In the function:- 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. - Return a replacement span with the new attributes.
ReadOnlySpancan’t be modified, so wrap the span in a struct that keeps everything the same except its attributes. ThecustomizedSpantype in the example below does this: it embeds the original span and overridesAttributes()to return the new list. 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 the function. Wrap it in a
config.SpanCustomizerand pass it tobraintrust.Newwith thebraintrust.WithSpanCustomizersoption.
main.go
draft-reply span’s input shows {"to": "[EMAIL]"}. The customizer redacts spans from Go SDK integrations the same way.
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 a string slice. - 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.
- Return a replacement span with the merged attributes, using
customizedSpanfrom the previous example.
environment metadata key and a production tag:
#skip-compile
- 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: Return 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.
- Pass them to
WithSpanCustomizersin the order you want them to run. Each customizer receives the span that the previous one returned. If you passWithSpanCustomizersmore than once, the SDK appends to the list.
redactEmails, then addEnvironment:
#skip-compile
How customizers run
- Every exported span: The SDK runs customizers on every span it exports, including spans you create with the OpenTelemetry API. If you set
braintrust.WithFilterAISpans(true), the SDK exports only root spans and spans whose name or attributes start withgen_ai.,braintrust.,llm.,ai., ortraceloop.. Customizers don’t run on the spans the filter drops. - Once per completed span: The SDK runs each customizer once per span, after the span ends, and can call it from a background goroutine. Make customizers safe for concurrent use, and don’t hold on to or change the
ReadOnlySpanvalues they receive or return. - Identity: Customizers can change anything except the trace ID, span ID, parent trace ID, and parent span ID.
Limitations
- Attachments: The SDK starts uploading attachments before customizers run. A customizer sees only the attachment reference, so it can’t keep the file out of Braintrust.
- Console output: Spans that
braintrust.WithEnableTraceConsoleLog(true)prints aren’t customized. - Code only: You register customizers in code. There’s no environment variable for them.
Next steps
- See all redaction options, including ingestion redaction, in Protect sensitive data.
- See
WithSpanCustomizersandconfig.SpanCustomizerin the API reference. - Set up Go SDK integrations for your AI libraries.