Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions lib/braintrust/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ module Braintrust
# and allows overriding with explicit options
class Config
attr_reader :api_key, :org_name, :default_project, :app_url, :api_url,
:filter_ai_spans, :span_filter_funcs
:filter_ai_spans, :span_filter_funcs, :compress_otel_payload

def initialize(api_key: nil, org_name: nil, default_project: nil, app_url: nil, api_url: nil,
filter_ai_spans: nil, span_filter_funcs: nil)
filter_ai_spans: nil, span_filter_funcs: nil, compress_otel_payload: true)
@api_key = api_key
@org_name = org_name
@default_project = default_project
@app_url = app_url
@api_url = api_url
@filter_ai_spans = filter_ai_spans
@span_filter_funcs = span_filter_funcs || []
@compress_otel_payload = compress_otel_payload
end

# Create a Config from environment variables, with option overrides
Expand All @@ -27,9 +28,10 @@ def initialize(api_key: nil, org_name: nil, default_project: nil, app_url: nil,
# @param api_url [String, nil] API URL (overrides BRAINTRUST_API_URL env var)
# @param filter_ai_spans [Boolean, nil] Enable AI span filtering (overrides BRAINTRUST_OTEL_FILTER_AI_SPANS env var)
# @param span_filter_funcs [Array<Proc>, nil] Custom span filter functions
# @param compress_otel_payload [Boolean, nil] Gzip OTEL export payloads (overrides BRAINTRUST_COMPRESS_OTEL_PAYLOAD env var). Default: true
# @return [Config] the created config
def self.from_env(api_key: nil, org_name: nil, default_project: nil, app_url: nil, api_url: nil,
filter_ai_spans: nil, span_filter_funcs: nil)
filter_ai_spans: nil, span_filter_funcs: nil, compress_otel_payload: nil)
# Parse filter_ai_spans from ENV if not explicitly provided
env_filter_ai_spans = ENV["BRAINTRUST_OTEL_FILTER_AI_SPANS"]
filter_ai_spans_value = if filter_ai_spans.nil?
Expand All @@ -38,15 +40,31 @@ def self.from_env(api_key: nil, org_name: nil, default_project: nil, app_url: ni
filter_ai_spans
end

# Gzip OTEL payloads by default; disable via env var if not explicitly provided
compress_otel_payload_value = if compress_otel_payload.nil?
parse_bool(ENV["BRAINTRUST_COMPRESS_OTEL_PAYLOAD"], default: true)
else
compress_otel_payload
end

new(
api_key: api_key || ((ENV["BRAINTRUST_API_KEY"] && ENV["BRAINTRUST_API_KEY"].empty?) ? nil : ENV["BRAINTRUST_API_KEY"]),
org_name: org_name || ENV["BRAINTRUST_ORG_NAME"],
default_project: default_project || ENV["BRAINTRUST_DEFAULT_PROJECT"],
app_url: app_url || ENV["BRAINTRUST_APP_URL"] || "https://www.braintrust.dev",
api_url: api_url || ENV["BRAINTRUST_API_URL"] || "https://api.braintrust.dev",
filter_ai_spans: filter_ai_spans_value,
span_filter_funcs: span_filter_funcs
span_filter_funcs: span_filter_funcs,
compress_otel_payload: compress_otel_payload_value
)
end

# Parse a boolean-ish env var value. Falsy values: "false", "0", "no", "off"
# (case-insensitive). nil/empty falls back to the default.
def self.parse_bool(value, default:)
return default if value.nil? || value.empty?

!%w[false 0 no off].include?(value.strip.downcase)
end
end
end
4 changes: 3 additions & 1 deletion lib/braintrust/trace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,11 @@ def self.enable(tracer_provider, state: nil, exporter: nil, config: nil)
config ||= state.respond_to?(:config) ? state.config : nil

# Create OTLP HTTP exporter unless override provided
compress = config.nil? || config.compress_otel_payload
exporter ||= SpanExporter.new(
endpoint: "#{state.api_url}/otel/v1/traces",
api_key: state.api_key
api_key: state.api_key,
compress: compress
)

# Use SimpleSpanProcessor for InMemorySpanExporter (testing), BatchSpanProcessor for production
Expand Down
8 changes: 6 additions & 2 deletions lib/braintrust/trace/span_exporter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ class SpanExporter < OpenTelemetry::Exporter::OTLP::Exporter
SUCCESS = OpenTelemetry::SDK::Trace::Export::SUCCESS
FAILURE = OpenTelemetry::SDK::Trace::Export::FAILURE

def initialize(endpoint:, api_key:)
super(endpoint: endpoint, headers: {"Authorization" => "Bearer #{api_key}"})
def initialize(endpoint:, api_key:, compress: true)
super(
endpoint: endpoint,
headers: {"Authorization" => "Bearer #{api_key}"},
compression: compress ? "gzip" : "none"
)
end

def export(span_data, timeout: nil)
Expand Down
36 changes: 35 additions & 1 deletion test/braintrust/config_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"BRAINTRUST_API_KEY" => ENV["BRAINTRUST_API_KEY"],
"BRAINTRUST_ORG_NAME" => ENV["BRAINTRUST_ORG_NAME"],
"BRAINTRUST_APP_URL" => ENV["BRAINTRUST_APP_URL"],
"BRAINTRUST_API_URL" => ENV["BRAINTRUST_API_URL"]
"BRAINTRUST_API_URL" => ENV["BRAINTRUST_API_URL"],
"BRAINTRUST_COMPRESS_OTEL_PAYLOAD" => ENV["BRAINTRUST_COMPRESS_OTEL_PAYLOAD"]
}.freeze

