コード例 #1
0
    def test_custom_tracer_provider(self):
        provider = TracerProvider(
            resource=Resource.create(
                {
                    "service.name": "test",
                    "deployment.environment": "env",
                    "service.version": "1234",
                },
            ),
        )
        provider.add_span_processor(
            export.SimpleSpanProcessor(self.memory_exporter)
        )

        SQLAlchemyInstrumentor().instrument(tracer_provider=provider)
        from sqlalchemy import create_engine  # pylint: disable-all

        engine = create_engine("sqlite:///:memory:")
        cnx = engine.connect()
        cnx.execute("SELECT	1 + 1;").fetchall()
        spans = self.memory_exporter.get_finished_spans()

        self.assertEqual(len(spans), 2)
        self.assertEqual(spans[0].resource.attributes["service.name"], "test")
        self.assertEqual(
            spans[0].resource.attributes["deployment.environment"], "env"
        )
        self.assertEqual(
            spans[0].resource.attributes["service.version"], "1234"
        )
コード例 #2
0
def setup_instrumentation(app):
    settings: TracingSettings = _get_settings()

    _TRACE_PROVIDER = TracerProvider(
        resource=Resource.create({"service.name": settings.jaeger_service})
    )
    trace.set_tracer_provider(_TRACE_PROVIDER)

    if settings.jaeger_hostname:  # pragma: no cover
        _JAEGER_EXPORTER = JaegerExporter(
            agent_host_name=settings.jaeger_hostname,
            agent_port=settings.jaeger_port,
        )

        _TRACE_PROVIDER.add_span_processor(BatchSpanProcessor(_JAEGER_EXPORTER))

    AioHttpClientInstrumentor().instrument()
    RequestsInstrumentor().instrument()

    # Register logging middleware
    app.middleware("http")(_log_requests_middleware)
    app.middleware("http")(_bind_logger_tracecontext_middleware)

    FastAPIInstrumentor.instrument_app(app)
    return app
コード例 #3
0
    def __init__(self, test_mode: bool):
        if SodaTelemetry.__instance is not None:
            raise Exception("This class is a singleton!")
        else:
            SodaTelemetry.__instance = self

        self.__send = self.soda_config.send_anonymous_usage_stats or test_mode

        if self.__send:
            logger.info("Setting up usage telemetry.")

            self.__provider = TracerProvider(
                resource=Resource.create(
                    {
                        'os.architecture': platform.architecture(),
                        'python.version': platform.python_version(),
                        'python.implementation': platform.python_implementation(),
                        ResourceAttributes.OS_TYPE: platform.system(),
                        ResourceAttributes.OS_VERSION: platform.version(),
                        'platform': platform.platform(),
                        ResourceAttributes.SERVICE_VERSION: SODA_SQL_VERSION,
                        ResourceAttributes.SERVICE_NAME: 'soda',
                        ResourceAttributes.SERVICE_NAMESPACE: 'soda-sql',
                    }
                )
            )

            if test_mode:
                self.__setup_for_test()
            else:
                self.__setup()
        else:
            logger.info("Skipping usage telemetry setup.")
コード例 #4
0
    def test_mixed_mode(self):
        """Test that span parent-child relationship is kept between
        OpenTelemetry and the OpenTracing shim"""

        span_shim = self.shim.start_span("TestSpan16")

        with self.shim.scope_manager.activate(span_shim, finish_on_close=True):

            with (TracerProvider().get_tracer(__name__).start_as_current_span(
                    "abc")) as opentelemetry_span:

                self.assertIs(
                    span_shim.unwrap().context,
                    opentelemetry_span.parent,
                )

        with (TracerProvider().get_tracer(__name__).start_as_current_span(
                "abc")) as opentelemetry_span:

            with self.shim.start_active_span("TestSpan17") as scope:

                self.assertIs(
                    scope.span.unwrap().parent,
                    opentelemetry_span.context,
                )
コード例 #5
0
def _configure_tracing(options: _Options) -> TracerProvider:
    provider = TracerProvider(resource=options.resource)
    set_global_response_propagator(options.response_propagator)  # type: ignore
    trace.set_tracer_provider(provider)
    for factory in options.span_exporter_factories:
        provider.add_span_processor(BatchSpanProcessor(factory(options)))
    return provider
