diff options
Diffstat (limited to '.venv/lib/python3.12/site-packages/azure/monitor/opentelemetry/_configure.py')
-rw-r--r-- | .venv/lib/python3.12/site-packages/azure/monitor/opentelemetry/_configure.py | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/azure/monitor/opentelemetry/_configure.py b/.venv/lib/python3.12/site-packages/azure/monitor/opentelemetry/_configure.py new file mode 100644 index 00000000..c9910392 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/azure/monitor/opentelemetry/_configure.py @@ -0,0 +1,288 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License in the project root for +# license information. +# -------------------------------------------------------------------------- +from functools import cached_property +from logging import getLogger +from typing import Dict, List, cast + +from opentelemetry._events import _set_event_logger_provider +from opentelemetry._logs import set_logger_provider +from opentelemetry.instrumentation.dependencies import ( + get_dist_dependency_conflicts, +) +from opentelemetry.instrumentation.instrumentor import ( # type: ignore + BaseInstrumentor, +) +from opentelemetry.metrics import set_meter_provider +from opentelemetry.sdk._events import EventLoggerProvider +from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler +from opentelemetry.sdk._logs.export import BatchLogRecordProcessor +from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader +from opentelemetry.sdk.metrics.view import View +from opentelemetry.sdk.resources import Resource +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import BatchSpanProcessor +from opentelemetry.trace import set_tracer_provider +from opentelemetry.util._importlib_metadata import ( + EntryPoint, + distributions, + entry_points, +) + +from azure.core.settings import settings +from azure.core.tracing.ext.opentelemetry_span import OpenTelemetrySpan +from azure.monitor.opentelemetry._constants import ( + _ALL_SUPPORTED_INSTRUMENTED_LIBRARIES, + _AZURE_SDK_INSTRUMENTATION_NAME, + DISABLE_LOGGING_ARG, + DISABLE_METRICS_ARG, + DISABLE_TRACING_ARG, + ENABLE_LIVE_METRICS_ARG, + LOGGER_NAME_ARG, + RESOURCE_ARG, + SAMPLING_RATIO_ARG, + SPAN_PROCESSORS_ARG, + VIEWS_ARG, +) +from azure.monitor.opentelemetry._types import ConfigurationValue +from azure.monitor.opentelemetry.exporter._quickpulse import ( # pylint: disable=import-error,no-name-in-module + enable_live_metrics, +) +from azure.monitor.opentelemetry.exporter._quickpulse._processor import ( # pylint: disable=import-error,no-name-in-module + _QuickpulseLogRecordProcessor, + _QuickpulseSpanProcessor, +) +from azure.monitor.opentelemetry.exporter import ( # pylint: disable=import-error,no-name-in-module + ApplicationInsightsSampler, + AzureMonitorLogExporter, + AzureMonitorMetricExporter, + AzureMonitorTraceExporter, +) +from azure.monitor.opentelemetry.exporter._utils import ( # pylint: disable=import-error,no-name-in-module + _is_attach_enabled, + _is_on_functions, +) +from azure.monitor.opentelemetry._diagnostics.diagnostic_logging import ( + _DISTRO_DETECTS_ATTACH, + AzureDiagnosticLogging, +) +from azure.monitor.opentelemetry._utils.configurations import ( + _get_configurations, + _is_instrumentation_enabled, +) + +_logger = getLogger(__name__) + + +def configure_azure_monitor(**kwargs) -> None: # pylint: disable=C4758 + """This function works as a configuration layer that allows the + end user to configure OpenTelemetry and Azure monitor components. The + configuration can be done via arguments passed to this function. + + :keyword str connection_string: Connection string for your Application Insights resource. + :keyword credential: Token credential, such as `ManagedIdentityCredential` or `ClientSecretCredential`, + used for Azure Active Directory (AAD) authentication. Defaults to `None`. + :paramtype credential: ~azure.core.credentials.TokenCredential or None + :keyword bool disable_offline_storage: Boolean value to determine whether to disable storing failed + telemetry records for retry. Defaults to `False`. + :keyword str logger_name: The name of the Python logger that telemetry will be collected. + :keyword dict instrumentation_options: A nested dictionary that determines which instrumentations + to enable or disable. Instrumentations are referred to by their Library Names. For example, + `{"azure_sdk": {"enabled": False}, "flask": {"enabled": False}, "django": {"enabled": True}}` + will disable Azure Core Tracing and the Flask instrumentation but leave Django and the other default + instrumentations enabled. + :keyword ~opentelemetry.sdk.resources.Resource resource: OpenTelemetry Resource object. Passed in Resource + Attributes take priority over default attributes and those from Resource Detectors. + :keyword list[~opentelemetry.sdk.trace.SpanProcessor] span_processors: List of `SpanProcessor` objects + to process every span prior to exporting. Will be run sequentially. + :keyword bool enable_live_metrics: Boolean value to determine whether to enable live metrics feature. + Defaults to `False`. + :keyword str storage_directory: Storage directory in which to store retry files. Defaults to + `<tempfile.gettempdir()>/Microsoft/AzureMonitor/opentelemetry-python-<your-instrumentation-key>`. + :keyword list[~opentelemetry.sdk.metrics.view.View] views: List of `View` objects to configure and filter + metric output. + :rtype: None + """ + + _send_attach_warning() + + configurations = _get_configurations(**kwargs) + + disable_tracing = configurations[DISABLE_TRACING_ARG] + disable_logging = configurations[DISABLE_LOGGING_ARG] + disable_metrics = configurations[DISABLE_METRICS_ARG] + enable_live_metrics_config = configurations[ENABLE_LIVE_METRICS_ARG] + + # Setup live metrics + if enable_live_metrics_config: + _setup_live_metrics(configurations) + + # Setup tracing pipeline + if not disable_tracing: + _setup_tracing(configurations) + + # Setup logging pipeline + if not disable_logging: + _setup_logging(configurations) + + # Setup metrics pipeline + if not disable_metrics: + _setup_metrics(configurations) + + # Setup instrumentations + # Instrumentations need to be setup last so to use the global providers + # instanstiated in the other setup steps + _setup_instrumentations(configurations) + + +def _setup_tracing(configurations: Dict[str, ConfigurationValue]): + resource: Resource = configurations[RESOURCE_ARG] # type: ignore + sampling_ratio = configurations[SAMPLING_RATIO_ARG] + tracer_provider = TracerProvider( + sampler=ApplicationInsightsSampler(sampling_ratio=cast(float, sampling_ratio)), resource=resource + ) + for span_processor in configurations[SPAN_PROCESSORS_ARG]: # type: ignore + tracer_provider.add_span_processor(span_processor) # type: ignore + if configurations.get(ENABLE_LIVE_METRICS_ARG): + qsp = _QuickpulseSpanProcessor() + tracer_provider.add_span_processor(qsp) + trace_exporter = AzureMonitorTraceExporter(**configurations) + bsp = BatchSpanProcessor( + trace_exporter, + ) + tracer_provider.add_span_processor(bsp) + set_tracer_provider(tracer_provider) + if _is_instrumentation_enabled(configurations, _AZURE_SDK_INSTRUMENTATION_NAME): + settings.tracing_implementation = OpenTelemetrySpan + + +def _setup_logging(configurations: Dict[str, ConfigurationValue]): + resource: Resource = configurations[RESOURCE_ARG] # type: ignore + logger_provider = LoggerProvider(resource=resource) + if configurations.get(ENABLE_LIVE_METRICS_ARG): + qlp = _QuickpulseLogRecordProcessor() + logger_provider.add_log_record_processor(qlp) + log_exporter = AzureMonitorLogExporter(**configurations) + log_record_processor = BatchLogRecordProcessor( + log_exporter, + ) + logger_provider.add_log_record_processor(log_record_processor) + set_logger_provider(logger_provider) + logger_name: str = configurations[LOGGER_NAME_ARG] # type: ignore + logger = getLogger(logger_name) + # Only add OpenTelemetry LoggingHandler if logger does not already have the handler + # This is to prevent most duplicate logging telemetry + if not any(isinstance(handler, LoggingHandler) for handler in logger.handlers): + handler = LoggingHandler(logger_provider=logger_provider) + logger.addHandler(handler) + + # Setup EventLoggerProvider + event_provider = EventLoggerProvider(logger_provider) + _set_event_logger_provider(event_provider, False) + + +def _setup_metrics(configurations: Dict[str, ConfigurationValue]): + resource: Resource = configurations[RESOURCE_ARG] # type: ignore + views: List[View] = configurations[VIEWS_ARG] # type: ignore + metric_exporter = AzureMonitorMetricExporter(**configurations) + reader = PeriodicExportingMetricReader(metric_exporter) + meter_provider = MeterProvider( + metric_readers=[reader], + resource=resource, + views=views, + ) + set_meter_provider(meter_provider) + + +def _setup_live_metrics(configurations): + enable_live_metrics(**configurations) + + +class _EntryPointDistFinder: + @cached_property + def _mapping(self): + return {self._key_for(ep): dist for dist in distributions() for ep in dist.entry_points} + + def dist_for(self, entry_point: EntryPoint): + dist = getattr(entry_point, "dist", None) + if dist: + return dist + + return self._mapping.get(self._key_for(entry_point)) + + @staticmethod + def _key_for(entry_point: EntryPoint): + return f"{entry_point.group}:{entry_point.name}:{entry_point.value}" + + +def _setup_instrumentations(configurations: Dict[str, ConfigurationValue]): + entry_point_finder = _EntryPointDistFinder() + # use pkg_resources for now until https://github.com/open-telemetry/opentelemetry-python/pull/3168 is merged + for entry_point in entry_points(group="opentelemetry_instrumentor"): + lib_name = entry_point.name + if lib_name not in _ALL_SUPPORTED_INSTRUMENTED_LIBRARIES: + continue + if not _is_instrumentation_enabled(configurations, lib_name): + _logger.debug("Instrumentation skipped for library %s", entry_point.name) + continue + try: + # Check if dependent libraries/version are installed + entry_point_dist = entry_point_finder.dist_for(entry_point) # type: ignore + conflict = get_dist_dependency_conflicts(entry_point_dist) # type: ignore + if conflict: + _logger.debug( + "Skipping instrumentation %s: %s", + entry_point.name, + conflict, + ) + continue + # Load the instrumentor via entrypoint + instrumentor: BaseInstrumentor = entry_point.load() + # tell instrumentation to not run dep checks again as we already did it above + instrumentor().instrument(skip_dep_check=True) + except Exception as ex: # pylint: disable=broad-except + _logger.warning( + "Exception occurred when instrumenting: %s.", + lib_name, + exc_info=ex, + ) + _setup_additional_azure_sdk_instrumentations(configurations) + + +def _send_attach_warning(): + if _is_attach_enabled() and not _is_on_functions(): + AzureDiagnosticLogging.warning( + "Distro detected that automatic attach may have occurred. Check your data to ensure " + "that telemetry is not being duplicated. This may impact your cost.", + _DISTRO_DETECTS_ATTACH, + ) + + +def _setup_additional_azure_sdk_instrumentations(configurations: Dict[str, ConfigurationValue]): + if _AZURE_SDK_INSTRUMENTATION_NAME not in _ALL_SUPPORTED_INSTRUMENTED_LIBRARIES: + return + + if not _is_instrumentation_enabled(configurations, _AZURE_SDK_INSTRUMENTATION_NAME): + _logger.debug("Instrumentation skipped for library azure_sdk") + return + + try: + from azure.ai.inference.tracing import AIInferenceInstrumentor # pylint: disable=import-error,no-name-in-module + except Exception as ex: # pylint: disable=broad-except + _logger.debug( + "Failed to import AIInferenceInstrumentor from azure-ai-inference", + exc_info=ex, + ) + return + + try: + AIInferenceInstrumentor().instrument() + except Exception as ex: # pylint: disable=broad-except + _logger.warning( + "Exception occurred when instrumenting: %s.", + "azure-ai-inference", + exc_info=ex, + ) |