From e9f79f138d21d4160c06669e72748dbd62947206 Mon Sep 17 00:00:00 2001 From: Jason Walkow Date: Tue, 2 Sep 2025 22:50:11 -0600 Subject: [PATCH 1/5] fix otel duplicates --- agentuity/otel/__init__.py | 150 +++++-------------------------------- 1 file changed, 18 insertions(+), 132 deletions(-) diff --git a/agentuity/otel/__init__.py b/agentuity/otel/__init__.py index e80964b..994382f 100644 --- a/agentuity/otel/__init__.py +++ b/agentuity/otel/__init__.py @@ -1,25 +1,8 @@ import logging -import signal import os from agentuity import __version__ from typing import Optional, Dict -from opentelemetry import trace -from opentelemetry.sdk.resources import SERVICE_NAME, SERVICE_VERSION, Resource -from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter -from opentelemetry.sdk.trace import TracerProvider -from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter -from opentelemetry import metrics -from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter -from opentelemetry.sdk.metrics import MeterProvider -from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader -from opentelemetry.exporter.otlp.proto.http.trace_exporter import Compression -from opentelemetry import _logs -from opentelemetry.sdk._logs import LoggingHandler, LoggerProvider -from opentelemetry.sdk._logs.export import BatchLogRecordProcessor -from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter -from opentelemetry.propagate import set_global_textmap -from .logfilter import ModuleFilter -from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator +from opentelemetry.sdk.resources import SERVICE_NAME, SERVICE_VERSION from .logger import create_logger from .span_patch import patch_span @@ -69,12 +52,19 @@ def init(config: Optional[Dict[str, str]] = {}): app_version = config.get( "app_version", os.environ.get("AGENTUITY_SDK_APP_VERSION", "unknown") ) - export_internal_ms = 500 if devmode else 60000 - max_export_batch_size = 1 if devmode else 512 - schedule_delay_millis = 500 if devmode else 30000 - resource = Resource( - attributes={ + # Initialize traceloop for automatic instrumentation + try: + from traceloop.sdk import Traceloop + + # Build app name from project and agent info if available + project_name = config.get("project_name", "") + agent_name = config.get("agent_name", "") + app_name = f"{project_name}:{agent_name}" + + headers = {"Authorization": f"Bearer {bearer_token}"} if bearer_token else {} + + resource_attributes = { SERVICE_NAME: config.get( "service_name", app_name, @@ -91,120 +81,16 @@ def init(config: Optional[Dict[str, str]] = {}): "@agentuity/sdkVersion": sdkVersion, "@agentuity/cliVersion": cliVersion, "@agentuity/language": "python", + "env": "dev" if devmode else "production", + "version": __version__, } - ) - - headers = { - "Authorization": "Bearer " + bearer_token, - } - - tracerProvider = TracerProvider( - resource=resource, - shutdown_on_exit=False, - ) - exporter = OTLPSpanExporter( - endpoint=endpoint + "/v1/traces", - headers=headers, - compression=Compression.Gzip, - timeout=10, - ) - processor = BatchSpanProcessor( - exporter, - export_timeout_millis=export_internal_ms, - max_export_batch_size=max_export_batch_size, - schedule_delay_millis=schedule_delay_millis, - ) - - if os.environ.get("AGENTUITY_OTLP_CONSOLE_EXPORTER", "false") == "true": - tracerProvider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter())) - - tracerProvider.add_span_processor(processor) - trace.set_tracer_provider(tracerProvider) - - reader = PeriodicExportingMetricReader( - OTLPMetricExporter( - endpoint=endpoint + "/v1/metrics", - headers=headers, - compression=Compression.Gzip, - timeout=10, - ), - export_interval_millis=export_internal_ms, - ) - meterProvider = MeterProvider( - resource=resource, - metric_readers=[reader], - shutdown_on_exit=False, - ) - metrics.set_meter_provider(meterProvider) - - # Set up logging - loggerProvider = LoggerProvider(resource=resource) - logProcessor = BatchLogRecordProcessor( - OTLPLogExporter( - endpoint=endpoint + "/v1/logs", - headers=headers, - compression=Compression.Gzip, - timeout=10, - ), - max_export_batch_size=max_export_batch_size, - export_timeout_millis=export_internal_ms, - schedule_delay_millis=schedule_delay_millis, - ) - loggerProvider.add_log_record_processor(logProcessor) - _logs.set_logger_provider(loggerProvider) - - handler = LoggingHandler( - level=logging.NOTSET, - logger_provider=loggerProvider, - ) - module_filter = ModuleFilter() - handler.addFilter(module_filter) - - root_logger = logging.getLogger() - root_logger.addHandler(handler) - - propagator = TraceContextTextMapPropagator() - set_global_textmap(propagator) - - stopped = False - - def signal_handler(sig, frame): - nonlocal stopped - if stopped: - return - stopped = True - logProcessor.force_flush() - meterProvider.force_flush() - tracerProvider.force_flush() - meterProvider.shutdown() - tracerProvider.shutdown() - logProcessor.shutdown() - - # Register signal handler for graceful shutdown - signal.signal(signal.SIGINT, signal_handler) - signal.signal(signal.SIGTERM, signal_handler) - - # Initialize traceloop for automatic instrumentation - try: - from traceloop.sdk import Traceloop - - # Build app name from project and agent info if available - project_name = config.get("project_name", "") - agent_name = config.get("agent_name", "") - app_name = f"{project_name}:{agent_name}" - - headers = {"Authorization": f"Bearer {bearer_token}"} if bearer_token else {} Traceloop.init( app_name=app_name, api_endpoint=endpoint, headers=headers, - disable_batch=devmode, # Only disable batching in dev mode - telemetry_enabled=False, # Don't send any data to Traceloop - resource_attributes={ - "env": "dev" if devmode else "production", - "version": __version__, - }, + disable_batch=devmode, + resource_attributes=resource_attributes, ) logger.debug(f"Traceloop initialized with app_name: {app_name}") logger.info("Traceloop configured successfully") @@ -213,7 +99,7 @@ def signal_handler(sig, frame): except Exception as e: logger.warning(f"Failed to configure Traceloop: {e}, continuing without it") - return handler + return None __all__ = ["init", "create_logger"] From 166e7bed9eaff7535eb694a2c3dae3a4f837a8eb Mon Sep 17 00:00:00 2001 From: Jason Walkow Date: Tue, 2 Sep 2025 22:52:20 -0600 Subject: [PATCH 2/5] disable telemtry --- agentuity/otel/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/agentuity/otel/__init__.py b/agentuity/otel/__init__.py index 994382f..e8fd7df 100644 --- a/agentuity/otel/__init__.py +++ b/agentuity/otel/__init__.py @@ -91,6 +91,7 @@ def init(config: Optional[Dict[str, str]] = {}): headers=headers, disable_batch=devmode, resource_attributes=resource_attributes, + telemetry_enabled=False ) logger.debug(f"Traceloop initialized with app_name: {app_name}") logger.info("Traceloop configured successfully") From 69e1241fbeaa3c96c6a0e814e8ebbd47cc9fec8f Mon Sep 17 00:00:00 2001 From: Jason Walkow Date: Tue, 2 Sep 2025 22:55:03 -0600 Subject: [PATCH 3/5] adjust tests --- tests/otel/test_init.py | 58 +++++------------------------------------ 1 file changed, 6 insertions(+), 52 deletions(-) diff --git a/tests/otel/test_init.py b/tests/otel/test_init.py index a6fae33..265ab2f 100644 --- a/tests/otel/test_init.py +++ b/tests/otel/test_init.py @@ -72,62 +72,16 @@ def test_init_with_config(self): } with ( - patch("agentuity.otel.TracerProvider") as mock_tracer_provider, - patch("agentuity.otel.OTLPSpanExporter") as mock_span_exporter, - patch("agentuity.otel.BatchSpanProcessor"), - patch("agentuity.otel.trace.set_tracer_provider") as mock_set_tracer, - patch("agentuity.otel.PeriodicExportingMetricReader"), - patch("agentuity.otel.OTLPMetricExporter"), - patch("agentuity.otel.MeterProvider") as mock_meter_provider, - patch("agentuity.otel.metrics.set_meter_provider") as mock_set_meter, - patch("agentuity.otel.LoggerProvider") as mock_logger_provider, - patch("agentuity.otel.BatchLogRecordProcessor"), - patch("agentuity.otel.OTLPLogExporter"), - patch("agentuity.otel._logs.set_logger_provider") as mock_set_logger, - patch("agentuity.otel.LoggingHandler") as mock_logging_handler, - patch("agentuity.otel.ModuleFilter"), - patch("agentuity.otel.TraceContextTextMapPropagator"), - patch("agentuity.otel.set_global_textmap") as mock_set_textmap, - patch("agentuity.otel.signal.signal") as mock_signal, patch("traceloop.sdk.Traceloop.init") as mock_traceloop_init, patch("agentuity.otel.logger"), - patch("agentuity.otel.logging.getLogger") as mock_get_logger, ): - mock_root_logger = MagicMock() - mock_get_logger.return_value = mock_root_logger - - mock_handler_instance = MagicMock() - mock_logging_handler.return_value = mock_handler_instance - result = init(config) - assert result is mock_handler_instance - - mock_tracer_provider.assert_called_once() - resource_arg = mock_tracer_provider.call_args[1]["resource"] - assert resource_arg.attributes[SERVICE_NAME] == "test_service" - assert resource_arg.attributes[SERVICE_VERSION] == "1.0.0" - - mock_span_exporter.assert_called_once_with( - endpoint="https://test.com/v1/traces", - headers={"Authorization": "Bearer test_token"}, - compression=Compression.Gzip, - timeout=10, - ) - - mock_set_tracer.assert_called_once() - - mock_meter_provider.assert_called_once() - mock_set_meter.assert_called_once() - - mock_logger_provider.assert_called_once() - mock_set_logger.assert_called_once() - - mock_logging_handler.assert_called_once() - mock_root_logger.addHandler.assert_called_once_with(mock_handler_instance) - - mock_set_textmap.assert_called_once() - - assert mock_signal.call_count == 2 + assert result is None mock_traceloop_init.assert_called_once() + args, kwargs = mock_traceloop_init.call_args + assert kwargs["api_endpoint"] == "https://test.com" + assert kwargs["headers"] == {"Authorization": "Bearer test_token"} + assert kwargs["resource_attributes"][SERVICE_NAME] == "test_service" + assert kwargs["resource_attributes"][SERVICE_VERSION] == "1.0.0" From 495d3e01780fe5f6dfb665be92beb0030c89b356 Mon Sep 17 00:00:00 2001 From: Jason Walkow Date: Tue, 2 Sep 2025 22:59:29 -0600 Subject: [PATCH 4/5] linted --- tests/otel/test_init.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/otel/test_init.py b/tests/otel/test_init.py index 265ab2f..a0cb836 100644 --- a/tests/otel/test_init.py +++ b/tests/otel/test_init.py @@ -2,7 +2,7 @@ import sys from unittest.mock import patch, MagicMock from opentelemetry.sdk.resources import SERVICE_NAME, SERVICE_VERSION -from opentelemetry.exporter.otlp.proto.http.trace_exporter import Compression + sys.modules["openlit"] = MagicMock() from agentuity.otel import init # noqa: E402 From 3d821c6d878e801df8c079e29895f5470dbdbb63 Mon Sep 17 00:00:00 2001 From: Jason Walkow Date: Wed, 3 Sep 2025 10:27:23 -0600 Subject: [PATCH 5/5] use same format as original --- agentuity/otel/__init__.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/agentuity/otel/__init__.py b/agentuity/otel/__init__.py index e8fd7df..7598192 100644 --- a/agentuity/otel/__init__.py +++ b/agentuity/otel/__init__.py @@ -57,11 +57,6 @@ def init(config: Optional[Dict[str, str]] = {}): try: from traceloop.sdk import Traceloop - # Build app name from project and agent info if available - project_name = config.get("project_name", "") - agent_name = config.get("agent_name", "") - app_name = f"{project_name}:{agent_name}" - headers = {"Authorization": f"Bearer {bearer_token}"} if bearer_token else {} resource_attributes = {