コード例 #6
0
ファイル: _helpers.py プロジェクト: zoercai/python-spanner
class OpenTelemetryBase(unittest.TestCase):
    def setUp(self):
        if HAS_OPENTELEMETRY_INSTALLED:
            self.original_tracer_provider = trace_api.get_tracer_provider()
            self.tracer_provider = TracerProvider()
            self.memory_exporter = InMemorySpanExporter()
            span_processor = export.SimpleExportSpanProcessor(
                self.memory_exporter)
            self.tracer_provider.add_span_processor(span_processor)
            trace_api.set_tracer_provider(self.tracer_provider)

    def tearDown(self):
        if HAS_OPENTELEMETRY_INSTALLED:
            trace_api.set_tracer_provider(self.original_tracer_provider)

    def assertNoSpans(self):
        if HAS_OPENTELEMETRY_INSTALLED:
            span_list = self.memory_exporter.get_finished_spans()
            self.assertEqual(len(span_list), 0)

    def assertSpanAttributes(self,
                             name,
                             status=StatusCanonicalCode.OK,
                             attributes=None,
                             span=None):
        if HAS_OPENTELEMETRY_INSTALLED:
            if not span:
                span_list = self.memory_exporter.get_finished_spans()
                self.assertEqual(len(span_list), 1)
                span = span_list[0]

            self.assertEqual(span.name, name)
            self.assertEqual(span.status.canonical_code, status)
            self.assertEqual(dict(span.attributes), attributes)
コード例 #7
0
def setup():
    reload_module(opentelemetry_tracing)
    tracer_provider = TracerProvider()
    memory_exporter = InMemorySpanExporter()
    span_processor = SimpleExportSpanProcessor(memory_exporter)
    tracer_provider.add_span_processor(span_processor)
    trace.set_tracer_provider(tracer_provider)
    yield memory_exporter
コード例 #8
0
ファイル: _helpers.py プロジェクト: zoercai/python-spanner
 def setUp(self):
     if HAS_OPENTELEMETRY_INSTALLED:
         self.original_tracer_provider = trace_api.get_tracer_provider()
         self.tracer_provider = TracerProvider()
         self.memory_exporter = InMemorySpanExporter()
         span_processor = export.SimpleExportSpanProcessor(
             self.memory_exporter)
         self.tracer_provider.add_span_processor(span_processor)
         trace_api.set_tracer_provider(self.tracer_provider)
コード例 #9
0
def init(tracefile: Optional[IO[str]] = None) -> None:
    tracer_provider = TracerProvider()
    if tracefile is not None:
        tracer_provider.add_span_processor(
            SimpleExportSpanProcessor(
                ConsoleSpanExporter(out=tracefile, formatter=single_line_json)
            )
        )
    trace.set_tracer_provider(tracer_provider)
コード例 #10
0
 def test_tracer_provider_override_warning(self):
     """trace.set_tracer_provider should throw a warning when overridden"""
     trace.set_tracer_provider(TracerProvider())
     with self.assertLogs(level=WARNING) as test:
         trace.set_tracer_provider(TracerProvider())
         self.assertEqual(
             test.output,
             [("WARNING:opentelemetry.trace:Overriding current "
               "TracerProvider")],
         )
