Source code for microsoft.opentelemetry.a365.hosting.middleware.observability_hosting_manager

# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

"""Singleton manager for configuring hosting-layer observability middleware."""

from __future__ import annotations

import logging
from dataclasses import dataclass

from microsoft_agents.hosting.core.middleware_set import MiddlewareSet

from microsoft.opentelemetry.a365.hosting.middleware.baggage_middleware import BaggageMiddleware
from microsoft.opentelemetry.a365.hosting.middleware.output_logging_middleware import OutputLoggingMiddleware

logger = logging.getLogger(__name__)


[docs] @dataclass class ObservabilityHostingOptions: """Configuration options for the hosting observability layer.""" enable_baggage: bool = False """Enable baggage propagation middleware. Defaults to ``False``.""" enable_output_logging: bool = False """Enable output logging middleware for tracing outgoing messages. Defaults to ``False``."""
[docs] class ObservabilityHostingManager: """Singleton manager for configuring hosting-layer observability middleware. Example: .. code-block:: python ObservabilityHostingManager.configure(adapter.middleware_set, ObservabilityHostingOptions( enable_output_logging=True, )) """ _instance: ObservabilityHostingManager | None = None def __init__(self) -> None: """Private constructor — use :meth:`configure` instead."""
[docs] @classmethod def configure( cls, middleware_set: MiddlewareSet, options: ObservabilityHostingOptions, ) -> ObservabilityHostingManager: """Configure the singleton instance and register middleware. Subsequent calls after the first are no-ops and return the existing instance. Args: middleware_set: The middleware set to register middleware on (e.g., ``adapter.middleware_set``). options: Configuration options controlling which middleware to enable. Returns: The singleton :class:`ObservabilityHostingManager` instance. Raises: TypeError: If *middleware_set* or *options* is ``None``. """ if middleware_set is None: raise TypeError("middleware_set must not be None") if options is None: raise TypeError("options must not be None") if cls._instance is not None: logger.warning( "[ObservabilityHostingManager] Already configured. Subsequent configure() calls are ignored." ) return cls._instance instance = cls() if options.enable_baggage: middleware_set.use(BaggageMiddleware()) logger.info("[ObservabilityHostingManager] BaggageMiddleware registered.") if options.enable_output_logging: middleware_set.use(OutputLoggingMiddleware()) logger.info("[ObservabilityHostingManager] OutputLoggingMiddleware registered.") logger.info( "[ObservabilityHostingManager] Configured. Baggage: %s, OutputLogging: %s.", options.enable_baggage, options.enable_output_logging, ) cls._instance = instance return instance
@classmethod def _reset(cls) -> None: """Reset the singleton instance. Intended for testing only.""" cls._instance = None