55import functools
66import json
77import os
8+ import platform
89import re
910import sys
1011import time
@@ -708,6 +709,8 @@ def _initialize(self) -> None:
708709 if self ._initialized : # pragma: no cover
709710 return
710711
712+ emscripten = platform .system ().lower () == 'emscripten'
713+
711714 with suppress_instrumentation ():
712715 otel_resource_attributes : dict [str , Any ] = {
713716 ResourceAttributes .SERVICE_NAME : self .service_name ,
@@ -744,7 +747,11 @@ def _initialize(self) -> None:
744747 ):
745748 otel_resource_attributes [RESOURCE_ATTRIBUTES_CODE_WORK_DIR ] = os .getcwd ()
746749
747- resource = Resource .create (otel_resource_attributes )
750+ if emscripten : # pragma: no cover
751+ # Resource.create creates a thread pool which fails in Pyodide / Emscripten
752+ resource = Resource (otel_resource_attributes )
753+ else :
754+ resource = Resource .create (otel_resource_attributes )
748755
749756 # Set service instance ID to a random UUID if it hasn't been set already.
750757 # Setting it above would have also mostly worked and allowed overriding via OTEL_RESOURCE_ATTRIBUTES,
@@ -849,8 +856,11 @@ def check_token():
849856 if show_project_link and validated_credentials is not None :
850857 validated_credentials .print_token_summary ()
851858
852- thread = Thread (target = check_token , name = 'check_logfire_token' )
853- thread .start ()
859+ if emscripten : # pragma: no cover
860+ check_token ()
861+ else :
862+ thread = Thread (target = check_token , name = 'check_logfire_token' )
863+ thread .start ()
854864
855865 headers = {'User-Agent' : f'logfire/{ VERSION } ' , 'Authorization' : self .token }
856866 session = OTLPExporterHttpSession (max_body_size = OTLP_MAX_BODY_SIZE )
@@ -864,10 +874,19 @@ def check_token():
864874 span_exporter = RetryFewerSpansSpanExporter (span_exporter )
865875 span_exporter = RemovePendingSpansExporter (span_exporter )
866876 schedule_delay_millis = _get_int_from_env (OTEL_BSP_SCHEDULE_DELAY ) or 500
867- add_span_processor (BatchSpanProcessor (span_exporter , schedule_delay_millis = schedule_delay_millis ))
877+ if emscripten : # pragma: no cover
878+ # BatchSpanProcessor uses threads which fail in Pyodide / Emscripten
879+ logfire_processor = SimpleSpanProcessor (span_exporter )
880+ else :
881+ logfire_processor = BatchSpanProcessor (
882+ span_exporter , schedule_delay_millis = schedule_delay_millis
883+ )
884+ add_span_processor (logfire_processor )
868885
869- if metric_readers is not None :
870- metric_readers += [
886+ # TODO should we warn here if we have metrics but we're in emscripten?
887+ # I guess we could do some hack to use InMemoryMetricReader and call it after user code has run?
888+ if metric_readers is not None and not emscripten :
889+ metric_readers .append (
871890 PeriodicExportingMetricReader (
872891 QuietMetricExporter (
873892 OTLPMetricExporter (
@@ -883,7 +902,7 @@ def check_token():
883902 preferred_temporality = METRICS_PREFERRED_TEMPORALITY ,
884903 )
885904 )
886- ]
905+ )
887906
888907 if processors_with_pending_spans :
889908 pending_multiprocessor = SynchronousMultiSpanProcessor ()
0 commit comments