コード例 #11
0
def configure_tracing(configuration: dict):
    # OTLP Exporter configuration
    if configuration['exporter'] == 'otlp':
        service_name = {'service.name': configuration['service_name']}
        resource = Resource(service_name)
        trace.set_tracer_provider(TracerProvider(resource=resource))
        exporter = OTLPSpanExporter(
            endpoint=configuration['exporter_endpoint'],
            insecure=configuration['exporter_insecure'])
        trace.get_tracer(__name__)
        span_processor = BatchExportSpanProcessor(exporter)
        trace.get_tracer_provider().add_span_processor(span_processor)

    # Jaeger HTTP Exporter configuration
    elif configuration['exporter'] == 'jaeger_http':
        exporter = JaegerSpanExporter(
            service_name=configuration['service_name'],
            collector_endpoint=configuration['exporter_endpoint'],
        )
        trace.set_tracer_provider(TracerProvider())
        trace.get_tracer(__name__)
        span_processor = BatchExportSpanProcessor(exporter)
        trace.get_tracer_provider().add_span_processor(span_processor)

    # Jaeger Thrifth Compact Exporter configuration
    elif configuration['exporter'] == 'jaeger_thrift':
        exporter = JaegerSpanExporter(
            service_name=configuration['service_name'],
            agent_host_name=configuration['exporter_host'],
            agent_port=configuration['exporter_port'],
        )
        trace.set_tracer_provider(TracerProvider())
        trace.get_tracer(__name__)
        span_processor = BatchExportSpanProcessor(exporter)
        trace.get_tracer_provider().add_span_processor(span_processor)

    # Zipkin Exporter configuration
    elif configuration['exporter'] == 'zipkin':
        exporter = ZipkinSpanExporter(
            service_name=configuration['service_name'],
            url=configuration['exporter_endpoint'])
        trace.set_tracer_provider(TracerProvider())
        trace.get_tracer(__name__)
        span_processor = BatchExportSpanProcessor(exporter)
        trace.get_tracer_provider().add_span_processor(span_processor)

    # Console Exporter configuration
    elif configuration['exporter'] == 'console':
        trace.set_tracer_provider(TracerProvider())
        trace.get_tracer_provider().add_span_processor(
            SimpleExportSpanProcessor(ConsoleSpanExporter()))
    else:
        raise ValueError(
            'Only Otlp, Jaeger Thrift/HTTP and Zipkin exporters are supported. '
            'Please check your configuration.')
コード例 #12
0
    def setUp(self):
        tracer_provider = TracerProvider()
        self.exporter = OTLPSpanExporter(insecure=True)
        tracer_provider.add_span_processor(
            SimpleExportSpanProcessor(self.exporter)
        )
        self.tracer = tracer_provider.get_tracer(__name__)

        self.server = server(ThreadPoolExecutor(max_workers=10))

        self.server.add_insecure_port("[::]:55680")

        self.server.start()

        event_mock = Mock(
            **{
                "timestamp": 1591240820506462784,
                "attributes": OrderedDict([("a", 1), ("b", False)]),
            }
        )

        type(event_mock).name = PropertyMock(return_value="a")

        self.span = _Span(
            "a",
            context=Mock(
                **{
                    "trace_state": OrderedDict([("a", "b"), ("c", "d")]),
                    "span_id": 10217189687419569865,
                    "trace_id": 67545097771067222548457157018666467027,
                }
            ),
            resource=SDKResource(OrderedDict([("a", 1), ("b", False)])),
            parent=Mock(**{"span_id": 12345}),
            attributes=OrderedDict([("a", 1), ("b", True)]),
            events=[event_mock],
            links=[
                Mock(
                    **{
                        "context.trace_id": 1,
                        "context.span_id": 2,
                        "attributes": OrderedDict([("a", 1), ("b", False)]),
                        "kind": OTLPSpan.SpanKind.SPAN_KIND_INTERNAL,  # pylint: disable=no-member
                    }
                )
            ],
            instrumentation_info=InstrumentationInfo(
                name="name", version="version"
            ),
        )

        self.span.start()
        self.span.end()

        Configuration._reset()  # pylint: disable=protected-access
コード例 #13
0
def init_tracing():
  global initialized
  if initialized:
    return
  initialized = True

  provider = TracerProvider()
  trace.set_tracer_provider(provider)

  provider.add_span_processor(BatchExportSpanProcessor(ConsoleSpanExporter()))
  auto_instrument()
コード例 #14
0
def _init_tracing(exporters: Sequence[SpanExporter],
                  id_generator: IdGenerator):
    # if env var OTEL_RESOURCE_ATTRIBUTES is given, it will read the service_name
    # from the env variable else defaults to "unknown_service"
    provider = TracerProvider(id_generator=id_generator(), )
    trace.set_tracer_provider(provider)

    for _, exporter_class in exporters.items():
        exporter_args = {}
        provider.add_span_processor(
            BatchSpanProcessor(exporter_class(**exporter_args)))
コード例 #15
0
def tracer_provider(memory_exporter):
    original_tracer_provider = trace_api.get_tracer_provider()

    tracer_provider = TracerProvider()

    span_processor = export.SimpleSpanProcessor(memory_exporter)
    tracer_provider.add_span_processor(span_processor)

    trace_api.set_tracer_provider(tracer_provider)

    yield tracer_provider

    trace_api.set_tracer_provider(original_tracer_provider)
