Support Environment Variables for JaegerSpanExporter configuration (#1114)

This commit is contained in:
Mateusz 'mat' Rumian
2020-10-15 17:48:46 +02:00
committed by alrex
parent 30b99f111e
commit dbef5cb0dc
4 changed files with 117 additions and 54 deletions

View File

@ -1,6 +1,9 @@
# Changelog # Changelog
## Unreleased ## Unreleased
- Add support for Jaeger Span Exporter configuration by environment variables and<br/>
change JaegerSpanExporter constructor parameters
([#1114](https://github.com/open-telemetry/opentelemetry-python/pull/1114))
## Version 0.13b0 ## Version 0.13b0

View File

@ -19,6 +19,14 @@ Installation
.. _Jaeger: https://www.jaegertracing.io/ .. _Jaeger: https://www.jaegertracing.io/
.. _OpenTelemetry: https://github.com/open-telemetry/opentelemetry-python/ .. _OpenTelemetry: https://github.com/open-telemetry/opentelemetry-python/
Configuration
-------------
OpenTelemetry Jaeger Exporter can be configured by setting `JaegerSpanExporter parameters
<https://github.com/open-telemetry/opentelemetry-python/blob/master/exporter/opentelemetry-exporter-jaeger
/src/opentelemetry/exporter/jaeger/__init__.py#L88>`_ or by setting
`environment variables <https://github.com/open-telemetry/opentelemetry-specification/blob/master/
specification/sdk-environment-variables.md#jaeger-exporter>`_
References References
---------- ----------

View File

@ -39,10 +39,7 @@ Usage
agent_host_name='localhost', agent_host_name='localhost',
agent_port=6831, agent_port=6831,
# optional: configure also collector # optional: configure also collector
# collector_host_name='localhost', # collector_endpoint='http://localhost:14268/api/traces?format=jaeger.thrift',
# collector_port=14268,
# collector_endpoint='/api/traces?format=jaeger.thrift',
# collector_protocol='http',
# username=xxxx, # optional # username=xxxx, # optional
# password=xxxx, # optional # password=xxxx, # optional
) )
@ -69,7 +66,7 @@ import socket
from thrift.protocol import TBinaryProtocol, TCompactProtocol from thrift.protocol import TBinaryProtocol, TCompactProtocol
from thrift.transport import THttpClient, TTransport from thrift.transport import THttpClient, TTransport
import opentelemetry.trace as trace_api from opentelemetry.configuration import Configuration
from opentelemetry.exporter.jaeger.gen.agent import Agent as agent from opentelemetry.exporter.jaeger.gen.agent import Agent as agent
from opentelemetry.exporter.jaeger.gen.jaeger import Collector as jaeger from opentelemetry.exporter.jaeger.gen.jaeger import Collector as jaeger
from opentelemetry.sdk.trace.export import Span, SpanExporter, SpanExportResult from opentelemetry.sdk.trace.export import Span, SpanExporter, SpanExportResult
@ -77,8 +74,6 @@ from opentelemetry.trace.status import StatusCanonicalCode
DEFAULT_AGENT_HOST_NAME = "localhost" DEFAULT_AGENT_HOST_NAME = "localhost"
DEFAULT_AGENT_PORT = 6831 DEFAULT_AGENT_PORT = 6831
DEFAULT_COLLECTOR_ENDPOINT = "/api/traces?format=jaeger.thrift"
DEFAULT_COLLECTOR_PROTOCOL = "http"
UDP_PACKET_MAX_LENGTH = 65000 UDP_PACKET_MAX_LENGTH = 65000
@ -93,11 +88,7 @@ class JaegerSpanExporter(SpanExporter):
when query for spans. when query for spans.
agent_host_name: The host name of the Jaeger-Agent. agent_host_name: The host name of the Jaeger-Agent.
agent_port: The port of the Jaeger-Agent. agent_port: The port of the Jaeger-Agent.
collector_host_name: The host name of the Jaeger-Collector HTTP/HTTPS
Thrift.
collector_port: The port of the Jaeger-Collector HTTP/HTTPS Thrift.
collector_endpoint: The endpoint of the Jaeger-Collector HTTP/HTTPS Thrift. collector_endpoint: The endpoint of the Jaeger-Collector HTTP/HTTPS Thrift.
collector_protocol: The transfer protocol for the Jaeger-Collector(HTTP or HTTPS).
username: The user name of the Basic Auth if authentication is username: The user name of the Basic Auth if authentication is
required. required.
password: The password of the Basic Auth if authentication is password: The password of the Basic Auth if authentication is
@ -107,25 +98,39 @@ class JaegerSpanExporter(SpanExporter):
def __init__( def __init__(
self, self,
service_name, service_name,
agent_host_name=DEFAULT_AGENT_HOST_NAME, agent_host_name=None,
agent_port=DEFAULT_AGENT_PORT, agent_port=None,
collector_host_name=None, collector_endpoint=None,
collector_port=None,
collector_endpoint=DEFAULT_COLLECTOR_ENDPOINT,
collector_protocol=DEFAULT_COLLECTOR_PROTOCOL,
username=None, username=None,
password=None, password=None,
): ):
self.service_name = service_name self.service_name = service_name
self.agent_host_name = agent_host_name self.agent_host_name = _parameter_setter(
self.agent_port = agent_port param=agent_host_name,
env_variable=Configuration().EXPORTER_JAEGER_AGENT_HOST,
default=DEFAULT_AGENT_HOST_NAME,
)
self.agent_port = _parameter_setter(
param=agent_port,
env_variable=Configuration().EXPORTER_JAEGER_AGENT_PORT,
default=DEFAULT_AGENT_PORT,
)
self._agent_client = None self._agent_client = None
self.collector_host_name = collector_host_name self.collector_endpoint = _parameter_setter(
self.collector_port = collector_port param=collector_endpoint,
self.collector_endpoint = collector_endpoint env_variable=Configuration().EXPORTER_JAEGER_ENDPOINT,
self.collector_protocol = collector_protocol default=None,
self.username = username )
self.password = password self.username = _parameter_setter(
param=username,
env_variable=Configuration().EXPORTER_JAEGER_USER,
default=None,
)
self.password = _parameter_setter(
param=password,
env_variable=Configuration().EXPORTER_JAEGER_PASSWORD,
default=None,
)
self._collector = None self._collector = None
@property @property
@ -141,21 +146,16 @@ class JaegerSpanExporter(SpanExporter):
if self._collector is not None: if self._collector is not None:
return self._collector return self._collector
if self.collector_host_name is None or self.collector_port is None: if self.collector_endpoint is None:
return None return None
thrift_url = "{}://{}:{}{}".format(
self.collector_protocol,
self.collector_host_name,
self.collector_port,
self.collector_endpoint,
)
auth = None auth = None
if self.username is not None and self.password is not None: if self.username is not None and self.password is not None:
auth = (self.username, self.password) auth = (self.username, self.password)
self._collector = Collector(thrift_url=thrift_url, auth=auth) self._collector = Collector(
thrift_url=self.collector_endpoint, auth=auth
)
return self._collector return self._collector
def export(self, spans): def export(self, spans):
@ -177,6 +177,22 @@ class JaegerSpanExporter(SpanExporter):
pass pass
def _parameter_setter(param, env_variable, default):
"""Returns value according to the provided data.
Args:
param: Constructor parameter value
env_variable: Environment variable related to the parameter
default: Constructor parameter default value
"""
if param is None:
res = env_variable or default
else:
res = param
return res
def _nsec_to_usec_round(nsec): def _nsec_to_usec_round(nsec):
"""Round nanoseconds to microseconds""" """Round nanoseconds to microseconds"""
return (nsec + 500) // 10 ** 3 return (nsec + 500) // 10 ** 3

View File

@ -20,6 +20,7 @@ from unittest import mock
# pylint:disable=import-error # pylint:disable=import-error
import opentelemetry.exporter.jaeger as jaeger_exporter import opentelemetry.exporter.jaeger as jaeger_exporter
from opentelemetry import trace as trace_api from opentelemetry import trace as trace_api
from opentelemetry.configuration import Configuration
from opentelemetry.exporter.jaeger.gen.jaeger import ttypes as jaeger from opentelemetry.exporter.jaeger.gen.jaeger import ttypes as jaeger
from opentelemetry.sdk import trace from opentelemetry.sdk import trace
from opentelemetry.sdk.trace import Resource from opentelemetry.sdk.trace import Resource
@ -43,20 +44,14 @@ class TestJaegerSpanExporter(unittest.TestCase):
def test_constructor_default(self): def test_constructor_default(self):
"""Test the default values assigned by constructor.""" """Test the default values assigned by constructor."""
service_name = "my-service-name" service_name = "my-service-name"
host_name = "localhost" agent_host_name = "localhost"
thrift_port = None
agent_port = 6831 agent_port = 6831
collector_endpoint = "/api/traces?format=jaeger.thrift"
collector_protocol = "http"
exporter = jaeger_exporter.JaegerSpanExporter(service_name) exporter = jaeger_exporter.JaegerSpanExporter(service_name)
self.assertEqual(exporter.service_name, service_name) self.assertEqual(exporter.service_name, service_name)
self.assertEqual(exporter.collector_host_name, None) self.assertEqual(exporter.agent_host_name, agent_host_name)
self.assertEqual(exporter.agent_host_name, host_name)
self.assertEqual(exporter.agent_port, agent_port) self.assertEqual(exporter.agent_port, agent_port)
self.assertEqual(exporter.collector_port, thrift_port) self.assertEqual(exporter.collector_endpoint, None)
self.assertEqual(exporter.collector_protocol, collector_protocol)
self.assertEqual(exporter.collector_endpoint, collector_endpoint)
self.assertEqual(exporter.username, None) self.assertEqual(exporter.username, None)
self.assertEqual(exporter.password, None) self.assertEqual(exporter.password, None)
self.assertTrue(exporter.collector is None) self.assertTrue(exporter.collector is None)
@ -65,10 +60,7 @@ class TestJaegerSpanExporter(unittest.TestCase):
def test_constructor_explicit(self): def test_constructor_explicit(self):
"""Test the constructor passing all the options.""" """Test the constructor passing all the options."""
service = "my-opentelemetry-jaeger" service = "my-opentelemetry-jaeger"
collector_host_name = "opentelemetry.io" collector_endpoint = "https://opentelemetry.io:15875"
collector_port = 15875
collector_endpoint = "/myapi/traces?format=jaeger.thrift"
collector_protocol = "https"
agent_port = 14268 agent_port = 14268
agent_host_name = "opentelemetry.io" agent_host_name = "opentelemetry.io"
@ -79,21 +71,16 @@ class TestJaegerSpanExporter(unittest.TestCase):
exporter = jaeger_exporter.JaegerSpanExporter( exporter = jaeger_exporter.JaegerSpanExporter(
service_name=service, service_name=service,
collector_host_name=collector_host_name,
collector_port=collector_port,
collector_endpoint=collector_endpoint,
collector_protocol="https",
agent_host_name=agent_host_name, agent_host_name=agent_host_name,
agent_port=agent_port, agent_port=agent_port,
collector_endpoint=collector_endpoint,
username=username, username=username,
password=password, password=password,
) )
self.assertEqual(exporter.service_name, service) self.assertEqual(exporter.service_name, service)
self.assertEqual(exporter.agent_host_name, agent_host_name) self.assertEqual(exporter.agent_host_name, agent_host_name)
self.assertEqual(exporter.agent_port, agent_port) self.assertEqual(exporter.agent_port, agent_port)
self.assertEqual(exporter.collector_host_name, collector_host_name)
self.assertEqual(exporter.collector_port, collector_port)
self.assertEqual(exporter.collector_protocol, collector_protocol)
self.assertTrue(exporter.collector is not None) self.assertTrue(exporter.collector is not None)
self.assertEqual(exporter.collector.auth, auth) self.assertEqual(exporter.collector.auth, auth)
# property should not construct new object # property should not construct new object
@ -107,6 +94,55 @@ class TestJaegerSpanExporter(unittest.TestCase):
self.assertNotEqual(exporter.collector, collector) self.assertNotEqual(exporter.collector, collector)
self.assertTrue(exporter.collector.auth is None) self.assertTrue(exporter.collector.auth is None)
def test_constructor_by_environment_variables(self):
"""Test the constructor using Environment Variables."""
service = "my-opentelemetry-jaeger"
agent_host_name = "opentelemetry.io"
agent_port = "6831"
collector_endpoint = "https://opentelemetry.io:15875"
username = "username"
password = "password"
auth = (username, password)
environ_patcher = mock.patch.dict(
"os.environ",
{
"OTEL_EXPORTER_JAEGER_AGENT_HOST": agent_host_name,
"OTEL_EXPORTER_JAEGER_AGENT_PORT": agent_port,
"OTEL_EXPORTER_JAEGER_ENDPOINT": collector_endpoint,
"OTEL_EXPORTER_JAEGER_USER": username,
"OTEL_EXPORTER_JAEGER_PASSWORD": password,
},
)
environ_patcher.start()
exporter = jaeger_exporter.JaegerSpanExporter(service_name=service)
self.assertEqual(exporter.service_name, service)
self.assertEqual(exporter.agent_host_name, agent_host_name)
self.assertEqual(exporter.agent_port, int(agent_port))
self.assertTrue(exporter.collector is not None)
self.assertEqual(exporter.collector_endpoint, collector_endpoint)
self.assertEqual(exporter.collector.auth, auth)
# property should not construct new object
collector = exporter.collector
self.assertEqual(exporter.collector, collector)
# property should construct new object
# pylint: disable=protected-access
exporter._collector = None
exporter.username = None
exporter.password = None
self.assertNotEqual(exporter.collector, collector)
self.assertTrue(exporter.collector.auth is None)
environ_patcher.stop()
Configuration._reset()
def test_nsec_to_usec_round(self): def test_nsec_to_usec_round(self):
# pylint: disable=protected-access # pylint: disable=protected-access
nsec_to_usec_round = jaeger_exporter._nsec_to_usec_round nsec_to_usec_round = jaeger_exporter._nsec_to_usec_round