class Braintrust::ConfigTest < Minitest::Test
Expand Down Expand Up @@ -61,4 +62,37 @@ def test_env_vars_override_defaults

assert_equal "https://custom.braintrust.dev", config.app_url
end

def test_compress_otel_payload_defaults_to_true
config = Braintrust::Config.from_env

assert_equal true, config.compress_otel_payload
end

def test_compress_otel_payload_disabled_by_env_var
%w[false 0 no off FALSE Off].each do |val|
ENV["BRAINTRUST_COMPRESS_OTEL_PAYLOAD"] = val

config = Braintrust::Config.from_env

assert_equal false, config.compress_otel_payload,
"expected #{val.inspect} to disable compression"
end
end

def test_compress_otel_payload_truthy_env_var
ENV["BRAINTRUST_COMPRESS_OTEL_PAYLOAD"] = "true"

config = Braintrust::Config.from_env

assert_equal true, config.compress_otel_payload
end

def test_compress_otel_payload_explicit_overrides_env_var
ENV["BRAINTRUST_COMPRESS_OTEL_PAYLOAD"] = "false"

config = Braintrust::Config.from_env(compress_otel_payload: true)

assert_equal true, config.compress_otel_payload
end
end
19 changes: 19 additions & 0 deletions test/braintrust/trace/span_exporter_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,25 @@ def test_empty_span_data
assert_equal 0, exporter.calls.length
end

def test_uses_gzip_compression_by_default
exporter = Braintrust::Trace::SpanExporter.new(
endpoint: "https://example.com/otel/v1/traces",
api_key: "test-key"
)

assert_equal "gzip", exporter.instance_variable_get(:@compression)
end

def test_compression_disabled_when_compress_false
exporter = Braintrust::Trace::SpanExporter.new(
endpoint: "https://example.com/otel/v1/traces",
api_key: "test-key",
compress: false
)

assert_equal "none", exporter.instance_variable_get(:@compression)
end

def test_header_cleaned_up_even_on_error
exporter = RecordingExporter.new
exporter.define_singleton_method(:send_bytes) do |data, timeout:|
Expand Down