コード例 #16
0
def _configure_tracing(options: Options) -> None:
    provider = TracerProvider(resource=Resource.create(
        attributes={
            "service.name": options.service_name,
            "telemetry.auto.version": __version__,
        }))
    propagate.set_global_textmap(B3Format())
    if options.response_propagation:
        set_global_response_propagator(
            ServerTimingResponsePropagator())  # type: ignore

    trace.set_tracer_provider(provider)
    exporter = _new_jaeger_exporter(options)
    provider.add_span_processor(BatchSpanProcessor(exporter))
コード例 #17
0
    def setUp(self):
        tracer_provider = TracerProvider()
        self.exporter = OTLPSpanExporter()
        tracer_provider.add_span_processor(
            SimpleExportSpanProcessor(self.exporter)
        )
        self.tracer = tracer_provider.get_tracer(__name__)

        self.server = server(ThreadPoolExecutor(max_workers=10))

        self.server.add_insecure_port("[::]:55678")

        self.server.start()

        event_mock = Mock(
            **{
                "timestamp": 1591240820506462784,
                "attributes": OrderedDict([("a", 1), ("b", False)]),
            }
        )

        type(event_mock).name = PropertyMock(return_value="a")

        self.span = Span(
            "a",
            context=Mock(
                **{
                    "trace_state": OrderedDict([("a", "b"), ("c", "d")]),
                    "span_id": 10217189687419569865,
                    "trace_id": 67545097771067222548457157018666467027,
                }
            ),
            resource=SDKResource(OrderedDict([("a", 1), ("b", False)])),
            parent=Mock(**{"span_id": 12345}),
            attributes=OrderedDict([("a", 1), ("b", True)]),
            events=[event_mock],
            links=[
                Mock(
                    **{
                        "context.trace_id": 1,
                        "context.span_id": 2,
                        "attributes": OrderedDict([("a", 1), ("b", False)]),
                        "kind": SpanKind.INTERNAL,
                    }
                )
            ],
        )

        self.span.start()
        self.span.end()
コード例 #18
0
def configure_opentelemetry(flask_app: flask.Flask):
    """Configure a flask application to use OpenTelemetry.

    This activates the specific components:

    * sets tracer to the SDK's Tracer
    * enables requests integration on the Tracer
    * uses a WSGI middleware to enable configuration

    TODO:

    * processors?
    * exporters?
    """
    # Start by configuring all objects required to ensure
    # a complete end to end workflow.
    # The preferred implementation of these objects must be set,
    # as the opentelemetry-api defines the interface with a no-op
    # implementation.
    trace.set_preferred_tracer_provider_implementation(
        lambda _: TracerProvider())

    # Next, we need to configure how the values that are used by
    # traces and metrics are propagated (such as what specific headers
    # carry this value).
    # Integrations are the glue that binds the OpenTelemetry API
    # and the frameworks and libraries that are used together, automatically
    # creating Spans and propagating context as appropriate.
    opentelemetry.ext.http_requests.enable(trace.tracer_provider())
    instrument_app(flask_app)
コード例 #19
0
    def test_constructor_by_environment_variables(self):
        """Test using Environment Variables."""
        # pylint: disable=protected-access
        service = "my-opentelemetry-jaeger"

        collector_endpoint = "localhost:14250"

        env_patch = mock.patch.dict(
            "os.environ",
            {
                OTEL_EXPORTER_JAEGER_ENDPOINT: collector_endpoint,
                OTEL_EXPORTER_JAEGER_CERTIFICATE:
                os.path.dirname(__file__) + "/certs/cred.cert",
                OTEL_RESOURCE_ATTRIBUTES:
                "service.name=my-opentelemetry-jaeger",
            },
        )

        env_patch.start()
        provider = TracerProvider(resource=Resource.create({}))
        trace_api.set_tracer_provider(provider)
        exporter = JaegerExporter()
        self.assertEqual(exporter.service_name, service)
        self.assertIsNotNone(exporter._collector_grpc_client)
        self.assertEqual(exporter.collector_endpoint, collector_endpoint)
        self.assertIsNotNone(exporter.credentials)
        env_patch.stop()
