Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions runtimes/v1/azure_functions_runtime_v1/handle_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
from .functions import FunctionInfo, Registry
from .loader import load_function, install
from .logging import logger
from .otel import otel_manager, initialize_azure_monitor, configure_opentelemetry
from .otel import (configure_opentelemetry,
initialize_azure_monitor,
otel_manager,
update_opentelemetry_status)
from .version import VERSION

from .bindings.context import get_context
Expand Down Expand Up @@ -70,6 +73,7 @@ async def worker_init_request(request):

if is_envvar_true(PYTHON_ENABLE_OPENTELEMETRY):
otel_manager.set_otel_libs_available(True)
update_opentelemetry_status()

if (otel_manager.get_azure_monitor_available()
or otel_manager.get_otel_libs_available()):
Expand Down Expand Up @@ -188,7 +192,8 @@ async def invocation_request(request):
args[name] = Out()

if fi.is_async:
if otel_manager.get_azure_monitor_available():
if (otel_manager.get_azure_monitor_available()
or otel_manager.get_otel_libs_available()):
configure_opentelemetry(fi_context)

# Not supporting Extensions
Expand Down Expand Up @@ -291,6 +296,7 @@ async def function_environment_reload_request(request):
capabilities = {}
if is_envvar_true(PYTHON_ENABLE_OPENTELEMETRY):
otel_manager.set_otel_libs_available(True)
update_opentelemetry_status()

if is_envvar_true(PYTHON_APPLICATIONINSIGHTS_ENABLE_TELEMETRY):
initialize_azure_monitor()
Expand Down
9 changes: 7 additions & 2 deletions runtimes/v1/azure_functions_runtime_v1/otel.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,15 @@ def initialize_azure_monitor():


def configure_opentelemetry(invocation_context):
trace_context_propagator = otel_manager.get_trace_context_propagator()
context_api = otel_manager.get_context_api()
if trace_context_propagator is None or context_api is None:
return

carrier = {TRACEPARENT: invocation_context.trace_context.trace_parent,
TRACESTATE: invocation_context.trace_context.trace_state}
ctx = otel_manager.get_trace_context_propagator().extract(carrier)
otel_manager.get_context_api().attach(ctx)
ctx = trace_context_propagator.extract(carrier)
context_api.attach(ctx)


otel_manager = OTelManager()
3 changes: 2 additions & 1 deletion runtimes/v1/azure_functions_runtime_v1/utils/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ def run_sync_func(invocation_id, context, func, params):
context.thread_local_storage.invocation_id = invocation_id
token = invocation_id_cv.set(invocation_id)
try:
if otel_manager.get_azure_monitor_available():
if (otel_manager.get_azure_monitor_available()
or otel_manager.get_otel_libs_available()):
configure_opentelemetry(context)
result = functools.partial(execute_sync, func)
return result(params)
Expand Down
38 changes: 33 additions & 5 deletions runtimes/v1/tests/unittests/test_opentelemetry.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
import asyncio
import os
import unittest

import tests.protos as protos

