11import json
22import logging
33import os
4+ import threading
45import warnings
56from urllib .parse import urljoin
67
@@ -39,6 +40,9 @@ def export(self, *args, **kwargs):
3940 def force_flush (self , * args , ** kwargs ):
4041 raise ImportError (INSTALL_ERR_MSG )
4142
43+ def shutdown (self , * args , ** kwargs ):
44+ raise ImportError (INSTALL_ERR_MSG )
45+
4246 class BatchSpanProcessor :
4347 def __init__ (self , * args , ** kwargs ):
4448 raise ImportError (INSTALL_ERR_MSG )
@@ -248,7 +252,6 @@ def __init__(
248252 )
249253
250254 self ._braintrust_api_key_arg = api_key_arg
251- self ._braintrust_headers_override_authorization = "Authorization" in headers
252255 self ._braintrust_has_api_key = bool (api_key and api_key .strip ())
253256
254257 exporter_headers = {}
@@ -261,21 +264,23 @@ def __init__(
261264
262265 self .parent = parent
263266
264- super ().__init__ (endpoint = endpoint , headers = exporter_headers , ** kwargs )
267+ self ._braintrust_exporter_kwargs = {"endpoint" : endpoint , "headers" : exporter_headers , ** kwargs }
268+ self ._braintrust_shutdown = False
269+ # Guards lazy re-init against a concurrent shutdown(), which re-init would undo.
270+ self ._braintrust_lifecycle_lock = threading .Lock ()
271+ super ().__init__ (** self ._braintrust_exporter_kwargs )
265272
266273 def _set_api_key_header (self , api_key : str ) -> None :
267- if not self ._braintrust_headers_override_authorization :
268- authorization = {"Authorization" : f"Bearer { api_key } " }
269- exporter_headers = getattr (self , "_headers" , None )
270- if isinstance (exporter_headers , dict ):
271- exporter_headers .update (authorization )
272- else :
273- self ._headers = {** dict (exporter_headers or {}), ** authorization }
274-
275- session = getattr (self , "_session" , None )
276- if session is not None :
277- session .headers .update (authorization )
278- self ._braintrust_has_api_key = True
274+ with self ._braintrust_lifecycle_lock :
275+ if self ._braintrust_has_api_key :
276+ return
277+ exporter_kwargs = self ._braintrust_exporter_kwargs
278+ if "Authorization" not in exporter_kwargs ["headers" ] and not self ._braintrust_shutdown :
279+ # Re-run the upstream constructor instead of patching its private header
280+ # storage, which moved in opentelemetry-exporter-otlp-proto-http 1.45.
281+ headers = {"Authorization" : f"Bearer { api_key } " , ** exporter_kwargs ["headers" ]}
282+ super ().__init__ (** {** exporter_kwargs , "headers" : headers })
283+ self ._braintrust_has_api_key = True
279284
280285 def _ensure_api_key (self ) -> None :
281286 if self ._braintrust_has_api_key :
@@ -298,6 +303,11 @@ def force_flush(self, timeout_millis=30000):
298303 self ._ensure_api_key ()
299304 return super ().force_flush (timeout_millis )
300305
306+ def shutdown (self ):
307+ with self ._braintrust_lifecycle_lock :
308+ self ._braintrust_shutdown = True
309+ return super ().shutdown ()
310+
301311
302312def add_braintrust_span_processor (
303313 tracer_provider ,
0 commit comments