コード例 #20
0
ファイル: app.py プロジェクト: fffergal/walterego
def init_tracing():
    """
    Add tracing to a Flask app.

    This is a bit different to the OpenTelemetry Python examples because Vercel does
    some forking and it needs to be handled without knowing which app runner is being
    used.

    To be used with Flask's before_first_request.
    """
    if not tracing_inited_holder.inited:
        otlp_exporter = OTLPSpanExporter(
            endpoint="https://api.honeycomb.io:443",
            insecure=False,
            credentials=ssl_channel_credentials(),
            headers=(
                ("x-honeycomb-team",
                 current_app.config["HONEYCOMB_WRITE_KEY"]),
                ("x-honeycomb-dataset", "Walter Ego"),
            ),
        )
        trace.set_tracer_provider(TracerProvider())
        tracing_inited_holder.processor = BatchSpanProcessor(otlp_exporter)
        trace.get_tracer_provider().add_span_processor(
            tracing_inited_holder.processor)
        tracing_inited_holder.inited = True
コード例 #21
0
    def test_export_service_name(self):
        trace_api.set_tracer_provider(
            TracerProvider(
                resource=Resource.create({SERVICE_NAME: "testServiceName"})))
        mock_client = mock.MagicMock()
        mock_export = mock.MagicMock()
        mock_client.Export = mock_export
        host_name = "testHostName"
        collector_exporter = OpenCensusSpanExporter(client=mock_client,
                                                    host_name=host_name)
        self.assertEqual(collector_exporter.node.service_info.name,
                         "testServiceName")

        trace_id = 0x6E0C63257DE34C926F9EFCD03927272E
        span_id = 0x34BF92DEEFC58C92
        span_context = trace_api.SpanContext(
            trace_id,
            span_id,
            is_remote=False,
            trace_flags=TraceFlags(TraceFlags.SAMPLED),
        )
        resource = Resource.create({SERVICE_NAME: "test"})
        otel_spans = [
            trace._Span(
                name="test1",
                context=span_context,
                kind=trace_api.SpanKind.CLIENT,
                resource=resource,
            )
        ]

        result_status = collector_exporter.export(otel_spans)
        self.assertEqual(SpanExportResult.SUCCESS, result_status)
        self.assertEqual(collector_exporter.node.service_info.name, "test")
コード例 #22
0
def get_tracer_with_processor(span_processor_class):
    span_processor = span_processor_class(OTLPSpanExporter())
    tracer = TracerProvider(
        active_span_processor=span_processor,
        sampler=sampling.DEFAULT_ON,
    ).get_tracer("pipeline_benchmark_tracer")
    return tracer
コード例 #23
0
def main():
    model_name = "my-model"
    model_version = "v1"
    client = sczpy.SCZClient(server_url)

    trace.set_tracer_provider(TracerProvider())

    jaeger_exporter = jaeger.JaegerSpanExporter(
        service_name="my-device",
        agent_host_name="localhost",
        agent_port=6831,
    )

    trace.get_tracer_provider().add_span_processor(
        BatchExportSpanProcessor(jaeger_exporter))
    trace.get_tracer_provider().add_span_processor(
        SimpleExportSpanProcessor(sczpy.SCZSpanExporter(client)))
    tracer = trace.get_tracer(__name__)

    while True:
        with tracer.start_as_current_span("inference") as inference:
            inference.set_attribute('device', 'my-device')
            inference.add_event(
                'inference', {
                    "confidence": random.randint(80, 101),
                    "model_name": model_name,
                    "model_version": model_version,
                    "file_ref": "dummy_data.txt"
                })
        time.sleep(3)
コード例 #24
0
    def test_export(self):
        """Test that agent and/or collector are invoked"""

        trace_api.set_tracer_provider(
            TracerProvider(
                resource=Resource.create({SERVICE_NAME: "text_export"})))

        exporter = jaeger_exporter.JaegerExporter(agent_host_name="localhost",
                                                  agent_port=6318)

        # just agent is configured now
        agent_client_mock = mock.Mock(spec=jaeger_exporter.AgentClientUDP)
        # pylint: disable=protected-access
        exporter._agent_client = agent_client_mock

        exporter.export((self._test_span, ))
        self.assertEqual(agent_client_mock.emit.call_count, 1)

        # add also a collector and test that both are called
        collector_mock = mock.Mock(spec=jaeger_exporter.Collector)
        # pylint: disable=protected-access
        exporter._collector = collector_mock

        exporter.export((self._test_span, ))
        self.assertEqual(agent_client_mock.emit.call_count, 1)
        self.assertEqual(collector_mock.submit.call_count, 1)
