|
3 | 3 | # Copyright (c) Microsoft Corporation. |
4 | 4 | # Licensed under the MIT License. |
5 | 5 | # ------------------------------------ |
6 | | -from typing import Any, Optional, TextIO, Union, cast |
7 | | - |
8 | | -import io |
| 6 | +from typing import Optional |
9 | 7 | import logging |
10 | | -import sys |
11 | | - |
12 | 8 | from enum import Enum |
13 | 9 |
|
14 | 10 | from azure.core.tracing import AbstractSpan, SpanKind # type: ignore |
@@ -157,150 +153,3 @@ def start_span( |
157 | 153 | span.add_attribute(GEN_AI_REQUEST_RESPONSE_FORMAT, response_format) |
158 | 154 |
|
159 | 155 | return span |
160 | | - |
161 | | - |
162 | | -# Internal helper functions to enable OpenTelemetry, used by both sync and async clients |
163 | | -def _get_trace_exporter(destination: Union[TextIO, str, None]) -> Any: |
164 | | - if isinstance(destination, str): |
165 | | - # `destination` is the OTLP endpoint |
166 | | - # See: https://opentelemetry-python.readthedocs.io/en/latest/exporter/otlp/otlp.html#usage |
167 | | - try: |
168 | | - from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter # type: ignore |
169 | | - except ModuleNotFoundError as e: |
170 | | - raise ModuleNotFoundError( |
171 | | - "OpenTelemetry OTLP exporter is not installed. " |
172 | | - + "Please install it using 'pip install opentelemetry-exporter-otlp-proto-grpc'" |
173 | | - ) from e |
174 | | - return OTLPSpanExporter(endpoint=destination) |
175 | | - |
176 | | - if isinstance(destination, io.TextIOWrapper): |
177 | | - if destination is sys.stdout: |
178 | | - # See: https://opentelemetry-python.readthedocs.io/en/latest/sdk/trace.export.html#opentelemetry.sdk.trace.export.ConsoleSpanExporter # pylint: disable=line-too-long |
179 | | - try: |
180 | | - from opentelemetry.sdk.trace.export import ConsoleSpanExporter |
181 | | - except ModuleNotFoundError as e: |
182 | | - raise ModuleNotFoundError( |
183 | | - "OpenTelemetry SDK is not installed. Please install it using 'pip install opentelemetry-sdk'" |
184 | | - ) from e |
185 | | - |
186 | | - return ConsoleSpanExporter() |
187 | | - raise ValueError("Only `sys.stdout` is supported at the moment for type `TextIO`") |
188 | | - |
189 | | - return None |
190 | | - |
191 | | - |
192 | | -def _get_log_exporter(destination: Union[TextIO, str, None]) -> Any: |
193 | | - if isinstance(destination, str): |
194 | | - # `destination` is the OTLP endpoint |
195 | | - # See: https://opentelemetry-python.readthedocs.io/en/latest/exporter/otlp/otlp.html#usage |
196 | | - try: |
197 | | - # _logs are considered beta (not internal) in OpenTelemetry Python API/SDK. |
198 | | - # So it's ok to use it for local development, but we'll swallow |
199 | | - # any errors in case of any breaking changes on OTel side. |
200 | | - from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter # type: ignore # pylint: disable=import-error,no-name-in-module |
201 | | - except Exception as ex: # pylint: disable=broad-exception-caught |
202 | | - # since OTel logging is still in beta in Python, we're going to swallow any errors |
203 | | - # and just warn about them. |
204 | | - logger.warning("Failed to configure OpenTelemetry logging.", exc_info=ex) |
205 | | - return None |
206 | | - |
207 | | - return OTLPLogExporter(endpoint=destination) |
208 | | - |
209 | | - if isinstance(destination, io.TextIOWrapper): |
210 | | - if destination is sys.stdout: |
211 | | - # See: https://opentelemetry-python.readthedocs.io/en/latest/sdk/trace.export.html#opentelemetry.sdk.trace.export.ConsoleSpanExporter # pylint: disable=line-too-long |
212 | | - try: |
213 | | - from opentelemetry.sdk._logs.export import ConsoleLogExporter |
214 | | - |
215 | | - return ConsoleLogExporter() |
216 | | - except ModuleNotFoundError as ex: |
217 | | - # since OTel logging is still in beta in Python, we're going to swallow any errors |
218 | | - # and just warn about them. |
219 | | - logger.warning("Failed to configure OpenTelemetry logging.", exc_info=ex) |
220 | | - return None |
221 | | - raise ValueError("Only `sys.stdout` is supported at the moment for type `TextIO`") |
222 | | - |
223 | | - return None |
224 | | - |
225 | | - |
226 | | -def _configure_tracing(span_exporter: Any) -> None: |
227 | | - if span_exporter is None: |
228 | | - return |
229 | | - |
230 | | - try: |
231 | | - from opentelemetry import trace |
232 | | - from opentelemetry.sdk.trace import TracerProvider |
233 | | - from opentelemetry.sdk.trace.export import SimpleSpanProcessor |
234 | | - except ModuleNotFoundError as e: |
235 | | - raise ModuleNotFoundError( |
236 | | - "OpenTelemetry SDK is not installed. Please install it using 'pip install opentelemetry-sdk'" |
237 | | - ) from e |
238 | | - |
239 | | - # if tracing was not setup before, we need to create a new TracerProvider |
240 | | - if not isinstance(trace.get_tracer_provider(), TracerProvider): |
241 | | - # If the provider is NoOpTracerProvider, we need to create a new TracerProvider |
242 | | - provider = TracerProvider() |
243 | | - trace.set_tracer_provider(provider) |
244 | | - |
245 | | - # get_tracer_provider returns opentelemetry.trace.TracerProvider |
246 | | - # however, we have opentelemetry.sdk.trace.TracerProvider, which implements |
247 | | - # add_span_processor method, though we need to cast it to fix type checking. |
248 | | - provider = cast(TracerProvider, trace.get_tracer_provider()) |
249 | | - provider.add_span_processor(SimpleSpanProcessor(span_exporter)) |
250 | | - |
251 | | - |
252 | | -def _configure_logging(log_exporter: Any) -> None: |
253 | | - if log_exporter is None: |
254 | | - return |
255 | | - |
256 | | - try: |
257 | | - # _events and _logs are considered beta (not internal) in |
258 | | - # OpenTelemetry Python API/SDK. |
259 | | - # So it's ok to use them for local development, but we'll swallow |
260 | | - # any errors in case of any breaking changes on OTel side. |
261 | | - from opentelemetry import _logs, _events |
262 | | - from opentelemetry.sdk._logs import LoggerProvider # pylint: disable=import-error,no-name-in-module |
263 | | - from opentelemetry.sdk._events import EventLoggerProvider # pylint: disable=import-error,no-name-in-module |
264 | | - from opentelemetry.sdk._logs.export import ( |
265 | | - SimpleLogRecordProcessor, |
266 | | - ) # pylint: disable=import-error,no-name-in-module |
267 | | - |
268 | | - if not isinstance(_logs.get_logger_provider(), LoggerProvider): |
269 | | - logger_provider = LoggerProvider() |
270 | | - _logs.set_logger_provider(logger_provider) |
271 | | - |
272 | | - # get_logger_provider returns opentelemetry._logs.LoggerProvider |
273 | | - # however, we have opentelemetry.sdk._logs.LoggerProvider, which implements |
274 | | - # add_log_record_processor method, though we need to cast it to fix type checking. |
275 | | - logger_provider = cast(LoggerProvider, _logs.get_logger_provider()) |
276 | | - logger_provider.add_log_record_processor(SimpleLogRecordProcessor(log_exporter)) |
277 | | - _events.set_event_logger_provider(EventLoggerProvider(logger_provider)) |
278 | | - except Exception as ex: # pylint: disable=broad-exception-caught |
279 | | - # since OTel logging is still in beta in Python, we're going to swallow any errors |
280 | | - # and just warn about them. |
281 | | - logger.warning("Failed to configure OpenTelemetry logging.", exc_info=ex) |
282 | | - |
283 | | - |
284 | | -def enable_telemetry(destination: Union[TextIO, str, None] = None, **kwargs) -> None: # pylint: disable=unused-argument |
285 | | - """Enable tracing and logging to console (sys.stdout), or to an OpenTelemetry Protocol (OTLP) endpoint. |
286 | | -
|
287 | | - :param destination: `sys.stdout` to print telemetry to console or a string holding the |
288 | | - OpenTelemetry protocol (OTLP) endpoint. |
289 | | - If not provided, this method enables instrumentation, but does not configure OpenTelemetry |
290 | | - SDK to export traces and logs. |
291 | | - :type destination: Union[TextIO, str, None] |
292 | | - """ |
293 | | - span_exporter = _get_trace_exporter(destination) |
294 | | - _configure_tracing(span_exporter) |
295 | | - |
296 | | - log_exporter = _get_log_exporter(destination) |
297 | | - _configure_logging(log_exporter) |
298 | | - |
299 | | - try: |
300 | | - from azure.ai.agents.telemetry import AIAgentsInstrumentor |
301 | | - |
302 | | - agents_instrumentor = AIAgentsInstrumentor() |
303 | | - if not agents_instrumentor.is_instrumented(): |
304 | | - agents_instrumentor.instrument() |
305 | | - except Exception as exc: # pylint: disable=broad-exception-caught |
306 | | - logger.warning("Could not call `AIAgentsInstrumentor().instrument()`", exc_info=exc) |
0 commit comments