Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add IDs Generator as Configurable Property of Auto Instrumentation #1404

Merged
merged 6 commits into from
Nov 24, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions opentelemetry-api/setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ opentelemetry_tracer_provider =
opentelemetry_propagator =
tracecontext = opentelemetry.trace.propagation.tracecontext:TraceContextTextMapPropagator
baggage = opentelemetry.baggage.propagation:BaggagePropagator
opentelemetry_ids_generator =
random = opentelemetry.trace.ids_generator:RandomIdsGenerator

[options.extras_require]
test =
2 changes: 2 additions & 0 deletions opentelemetry-instrumentation/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Add IDs Generator as Configurable Property of Auto Instrumentation
([#1404](https://github.com/open-telemetry/opentelemetry-python/pull/1404))
- Added support for `OTEL_EXPORTER` to the `opentelemetry-instrument` command ([#1036](https://github.com/open-telemetry/opentelemetry-python/pull/1036))

## Version 0.14b0
Expand Down
12 changes: 12 additions & 0 deletions opentelemetry-instrumentation/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ Well known trace exporter names:

When present the value is passed on to the relevant exporter initializer as ``service_name`` argument.

* ``--ids-generator`` or ``OTEL_IDS_GENERATOR``

Used to specify which IDs Generator to use for the global Tracer Provider. By default, it
will use the random IDs generator.

The code in ``program.py`` needs to use one of the packages for which there is
an OpenTelemetry integration. For a list of the available integrations please
check `here <https://opentelemetry-python.readthedocs.io/en/stable/index.html#integrations>`_
Expand All @@ -93,6 +98,13 @@ The above command will pass ``-e otlp`` to the instrument command and ``--port=3
The above command will configure global trace provider, attach zipkin and otlp exporters to it and then
start celery with the rest of the arguments.

::

opentelemetry-instrument --ids-generator random flask run --port=3000

The above command will configure the global trace provider to use the Random IDs Generator, and then
pass ``--port=3000`` to ``flask run``.

References
----------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,18 @@ def parse_args():
""",
)

parser.add_argument(
"--ids-generator",
required=False,
help="""
The IDs Generator to be used with the Tracer Provider.

Examples:

--ids-generator=random
""",
)

parser.add_argument(
"-s",
"--service-name",
Expand All @@ -70,6 +82,8 @@ def load_config_from_cli_args(args):
environ["OTEL_EXPORTER"] = args.exporter
if args.service_name:
environ["OTEL_SERVICE_NAME"] = args.service_name
if args.ids_generator:
environ["OTEL_IDS_GENERATOR"] = args.ids_generator
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there should be a proposal to the spec around this env variable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will follow up with this soon!



def run() -> None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@
EXPORTER_OTLP_METRIC = "otlp_metric"
_DEFAULT_EXPORTER = EXPORTER_OTLP

RANDOM_IDS_GENERATOR = "random"
_DEFAULT_IDS_GENERATOR = RANDOM_IDS_GENERATOR


def get_ids_generator() -> str:
return Configuration().IDS_GENERATOR or _DEFAULT_IDS_GENERATOR


def get_service_name() -> str:
return Configuration().SERVICE_NAME or ""
Expand All @@ -55,10 +62,13 @@ def get_exporter_names() -> Sequence[str]:
return names


def init_tracing(exporters: Sequence[SpanExporter]):
def init_tracing(
exporters: Sequence[SpanExporter], ids_generator: trace.IdsGenerator
):
service_name = get_service_name()
provider = TracerProvider(
resource=Resource.create({"service.name": service_name}),
ids_generator=ids_generator(),
)
trace.set_tracer_provider(provider)

Expand All @@ -80,23 +90,39 @@ def init_metrics(exporters: Sequence[MetricsExporter]):
logger.warning("automatic metric initialization is not supported yet.")


def import_exporters(
exporter_names: Sequence[str],
) -> Tuple[Sequence[SpanExporter], Sequence[MetricsExporter]]:
trace_exporters, metric_exporters = {}, {}

exporters = {
ep.name: ep for ep in iter_entry_points("opentelemetry_exporter")
def import_tracer_provider_config_components(
selected_components, entry_point_name
) -> Sequence[Tuple[str, object]]:
component_entry_points = {
ep.name: ep for ep in iter_entry_points(entry_point_name)
}

for exporter_name in exporter_names:
entry_point = exporters.get(exporter_name, None)
component_impls = []
for selected_component in selected_components:
entry_point = component_entry_points.get(selected_component, None)
if not entry_point:
raise RuntimeError(
"Requested exporter not found: {0}".format(exporter_name)
"Requested component '{}' not found in entry points for '{}'".format(
selected_component, entry_point_name
)
)

exporter_impl = entry_point.load()
component_impl = entry_point.load()
component_impls.append((selected_component, component_impl))

return component_impls


def import_exporters(
exporter_names: Sequence[str],
) -> Tuple[Sequence[SpanExporter], Sequence[MetricsExporter]]:
trace_exporters, metric_exporters = {}, {}

for (
exporter_name,
exporter_impl,
) in import_tracer_provider_config_components(
exporter_names, "opentelemetry_exporter"
):
if issubclass(exporter_impl, SpanExporter):
trace_exporters[exporter_name] = exporter_impl
elif issubclass(exporter_impl, MetricsExporter):
Expand All @@ -110,10 +136,26 @@ def import_exporters(
return trace_exporters, metric_exporters


def import_ids_generator(ids_generator_name: str) -> trace.IdsGenerator:
NathanielRN marked this conversation as resolved.
Show resolved Hide resolved
# pylint: disable=unbalanced-tuple-unpacking
[
(ids_generator_name, ids_generator_impl)
] = import_tracer_provider_config_components(
[ids_generator_name.strip()], "opentelemetry_ids_generator"
)

if issubclass(ids_generator_impl, trace.IdsGenerator):
return ids_generator_impl

raise RuntimeError("{0} is not an IdsGenerator".format(ids_generator_name))


def initialize_components():
exporter_names = get_exporter_names()
trace_exporters, metric_exporters = import_exporters(exporter_names)
init_tracing(trace_exporters)
ids_generator_name = get_ids_generator()
ids_generator = import_ids_generator(ids_generator_name)
init_tracing(trace_exporters, ids_generator)

# We don't support automatic initialization for metric yet but have added
# some boilerplate in order to make sure current implementation does not
Expand Down
48 changes: 45 additions & 3 deletions opentelemetry-instrumentation/tests/test_auto_tracing.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
from opentelemetry.configuration import Configuration
from opentelemetry.instrumentation.auto_instrumentation import components
from opentelemetry.sdk.resources import Resource
from opentelemetry.trace.ids_generator import RandomIdsGenerator


class Provider:
def __init__(self, resource=None):
def __init__(self, resource=None, ids_generator=None):
self.ids_generator = ids_generator
self.processor = None
self.resource = resource

Expand All @@ -48,6 +50,23 @@ class OTLPExporter:
pass


class IdsGenerator:
pass


class CustomIdsGenerator(IdsGenerator):
pass


class IterEntryPoint:
def __init__(self, name, class_type):
self.name = name
self.class_type = class_type

def load(self):
return self.class_type


class TestTraceInit(TestCase):
def setUp(self):
super()
Expand Down Expand Up @@ -77,11 +96,12 @@ def tearDown(self):
def test_trace_init_default(self):
environ["OTEL_SERVICE_NAME"] = "my-test-service"
Configuration._reset()
components.init_tracing({"zipkin": Exporter})
components.init_tracing({"zipkin": Exporter}, RandomIdsGenerator)

self.assertEqual(self.set_provider_mock.call_count, 1)
provider = self.set_provider_mock.call_args[0][0]
self.assertIsInstance(provider, Provider)
self.assertIsInstance(provider.ids_generator, RandomIdsGenerator)
self.assertIsInstance(provider.processor, Processor)
self.assertIsInstance(provider.processor.exporter, Exporter)
self.assertEqual(
Expand All @@ -91,11 +111,12 @@ def test_trace_init_default(self):
def test_trace_init_otlp(self):
environ["OTEL_SERVICE_NAME"] = "my-otlp-test-service"
Configuration._reset()
components.init_tracing({"otlp": OTLPExporter})
components.init_tracing({"otlp": OTLPExporter}, RandomIdsGenerator)

self.assertEqual(self.set_provider_mock.call_count, 1)
provider = self.set_provider_mock.call_args[0][0]
self.assertIsInstance(provider, Provider)
self.assertIsInstance(provider.ids_generator, RandomIdsGenerator)
self.assertIsInstance(provider.processor, Processor)
self.assertIsInstance(provider.processor.exporter, OTLPExporter)
self.assertIsInstance(provider.resource, Resource)
Expand All @@ -104,3 +125,24 @@ def test_trace_init_otlp(self):
"my-otlp-test-service",
)
del environ["OTEL_SERVICE_NAME"]

@patch.dict(environ, {"OTEL_IDS_GENERATOR": "custom_ids_generator"})
@patch(
"opentelemetry.instrumentation.auto_instrumentation.components.trace.IdsGenerator",
new=IdsGenerator,
)
@patch(
"opentelemetry.instrumentation.auto_instrumentation.components.iter_entry_points"
)
def test_trace_init_custom_ids_generator(self, mock_iter_entry_points):
mock_iter_entry_points.configure_mock(
return_value=[
IterEntryPoint("custom_ids_generator", CustomIdsGenerator)
NathanielRN marked this conversation as resolved.
Show resolved Hide resolved
]
)
Configuration._reset()
ids_generator_name = components.get_ids_generator()
ids_generator = components.import_ids_generator(ids_generator_name)
components.init_tracing({}, ids_generator)
provider = self.set_provider_mock.call_args[0][0]
self.assertIsInstance(provider.ids_generator, CustomIdsGenerator)