コード例 #25
0
ファイル: __init__.py プロジェクト: alertedsnake/otel-test
    def _enable_otel(self):

        from opentelemetry                  import trace
        from opentelemetry.propagate        import set_global_textmap
        from opentelemetry.sdk.resources    import Resource
        from opentelemetry.sdk.trace        import TracerProvider
        from opentelemetry.exporter.datadog import DatadogSpanExporter, DatadogExportSpanProcessor
        from opentelemetry.exporter.datadog.propagator import DatadogFormat
        from opentelemetry.instrumentation.grpc import GrpcInstrumentorClient

        r = Resource({
            'app.version':      __version__,
            'app.framework':    ':'.join((self._name, __version__)),
            'net.hostname':     socket.gethostname(),
            'service.name':     self._name,
            'service.version':  __version__,
        })
        trace.set_tracer_provider(TracerProvider(resource = r))
        self.tracer = trace.get_tracer_provider()

        self.exporter = DatadogSpanExporter(
            service   = self._name,
            agent_url = 'http://localhost:8126',
            version   = __version__,
            env       = 'dev',
        )
        self.tracer.add_span_processor(DatadogExportSpanProcessor(self.exporter))
        set_global_textmap(DatadogFormat())


        # setup client instrumentation
        GrpcInstrumentorClient().instrument(tracer = self.tracer)
        BotocoreInstrumentor().instrument(tracer_provider = self.tracer)
        BotoInstrumentor().instrument(tracer_provider = self.tracer)
コード例 #26
0
 def setUpClass(cls):
     global _MEMORY_EXPORTER  # pylint:disable=global-statement
     trace_api.set_tracer_provider(TracerProvider())
     tracer_provider = trace_api.get_tracer_provider()
     _MEMORY_EXPORTER = InMemorySpanExporter()
     span_processor = export.SimpleSpanProcessor(_MEMORY_EXPORTER)
     tracer_provider.add_span_processor(span_processor)
コード例 #27
0
def init_tracing(service_name):
    if Config.TRACE_EXPORTER is None:
        return
    elif Config.TRACE_EXPORTER == 'jaeger':
        exporter = JaegerSpanExporter(
            service_name=service_name,
            agent_host_name=Config.JAEGER_HOST,
            agent_port=6831,
        )
    elif Config.TRACE_EXPORTER == 'honeycomb':
        exporter = HoneycombSpanExporter(
            service_name=service_name,
            writekey=Config.HONEYCOMB_API_KEY,
            dataset=Config.HONEYCOMB_DATASET,
        )
    else:
        raise ValueError(
            f"TRACE_EXPORTER {Config.TRACE_EXPORTER} is not valid")

    trace.set_tracer_provider(TracerProvider())
    span_processor = BatchExportSpanProcessor(exporter)
    trace.get_tracer_provider().add_span_processor(span_processor)

    # This isn't great but oh well
    global tracer
    tracer = trace.get_tracer(service_name)
コード例 #28
0
ファイル: main.py プロジェクト: nipponnp/Allproject
def initialize_tracer(project_id):
    trace.set_tracer_provider(TracerProvider())
    cloud_trace_exporter = CloudTraceSpanExporter(project_id)
    trace.get_tracer_provider().add_span_processor(
        SimpleExportSpanProcessor(cloud_trace_exporter))
    opentelemetry_tracer = trace.get_tracer(__name__)

    return opentelemetry_tracer
    def setUp(self):
        super().setUp()

        trace.set_tracer_provider(TracerProvider())
        self.tracer = trace.get_tracer(__name__)
        self.span_processor = self.get_span_processor()

        trace.get_tracer_provider().add_span_processor(self.span_processor)
コード例 #30
0
 def setUpClass(cls):
     global _MEMORY_EXPORTER  # pylint:disable=global-statement
     trace_api.set_preferred_tracer_provider_implementation(
         lambda T: TracerProvider())
     tracer_provider = trace_api.tracer_provider()
     _MEMORY_EXPORTER = InMemorySpanExporter()
     span_processor = export.SimpleExportSpanProcessor(_MEMORY_EXPORTER)
     tracer_provider.add_span_processor(span_processor)