|
14 | 14 | import sys
|
15 | 15 | import threading
|
16 | 16 | from asyncio import BaseEventLoop
|
| 17 | +from datetime import datetime |
17 | 18 | from logging import LogRecord
|
18 | 19 | from typing import List, Optional
|
19 |
| -from datetime import datetime |
20 | 20 |
|
21 | 21 | import grpc
|
22 | 22 | from . import bindings, constants, functions, loader, protos
|
|
46 | 46 | from .version import VERSION
|
47 | 47 |
|
48 | 48 | _TRUE = "true"
|
| 49 | +_TRACEPARENT = "traceparent" |
| 50 | +_TRACESTATE = "tracestate" |
49 | 51 |
|
50 | 52 |
|
51 | 53 | class DispatcherMeta(type):
|
@@ -80,6 +82,11 @@ def __init__(self, loop: BaseEventLoop, host: str, port: int,
|
80 | 82 | self._function_metadata_result = None
|
81 | 83 | self._function_metadata_exception = None
|
82 | 84 |
|
| 85 | + # Used for checking if open telemetry is enabled |
| 86 | + self._otel_libs_available = False |
| 87 | + self._context_api = None |
| 88 | + self._trace_context_propagator = None |
| 89 | + |
83 | 90 | # We allow the customer to change synchronous thread pool max worker
|
84 | 91 | # count by setting the PYTHON_THREADPOOL_THREAD_COUNT app setting.
|
85 | 92 | # For 3.[6|7|8] The default value is 1.
|
@@ -265,6 +272,23 @@ async def _dispatch_grpc_request(self, request):
|
265 | 272 | resp = await request_handler(request)
|
266 | 273 | self._grpc_resp_queue.put_nowait(resp)
|
267 | 274 |
|
| 275 | + def update_opentelemetry_status(self): |
| 276 | + """Check for OpenTelemetry library availability and |
| 277 | + update the status attribute.""" |
| 278 | + try: |
| 279 | + from opentelemetry import context as context_api |
| 280 | + from opentelemetry.trace.propagation.tracecontext import ( |
| 281 | + TraceContextTextMapPropagator) |
| 282 | + |
| 283 | + self._context_api = context_api |
| 284 | + self._trace_context_propagator = TraceContextTextMapPropagator() |
| 285 | + self._otel_libs_available = True |
| 286 | + |
| 287 | + logger.info("Successfully loaded OpenTelemetry modules. " |
| 288 | + "OpenTelemetry is now enabled.") |
| 289 | + except ImportError: |
| 290 | + self._otel_libs_available = False |
| 291 | + |
268 | 292 | async def _handle__worker_init_request(self, request):
|
269 | 293 | logger.info('Received WorkerInitRequest, '
|
270 | 294 | 'python version %s, '
|
@@ -294,6 +318,11 @@ async def _handle__worker_init_request(self, request):
|
294 | 318 | constants.SHARED_MEMORY_DATA_TRANSFER: _TRUE,
|
295 | 319 | }
|
296 | 320 |
|
| 321 | + self.update_opentelemetry_status() |
| 322 | + |
| 323 | + if self._otel_libs_available: |
| 324 | + capabilities[constants.WORKER_OPEN_TELEMETRY_ENABLED] = _TRUE |
| 325 | + |
297 | 326 | if DependencyManager.should_load_cx_dependencies():
|
298 | 327 | DependencyManager.prioritize_customer_dependencies()
|
299 | 328 |
|
@@ -559,6 +588,9 @@ async def _handle__invocation_request(self, request):
|
559 | 588 | args[name] = bindings.Out()
|
560 | 589 |
|
561 | 590 | if fi.is_async:
|
| 591 | + if self._otel_libs_available: |
| 592 | + self.configure_opentelemetry(fi_context) |
| 593 | + |
562 | 594 | call_result = \
|
563 | 595 | await self._run_async_func(fi_context, fi.func, args)
|
564 | 596 | else:
|
@@ -673,6 +705,10 @@ async def _handle__function_environment_reload_request(self, request):
|
673 | 705 | bindings.load_binding_registry()
|
674 | 706 |
|
675 | 707 | capabilities = {}
|
| 708 | + self.update_opentelemetry_status() |
| 709 | + if self._otel_libs_available: |
| 710 | + capabilities[constants.WORKER_OPEN_TELEMETRY_ENABLED] = _TRUE |
| 711 | + |
676 | 712 | if is_envvar_true(PYTHON_ENABLE_INIT_INDEXING):
|
677 | 713 | try:
|
678 | 714 | self.load_function_metadata(
|
@@ -785,6 +821,12 @@ async def _handle__close_shared_memory_resources_request(self, request):
|
785 | 821 | request_id=self.request_id,
|
786 | 822 | close_shared_memory_resources_response=response)
|
787 | 823 |
|
| 824 | + def configure_opentelemetry(self, invocation_context): |
| 825 | + carrier = {_TRACEPARENT: invocation_context.trace_context.trace_parent, |
| 826 | + _TRACESTATE: invocation_context.trace_context.trace_state} |
| 827 | + ctx = self._trace_context_propagator.extract(carrier) |
| 828 | + self._context_api.attach(ctx) |
| 829 | + |
788 | 830 | @staticmethod
|
789 | 831 | def _get_context(invoc_request: protos.InvocationRequest, name: str,
|
790 | 832 | directory: str) -> bindings.Context:
|
@@ -873,6 +915,8 @@ def _run_sync_func(self, invocation_id, context, func, params):
|
873 | 915 | # invocation_id from ThreadPoolExecutor's threads.
|
874 | 916 | context.thread_local_storage.invocation_id = invocation_id
|
875 | 917 | try:
|
| 918 | + if self._otel_libs_available: |
| 919 | + self.configure_opentelemetry(context) |
876 | 920 | return ExtensionManager.get_sync_invocation_wrapper(context,
|
877 | 921 | func)(params)
|
878 | 922 | finally:
|
|
0 commit comments