-
Notifications
You must be signed in to change notification settings - Fork 250
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extension for initializing OpenCensus tracer into Azure Functions (#1010
- Loading branch information
Showing
6 changed files
with
281 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
contrib/opencensus-ext-azure/examples/traces/azure_functions_extension.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Copyright 2021, OpenCensus Authors | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
# TODO: Configure PYTHON_ENABLE_WORKER_EXTENSIONS = 1 function app setting. | ||
# Ensure opencensus-ext-azure, opencensus-ext-requests and azure-functions | ||
# are defined in your function app's requirements.txt and properly installed. | ||
# | ||
# For more information about getting started with Azure Functions, please visit | ||
# https://aka.ms/functions-python-vscode | ||
import json | ||
import logging | ||
|
||
import requests | ||
|
||
from opencensus.ext.azure.extension.azure_functions import OpenCensusExtension | ||
|
||
OpenCensusExtension.configure( | ||
libraries=['requests'], | ||
connection_string='InstrumentationKey=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' | ||
) | ||
|
||
def main(req, context): | ||
logging.info('Executing HttpTrigger with OpenCensus extension') | ||
|
||
with context.tracer.span("parent"): | ||
requests.get(url='http://example.com') | ||
|
||
return json.dumps({ | ||
'method': req.method, | ||
'ctx_func_name': context.function_name, | ||
'ctx_func_dir': context.function_directory, | ||
'ctx_invocation_id': context.invocation_id, | ||
'ctx_trace_context_Traceparent': context.trace_context.Traceparent, | ||
'ctx_trace_context_Tracestate': context.trace_context.Tracestate, | ||
}) |
13 changes: 13 additions & 0 deletions
13
contrib/opencensus-ext-azure/opencensus/ext/azure/extension/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Copyright 2021, OpenCensus Authors | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. |
106 changes: 106 additions & 0 deletions
106
contrib/opencensus-ext-azure/opencensus/ext/azure/extension/azure_functions.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
# Copyright 2021, OpenCensus Authors | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
from azure.functions import AppExtensionBase | ||
from opencensus.trace import config_integration | ||
from opencensus.trace.propagation.trace_context_http_header_format import ( | ||
TraceContextPropagator, | ||
) | ||
from opencensus.trace.samplers import ProbabilitySampler | ||
from opencensus.trace.tracer import Tracer | ||
|
||
from ..trace_exporter import AzureExporter | ||
|
||
|
||
class OpenCensusExtension(AppExtensionBase): | ||
"""Extension for Azure Functions integration to export traces into Azure | ||
Monitor. Ensure the following requirements are met: | ||
1. Azure Functions version is greater or equal to v3.0.15584 | ||
2. App setting PYTHON_ENABLE_WORKER_EXTENSIONS is set to 1 | ||
""" | ||
|
||
@classmethod | ||
def init(cls): | ||
cls._exporter = None | ||
cls._trace_integrations = [] | ||
|
||
@classmethod | ||
def configure(cls, | ||
libraries, | ||
connection_string = None, | ||
*args, | ||
**kwargs): | ||
"""Configure libraries for integrating into OpenCensus extension. | ||
Initialize an Azure Exporter that will write traces to AppInsights. | ||
:type libraries: List[str] | ||
:param libraries: the libraries opencensus-ext-* that need to be | ||
integrated into OpenCensus tracer. (e.g. ['requests']) | ||
:type connection_string: Optional[str] | ||
:param connection_string: the connection string of azure exporter | ||
to write into. If this is set to None, the extension will use | ||
an instrumentation connection string from your app settings. | ||
""" | ||
cls._trace_integrations = config_integration.trace_integrations( | ||
libraries | ||
) | ||
|
||
cls._exporter = AzureExporter(connection_string=connection_string) | ||
|
||
@classmethod | ||
def pre_invocation_app_level(cls, | ||
logger, | ||
context, | ||
func_args = {}, | ||
*args, | ||
**kwargs): | ||
"""An implementation of pre invocation hooks on Function App's level. | ||
The Python Worker Extension Interface is defined in | ||
https://github.com/Azure/azure-functions-python-library/ | ||
blob/dev/azure/functions/extension/app_extension_base.py | ||
""" | ||
if not cls._exporter: | ||
logger.warning( | ||
'Please call OpenCensusExtension.configure() after the import ' | ||
'statement to ensure AzureExporter is setup correctly.' | ||
) | ||
return | ||
|
||
span_context = TraceContextPropagator().from_headers({ | ||
"traceparent": context.trace_context.Traceparent, | ||
"tracestate": context.trace_context.Tracestate | ||
}) | ||
|
||
tracer = Tracer( | ||
span_context=span_context, | ||
exporter=cls._exporter, | ||
sampler=ProbabilitySampler(1.0) | ||
) | ||
|
||
setattr(context, 'tracer', tracer) | ||
|
||
@classmethod | ||
def post_invocation_app_level(cls, | ||
logger, | ||
context, | ||
func_args, | ||
func_ret, | ||
*args, | ||
**kwargs): | ||
"""An implementation of post invocation hooks on Function App's level. | ||
The Python Worker Extension Interface is defined in | ||
https://github.com/Azure/azure-functions-python-library/ | ||
blob/dev/azure/functions/extension/app_extension_base.py | ||
""" | ||
if getattr(context, 'tracer', None): | ||
del context.tracer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
113 changes: 113 additions & 0 deletions
113
contrib/opencensus-ext-azure/tests/test_azure_functions_extension.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
# Copyright 2021, OpenCensus Authors | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import os | ||
import sys | ||
import unittest | ||
|
||
import mock | ||
|
||
from opencensus.ext.azure.extension.azure_functions import OpenCensusExtension | ||
|
||
IS_SUPPORTED_PYTHON_VERSION = sys.version_info.major == 3 | ||
|
||
MOCK_APPINSIGHTS_KEY = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' | ||
MOCK_AZURE_EXPORTER_CONNSTRING = ( | ||
'InstrumentationKey=11111111-2222-3333-4444-555555555555;' | ||
'IngestionEndpoint=https://mock.in.applicationinsights.azure.com/' | ||
) | ||
|
||
unittest.skipIf( | ||
not IS_SUPPORTED_PYTHON_VERSION, | ||
'Azure Functions only support Python 3.x' | ||
) | ||
class MockContext(object): | ||
class MockTraceContext(object): | ||
Tracestate = 'rojo=00f067aa0ba902b7' | ||
Traceparent = '00-4bf92f3577b34da6a3ce929d0e0e4736-5fd358d59f88ce45-01' | ||
|
||
trace_context = MockTraceContext() | ||
|
||
class TestAzureFunctionsExtension(unittest.TestCase): | ||
def setUp(self): | ||
self._instance = OpenCensusExtension | ||
OpenCensusExtension.init() | ||
os.environ['APPINSIGHTS_INSTRUMENTATIONKEY'] = MOCK_APPINSIGHTS_KEY | ||
|
||
def tearDown(self): | ||
if 'APPINSIGHTS_INSTRUMENTATIONKEY' in os.environ: | ||
del os.environ['APPINSIGHTS_INSTRUMENTATIONKEY'] | ||
|
||
@mock.patch('opencensus.ext.azure.extension.azure_functions' | ||
'.config_integration') | ||
def test_configure_method_should_setup_trace_integration(self, cfg_mock): | ||
self._instance.configure(['requests']) | ||
cfg_mock.trace_integrations.assert_called_once_with(['requests']) | ||
|
||
@mock.patch('opencensus.ext.azure.extension.azure_functions' | ||
'.AzureExporter') | ||
def test_configure_method_should_setup_azure_exporter( | ||
self, | ||
azure_exporter_mock | ||
): | ||
self._instance.configure(['requests']) | ||
azure_exporter_mock.assert_called_with(connection_string=None) | ||
|
||
@mock.patch('opencensus.ext.azure.extension.azure_functions' | ||
'.AzureExporter') | ||
def test_configure_method_shouold_setup_azure_exporter_with_connstring( | ||
self, | ||
azure_exporter_mock | ||
): | ||
self._instance.configure(['request'], MOCK_AZURE_EXPORTER_CONNSTRING) | ||
azure_exporter_mock.assert_called_with( | ||
connection_string=MOCK_AZURE_EXPORTER_CONNSTRING | ||
) | ||
|
||
def test_pre_invocation_should_warn_if_not_configured(self): | ||
mock_context = MockContext() | ||
mock_logger = mock.Mock() | ||
self._instance.pre_invocation_app_level(mock_logger, mock_context) | ||
mock_logger.warning.assert_called_once() | ||
|
||
def test_pre_invocation_should_attach_tracer_to_context(self): | ||
# Attach a mock object to exporter | ||
self._instance._exporter = mock.Mock() | ||
|
||
# Check if the tracer is attached to mock_context | ||
mock_context = MockContext() | ||
mock_logger = mock.Mock() | ||
self._instance.pre_invocation_app_level(mock_logger, mock_context) | ||
self.assertTrue(hasattr(mock_context, 'tracer')) | ||
|
||
def test_post_invocation_should_ignore_tracer_deallocation_if_not_set(self): | ||
mock_context = MockContext() | ||
mock_logger = mock.Mock() | ||
mock_func_args = {} | ||
mock_func_ret = None | ||
self._instance.post_invocation_app_level( | ||
mock_logger, mock_context, mock_func_args, mock_func_ret | ||
) | ||
|
||
def test_post_invocation_should_delete_tracer_from_context(self): | ||
mock_context = MockContext() | ||
mock_tracer = mock.Mock() | ||
setattr(mock_context, 'tracer', mock_tracer) | ||
mock_logger = mock.Mock() | ||
mock_func_args = {} | ||
mock_func_ret = None | ||
self._instance.post_invocation_app_level( | ||
mock_logger, mock_context, mock_func_args, mock_func_ret | ||
) | ||
self.assertFalse(hasattr(mock_context, 'tracer')) |