# Copyright 2018, OpenCensus Authors
# Copyright The OpenTelemetry Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
OpenTelemetry Jaeger Protobuf Exporter
--------------------------------------
The **OpenTelemetry Jaeger Protobuf Exporter** allows to export `OpenTelemetry`_ traces to `Jaeger`_.
This exporter always sends traces to the configured agent using Protobuf via gRPC.
Usage
-----
.. code:: python
from opentelemetry import trace
from opentelemetry.exporter.jaeger.proto.grpc import JaegerExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
trace.set_tracer_provider(TracerProvider())
tracer = trace.get_tracer(__name__)
# create a JaegerExporter
jaeger_exporter = JaegerExporter(
# optional: configure collector
# collector_endpoint='localhost:14250',
# insecure=True, # optional
# credentials=xxx # optional channel creds
# max_tag_value_length=None # optional
)
# Create a BatchSpanProcessor and add the exporter to it
span_processor = BatchSpanProcessor(jaeger_exporter)
# add to the tracer
trace.get_tracer_provider().add_span_processor(span_processor)
with tracer.start_as_current_span('foo'):
print('Hello world!')
You can configure the exporter with the following environment variables:
- :envvar:`OTEL_EXPORTER_JAEGER_ENDPOINT`
- :envvar:`OTEL_EXPORTER_JAEGER_CERTIFICATE`
- :envvar:`OTEL_EXPORTER_JAEGER_TIMEOUT`
API
---
.. _Jaeger: https://www.jaegertracing.io/
.. _OpenTelemetry: https://github.com/open-telemetry/opentelemetry-python/
"""
# pylint: disable=protected-access
import logging
from os import environ
from typing import Optional
from grpc import ChannelCredentials, RpcError, insecure_channel, secure_channel
from opentelemetry import trace
from opentelemetry.exporter.jaeger.proto.grpc import util
from opentelemetry.exporter.jaeger.proto.grpc.gen import model_pb2
from opentelemetry.exporter.jaeger.proto.grpc.gen.collector_pb2 import (
PostSpansRequest,
)
from opentelemetry.exporter.jaeger.proto.grpc.gen.collector_pb2_grpc import (
CollectorServiceStub,
)
from opentelemetry.exporter.jaeger.proto.grpc.translate import (
ProtobufTranslator,
Translate,
)
from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_JAEGER_ENDPOINT,
OTEL_EXPORTER_JAEGER_TIMEOUT,
)
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
DEFAULT_GRPC_COLLECTOR_ENDPOINT = "localhost:14250"
DEFAULT_EXPORT_TIMEOUT = 10
logger = logging.getLogger(__name__)
[docs]class JaegerExporter(SpanExporter):
"""Jaeger span exporter for OpenTelemetry.
Args:
collector_endpoint: The endpoint of the Jaeger collector that uses
Protobuf via gRPC.
insecure: True if collector has no encryption or authentication
credentials: Credentials for server authentication.
max_tag_value_length: Max length string attribute values can have. Set to None to disable.
timeout: Maximum time the Jaeger exporter should wait for each batch export.
"""
def __init__(
self,
collector_endpoint: Optional[str] = None,
insecure: Optional[bool] = None,
credentials: Optional[ChannelCredentials] = None,
max_tag_value_length: Optional[int] = None,
timeout: Optional[int] = None,
):
self._max_tag_value_length = max_tag_value_length
self.collector_endpoint = collector_endpoint or environ.get(
OTEL_EXPORTER_JAEGER_ENDPOINT, DEFAULT_GRPC_COLLECTOR_ENDPOINT
)
self._timeout = timeout or int(
environ.get(OTEL_EXPORTER_JAEGER_TIMEOUT, DEFAULT_EXPORT_TIMEOUT)
)
self._grpc_client = None
self.insecure = insecure
self.credentials = util._get_credentials(credentials)
tracer_provider = trace.get_tracer_provider()
self.service_name = (
tracer_provider.resource.attributes[SERVICE_NAME]
if getattr(tracer_provider, "resource", None)
else Resource.create().attributes.get(SERVICE_NAME)
)
@property
def _collector_grpc_client(self) -> Optional[CollectorServiceStub]:
if self._grpc_client is None:
if self.insecure:
self._grpc_client = CollectorServiceStub(
insecure_channel(self.collector_endpoint)
)
else:
self._grpc_client = CollectorServiceStub(
secure_channel(self.collector_endpoint, self.credentials)
)
return self._grpc_client
[docs] def export(self, spans) -> SpanExportResult:
# Populate service_name from first span
# We restrict any SpanProcessor to be only associated with a single
# TracerProvider, so it is safe to assume that all Spans in a single
# batch all originate from one TracerProvider (and in turn have all
# the same service.name)
if spans:
service_name = spans[0].resource.attributes.get(SERVICE_NAME)
if service_name:
self.service_name = service_name
translator = Translate(spans)
pb_translator = ProtobufTranslator(
self.service_name, self._max_tag_value_length
)
jaeger_spans = translator._translate(pb_translator)
batch = model_pb2.Batch(spans=jaeger_spans)
request = PostSpansRequest(batch=batch)
try:
self._collector_grpc_client.PostSpans(
request, timeout=self._timeout
)
return SpanExportResult.SUCCESS
except RpcError as error:
logger.warning(
"Failed to export batch. Status code: %s", error.code()
)
return SpanExportResult.FAILURE
[docs] def shutdown(self):
pass