Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
74 changes: 2 additions & 72 deletions src/elasticotel/distro/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
import logging
import os

from opentelemetry._logs import set_logger_provider
from opentelemetry._events import set_event_logger_provider
from opentelemetry.environment_variables import (
OTEL_LOGS_EXPORTER,
OTEL_METRICS_EXPORTER,
Expand All @@ -30,29 +28,13 @@
_DEFAULT_CONFIG as SYSTEM_METRICS_DEFAULT_CONFIG,
SystemMetricsInstrumentor,
)
from opentelemetry.semconv.resource import ResourceAttributes
from opentelemetry.sdk._configuration import (
_OTelSDKConfigurator,
_import_exporters,
_get_exporter_names,
_get_sampler,
_import_sampler,
_get_id_generator,
_import_id_generator,
_init_tracing,
_init_metrics,
)
from opentelemetry.sdk._events import EventLoggerProvider
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.sdk._configuration import _OTelSDKConfigurator
from opentelemetry.sdk.environment_variables import (
OTEL_METRICS_EXEMPLAR_FILTER,
OTEL_EXPERIMENTAL_RESOURCE_DETECTORS,
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE,
OTEL_EXPORTER_OTLP_PROTOCOL,
_OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED,
)
from opentelemetry.sdk.resources import Resource
from opentelemetry.util._importlib_metadata import EntryPoint

from elasticotel.distro.environment_variables import ELASTIC_OTEL_SYSTEM_METRICS_ENABLED
Expand All @@ -63,59 +45,7 @@


class ElasticOpenTelemetryConfigurator(_OTelSDKConfigurator):
def _configure(self, **kwargs):
"""This is overriden to enable the log machinery (and thus log events) without
attaching the OTel handler to the Python logging module.

This code is a simplified version of _initialize_components plus the changes
required to have log events enabled out of the box"""
span_exporters, metric_exporters, log_exporters = _import_exporters(
_get_exporter_names("traces"),
_get_exporter_names("metrics"),
_get_exporter_names("logs"),
)
sampler_name = _get_sampler()
sampler = _import_sampler(sampler_name)
id_generator_name = _get_id_generator()
id_generator = _import_id_generator(id_generator_name)

resource_attributes = {}
# populate version if using auto-instrumentation
auto_instrumentation_version = kwargs.get("auto_instrumentation_version")
if auto_instrumentation_version:
resource_attributes[ResourceAttributes.TELEMETRY_AUTO_VERSION] = auto_instrumentation_version
# if env var OTEL_RESOURCE_ATTRIBUTES is given, it will read the service_name
# from the env variable else defaults to "unknown_service"
resource = Resource.create(resource_attributes)

_init_tracing(
exporters=span_exporters,
id_generator=id_generator,
sampler=sampler,
resource=resource,
)
_init_metrics(metric_exporters, resource)

# from here we change the semantics of _OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED from
# controlling all the logging support to just the logging handler. So we can use log events without
# exporting all the logs to OTLP
logger_provider = LoggerProvider(resource=resource)
set_logger_provider(logger_provider)

for _, exporter_class in log_exporters.items():
exporter_args = {}
logger_provider.add_log_record_processor(BatchLogRecordProcessor(exporter_class(**exporter_args)))

setup_logging_handler = (
os.getenv(_OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED, "false").strip().lower() == "true"
)
if setup_logging_handler:
handler = LoggingHandler(level=logging.NOTSET, logger_provider=logger_provider)
logging.getLogger().addHandler(handler)

# now setup the event logger
event_logger_provider = EventLoggerProvider(logger_provider=logger_provider)
set_event_logger_provider(event_logger_provider)
pass


class ElasticOpenTelemetryDistro(BaseDistro):
Expand Down
16 changes: 16 additions & 0 deletions tests/integration/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,19 @@ def test_metrics_with_system_metrics(self):
"process.open_file_descriptor.count",
],
)

def test_log_events_are_sent(self):
def send_event():
from opentelemetry._events import Event
from opentelemetry._events import get_event_logger

event = Event(name="test.event", attributes={}, body={"key": "value", "dict": {"nestedkey": "nestedvalue"}})
event_logger = get_event_logger(__name__)
event_logger.emit(event)

stdout, stderr, returncode = self.run_script(send_event, wrapper_script="opentelemetry-instrument")

telemetry = self.get_telemetry()
(log,) = telemetry["logs"]
self.assertEqual(log["attributes"]["event.name"], "test.event")
self.assertEqual(log["body"], {"key": "value", "dict": {"nestedkey": "nestedvalue"}})
35 changes: 34 additions & 1 deletion tests/integration/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,27 @@ def normalize_attributes(attributes) -> dict:
for a in attributes
}

def normalize_kvlist(body) -> dict:
"""
normalizes oteltest values in the form
{'kvlistValue': {'values': [{'key': 'key', 'value': {'stringValue': 'value'}},
{'key': 'dict',
'value': {'kvlistValue': {'values': [{'key': 'nestedkey',
'value': {'stringValue': 'nestedvalue'}}]}}}]}}
to plain dicts
"""
dict_values = {}
values = body["kvlistValue"]["values"]
for value in values:
key = value["key"]
if "kvlistValue" in value["value"]:
dict_values[key] = normalize_kvlist(value["value"])
elif "stringValue" in value["value"]:
dict_values[key] = value["value"]["stringValue"]
elif "intValue" in value["value"]:
dict_values[key] = value["value"]["intValue"]
return dict_values

metrics = []
for request in telemetry["metric_requests"]:
elems = []
Expand Down Expand Up @@ -168,8 +189,20 @@ def normalize_attributes(attributes) -> dict:
span["traceId"] = decode_id(span["traceId"])
traces.append(span)

logs = []
for request in telemetry["log_requests"]:
for resource_log in request["pbreq"]["resourceLogs"]:
resource_attributes = normalize_attributes(resource_log["resource"]["attributes"])
for proto_scope_logs in resource_log["scopeLogs"]:
for proto_log in proto_scope_logs["logRecords"]:
log = proto_log.copy()
log["attributes"] = normalize_attributes(log["attributes"])
log["body"] = normalize_kvlist(log["body"])
log["resource"] = resource_attributes
logs.append(log)

return {
"logs": telemetry["log_requests"], # TODO
"logs": logs,
"metrics": metrics,
"traces": traces,
}
Expand Down