mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
[BREAKING] Python: Observability cleanup (#905)
* Further observability cleanup and update telemetry samples * Add VS Code Extension config * Fix unit tests * Fix unit tests * Add more comments * Remove live metric
This commit is contained in:
committed by
GitHub
Unverified
parent
f527fbe6ce
commit
a480ccfd16
@@ -404,18 +404,17 @@ class AIFunction(BaseTool, Generic[ArgsT, ReturnT]):
|
||||
|
||||
Args:
|
||||
arguments: A Pydantic model instance containing the arguments for the function.
|
||||
otel_settings: Optional model diagnostics settings to override the default settings.
|
||||
kwargs: keyword arguments to pass to the function, will not be used if `arguments` is provided.
|
||||
"""
|
||||
global OTEL_SETTINGS
|
||||
from .observability import OTEL_SETTINGS
|
||||
global OBSERVABILITY_SETTINGS
|
||||
from .observability import OBSERVABILITY_SETTINGS
|
||||
|
||||
tool_call_id = kwargs.pop("tool_call_id", None)
|
||||
if arguments is not None:
|
||||
if not isinstance(arguments, self.input_model):
|
||||
raise TypeError(f"Expected {self.input_model.__name__}, got {type(arguments).__name__}")
|
||||
kwargs = arguments.model_dump(exclude_none=True)
|
||||
if not OTEL_SETTINGS.ENABLED: # type: ignore[name-defined]
|
||||
if not OBSERVABILITY_SETTINGS.ENABLED: # type: ignore[name-defined]
|
||||
logger.info(f"Function name: {self.name}")
|
||||
logger.debug(f"Function arguments: {kwargs}")
|
||||
res = self.__call__(**kwargs)
|
||||
@@ -425,7 +424,7 @@ class AIFunction(BaseTool, Generic[ArgsT, ReturnT]):
|
||||
return result # type: ignore[reportReturnType]
|
||||
|
||||
attributes = get_function_span_attributes(self, tool_call_id=tool_call_id)
|
||||
if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED: # type: ignore[name-defined]
|
||||
if OBSERVABILITY_SETTINGS.SENSITIVE_DATA_ENABLED: # type: ignore[name-defined]
|
||||
attributes.update({
|
||||
OtelAttr.TOOL_ARGUMENTS: arguments.model_dump_json()
|
||||
if arguments
|
||||
@@ -436,7 +435,7 @@ class AIFunction(BaseTool, Generic[ArgsT, ReturnT]):
|
||||
with get_function_span(attributes=attributes) as span:
|
||||
attributes[OtelAttr.MEASUREMENT_FUNCTION_TAG_NAME] = self.name
|
||||
logger.info(f"Function name: {self.name}")
|
||||
if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED: # type: ignore[name-defined]
|
||||
if OBSERVABILITY_SETTINGS.SENSITIVE_DATA_ENABLED: # type: ignore[name-defined]
|
||||
logger.debug(f"Function arguments: {kwargs}")
|
||||
start_time_stamp = perf_counter()
|
||||
end_time_stamp: float | None = None
|
||||
@@ -452,7 +451,7 @@ class AIFunction(BaseTool, Generic[ArgsT, ReturnT]):
|
||||
raise
|
||||
else:
|
||||
logger.info(f"Function {self.name} succeeded.")
|
||||
if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED: # type: ignore[name-defined]
|
||||
if OBSERVABILITY_SETTINGS.SENSITIVE_DATA_ENABLED: # type: ignore[name-defined]
|
||||
try:
|
||||
json_result = json.dumps(result)
|
||||
except (TypeError, OverflowError):
|
||||
|
||||
@@ -356,8 +356,8 @@ class WorkflowContext(Generic[T_Out, T_W_Out]):
|
||||
target_id: The ID of the target executor to send the message to.
|
||||
If None, the message will be sent to all target executors.
|
||||
"""
|
||||
global OTEL_SETTINGS
|
||||
from ..observability import OTEL_SETTINGS
|
||||
global OBSERVABILITY_SETTINGS
|
||||
from ..observability import OBSERVABILITY_SETTINGS
|
||||
|
||||
# Create publishing span (inherits current trace context automatically)
|
||||
attributes: dict[str, str] = {OtelAttr.MESSAGE_TYPE: type(message).__name__}
|
||||
@@ -368,7 +368,7 @@ class WorkflowContext(Generic[T_Out, T_W_Out]):
|
||||
msg = Message(data=message, source_id=self._executor_id, target_id=target_id)
|
||||
|
||||
# Inject current trace context if tracing enabled
|
||||
if OTEL_SETTINGS.ENABLED and span and span.is_recording(): # type: ignore[name-defined]
|
||||
if OBSERVABILITY_SETTINGS.ENABLED and span and span.is_recording(): # type: ignore[name-defined]
|
||||
trace_context: dict[str, str] = {}
|
||||
inject(trace_context) # Inject current trace context for message propagation
|
||||
|
||||
|
||||
@@ -20,13 +20,9 @@ from .exceptions import AgentInitializationError, ChatClientInitializationError
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from azure.core.credentials import TokenCredential
|
||||
from opentelemetry.sdk._events import EventLoggerProvider
|
||||
from opentelemetry.sdk._logs import LoggerProvider
|
||||
from opentelemetry.sdk._logs._internal.export import LogExporter
|
||||
from opentelemetry.sdk.metrics import MeterProvider
|
||||
from opentelemetry.sdk.metrics.export import MetricExporter
|
||||
from opentelemetry.sdk.resources import Resource
|
||||
from opentelemetry.sdk.trace import TracerProvider
|
||||
from opentelemetry.sdk.trace.export import SpanExporter
|
||||
from opentelemetry.trace import Tracer
|
||||
from opentelemetry.util._decorator import _AgnosticContextManager # type: ignore[reportPrivateUsage]
|
||||
@@ -328,11 +324,9 @@ def _create_resource() -> "Resource":
|
||||
return Resource.create({service_attributes.SERVICE_NAME: service_name})
|
||||
|
||||
|
||||
class OtelSettings(AFBaseSettings):
|
||||
"""Settings for Open Telemetry.
|
||||
class ObservabilitySettings(AFBaseSettings):
|
||||
"""Settings for Agent Framework Observability.
|
||||
|
||||
The settings are first loaded from environment variables with
|
||||
the prefix 'AGENT_FRAMEWORK_GENAI_'.
|
||||
If the environment variables are not found, the settings can
|
||||
be loaded from a .env file with the encoding 'utf-8'.
|
||||
If the settings are not found in the .env file, the settings
|
||||
@@ -349,10 +343,11 @@ class OtelSettings(AFBaseSettings):
|
||||
(Env var ENABLE_SENSITIVE_DATA)
|
||||
applicationinsights_connection_string: The Azure Monitor connection string. Default is None.
|
||||
(Env var APPLICATIONINSIGHTS_CONNECTION_STRING)
|
||||
applicationinsights_live_metrics: Enable Azure Monitor live metrics. Default is False.
|
||||
(Env var APPLICATIONINSIGHTS_LIVE_METRICS)
|
||||
otlp_endpoint: The OpenTelemetry Protocol (OTLP) endpoint. Default is None.
|
||||
(Env var OTLP_ENDPOINT)
|
||||
vs_code_extension_port: The port the AI Toolkit or AzureAI Foundry VS Code extensions are listening on.
|
||||
Default is None.
|
||||
(Env var VS_CODE_EXTENSION_PORT)
|
||||
"""
|
||||
|
||||
env_prefix: ClassVar[str] = ""
|
||||
@@ -360,14 +355,10 @@ class OtelSettings(AFBaseSettings):
|
||||
enable_otel: bool = False
|
||||
enable_sensitive_data: bool = False
|
||||
applicationinsights_connection_string: str | list[str] | None = None
|
||||
applicationinsights_live_metrics: bool = False
|
||||
otlp_endpoint: str | list[str] | None = None
|
||||
vs_code_extension_port: int | None = None
|
||||
_resource: "Resource" = PrivateAttr(default_factory=_create_resource)
|
||||
_executed_setup: bool = PrivateAttr(default=False)
|
||||
_tracer_provider: "TracerProvider | None" = PrivateAttr(default=None)
|
||||
_meter_provider: "MeterProvider | None" = PrivateAttr(default=None)
|
||||
_logger_provider: "LoggerProvider | None" = PrivateAttr(default=None)
|
||||
_event_logger_provider: "EventLoggerProvider | None" = PrivateAttr(default=None)
|
||||
|
||||
@property
|
||||
def ENABLED(self) -> bool:
|
||||
@@ -400,24 +391,24 @@ class OtelSettings(AFBaseSettings):
|
||||
"""Set the resource."""
|
||||
self._resource = value
|
||||
|
||||
def setup_observability(
|
||||
def _configure(
|
||||
self,
|
||||
credential: "TokenCredential | None" = None,
|
||||
additional_exporters: list["LogExporter | SpanExporter | MetricExporter"] | None = None,
|
||||
force_setup: bool = False,
|
||||
) -> None:
|
||||
"""Setup telemetry based on the settings.
|
||||
"""Configure application-wide observability based on the settings.
|
||||
|
||||
This method is a helper method to create the log, trace and metric providers.
|
||||
This method is intended to be called once during the application startup. Calling it multiple times
|
||||
will have no effect.
|
||||
|
||||
Args:
|
||||
credential: The credential to use for Azure Monitor Entra ID authentication. Default is None.
|
||||
additional_exporters: A list of additional exporters to add to the configuration. Default is None.
|
||||
force_setup: Force the setup to be executed even if it has already been executed. Default is False.
|
||||
"""
|
||||
if (not self.ENABLED) or (self._executed_setup and not force_setup):
|
||||
if not self.ENABLED or self._executed_setup:
|
||||
return
|
||||
|
||||
global_logger = logging.getLogger()
|
||||
global_logger.setLevel(logging.NOTSET)
|
||||
exporters: list["LogExporter | SpanExporter | MetricExporter"] = additional_exporters or []
|
||||
if self.otlp_endpoint:
|
||||
exporters.extend(
|
||||
@@ -438,26 +429,6 @@ class OtelSettings(AFBaseSettings):
|
||||
)
|
||||
self._configure_providers(exporters)
|
||||
self._executed_setup = True
|
||||
if self.applicationinsights_connection_string and self.applicationinsights_live_metrics:
|
||||
from azure.monitor.opentelemetry import configure_azure_monitor
|
||||
|
||||
conn_strings = (
|
||||
self.applicationinsights_connection_string
|
||||
if isinstance(self.applicationinsights_connection_string, list)
|
||||
else [self.applicationinsights_connection_string]
|
||||
)
|
||||
for con_str in conn_strings:
|
||||
# only configure using this for live_metrics, ignore the rest.
|
||||
configure_azure_monitor(
|
||||
connection_string=con_str,
|
||||
credential=credential,
|
||||
logger_name="agent_framework",
|
||||
resource=self.resource,
|
||||
enable_live_metrics=self.applicationinsights_live_metrics,
|
||||
disable_logging=True,
|
||||
disable_metric=True,
|
||||
disable_tracing=True,
|
||||
)
|
||||
|
||||
def check_endpoint_already_configured(self, otlp_endpoint: str) -> bool:
|
||||
"""Check if the endpoint is already configured.
|
||||
@@ -467,9 +438,7 @@ class OtelSettings(AFBaseSettings):
|
||||
"""
|
||||
if not self.otlp_endpoint:
|
||||
return False
|
||||
return otlp_endpoint not in (
|
||||
self.otlp_endpoint if isinstance(self.otlp_endpoint, list) else [self.otlp_endpoint]
|
||||
)
|
||||
return otlp_endpoint in (self.otlp_endpoint if isinstance(self.otlp_endpoint, list) else [self.otlp_endpoint])
|
||||
|
||||
def check_connection_string_already_configured(self, connection_string: str) -> bool:
|
||||
"""Check if the connection string is already configured.
|
||||
@@ -479,7 +448,7 @@ class OtelSettings(AFBaseSettings):
|
||||
"""
|
||||
if not self.applicationinsights_connection_string:
|
||||
return False
|
||||
return connection_string not in (
|
||||
return connection_string in (
|
||||
self.applicationinsights_connection_string
|
||||
if isinstance(self.applicationinsights_connection_string, list)
|
||||
else [self.applicationinsights_connection_string]
|
||||
@@ -488,7 +457,6 @@ class OtelSettings(AFBaseSettings):
|
||||
def _configure_providers(self, exporters: list["LogExporter | MetricExporter | SpanExporter"]) -> None:
|
||||
"""Configure tracing, logging, events and metrics with the provided exporters."""
|
||||
from opentelemetry._logs import set_logger_provider
|
||||
from opentelemetry.sdk._events import EventLoggerProvider
|
||||
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
|
||||
from opentelemetry.sdk._logs._internal.export import LogExporter
|
||||
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
|
||||
@@ -498,52 +466,49 @@ class OtelSettings(AFBaseSettings):
|
||||
from opentelemetry.sdk.trace import TracerProvider
|
||||
from opentelemetry.sdk.trace.export import BatchSpanProcessor, SpanExporter
|
||||
|
||||
# Use SimpleSpanProcessor for in-memory exporter (tests) so spans are
|
||||
# exported synchronously and immediately available via
|
||||
# InMemorySpanExporter.get_finished_spans(). For all other exporters
|
||||
# keep using the BatchSpanProcessor behavior.
|
||||
|
||||
# Tracing
|
||||
if not self._executed_setup:
|
||||
new_tracer_provider = TracerProvider(resource=self.resource)
|
||||
# setting global tracer provider, other libaries can use this,
|
||||
# but if another global tracer provider is already set this will not override it.
|
||||
trace.set_tracer_provider(new_tracer_provider)
|
||||
tracer_provider = trace.get_tracer_provider()
|
||||
tracer_provider = TracerProvider(resource=self.resource)
|
||||
trace.set_tracer_provider(tracer_provider)
|
||||
should_add_console_exporter = True
|
||||
for exporter in exporters:
|
||||
if not isinstance(exporter, SpanExporter):
|
||||
continue
|
||||
if (add_span_processor := getattr(tracer_provider, "add_span_processor", None)) and callable(
|
||||
add_span_processor
|
||||
):
|
||||
add_span_processor(BatchSpanProcessor(exporter))
|
||||
if isinstance(exporter, SpanExporter):
|
||||
tracer_provider.add_span_processor(BatchSpanProcessor(exporter))
|
||||
should_add_console_exporter = False
|
||||
if should_add_console_exporter:
|
||||
from opentelemetry.sdk.trace.export import ConsoleSpanExporter
|
||||
|
||||
tracer_provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
|
||||
|
||||
# Logging
|
||||
if not self._logger_provider:
|
||||
self._logger_provider = LoggerProvider(resource=self.resource)
|
||||
logger_provider = LoggerProvider(resource=self.resource)
|
||||
should_add_console_exporter = True
|
||||
for exporter in exporters:
|
||||
if isinstance(exporter, LogExporter):
|
||||
logger_provider.add_log_record_processor(BatchLogRecordProcessor(exporter))
|
||||
should_add_console_exporter = False
|
||||
if should_add_console_exporter:
|
||||
from opentelemetry.sdk._logs._internal.export import ConsoleLogExporter
|
||||
|
||||
[
|
||||
self._logger_provider.add_log_record_processor(BatchLogRecordProcessor(exporter))
|
||||
for exporter in exporters
|
||||
if isinstance(exporter, LogExporter)
|
||||
]
|
||||
logger = get_logger()
|
||||
if not any(isinstance(handler, LoggingHandler) for handler in logger.handlers):
|
||||
handler = LoggingHandler(logger_provider=self._logger_provider)
|
||||
logger.addHandler(handler)
|
||||
logger.setLevel(logging.NOTSET)
|
||||
set_logger_provider(self._logger_provider)
|
||||
# Events
|
||||
if not self._event_logger_provider:
|
||||
self._event_logger_provider = EventLoggerProvider(self._logger_provider)
|
||||
logger_provider.add_log_record_processor(BatchLogRecordProcessor(ConsoleLogExporter()))
|
||||
|
||||
# Attach a handler with the provider to the root logger
|
||||
logger = logging.getLogger()
|
||||
handler = LoggingHandler(logger_provider=logger_provider)
|
||||
logger.addHandler(handler)
|
||||
set_logger_provider(logger_provider)
|
||||
|
||||
# metrics
|
||||
metric_readers = [
|
||||
PeriodicExportingMetricReader(exporter, export_interval_millis=5000)
|
||||
for exporter in exporters
|
||||
if isinstance(exporter, MetricExporter)
|
||||
]
|
||||
if not metric_readers:
|
||||
from opentelemetry.sdk.metrics.export import ConsoleMetricExporter
|
||||
|
||||
metric_readers = [PeriodicExportingMetricReader(ConsoleMetricExporter(), export_interval_millis=5000)]
|
||||
meter_provider = MeterProvider(
|
||||
metric_readers=[
|
||||
PeriodicExportingMetricReader(exporter, export_interval_millis=5000)
|
||||
for exporter in exporters
|
||||
if isinstance(exporter, MetricExporter)
|
||||
],
|
||||
metric_readers=metric_readers,
|
||||
resource=self.resource,
|
||||
views=[
|
||||
# Dropping all instrument names except for those starting with "agent_framework"
|
||||
@@ -555,10 +520,6 @@ class OtelSettings(AFBaseSettings):
|
||||
metrics.set_meter_provider(meter_provider)
|
||||
|
||||
|
||||
global OTEL_SETTINGS
|
||||
OTEL_SETTINGS: OtelSettings = OtelSettings()
|
||||
|
||||
|
||||
def get_tracer(
|
||||
instrumenting_module_name: str = "agent_framework",
|
||||
instrumenting_library_version: str = version_info,
|
||||
@@ -606,74 +567,152 @@ def get_meter(
|
||||
return metrics.get_meter(name=name, version=version, schema_url=schema_url, attributes=attributes)
|
||||
|
||||
|
||||
global OBSERVABILITY_SETTINGS
|
||||
OBSERVABILITY_SETTINGS: ObservabilitySettings = ObservabilitySettings()
|
||||
|
||||
|
||||
def setup_observability(
|
||||
enable_sensitive_data: bool | None = None,
|
||||
otlp_endpoint: str | list[str] | None = None,
|
||||
applicationinsights_connection_string: str | list[str] | None = None,
|
||||
credential: "TokenCredential | None" = None,
|
||||
enable_live_metrics: bool | None = None,
|
||||
exporters: list["LogExporter | SpanExporter | MetricExporter"] | None = None,
|
||||
vs_code_extension_port: int | None = None,
|
||||
) -> None:
|
||||
"""Setup telemetry with optionally provided settings, it is implied that you want to enable telemetry.
|
||||
"""Convenient method to setup observability for the application.
|
||||
|
||||
All of these values can be set through environment variables or you can pass them here,
|
||||
in the case where both are present, the provided value takes precedence.
|
||||
This method will create the exporters and the providers for the application,
|
||||
based on the provided values and the environment variables.
|
||||
|
||||
If you have both connection_string and otlp_endpoint, the connection_string will be used.
|
||||
Call this method once during application startup, before any telemetry is captured.
|
||||
DO NOT call this method multiple times, as it may lead to unexpected behavior.
|
||||
|
||||
If you have configured the providers manually, calling this method will not have any effect:
|
||||
|
||||
```python
|
||||
# Some where in your application startup code
|
||||
trace.set_tracer_provider(TracerProvider(...))
|
||||
|
||||
# After the above call, calling setup_observability will not have any effect
|
||||
setup_observability()
|
||||
```
|
||||
|
||||
The reverse is also true:
|
||||
|
||||
```python
|
||||
# Some where in your application startup code
|
||||
setup_observability()
|
||||
|
||||
# After the above call, calling trace.set_tracer_provider will not have any effect
|
||||
trace.set_tracer_provider(TracerProvider(...))
|
||||
```
|
||||
|
||||
The OTel endpoint and the Application Insights connection string can be set through
|
||||
environment variables or you can pass additional ones here. In the case where both
|
||||
are present, non-duplicate values will be added:
|
||||
|
||||
## With environment variables
|
||||
|
||||
This method will read the settings from the environment:
|
||||
|
||||
```python
|
||||
setup_observability()
|
||||
```
|
||||
|
||||
## Without environment variables and use parameters
|
||||
|
||||
It is also possible to pass the settings directly:
|
||||
|
||||
```python
|
||||
setup_observability(
|
||||
enable_sensitive_data=True,
|
||||
otlp_endpoint=["http://localhost:7431"],
|
||||
applicationinsights_connection_string=["..."],
|
||||
exporters=[...], # your custom exporters
|
||||
vs_code_extension_port=4317,
|
||||
)
|
||||
```
|
||||
|
||||
## Mixed
|
||||
|
||||
When both environment variables and parameters are used, the following settings will get overridden:
|
||||
- enable_sensitive_data
|
||||
- vs_code_extension_port
|
||||
|
||||
The endpoints and connection strings will be combined, excluding duplicates.
|
||||
|
||||
```env
|
||||
OTEL_ENDPOINT="http://localhost:7431"
|
||||
```
|
||||
|
||||
```python
|
||||
setup_observability(
|
||||
enable_sensitive_data=True,
|
||||
otlp_endpoint=["http://localhost:4317"],
|
||||
)
|
||||
```
|
||||
|
||||
Exporters will be created for both endpoints.
|
||||
|
||||
Args:
|
||||
enable_sensitive_data: Enable OpenTelemetry sensitive events. Default is False.
|
||||
enable_sensitive_data: Enable OpenTelemetry sensitive events.
|
||||
If set, this will override the value set through the environment variable.
|
||||
Default is None.
|
||||
otlp_endpoint: The OpenTelemetry Protocol (OTLP) endpoint. Default is None.
|
||||
Will be used to create a `OTLPLogExporter`, `OTLPMetricExporter` and `OTLPSpanExporter`
|
||||
applicationinsights_connection_string: The Azure Monitor connection string. Default is None.
|
||||
Will be used to create AzureMonitorExporters.
|
||||
credential: The credential to use for Azure Monitor Entra ID authentication.
|
||||
Default is None.
|
||||
enable_live_metrics: Enable Azure Monitor live metrics. Default is False.
|
||||
exporters: a list of exporters, for logs, metrics or spans, or any combination,
|
||||
these will be added directly, and allows you to customize the spans completely
|
||||
|
||||
exporters: A list of exporters, for logs, metrics or spans, or any combination.
|
||||
These will be added directly, and allows you to customize the spans completely.
|
||||
vs_code_extension_port: The port the AI Toolkit or AzureAI Foundry VS Code extensions are
|
||||
listening on. When this is set, additional OTEL exporters will be created with endpoint
|
||||
`http://localhost:{vs_code_extension_port}` unless this endpoint is already configured.
|
||||
This will override the value set through the environment variable.
|
||||
Default is None.
|
||||
"""
|
||||
global OTEL_SETTINGS
|
||||
# Update the otel settings with the provided values
|
||||
OTEL_SETTINGS.enable_otel = True
|
||||
global OBSERVABILITY_SETTINGS
|
||||
# Update the observability settings with the provided values
|
||||
OBSERVABILITY_SETTINGS.enable_otel = True
|
||||
if enable_sensitive_data is not None:
|
||||
OTEL_SETTINGS.enable_sensitive_data = enable_sensitive_data
|
||||
if enable_live_metrics is not None:
|
||||
OTEL_SETTINGS.applicationinsights_live_metrics = enable_live_metrics
|
||||
# Run the initial setup, which will create the providers, and add env setting exporters
|
||||
new_exporters: list["LogExporter | SpanExporter | MetricExporter"] = []
|
||||
if OTEL_SETTINGS.ENABLED and (otlp_endpoint or applicationinsights_connection_string or exporters):
|
||||
# create the exporters, after checking if they are already configured through the env.
|
||||
new_exporters = exporters or []
|
||||
if otlp_endpoint:
|
||||
if isinstance(otlp_endpoint, str):
|
||||
otlp_endpoint = [otlp_endpoint]
|
||||
new_exporters.extend(
|
||||
_get_otlp_exporters(
|
||||
endpoints=[
|
||||
endpoint
|
||||
for endpoint in otlp_endpoint
|
||||
if not OTEL_SETTINGS.check_endpoint_already_configured(endpoint)
|
||||
]
|
||||
)
|
||||
OBSERVABILITY_SETTINGS.enable_sensitive_data = enable_sensitive_data
|
||||
if vs_code_extension_port is not None:
|
||||
OBSERVABILITY_SETTINGS.vs_code_extension_port = vs_code_extension_port
|
||||
|
||||
# Create exporters, after checking if they are already configured through the env.
|
||||
new_exporters: list["LogExporter | SpanExporter | MetricExporter"] = exporters or []
|
||||
if otlp_endpoint:
|
||||
if isinstance(otlp_endpoint, str):
|
||||
otlp_endpoint = [otlp_endpoint]
|
||||
new_exporters.extend(
|
||||
_get_otlp_exporters(
|
||||
endpoints=[
|
||||
endpoint
|
||||
for endpoint in otlp_endpoint
|
||||
if not OBSERVABILITY_SETTINGS.check_endpoint_already_configured(endpoint)
|
||||
]
|
||||
)
|
||||
if applicationinsights_connection_string:
|
||||
if isinstance(applicationinsights_connection_string, str):
|
||||
applicationinsights_connection_string = [applicationinsights_connection_string]
|
||||
new_exporters.extend(
|
||||
_get_azure_monitor_exporters(
|
||||
connection_strings=[
|
||||
conn_str
|
||||
for conn_str in applicationinsights_connection_string
|
||||
if not OTEL_SETTINGS.check_connection_string_already_configured(conn_str)
|
||||
],
|
||||
credential=credential,
|
||||
)
|
||||
)
|
||||
if applicationinsights_connection_string:
|
||||
if isinstance(applicationinsights_connection_string, str):
|
||||
applicationinsights_connection_string = [applicationinsights_connection_string]
|
||||
new_exporters.extend(
|
||||
_get_azure_monitor_exporters(
|
||||
connection_strings=[
|
||||
conn_str
|
||||
for conn_str in applicationinsights_connection_string
|
||||
if not OBSERVABILITY_SETTINGS.check_connection_string_already_configured(conn_str)
|
||||
],
|
||||
credential=credential,
|
||||
)
|
||||
OTEL_SETTINGS.setup_observability(
|
||||
credential=credential, additional_exporters=new_exporters, force_setup=bool(new_exporters)
|
||||
)
|
||||
)
|
||||
if OBSERVABILITY_SETTINGS.vs_code_extension_port:
|
||||
endpoint = f"http://localhost:{OBSERVABILITY_SETTINGS.vs_code_extension_port}"
|
||||
if OBSERVABILITY_SETTINGS.check_endpoint_already_configured(endpoint):
|
||||
new_exporters.extend(_get_otlp_exporters(endpoints=[endpoint]))
|
||||
|
||||
OBSERVABILITY_SETTINGS._configure(credential=credential, additional_exporters=new_exporters) # pyright: ignore[reportPrivateUsage]
|
||||
|
||||
|
||||
# region Chat Client Telemetry
|
||||
@@ -721,8 +760,8 @@ def _trace_get_response(
|
||||
messages: "str | ChatMessage | list[str] | list[ChatMessage]",
|
||||
**kwargs: Any,
|
||||
) -> "ChatResponse":
|
||||
global OTEL_SETTINGS
|
||||
if not OTEL_SETTINGS.ENABLED:
|
||||
global OBSERVABILITY_SETTINGS
|
||||
if not OBSERVABILITY_SETTINGS.ENABLED:
|
||||
# If model diagnostics are not enabled, just return the completion
|
||||
return await func(
|
||||
self,
|
||||
@@ -747,7 +786,7 @@ def _trace_get_response(
|
||||
**kwargs,
|
||||
)
|
||||
with _get_span(attributes=attributes, span_name_attribute=SpanAttributes.LLM_REQUEST_MODEL) as span:
|
||||
if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED and messages:
|
||||
if OBSERVABILITY_SETTINGS.SENSITIVE_DATA_ENABLED and messages:
|
||||
_capture_messages(span=span, provider_name=provider_name, messages=messages)
|
||||
start_time_stamp = perf_counter()
|
||||
end_time_stamp: float | None = None
|
||||
@@ -767,7 +806,7 @@ def _trace_get_response(
|
||||
token_usage_histogram=self.additional_properties["token_usage_histogram"],
|
||||
operation_duration_histogram=self.additional_properties["operation_duration_histogram"],
|
||||
)
|
||||
if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED and response.messages:
|
||||
if OBSERVABILITY_SETTINGS.SENSITIVE_DATA_ENABLED and response.messages:
|
||||
_capture_messages(
|
||||
span=span,
|
||||
provider_name=provider_name,
|
||||
@@ -803,8 +842,8 @@ def _trace_get_streaming_response(
|
||||
async def trace_get_streaming_response(
|
||||
self: "ChatClientProtocol", messages: "str | ChatMessage | list[str] | list[ChatMessage]", **kwargs: Any
|
||||
) -> AsyncIterable["ChatResponseUpdate"]:
|
||||
global OTEL_SETTINGS
|
||||
if not OTEL_SETTINGS.ENABLED:
|
||||
global OBSERVABILITY_SETTINGS
|
||||
if not OBSERVABILITY_SETTINGS.ENABLED:
|
||||
# If model diagnostics are not enabled, just return the completion
|
||||
async for update in func(self, messages=messages, **kwargs):
|
||||
yield update
|
||||
@@ -829,7 +868,7 @@ def _trace_get_streaming_response(
|
||||
)
|
||||
all_updates: list["ChatResponseUpdate"] = []
|
||||
with _get_span(attributes=attributes, span_name_attribute=SpanAttributes.LLM_REQUEST_MODEL) as span:
|
||||
if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED and messages:
|
||||
if OBSERVABILITY_SETTINGS.SENSITIVE_DATA_ENABLED and messages:
|
||||
_capture_messages(
|
||||
span=span,
|
||||
provider_name=provider_name,
|
||||
@@ -859,7 +898,7 @@ def _trace_get_streaming_response(
|
||||
operation_duration_histogram=self.additional_properties["operation_duration_histogram"],
|
||||
)
|
||||
|
||||
if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED and response.messages:
|
||||
if OBSERVABILITY_SETTINGS.SENSITIVE_DATA_ENABLED and response.messages:
|
||||
_capture_messages(
|
||||
span=span,
|
||||
provider_name=provider_name,
|
||||
@@ -937,9 +976,9 @@ def _trace_agent_run(
|
||||
thread: "AgentThread | None" = None,
|
||||
**kwargs: Any,
|
||||
) -> "AgentRunResponse":
|
||||
global OTEL_SETTINGS
|
||||
global OBSERVABILITY_SETTINGS
|
||||
|
||||
if not OTEL_SETTINGS.ENABLED:
|
||||
if not OBSERVABILITY_SETTINGS.ENABLED:
|
||||
# If model diagnostics are not enabled, just return the completion
|
||||
return await run_func(self, messages=messages, thread=thread, **kwargs)
|
||||
|
||||
@@ -953,7 +992,7 @@ def _trace_agent_run(
|
||||
**kwargs,
|
||||
)
|
||||
with _get_span(attributes=attributes, span_name_attribute=OtelAttr.AGENT_NAME) as span:
|
||||
if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED and messages:
|
||||
if OBSERVABILITY_SETTINGS.SENSITIVE_DATA_ENABLED and messages:
|
||||
_capture_messages(
|
||||
span=span,
|
||||
provider_name=provider_name,
|
||||
@@ -968,7 +1007,7 @@ def _trace_agent_run(
|
||||
else:
|
||||
attributes = _get_response_attributes(attributes, response)
|
||||
_capture_response(span=span, attributes=attributes)
|
||||
if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED and response.messages:
|
||||
if OBSERVABILITY_SETTINGS.SENSITIVE_DATA_ENABLED and response.messages:
|
||||
_capture_messages(
|
||||
span=span,
|
||||
provider_name=provider_name,
|
||||
@@ -1000,9 +1039,9 @@ def _trace_agent_run_stream(
|
||||
thread: "AgentThread | None" = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable["AgentRunResponseUpdate"]:
|
||||
global OTEL_SETTINGS
|
||||
global OBSERVABILITY_SETTINGS
|
||||
|
||||
if not OTEL_SETTINGS.ENABLED:
|
||||
if not OBSERVABILITY_SETTINGS.ENABLED:
|
||||
# If model diagnostics are not enabled, just return the completion
|
||||
async for streaming_agent_response in run_streaming_func(self, messages=messages, thread=thread, **kwargs):
|
||||
yield streaming_agent_response
|
||||
@@ -1022,7 +1061,7 @@ def _trace_agent_run_stream(
|
||||
**kwargs,
|
||||
)
|
||||
with _get_span(attributes=attributes, span_name_attribute=OtelAttr.AGENT_NAME) as span:
|
||||
if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED and messages:
|
||||
if OBSERVABILITY_SETTINGS.SENSITIVE_DATA_ENABLED and messages:
|
||||
_capture_messages(
|
||||
span=span,
|
||||
provider_name=provider_name,
|
||||
@@ -1040,7 +1079,7 @@ def _trace_agent_run_stream(
|
||||
response = AgentRunResponse.from_agent_run_response_updates(all_updates)
|
||||
attributes = _get_response_attributes(attributes, response)
|
||||
_capture_response(span=span, attributes=attributes)
|
||||
if OTEL_SETTINGS.SENSITIVE_DATA_ENABLED and response.messages:
|
||||
if OBSERVABILITY_SETTINGS.SENSITIVE_DATA_ENABLED and response.messages:
|
||||
_capture_messages(
|
||||
span=span,
|
||||
provider_name=provider_name,
|
||||
@@ -1342,8 +1381,8 @@ class EdgeGroupDeliveryStatus(Enum):
|
||||
|
||||
def workflow_tracer() -> "Tracer":
|
||||
"""Get a workflow tracer or a no-op tracer if not enabled."""
|
||||
global OTEL_SETTINGS
|
||||
return get_tracer() if OTEL_SETTINGS.ENABLED else trace.NoOpTracer()
|
||||
global OBSERVABILITY_SETTINGS
|
||||
return get_tracer() if OBSERVABILITY_SETTINGS.ENABLED else trace.NoOpTracer()
|
||||
|
||||
|
||||
def create_workflow_span(
|
||||
|
||||
Reference in New Issue
Block a user