diff --git a/src/elasticotel/distro/__init__.py b/src/elasticotel/distro/__init__.py index 617192b..ca74cba 100644 --- a/src/elasticotel/distro/__init__.py +++ b/src/elasticotel/distro/__init__.py @@ -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, @@ -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 @@ -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): diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py index 62dd1be..8cf4f4a 100644 --- a/tests/integration/test_integration.py +++ b/tests/integration/test_integration.py @@ -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"}}) diff --git a/tests/integration/utils.py b/tests/integration/utils.py index 67f5675..78b1db9 100644 --- a/tests/integration/utils.py +++ b/tests/integration/utils.py @@ -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 = [] @@ -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, }