from azure_functions_runtime_v1.handle_event import otel_manager, worker_init_request
from azure_functions_runtime_v1.otel import (initialize_azure_monitor,
from azure_functions_runtime_v1.otel import (configure_opentelemetry,
initialize_azure_monitor,
update_opentelemetry_status)
from azure_functions_runtime_v1.logging import logger
from tests.utils.constants import UNIT_TESTS_FOLDER
Expand All @@ -18,6 +20,24 @@

class TestOpenTelemetry(unittest.TestCase):

def setUp(self):
self._otel_manager_state = (
otel_manager.get_azure_monitor_available(),
otel_manager.get_otel_libs_available(),
otel_manager.get_context_api(),
otel_manager.get_trace_context_propagator(),
)

def tearDown(self):
(azure_monitor_available,
otel_libs_available,
context_api,
trace_context_propagator) = self._otel_manager_state
otel_manager.set_azure_monitor_available(azure_monitor_available)
otel_manager.set_otel_libs_available(otel_libs_available)
otel_manager.set_context_api(context_api)
otel_manager.set_trace_context_propagator(trace_context_propagator)

def test_update_opentelemetry_status_import_error(self):
with patch.dict('sys.modules', {
'opentelemetry': None,
Expand All @@ -42,6 +62,12 @@ def test_update_opentelemetry_status_success(
self.assertIsNotNone(otel_manager.get_context_api())
self.assertIsNotNone(otel_manager.get_trace_context_propagator())

def test_configure_opentelemetry_noops_when_not_initialized(self):
otel_manager.set_context_api(None)
otel_manager.set_trace_context_propagator(None)

configure_opentelemetry(MagicMock())

@patch('builtins.__import__')
@patch("azure_functions_runtime_v1.otel.update_opentelemetry_status")
def test_initialize_azure_monitor_success(
Expand Down Expand Up @@ -145,7 +171,7 @@ async def test_init_request_initialize_azure_monitor_disabled_app_setting(
self.assertNotIn("WorkerOpenTelemetryEnabled", capabilities)

@patch.dict(os.environ, {'PYTHON_ENABLE_OPENTELEMETRY': 'true'})
async def test_init_request_enable_opentelemetry_enabled_app_setting(
def test_init_request_enable_opentelemetry_enabled_app_setting(
self,
):
worker_request = WorkerRequest(name='worker_init_request',
Expand All @@ -154,13 +180,15 @@ async def test_init_request_enable_opentelemetry_enabled_app_setting(
FUNCTION_APP_DIRECTORY)),
properties={'host': '123',
'protos': protos})
init_response = await worker_init_request(worker_request)
init_response = asyncio.run(worker_init_request(worker_request))

self.assertEqual(init_response.result.status,
protos.StatusResult.Success)

# Verify otel_libs_available is set to True
self.assertTrue(otel_manager.get_azure_monitor_available())
# Verify OpenTelemetry propagation is initialized
self.assertTrue(otel_manager.get_otel_libs_available())
self.assertIsNotNone(otel_manager.get_context_api())
self.assertIsNotNone(otel_manager.get_trace_context_propagator())
# Verify that WorkerOpenTelemetryEnabled capability is set to _TRUE
capabilities = init_response.capabilities
self.assertIn("WorkerOpenTelemetryEnabled", capabilities)
Expand Down
7 changes: 6 additions & 1 deletion runtimes/v2/azure_functions_runtime/handle_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
)
from .loader import index_function_app, process_indexed_function
from .logging import logger
from .otel import otel_manager, configure_opentelemetry, initialize_azure_monitor
from .otel import (configure_opentelemetry,
initialize_azure_monitor,
otel_manager,
update_opentelemetry_status)
from .version import VERSION

from .bindings.context import get_context
Expand Down Expand Up @@ -86,6 +89,7 @@ async def worker_init_request(request):

if is_envvar_true(PYTHON_ENABLE_OPENTELEMETRY):
otel_manager.set_otel_libs_available(True)
update_opentelemetry_status()

if (otel_manager.get_azure_monitor_available()
or otel_manager.get_otel_libs_available()):
Expand Down Expand Up @@ -337,6 +341,7 @@ async def function_environment_reload_request(request):
capabilities = {}
if is_envvar_true(PYTHON_ENABLE_OPENTELEMETRY):
otel_manager.set_otel_libs_available(True)
update_opentelemetry_status()

if is_envvar_true(PYTHON_APPLICATIONINSIGHTS_ENABLE_TELEMETRY):
initialize_azure_monitor()
Expand Down
9 changes: 7 additions & 2 deletions runtimes/v2/azure_functions_runtime/otel.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,15 @@ def initialize_azure_monitor():


def configure_opentelemetry(invocation_context):
trace_context_propagator = otel_manager.get_trace_context_propagator()
context_api = otel_manager.get_context_api()
if trace_context_propagator is None or context_api is None:
return

carrier = {TRACEPARENT: invocation_context.trace_context.trace_parent,
TRACESTATE: invocation_context.trace_context.trace_state}
ctx = otel_manager.get_trace_context_propagator().extract(carrier)
otel_manager.get_context_api().attach(ctx)
ctx = trace_context_propagator.extract(carrier)
context_api.attach(ctx)


otel_manager = OTelManager()
38 changes: 33 additions & 5 deletions runtimes/v2/tests/unittests/test_opentelemetry.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
import asyncio
import os
import unittest

import tests.protos as protos

from azure_functions_runtime.handle_event import otel_manager, worker_init_request
from azure_functions_runtime.otel import (initialize_azure_monitor,
from azure_functions_runtime.otel import (configure_opentelemetry,
initialize_azure_monitor,
update_opentelemetry_status,
OTelManager)
from azure_functions_runtime.logging import logger
Expand All @@ -19,6 +21,24 @@

class TestOpenTelemetry(unittest.TestCase):

def setUp(self):
self._otel_manager_state = (
otel_manager.get_azure_monitor_available(),
otel_manager.get_otel_libs_available(),
otel_manager.get_context_api(),
otel_manager.get_trace_context_propagator(),
)

def tearDown(self):
(azure_monitor_available,
otel_libs_available,
context_api,
trace_context_propagator) = self._otel_manager_state
otel_manager.set_azure_monitor_available(azure_monitor_available)
otel_manager.set_otel_libs_available(otel_libs_available)
otel_manager.set_context_api(context_api)
otel_manager.set_trace_context_propagator(trace_context_propagator)

def test_update_opentelemetry_status_import_error(self):
with patch.dict('sys.modules', {
'opentelemetry': None,
Expand All @@ -43,6 +63,12 @@ def test_update_opentelemetry_status_success(
self.assertIsNotNone(otel_manager.get_context_api())
self.assertIsNotNone(otel_manager.get_trace_context_propagator())

def test_configure_opentelemetry_noops_when_not_initialized(self):
otel_manager.set_context_api(None)
otel_manager.set_trace_context_propagator(None)

configure_opentelemetry(MagicMock())

@patch('builtins.__import__')
@patch("azure_functions_runtime.otel.update_opentelemetry_status")
def test_initialize_azure_monitor_success(
Expand Down Expand Up @@ -146,7 +172,7 @@ async def test_init_request_initialize_azure_monitor_disabled_app_setting(
self.assertNotIn("WorkerOpenTelemetryEnabled", capabilities)

@patch.dict(os.environ, {'PYTHON_ENABLE_OPENTELEMETRY': 'true'})
async def test_init_request_enable_opentelemetry_enabled_app_setting(
def test_init_request_enable_opentelemetry_enabled_app_setting(
self,
):
worker_request = WorkerRequest(name='worker_init_request',
Expand All @@ -155,13 +181,15 @@ async def test_init_request_enable_opentelemetry_enabled_app_setting(
FUNCTION_APP_DIRECTORY)),
properties={'host': '123',
'protos': protos})
init_response = await worker_init_request(worker_request)
init_response = asyncio.run(worker_init_request(worker_request))

self.assertEqual(init_response.result.status,
protos.StatusResult.Success)

# Verify otel_libs_available is set to True
self.assertTrue(otel_manager.get_azure_monitor_available())
# Verify OpenTelemetry propagation is initialized
self.assertTrue(otel_manager.get_otel_libs_available())
self.assertIsNotNone(otel_manager.get_context_api())
self.assertIsNotNone(otel_manager.get_trace_context_propagator())
# Verify that WorkerOpenTelemetryEnabled capability is set to _TRUE
capabilities = init_response.capabilities
self.assertIn("WorkerOpenTelemetryEnabled", capabilities)
Expand Down
Loading