From 06c3a07fb1f08ed0d20c0b850daf5a6a37cef1b4 Mon Sep 17 00:00:00 2001 From: Leighton Date: Fri, 16 Aug 2019 15:55:49 -0700 Subject: [PATCH 01/13] Connection string --- .../opencensus/ext/azure/common/__init__.py | 65 ++++++++++++++++++- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py b/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py index 15687db17..bc3d843fe 100644 --- a/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py +++ b/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py @@ -12,19 +12,80 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging import os import sys from opencensus.ext.azure.common.protocol import BaseObject +logger = logging.getLogger(__name__) + +ENV_INSTRUMENTATION_KEY = 'APPINSIGHTS_INSTRUMENTATIONKEY' +ENV_CONNECTION_STRING = 'APPLICATIONINSIGHTS_CONNECTION_STRING' +INSTRUMENTATION_KEY = 'InstrumentationKey' +INGESTION_ENDPOINT = 'IngestionEndpoint' + + +def process_options(options): + env_ikey = os.getenv(ENV_INSTRUMENTATION_KEY) + env_cs = os.getenv(ENV_CONNECTION_STRING) + code_ikey = options.instrumentation_key + code_endpoint = options.endpoint + options.instrumentation_key = None + options.endpoint = None + + if options.connection_string is not None: + # Hardcoded connection string + cs = parse_connection_string(options.connection_string) + ikey = cs.get(INSTRUMENTATION_KEY) + if ikey is not None: + options.instrumentation_key = ikey + else: + logger.warning('Missing \'InstrumentationKey\' in connection string') + endpoint = cs.get(INGESTION_ENDPOINT) + if endpoint is not None: + options.endpoint = endpoint + '/v2/track' + if options.instrumentation_key is None and code_ikey is not None: + # Hardcoded instrumentation key + options.instrumentation_key = code_ikey + if options.instrumentation_key is None and env_cs is not None: + # Environment variable connection string + ikey = parse_connection_string(env_cs).get(INSTRUMENTATION_KEY) + if ikey is not None: + options.instrumentation_key = ikey + return + else: + logger.warning('Missing \'InstrumentationKey\' in environment connection string') + endpoint = cs.get(INGESTION_ENDPOINT) + if endpoint is not None and options.endpoint is None: + options.endpoint = endpoint + '/v2/track' + # Environment variable instrumentation key + if options.instrumentation_key is None: + options.instrumentation_key = env_ikey + if options.endpoint is None: + options.endpoint = code_endpoint + + +def parse_connection_string(connection_string): + try: + pairs = connection_string.split(';') + return dict(s.split('=') for s in pairs) + except Exception: + raise ValueError("Invalid connection string: " + connection_string) + class Options(BaseObject): + def __init__(self, *args, **kwargs): + super(Options, self).__init__(*args, **kwargs) + process_options(self) + _default = BaseObject( - enable_standard_metrics=True, + connection_string=None, + enable_standard_metrics=False, endpoint='https://dc.services.visualstudio.com/v2/track', export_interval=15.0, grace_period=5.0, - instrumentation_key=os.getenv('APPINSIGHTS_INSTRUMENTATIONKEY', None), + instrumentation_key=None, max_batch_size=100, minimum_retry_interval=60, # minimum retry interval in seconds proxy=None, From ce9233c9448b00d17781c1a9d563d1439ed34d63 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 19 Aug 2019 12:51:15 -0700 Subject: [PATCH 02/13] Implement derived endpoint, authorization --- .../opencensus/ext/azure/common/__init__.py | 84 +++++++++---------- 1 file changed, 38 insertions(+), 46 deletions(-) diff --git a/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py b/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py index bc3d843fe..00807f526 100644 --- a/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py +++ b/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py @@ -12,67 +12,59 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging import os import sys from opencensus.ext.azure.common.protocol import BaseObject -logger = logging.getLogger(__name__) - -ENV_INSTRUMENTATION_KEY = 'APPINSIGHTS_INSTRUMENTATIONKEY' +AUTHORIZATION = 'Authorization' +DEFAULT_BREEZE_ENDPOINT = 'https://dc.services.visualstudio.com' +ENDPOINT_SUFFIX = 'EndpointSuffix' ENV_CONNECTION_STRING = 'APPLICATIONINSIGHTS_CONNECTION_STRING' -INSTRUMENTATION_KEY = 'InstrumentationKey' +ENV_INSTRUMENTATION_KEY = 'APPINSIGHTS_INSTRUMENTATIONKEY' INGESTION_ENDPOINT = 'IngestionEndpoint' +INSTRUMENTATION_KEY = 'InstrumentationKey' +LOCATION = 'Location' def process_options(options): - env_ikey = os.getenv(ENV_INSTRUMENTATION_KEY) - env_cs = os.getenv(ENV_CONNECTION_STRING) + code_cs = parse_connection_string(options.connection_string) code_ikey = options.instrumentation_key - code_endpoint = options.endpoint - options.instrumentation_key = None - options.endpoint = None - - if options.connection_string is not None: - # Hardcoded connection string - cs = parse_connection_string(options.connection_string) - ikey = cs.get(INSTRUMENTATION_KEY) - if ikey is not None: - options.instrumentation_key = ikey - else: - logger.warning('Missing \'InstrumentationKey\' in connection string') - endpoint = cs.get(INGESTION_ENDPOINT) - if endpoint is not None: - options.endpoint = endpoint + '/v2/track' - if options.instrumentation_key is None and code_ikey is not None: - # Hardcoded instrumentation key - options.instrumentation_key = code_ikey - if options.instrumentation_key is None and env_cs is not None: - # Environment variable connection string - ikey = parse_connection_string(env_cs).get(INSTRUMENTATION_KEY) - if ikey is not None: - options.instrumentation_key = ikey - return - else: - logger.warning('Missing \'InstrumentationKey\' in environment connection string') - endpoint = cs.get(INGESTION_ENDPOINT) - if endpoint is not None and options.endpoint is None: - options.endpoint = endpoint + '/v2/track' - # Environment variable instrumentation key - if options.instrumentation_key is None: - options.instrumentation_key = env_ikey - if options.endpoint is None: - options.endpoint = code_endpoint - + env_cs = parse_connection_string(os.getenv(ENV_CONNECTION_STRING)) + env_ikey = os.getenv(ENV_INSTRUMENTATION_KEY) + + options.instrumentation_key = code_cs.get(INSTRUMENTATION_KEY) or code_ikey or env_ikey + endpoint = code_cs.get(INGESTION_ENDPOINT) or env_cs.get(INGESTION_ENDPOINT) or DEFAULT_BREEZE_ENDPOINT + options.endpoint = endpoint + '/v2/track' def parse_connection_string(connection_string): + if connection_string is None: + return {} try: pairs = connection_string.split(';') - return dict(s.split('=') for s in pairs) + result = dict(s.split('=') for s in pairs) except Exception: - raise ValueError("Invalid connection string: " + connection_string) - + raise ValueError('Invalid connection string: ' + connection_string) + # Validate authorization + auth = result.get(AUTHORIZATION) + if auth is None: + raise ValueError('Missing \'Authorization\' in connection string: ' + connection_string) + if auth.lower() != 'ikey': + raise ValueError('Invalid authorization mechanism: ' + auth) + # Construct the ingestion endpoint if not passed in explicitly + if result.get(INGESTION_ENDPOINT) is None: + endpoint_suffix = '' + location_prefix = '' + if result.get(ENDPOINT_SUFFIX) is not None: + endpoint_suffix = result.get(ENDPOINT_SUFFIX) + # Get regional information if provided + if result.get(LOCATION) is not None: + location_prefix = result.get(LOCATION) + '.' + result[INGESTION_ENDPOINT] = 'https://' + location_prefix + 'dc.' + endpoint_suffix + else: + # Use default endpoint if cannot construct + result[INGESTION_ENDPOINT] = DEFAULT_BREEZE_ENDPOINT + return result class Options(BaseObject): def __init__(self, *args, **kwargs): @@ -81,7 +73,7 @@ def __init__(self, *args, **kwargs): _default = BaseObject( connection_string=None, - enable_standard_metrics=False, + enable_standard_metrics=True, endpoint='https://dc.services.visualstudio.com/v2/track', export_interval=15.0, grace_period=5.0, From a349111c3faa52850ca7976c69223236422a2f1d Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 19 Aug 2019 14:00:32 -0700 Subject: [PATCH 03/13] Update README, add tests --- contrib/opencensus-ext-azure/README.rst | 20 ++- .../opencensus/ext/azure/common/__init__.py | 2 +- .../tests/test_options.py | 130 ++++++++++++++++++ 3 files changed, 146 insertions(+), 6 deletions(-) create mode 100644 contrib/opencensus-ext-azure/tests/test_options.py diff --git a/contrib/opencensus-ext-azure/README.rst b/contrib/opencensus-ext-azure/README.rst index 451086d02..20f021e62 100644 --- a/contrib/opencensus-ext-azure/README.rst +++ b/contrib/opencensus-ext-azure/README.rst @@ -25,7 +25,9 @@ This example shows how to send a warning level log to Azure Monitor. * Create an Azure Monitor resource and get the instrumentation key, more information can be found `here `_. * Put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. -* You can also specify the instrumentation key explicitly in the code, which will take priority over a set environment variable. +* You can also utilize a connection string with an instrumentation key. Place the connetion string in ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable, which will take priority over the instrumentation key environment variable. +* You can also specify the instrumentation key explicitly in the code, which will take priority over the previous methods. +* Finally, you can specify a connection string explicitly in the code, which will take priority over all methods. .. code:: python @@ -42,7 +44,9 @@ You can enrich the logs with trace IDs and span IDs by using the `logging integr * Create an Azure Monitor resource and get the instrumentation key, more information can be found `here `_. * Install the `logging integration package <../opencensus-ext-logging>`_ using ``pip install opencensus-ext-logging``. * Put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. -* You can also specify the instrumentation key explicitly in the code, which will take priority over a set environment variable. +* You can also utilize a connection string with an instrumentation key. Place the connetion string in ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable, which will take priority over the instrumentation key environment variable. +* You can also specify the instrumentation key explicitly in the code, which will take priority over the previous methods. +* Finally, you can specify a connection string explicitly in the code, which will take priority over all methods. .. code:: python @@ -76,7 +80,9 @@ The **Azure Monitor Metrics Exporter** allows you to export metrics to `Azure Mo * Create an Azure Monitor resource and get the instrumentation key, more information can be found `here `_. * Put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. -* You can also specify the instrumentation key explicitly in the code, which will take priority over a set environment variable. +* You can also utilize a connection string with an instrumentation key. Place the connetion string in ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable, which will take priority over the instrumentation key environment variable. +* You can also specify the instrumentation key explicitly in the code, which will take priority over the previous methods. +* Finally, you can specify a connection string explicitly in the code, which will take priority over all methods. .. code:: python @@ -167,7 +173,9 @@ This example shows how to send a span "hello" to Azure Monitor. * Create an Azure Monitor resource and get the instrumentation key, more information can be found `here `_. * Put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. -* You can also specify the instrumentation key explicitly in the code, which will take priority over a set environment variable. +* You can also utilize a connection string with an instrumentation key. Place the connetion string in ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable, which will take priority over the instrumentation key environment variable. +* You can also specify the instrumentation key explicitly in the code, which will take priority over the previous methods. +* Finally, you can specify a connection string explicitly in the code, which will take priority over all methods. .. code:: python @@ -185,7 +193,9 @@ You can also specify the instrumentation key explicitly in the code. * Create an Azure Monitor resource and get the instrumentation key, more information can be found `here `_. * Install the `requests integration package <../opencensus-ext-requests>`_ using ``pip install opencensus-ext-requests``. * Put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. -* You can also specify the instrumentation key explicitly in the code, which will take priority over a set environment variable. +* You can also utilize a connection string with an instrumentation key. Place the connetion string in ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable, which will take priority over the instrumentation key environment variable. +* You can also specify the instrumentation key explicitly in the code, which will take priority over the previous methods. +* Finally, you can specify a connection string explicitly in the code, which will take priority over all methods. .. code:: python diff --git a/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py b/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py index 00807f526..7dbefbb78 100644 --- a/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py +++ b/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py @@ -33,7 +33,7 @@ def process_options(options): env_cs = parse_connection_string(os.getenv(ENV_CONNECTION_STRING)) env_ikey = os.getenv(ENV_INSTRUMENTATION_KEY) - options.instrumentation_key = code_cs.get(INSTRUMENTATION_KEY) or code_ikey or env_ikey + options.instrumentation_key = code_cs.get(INSTRUMENTATION_KEY) or code_ikey or env_cs.get(INSTRUMENTATION_KEY) or env_ikey endpoint = code_cs.get(INGESTION_ENDPOINT) or env_cs.get(INGESTION_ENDPOINT) or DEFAULT_BREEZE_ENDPOINT options.endpoint = endpoint + '/v2/track' diff --git a/contrib/opencensus-ext-azure/tests/test_options.py b/contrib/opencensus-ext-azure/tests/test_options.py new file mode 100644 index 000000000..7891f73ac --- /dev/null +++ b/contrib/opencensus-ext-azure/tests/test_options.py @@ -0,0 +1,130 @@ +# Copyright 2019, 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 unittest + +from opencensus.ext.azure import common + + +class TestOptions(unittest.TestCase): + def setUp(self): + os.environ.clear() + + def test_process_options_ikey_code_cs(self): + options = common.Options() + options.connection_string = 'Authorization=ikey;InstrumentationKey=123' + options.instrumentation_key = '456' + os.environ['APPLICATIONINSIGHTS_CONNECTION_STRING'] = 'Authorization=ikey;InstrumentationKey=789' + os.environ['APPINSIGHTS_INSTRUMENTATIONKEY'] = '101112' + common.process_options(options) + + self.assertEqual(options.instrumentation_key, '123') + + def test_process_options_ikey_code_ikey(self): + options = common.Options() + options.connection_string = None + options.instrumentation_key = '456' + os.environ['APPLICATIONINSIGHTS_CONNECTION_STRING'] = 'Authorization=ikey;InstrumentationKey=789' + os.environ['APPINSIGHTS_INSTRUMENTATIONKEY'] = '101112' + common.process_options(options) + + self.assertEqual(options.instrumentation_key, '456') + + def test_process_options_ikey_env_cs(self): + options = common.Options() + options.connection_string = None + options.instrumentation_key = None + os.environ['APPLICATIONINSIGHTS_CONNECTION_STRING'] = 'Authorization=ikey;InstrumentationKey=789' + os.environ['APPINSIGHTS_INSTRUMENTATIONKEY'] = '101112' + common.process_options(options) + + self.assertEqual(options.instrumentation_key, '789') + + def test_process_options_ikey_env_ikey(self): + options = common.Options() + options.connection_string = None + options.instrumentation_key = None + os.environ['APPINSIGHTS_INSTRUMENTATIONKEY'] = '101112' + common.process_options(options) + + self.assertEqual(options.instrumentation_key, '101112') + + def test_process_options_endpoint_code_cs(self): + options = common.Options() + suffix = '/v2/track' + options.connection_string = 'Authorization=ikey;IngestionEndpoint=123' + os.environ['APPLICATIONINSIGHTS_CONNECTION_STRING'] = 'Authorization=ikey;IngestionEndpoint=456' + common.process_options(options) + + self.assertEqual(options.endpoint, '123/v2/track') + + def test_process_options_endpoint_env_cs(self): + options = common.Options() + suffix = '/v2/track' + options.connection_string = None + os.environ['APPLICATIONINSIGHTS_CONNECTION_STRING'] = 'Authorization=ikey;IngestionEndpoint=456' + common.process_options(options) + + self.assertEqual(options.endpoint, '456/v2/track') + + def test_process_options_endpoint_default(self): + options = common.Options() + suffix = '/v2/track' + options.connection_string = None + common.process_options(options) + + self.assertEqual(options.endpoint, 'https://dc.services.visualstudio.com/v2/track') + + def test_parse_connection_string_none(self): + cs = None + result = common.parse_connection_string(cs) + + self.assertEqual(result, {}) + + def test_parse_connection_string_invalid(self): + cs = 'asd' + self.assertRaises(ValueError, lambda: common.parse_connection_string(cs)) + + def test_parse_connection_string_missing_auth(self): + cs = 'asd=asd' + self.assertRaises(ValueError, lambda: common.parse_connection_string(cs)) + + def test_parse_connection_string_invalid_auth(self): + cs = 'Authorization=asd' + self.assertRaises(ValueError, lambda: common.parse_connection_string(cs)) + + def test_parse_connection_string_explicit_endpoint(self): + cs = 'Authorization=ikey;IngestionEndpoint=123;Location=us;EndpointSuffix=suffix' + result = common.parse_connection_string(cs) + + self.assertEqual(result['IngestionEndpoint'], '123') + + def test_parse_connection_string_default(self): + cs = 'Authorization=ikey;Location=us' + result = common.parse_connection_string(cs) + + self.assertEqual(result['IngestionEndpoint'], 'https://dc.services.visualstudio.com') + + def test_parse_connection_string_no_location(self): + cs = 'Authorization=ikey;EndpointSuffix=suffix' + result = common.parse_connection_string(cs) + + self.assertEqual(result['IngestionEndpoint'], 'https://dc.suffix') + + def test_parse_connection_string_location(self): + cs = 'Authorization=ikey;EndpointSuffix=suffix;Location=us' + result = common.parse_connection_string(cs) + + self.assertEqual(result['IngestionEndpoint'], 'https://us.dc.suffix') \ No newline at end of file From 8a87eadc4d084dc0664ece1537f394af7f953df5 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 19 Aug 2019 14:23:27 -0700 Subject: [PATCH 04/13] Fix lint --- .../opencensus/ext/azure/common/__init__.py | 17 ++++++--- .../tests/test_options.py | 36 +++++++++++-------- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py b/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py index 7dbefbb78..c84804368 100644 --- a/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py +++ b/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py @@ -33,10 +33,16 @@ def process_options(options): env_cs = parse_connection_string(os.getenv(ENV_CONNECTION_STRING)) env_ikey = os.getenv(ENV_INSTRUMENTATION_KEY) - options.instrumentation_key = code_cs.get(INSTRUMENTATION_KEY) or code_ikey or env_cs.get(INSTRUMENTATION_KEY) or env_ikey - endpoint = code_cs.get(INGESTION_ENDPOINT) or env_cs.get(INGESTION_ENDPOINT) or DEFAULT_BREEZE_ENDPOINT + options.instrumentation_key = code_cs.get(INSTRUMENTATION_KEY) \ + or code_ikey \ + or env_cs.get(INSTRUMENTATION_KEY) \ + or env_ikey + endpoint = code_cs.get(INGESTION_ENDPOINT) \ + or env_cs.get(INGESTION_ENDPOINT) \ + or DEFAULT_BREEZE_ENDPOINT options.endpoint = endpoint + '/v2/track' + def parse_connection_string(connection_string): if connection_string is None: return {} @@ -48,7 +54,8 @@ def parse_connection_string(connection_string): # Validate authorization auth = result.get(AUTHORIZATION) if auth is None: - raise ValueError('Missing \'Authorization\' in connection string: ' + connection_string) + raise ValueError('Missing \'Authorization\' in connection string:' \ + + connection_string) if auth.lower() != 'ikey': raise ValueError('Invalid authorization mechanism: ' + auth) # Construct the ingestion endpoint if not passed in explicitly @@ -60,12 +67,14 @@ def parse_connection_string(connection_string): # Get regional information if provided if result.get(LOCATION) is not None: location_prefix = result.get(LOCATION) + '.' - result[INGESTION_ENDPOINT] = 'https://' + location_prefix + 'dc.' + endpoint_suffix + endpoint = 'https://' + location_prefix + 'dc.' + endpoint_suffix + result[INGESTION_ENDPOINT] = endpoint else: # Use default endpoint if cannot construct result[INGESTION_ENDPOINT] = DEFAULT_BREEZE_ENDPOINT return result + class Options(BaseObject): def __init__(self, *args, **kwargs): super(Options, self).__init__(*args, **kwargs) diff --git a/contrib/opencensus-ext-azure/tests/test_options.py b/contrib/opencensus-ext-azure/tests/test_options.py index 7891f73ac..a0ab921d6 100644 --- a/contrib/opencensus-ext-azure/tests/test_options.py +++ b/contrib/opencensus-ext-azure/tests/test_options.py @@ -26,7 +26,8 @@ def test_process_options_ikey_code_cs(self): options = common.Options() options.connection_string = 'Authorization=ikey;InstrumentationKey=123' options.instrumentation_key = '456' - os.environ['APPLICATIONINSIGHTS_CONNECTION_STRING'] = 'Authorization=ikey;InstrumentationKey=789' + os.environ['APPLICATIONINSIGHTS_CONNECTION_STRING'] = \ + 'Authorization=ikey;InstrumentationKey=789' os.environ['APPINSIGHTS_INSTRUMENTATIONKEY'] = '101112' common.process_options(options) @@ -36,7 +37,8 @@ def test_process_options_ikey_code_ikey(self): options = common.Options() options.connection_string = None options.instrumentation_key = '456' - os.environ['APPLICATIONINSIGHTS_CONNECTION_STRING'] = 'Authorization=ikey;InstrumentationKey=789' + os.environ['APPLICATIONINSIGHTS_CONNECTION_STRING'] = \ + 'Authorization=ikey;InstrumentationKey=789' os.environ['APPINSIGHTS_INSTRUMENTATIONKEY'] = '101112' common.process_options(options) @@ -46,7 +48,8 @@ def test_process_options_ikey_env_cs(self): options = common.Options() options.connection_string = None options.instrumentation_key = None - os.environ['APPLICATIONINSIGHTS_CONNECTION_STRING'] = 'Authorization=ikey;InstrumentationKey=789' + os.environ['APPLICATIONINSIGHTS_CONNECTION_STRING'] = \ + 'Authorization=ikey;InstrumentationKey=789' os.environ['APPINSIGHTS_INSTRUMENTATIONKEY'] = '101112' common.process_options(options) @@ -63,29 +66,29 @@ def test_process_options_ikey_env_ikey(self): def test_process_options_endpoint_code_cs(self): options = common.Options() - suffix = '/v2/track' options.connection_string = 'Authorization=ikey;IngestionEndpoint=123' - os.environ['APPLICATIONINSIGHTS_CONNECTION_STRING'] = 'Authorization=ikey;IngestionEndpoint=456' + os.environ['APPLICATIONINSIGHTS_CONNECTION_STRING'] = \ + 'Authorization=ikey;IngestionEndpoint=456' common.process_options(options) self.assertEqual(options.endpoint, '123/v2/track') def test_process_options_endpoint_env_cs(self): options = common.Options() - suffix = '/v2/track' options.connection_string = None - os.environ['APPLICATIONINSIGHTS_CONNECTION_STRING'] = 'Authorization=ikey;IngestionEndpoint=456' + os.environ['APPLICATIONINSIGHTS_CONNECTION_STRING'] = \ + 'Authorization=ikey;IngestionEndpoint=456' common.process_options(options) self.assertEqual(options.endpoint, '456/v2/track') def test_process_options_endpoint_default(self): options = common.Options() - suffix = '/v2/track' options.connection_string = None common.process_options(options) - self.assertEqual(options.endpoint, 'https://dc.services.visualstudio.com/v2/track') + self.assertEqual(options.endpoint, \ + 'https://dc.services.visualstudio.com/v2/track') def test_parse_connection_string_none(self): cs = None @@ -95,18 +98,22 @@ def test_parse_connection_string_none(self): def test_parse_connection_string_invalid(self): cs = 'asd' - self.assertRaises(ValueError, lambda: common.parse_connection_string(cs)) + self.assertRaises(ValueError, \ + lambda: common.parse_connection_string(cs)) def test_parse_connection_string_missing_auth(self): cs = 'asd=asd' - self.assertRaises(ValueError, lambda: common.parse_connection_string(cs)) + self.assertRaises(ValueError, \ + lambda: common.parse_connection_string(cs)) def test_parse_connection_string_invalid_auth(self): cs = 'Authorization=asd' - self.assertRaises(ValueError, lambda: common.parse_connection_string(cs)) + self.assertRaises(ValueError, \ + lambda: common.parse_connection_string(cs)) def test_parse_connection_string_explicit_endpoint(self): - cs = 'Authorization=ikey;IngestionEndpoint=123;Location=us;EndpointSuffix=suffix' + cs = 'Authorization=ikey;IngestionEndpoint=123;' \ + 'Location=us;EndpointSuffix=suffix' result = common.parse_connection_string(cs) self.assertEqual(result['IngestionEndpoint'], '123') @@ -115,7 +122,8 @@ def test_parse_connection_string_default(self): cs = 'Authorization=ikey;Location=us' result = common.parse_connection_string(cs) - self.assertEqual(result['IngestionEndpoint'], 'https://dc.services.visualstudio.com') + self.assertEqual(result['IngestionEndpoint'], \ + 'https://dc.services.visualstudio.com') def test_parse_connection_string_no_location(self): cs = 'Authorization=ikey;EndpointSuffix=suffix' From 8d7a282b5758fb53090e2e2f056edb980c3e4418 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 19 Aug 2019 14:26:49 -0700 Subject: [PATCH 05/13] Update CHANGELOG --- contrib/opencensus-ext-azure/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/opencensus-ext-azure/CHANGELOG.md b/contrib/opencensus-ext-azure/CHANGELOG.md index 9655e02e0..b33c45077 100644 --- a/contrib/opencensus-ext-azure/CHANGELOG.md +++ b/contrib/opencensus-ext-azure/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## Unreleased +- Implement connection strings + ([#767](https://github.com/census-instrumentation/opencensus-python/pull/767)) - Standard metrics incoming requests per second ([#758](https://github.com/census-instrumentation/opencensus-python/pull/758)) From 1453830d657c69781d564f1fd6c7f3fad352d2a1 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 19 Aug 2019 14:35:18 -0700 Subject: [PATCH 06/13] Fix lint --- .../opencensus/ext/azure/common/__init__.py | 14 ++++++------ .../tests/test_options.py | 22 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py b/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py index c84804368..77d2a1be5 100644 --- a/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py +++ b/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py @@ -34,12 +34,12 @@ def process_options(options): env_ikey = os.getenv(ENV_INSTRUMENTATION_KEY) options.instrumentation_key = code_cs.get(INSTRUMENTATION_KEY) \ - or code_ikey \ - or env_cs.get(INSTRUMENTATION_KEY) \ - or env_ikey + or code_ikey \ + or env_cs.get(INSTRUMENTATION_KEY) \ + or env_ikey endpoint = code_cs.get(INGESTION_ENDPOINT) \ - or env_cs.get(INGESTION_ENDPOINT) \ - or DEFAULT_BREEZE_ENDPOINT + or env_cs.get(INGESTION_ENDPOINT) \ + or DEFAULT_BREEZE_ENDPOINT options.endpoint = endpoint + '/v2/track' @@ -54,8 +54,8 @@ def parse_connection_string(connection_string): # Validate authorization auth = result.get(AUTHORIZATION) if auth is None: - raise ValueError('Missing \'Authorization\' in connection string:' \ - + connection_string) + raise ValueError('Missing \'Authorization\' in connection string:' + + connection_string) if auth.lower() != 'ikey': raise ValueError('Invalid authorization mechanism: ' + auth) # Construct the ingestion endpoint if not passed in explicitly diff --git a/contrib/opencensus-ext-azure/tests/test_options.py b/contrib/opencensus-ext-azure/tests/test_options.py index a0ab921d6..10b4eafcd 100644 --- a/contrib/opencensus-ext-azure/tests/test_options.py +++ b/contrib/opencensus-ext-azure/tests/test_options.py @@ -87,8 +87,8 @@ def test_process_options_endpoint_default(self): options.connection_string = None common.process_options(options) - self.assertEqual(options.endpoint, \ - 'https://dc.services.visualstudio.com/v2/track') + self.assertEqual(options.endpoint, + 'https://dc.services.visualstudio.com/v2/track') def test_parse_connection_string_none(self): cs = None @@ -98,18 +98,18 @@ def test_parse_connection_string_none(self): def test_parse_connection_string_invalid(self): cs = 'asd' - self.assertRaises(ValueError, \ - lambda: common.parse_connection_string(cs)) + self.assertRaises(ValueError, + lambda: common.parse_connection_string(cs)) def test_parse_connection_string_missing_auth(self): cs = 'asd=asd' - self.assertRaises(ValueError, \ - lambda: common.parse_connection_string(cs)) + self.assertRaises(ValueError, + lambda: common.parse_connection_string(cs)) def test_parse_connection_string_invalid_auth(self): cs = 'Authorization=asd' - self.assertRaises(ValueError, \ - lambda: common.parse_connection_string(cs)) + self.assertRaises(ValueError, + lambda: common.parse_connection_string(cs)) def test_parse_connection_string_explicit_endpoint(self): cs = 'Authorization=ikey;IngestionEndpoint=123;' \ @@ -122,8 +122,8 @@ def test_parse_connection_string_default(self): cs = 'Authorization=ikey;Location=us' result = common.parse_connection_string(cs) - self.assertEqual(result['IngestionEndpoint'], \ - 'https://dc.services.visualstudio.com') + self.assertEqual(result['IngestionEndpoint'], + 'https://dc.services.visualstudio.com') def test_parse_connection_string_no_location(self): cs = 'Authorization=ikey;EndpointSuffix=suffix' @@ -135,4 +135,4 @@ def test_parse_connection_string_location(self): cs = 'Authorization=ikey;EndpointSuffix=suffix;Location=us' result = common.parse_connection_string(cs) - self.assertEqual(result['IngestionEndpoint'], 'https://us.dc.suffix') \ No newline at end of file + self.assertEqual(result['IngestionEndpoint'], 'https://us.dc.suffix') From 00c2a3a6866f48203c6ab1fcefe318c46fd599e7 Mon Sep 17 00:00:00 2001 From: Leighton Date: Mon, 19 Aug 2019 16:29:05 -0700 Subject: [PATCH 07/13] Fix examples --- contrib/opencensus-ext-azure/README.rst | 37 ++++++++----------- .../examples/logs/correlated.py | 5 ++- .../examples/logs/error.py | 5 ++- .../examples/logs/simple.py | 5 ++- .../examples/metrics/simple.py | 3 ++ .../examples/metrics/standard.py | 3 ++ .../examples/metrics/sum.py | 3 ++ .../examples/traces/client.py | 5 ++- .../examples/traces/config.py | 3 +- .../examples/traces/custom.py | 3 +- .../examples/traces/server.py | 5 ++- .../examples/traces/simple.py | 5 ++- .../opencensus/ext/azure/common/__init__.py | 9 ++--- .../tests/test_options.py | 8 ++-- 14 files changed, 53 insertions(+), 46 deletions(-) diff --git a/contrib/opencensus-ext-azure/README.rst b/contrib/opencensus-ext-azure/README.rst index 20f021e62..e7c614448 100644 --- a/contrib/opencensus-ext-azure/README.rst +++ b/contrib/opencensus-ext-azure/README.rst @@ -24,10 +24,9 @@ The **Azure Monitor Log Handler** allows you to export Python logs to `Azure Mon This example shows how to send a warning level log to Azure Monitor. * Create an Azure Monitor resource and get the instrumentation key, more information can be found `here `_. -* Put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. -* You can also utilize a connection string with an instrumentation key. Place the connetion string in ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable, which will take priority over the instrumentation key environment variable. -* You can also specify the instrumentation key explicitly in the code, which will take priority over the previous methods. -* Finally, you can specify a connection string explicitly in the code, which will take priority over all methods. +* Place your instrumentation key in a connection string and into ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable. +* You can also put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. +* Alternatively, you can specify either the connection string or instrumentation key directly in your code, which will take priority over a set environment variable. .. code:: python @@ -42,11 +41,9 @@ This example shows how to send a warning level log to Azure Monitor. You can enrich the logs with trace IDs and span IDs by using the `logging integration <../opencensus-ext-logging>`_. * Create an Azure Monitor resource and get the instrumentation key, more information can be found `here `_. -* Install the `logging integration package <../opencensus-ext-logging>`_ using ``pip install opencensus-ext-logging``. -* Put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. -* You can also utilize a connection string with an instrumentation key. Place the connetion string in ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable, which will take priority over the instrumentation key environment variable. -* You can also specify the instrumentation key explicitly in the code, which will take priority over the previous methods. -* Finally, you can specify a connection string explicitly in the code, which will take priority over all methods. +* Place your instrumentation key in a connection string and into ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable. +* You can also put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. +* Alternatively, you can specify either the connection string or instrumentation key directly in your code, which will take priority over a set environment variable. .. code:: python @@ -79,10 +76,9 @@ Metrics The **Azure Monitor Metrics Exporter** allows you to export metrics to `Azure Monitor`_. * Create an Azure Monitor resource and get the instrumentation key, more information can be found `here `_. -* Put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. -* You can also utilize a connection string with an instrumentation key. Place the connetion string in ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable, which will take priority over the instrumentation key environment variable. -* You can also specify the instrumentation key explicitly in the code, which will take priority over the previous methods. -* Finally, you can specify a connection string explicitly in the code, which will take priority over all methods. +* Place your instrumentation key in a connection string and into ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable. +* You can also put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. +* Alternatively, you can specify either the connection string or instrumentation key directly in your code, which will take priority over a set environment variable. .. code:: python @@ -172,10 +168,9 @@ The **Azure Monitor Trace Exporter** allows you to export `OpenCensus`_ traces t This example shows how to send a span "hello" to Azure Monitor. * Create an Azure Monitor resource and get the instrumentation key, more information can be found `here `_. -* Put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. -* You can also utilize a connection string with an instrumentation key. Place the connetion string in ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable, which will take priority over the instrumentation key environment variable. -* You can also specify the instrumentation key explicitly in the code, which will take priority over the previous methods. -* Finally, you can specify a connection string explicitly in the code, which will take priority over all methods. +* Place your instrumentation key in a connection string and into ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable. +* You can also put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. +* Alternatively, you can specify either the connection string or instrumentation key directly in your code, which will take priority over a set environment variable. .. code:: python @@ -191,11 +186,9 @@ This example shows how to send a span "hello" to Azure Monitor. You can also specify the instrumentation key explicitly in the code. * Create an Azure Monitor resource and get the instrumentation key, more information can be found `here `_. -* Install the `requests integration package <../opencensus-ext-requests>`_ using ``pip install opencensus-ext-requests``. -* Put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. -* You can also utilize a connection string with an instrumentation key. Place the connetion string in ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable, which will take priority over the instrumentation key environment variable. -* You can also specify the instrumentation key explicitly in the code, which will take priority over the previous methods. -* Finally, you can specify a connection string explicitly in the code, which will take priority over all methods. +* Place your instrumentation key in a connection string and into ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable. +* You can also put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. +* Alternatively, you can specify either the connection string or instrumentation key directly in your code, which will take priority over a set environment variable. .. code:: python diff --git a/contrib/opencensus-ext-azure/examples/logs/correlated.py b/contrib/opencensus-ext-azure/examples/logs/correlated.py index 205a46bc1..69445e997 100644 --- a/contrib/opencensus-ext-azure/examples/logs/correlated.py +++ b/contrib/opencensus-ext-azure/examples/logs/correlated.py @@ -24,8 +24,9 @@ logger = logging.getLogger(__name__) -# TODO: you need to specify the instrumentation key in the -# APPINSIGHTS_INSTRUMENTATIONKEY environment variable. +# TODO: you need to specify the instrumentation key in a connection string +# and place it in the APPLICATIONINSIGHTS_CONNECTION_STRING +# environment variable. handler = AzureLogHandler() logger.addHandler(handler) diff --git a/contrib/opencensus-ext-azure/examples/logs/error.py b/contrib/opencensus-ext-azure/examples/logs/error.py index 4f801342a..772861cf1 100644 --- a/contrib/opencensus-ext-azure/examples/logs/error.py +++ b/contrib/opencensus-ext-azure/examples/logs/error.py @@ -17,8 +17,9 @@ from opencensus.ext.azure.log_exporter import AzureLogHandler logger = logging.getLogger(__name__) -# TODO: you need to specify the instrumentation key in the -# APPINSIGHTS_INSTRUMENTATIONKEY environment variable. +# TODO: you need to specify the instrumentation key in a connection string +# and place it in the APPLICATIONINSIGHTS_CONNECTION_STRING +# environment variable. logger.addHandler(AzureLogHandler()) diff --git a/contrib/opencensus-ext-azure/examples/logs/simple.py b/contrib/opencensus-ext-azure/examples/logs/simple.py index cdda2b688..1fba3d668 100644 --- a/contrib/opencensus-ext-azure/examples/logs/simple.py +++ b/contrib/opencensus-ext-azure/examples/logs/simple.py @@ -17,7 +17,8 @@ from opencensus.ext.azure.log_exporter import AzureLogHandler logger = logging.getLogger(__name__) -# TODO: you need to specify the instrumentation key in the -# APPINSIGHTS_INSTRUMENTATIONKEY environment variable. +# TODO: you need to specify the instrumentation key in a connection string +# and place it in the APPLICATIONINSIGHTS_CONNECTION_STRING +# environment variable. logger.addHandler(AzureLogHandler()) logger.warning('Hello, World!') diff --git a/contrib/opencensus-ext-azure/examples/metrics/simple.py b/contrib/opencensus-ext-azure/examples/metrics/simple.py index 2409f21ad..67115f6bd 100644 --- a/contrib/opencensus-ext-azure/examples/metrics/simple.py +++ b/contrib/opencensus-ext-azure/examples/metrics/simple.py @@ -38,6 +38,9 @@ def main(): # Enable metrics # Set the interval in seconds in which you want to send metrics + # TODO: you need to specify the instrumentation key in a connection string + # and place it in the APPLICATIONINSIGHTS_CONNECTION_STRING + # environment variable. exporter = metrics_exporter.new_metrics_exporter() view_manager.register_exporter(exporter) diff --git a/contrib/opencensus-ext-azure/examples/metrics/standard.py b/contrib/opencensus-ext-azure/examples/metrics/standard.py index 7951c8aa9..3c4627765 100644 --- a/contrib/opencensus-ext-azure/examples/metrics/standard.py +++ b/contrib/opencensus-ext-azure/examples/metrics/standard.py @@ -19,6 +19,9 @@ def main(): + # TODO: you need to specify the instrumentation key in a connection string + # and place it in the APPLICATIONINSIGHTS_CONNECTION_STRING + # environment variable. # All you need is the next line. You can disable standard metrics by # passing in enable_standard_metrics=False into the constructor of # new_metrics_exporter() diff --git a/contrib/opencensus-ext-azure/examples/metrics/sum.py b/contrib/opencensus-ext-azure/examples/metrics/sum.py index e92a5e406..355c72954 100644 --- a/contrib/opencensus-ext-azure/examples/metrics/sum.py +++ b/contrib/opencensus-ext-azure/examples/metrics/sum.py @@ -38,6 +38,9 @@ def main(): # Enable metrics # Set the interval in seconds in which you want to send metrics + # TODO: you need to specify the instrumentation key in a connection string + # and place it in the APPLICATIONINSIGHTS_CONNECTION_STRING + # environment variable. exporter = metrics_exporter.new_metrics_exporter() view_manager.register_exporter(exporter) diff --git a/contrib/opencensus-ext-azure/examples/traces/client.py b/contrib/opencensus-ext-azure/examples/traces/client.py index eb8480089..004c79ab2 100644 --- a/contrib/opencensus-ext-azure/examples/traces/client.py +++ b/contrib/opencensus-ext-azure/examples/traces/client.py @@ -20,8 +20,9 @@ from opencensus.trace.tracer import Tracer config_integration.trace_integrations(['requests']) -# TODO: you need to specify the instrumentation key in the -# APPINSIGHTS_INSTRUMENTATIONKEY environment variable. +# TODO: you need to specify the instrumentation key in a connection string +# and place it in the APPLICATIONINSIGHTS_CONNECTION_STRING +# environment variable. tracer = Tracer(exporter=AzureExporter(), sampler=ProbabilitySampler(1.0)) with tracer.span(name='parent'): with tracer.span(name='child'): diff --git a/contrib/opencensus-ext-azure/examples/traces/config.py b/contrib/opencensus-ext-azure/examples/traces/config.py index 9c4c7fd0f..c5a9e025a 100644 --- a/contrib/opencensus-ext-azure/examples/traces/config.py +++ b/contrib/opencensus-ext-azure/examples/traces/config.py @@ -19,7 +19,8 @@ tracer = Tracer( exporter=AzureExporter( # TODO: replace the all-zero GUID with your instrumentation key. - instrumentation_key='00000000-0000-0000-0000-000000000000', + connection_string='InstrumentationKey= \ + 00000000-0000-0000-0000-000000000000', ), sampler=ProbabilitySampler(rate=1.0), ) diff --git a/contrib/opencensus-ext-azure/examples/traces/custom.py b/contrib/opencensus-ext-azure/examples/traces/custom.py index edcb187c5..5b23b10a7 100644 --- a/contrib/opencensus-ext-azure/examples/traces/custom.py +++ b/contrib/opencensus-ext-azure/examples/traces/custom.py @@ -21,7 +21,8 @@ 'TRACE': { 'SAMPLER': 'opencensus.trace.samplers.ProbabilitySampler(rate=1.0)', 'EXPORTER': '''opencensus.ext.azure.trace_exporter.AzureExporter( - instrumentation_key='00000000-0000-0000-0000-000000000000', + connection_string= + 'InstrumentationKey=00000000-0000-0000-0000-000000000000', )''', }, } diff --git a/contrib/opencensus-ext-azure/examples/traces/server.py b/contrib/opencensus-ext-azure/examples/traces/server.py index a76e5d3a2..3702e7ef3 100644 --- a/contrib/opencensus-ext-azure/examples/traces/server.py +++ b/contrib/opencensus-ext-azure/examples/traces/server.py @@ -20,8 +20,9 @@ from opencensus.trace import config_integration from opencensus.trace.samplers import ProbabilitySampler -# TODO: you need to specify the instrumentation key in the -# APPINSIGHTS_INSTRUMENTATIONKEY environment variable. +# TODO: you need to specify the instrumentation key in a connection string +# and place it in the APPLICATIONINSIGHTS_CONNECTION_STRING +# environment variable. app = Flask(__name__) middleware = FlaskMiddleware( app, diff --git a/contrib/opencensus-ext-azure/examples/traces/simple.py b/contrib/opencensus-ext-azure/examples/traces/simple.py index 49c69d6e1..b0008f464 100644 --- a/contrib/opencensus-ext-azure/examples/traces/simple.py +++ b/contrib/opencensus-ext-azure/examples/traces/simple.py @@ -16,8 +16,9 @@ from opencensus.trace.samplers import ProbabilitySampler from opencensus.trace.tracer import Tracer -# TODO: you need to specify the instrumentation key in the -# APPINSIGHTS_INSTRUMENTATIONKEY environment variable. +# TODO: you need to specify the instrumentation key in a connection string +# and place it in the APPLICATIONINSIGHTS_CONNECTION_STRING +# environment variable. tracer = Tracer(exporter=AzureExporter(), sampler=ProbabilitySampler(1.0)) with tracer.span(name='foo'): diff --git a/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py b/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py index 77d2a1be5..135d5ff8d 100644 --- a/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py +++ b/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py @@ -50,14 +50,11 @@ def parse_connection_string(connection_string): pairs = connection_string.split(';') result = dict(s.split('=') for s in pairs) except Exception: - raise ValueError('Invalid connection string: ' + connection_string) + raise ValueError('Invalid connection string') # Validate authorization auth = result.get(AUTHORIZATION) - if auth is None: - raise ValueError('Missing \'Authorization\' in connection string:' - + connection_string) - if auth.lower() != 'ikey': - raise ValueError('Invalid authorization mechanism: ' + auth) + if auth is not None and auth.lower() != 'ikey': + raise ValueError('Invalid authorization mechanism') # Construct the ingestion endpoint if not passed in explicitly if result.get(INGESTION_ENDPOINT) is None: endpoint_suffix = '' diff --git a/contrib/opencensus-ext-azure/tests/test_options.py b/contrib/opencensus-ext-azure/tests/test_options.py index 10b4eafcd..53e9231af 100644 --- a/contrib/opencensus-ext-azure/tests/test_options.py +++ b/contrib/opencensus-ext-azure/tests/test_options.py @@ -101,10 +101,10 @@ def test_parse_connection_string_invalid(self): self.assertRaises(ValueError, lambda: common.parse_connection_string(cs)) - def test_parse_connection_string_missing_auth(self): - cs = 'asd=asd' - self.assertRaises(ValueError, - lambda: common.parse_connection_string(cs)) + def test_parse_connection_string_default_auth(self): + cs = 'InstrumentationKey=123' + result = common.parse_connection_string(cs) + self.assertEqual(result['InstrumentationKey'], '123') def test_parse_connection_string_invalid_auth(self): cs = 'Authorization=asd' From a07b17e00d3c5b81946cba443d9eb256e742c79c Mon Sep 17 00:00:00 2001 From: Leighton Date: Wed, 18 Sep 2019 16:50:28 -0700 Subject: [PATCH 08/13] Address comments --- .../opencensus/ext/azure/common/__init__.py | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py b/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py index 135d5ff8d..99f61428c 100644 --- a/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py +++ b/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py @@ -17,29 +17,33 @@ from opencensus.ext.azure.common.protocol import BaseObject -AUTHORIZATION = 'Authorization' -DEFAULT_BREEZE_ENDPOINT = 'https://dc.services.visualstudio.com' -ENDPOINT_SUFFIX = 'EndpointSuffix' -ENV_CONNECTION_STRING = 'APPLICATIONINSIGHTS_CONNECTION_STRING' -ENV_INSTRUMENTATION_KEY = 'APPINSIGHTS_INSTRUMENTATIONKEY' INGESTION_ENDPOINT = 'IngestionEndpoint' INSTRUMENTATION_KEY = 'InstrumentationKey' -LOCATION = 'Location' def process_options(options): code_cs = parse_connection_string(options.connection_string) code_ikey = options.instrumentation_key - env_cs = parse_connection_string(os.getenv(ENV_CONNECTION_STRING)) - env_ikey = os.getenv(ENV_INSTRUMENTATION_KEY) + env_cs = parse_connection_string( + os.getenv('APPLICATIONINSIGHTS_CONNECTION_STRING')) + env_ikey = os.getenv('APPINSIGHTS_INSTRUMENTATIONKEY') + # The priority of which value takes on the instrumentation key is: + # 1. Key from explicitly passed in connection string + # 2. Key from explicitly passed in instrumentation key + # 3. Key from connection string in environment variable + # 4. Key from instrumentation key in environment variable options.instrumentation_key = code_cs.get(INSTRUMENTATION_KEY) \ or code_ikey \ or env_cs.get(INSTRUMENTATION_KEY) \ or env_ikey + # The priority of the ingestion endpoint is as follows: + # 1. The endpoint explicitly passed in connection string + # 2. The endpoint from the connection string in environment variable + # 3. The default breeze endpoint endpoint = code_cs.get(INGESTION_ENDPOINT) \ or env_cs.get(INGESTION_ENDPOINT) \ - or DEFAULT_BREEZE_ENDPOINT + or 'https://dc.services.visualstudio.com' options.endpoint = endpoint + '/v2/track' @@ -52,23 +56,25 @@ def parse_connection_string(connection_string): except Exception: raise ValueError('Invalid connection string') # Validate authorization - auth = result.get(AUTHORIZATION) + auth = result.get('Authorization') if auth is not None and auth.lower() != 'ikey': raise ValueError('Invalid authorization mechanism') # Construct the ingestion endpoint if not passed in explicitly if result.get(INGESTION_ENDPOINT) is None: endpoint_suffix = '' location_prefix = '' - if result.get(ENDPOINT_SUFFIX) is not None: - endpoint_suffix = result.get(ENDPOINT_SUFFIX) + suffix = result.get('EndpointSuffix') + if suffix is not None: + endpoint_suffix = suffix # Get regional information if provided - if result.get(LOCATION) is not None: - location_prefix = result.get(LOCATION) + '.' + prefix = result.get('Location') + if prefix is not None: + location_prefix = prefix + '.' endpoint = 'https://' + location_prefix + 'dc.' + endpoint_suffix result[INGESTION_ENDPOINT] = endpoint else: # Use default endpoint if cannot construct - result[INGESTION_ENDPOINT] = DEFAULT_BREEZE_ENDPOINT + result[INGESTION_ENDPOINT] = 'https://dc.services.visualstudio.com' return result From 0d300765f2a7aa0dd524f82ae6b613a04f87c968 Mon Sep 17 00:00:00 2001 From: Leighton Date: Wed, 18 Sep 2019 17:19:42 -0700 Subject: [PATCH 09/13] Fix use case --- contrib/opencensus-ext-azure/examples/logs/simple.py | 6 +++++- .../opencensus/ext/azure/common/__init__.py | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/contrib/opencensus-ext-azure/examples/logs/simple.py b/contrib/opencensus-ext-azure/examples/logs/simple.py index 1fba3d668..ad4b8fb36 100644 --- a/contrib/opencensus-ext-azure/examples/logs/simple.py +++ b/contrib/opencensus-ext-azure/examples/logs/simple.py @@ -20,5 +20,9 @@ # TODO: you need to specify the instrumentation key in a connection string # and place it in the APPLICATIONINSIGHTS_CONNECTION_STRING # environment variable. -logger.addHandler(AzureLogHandler()) +#70c241c9-206e-4811-82b4-2bc8a52170b9 +handler = AzureLogHandler(connection_string='Authorization=IKEY') +logger.addHandler(handler) +print(handler.options.instrumentation_key) +print(handler.options.endpoint) logger.warning('Hello, World!') diff --git a/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py b/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py index 99f61428c..55aab0f88 100644 --- a/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py +++ b/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py @@ -73,8 +73,8 @@ def parse_connection_string(connection_string): endpoint = 'https://' + location_prefix + 'dc.' + endpoint_suffix result[INGESTION_ENDPOINT] = endpoint else: - # Use default endpoint if cannot construct - result[INGESTION_ENDPOINT] = 'https://dc.services.visualstudio.com' + # Default to None if cannot construct + result[INGESTION_ENDPOINT] = None return result From 1e811e1bff098fba3fd877451fe205ce5a129c88 Mon Sep 17 00:00:00 2001 From: Leighton Date: Wed, 18 Sep 2019 17:27:37 -0700 Subject: [PATCH 10/13] fix test --- contrib/opencensus-ext-azure/tests/test_options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/opencensus-ext-azure/tests/test_options.py b/contrib/opencensus-ext-azure/tests/test_options.py index 53e9231af..63afa3b8c 100644 --- a/contrib/opencensus-ext-azure/tests/test_options.py +++ b/contrib/opencensus-ext-azure/tests/test_options.py @@ -123,7 +123,7 @@ def test_parse_connection_string_default(self): result = common.parse_connection_string(cs) self.assertEqual(result['IngestionEndpoint'], - 'https://dc.services.visualstudio.com') + None) def test_parse_connection_string_no_location(self): cs = 'Authorization=ikey;EndpointSuffix=suffix' From bfd81ea295eb628cc0003704802e81af5b56eea2 Mon Sep 17 00:00:00 2001 From: Leighton Date: Wed, 18 Sep 2019 17:33:05 -0700 Subject: [PATCH 11/13] Fix example --- contrib/opencensus-ext-azure/examples/logs/simple.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/contrib/opencensus-ext-azure/examples/logs/simple.py b/contrib/opencensus-ext-azure/examples/logs/simple.py index ad4b8fb36..1fba3d668 100644 --- a/contrib/opencensus-ext-azure/examples/logs/simple.py +++ b/contrib/opencensus-ext-azure/examples/logs/simple.py @@ -20,9 +20,5 @@ # TODO: you need to specify the instrumentation key in a connection string # and place it in the APPLICATIONINSIGHTS_CONNECTION_STRING # environment variable. -#70c241c9-206e-4811-82b4-2bc8a52170b9 -handler = AzureLogHandler(connection_string='Authorization=IKEY') -logger.addHandler(handler) -print(handler.options.instrumentation_key) -print(handler.options.endpoint) +logger.addHandler(AzureLogHandler()) logger.warning('Hello, World!') From 98f66ea5eb2fc9914d9633a00d9656a4f5107f13 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 24 Sep 2019 16:06:48 -0700 Subject: [PATCH 12/13] Address comments --- contrib/opencensus-ext-azure/README.rst | 54 ++++++++++--------- .../opencensus/ext/azure/common/__init__.py | 12 +++-- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/contrib/opencensus-ext-azure/README.rst b/contrib/opencensus-ext-azure/README.rst index 6d3b9d200..1d844e153 100644 --- a/contrib/opencensus-ext-azure/README.rst +++ b/contrib/opencensus-ext-azure/README.rst @@ -24,9 +24,8 @@ The **Azure Monitor Log Handler** allows you to export Python logs to `Azure Mon This example shows how to send a warning level log to Azure Monitor. * Create an Azure Monitor resource and get the instrumentation key, more information can be found `here `_. -* Place your instrumentation key in a connection string and into ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable. -* You can also put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. -* Alternatively, you can specify either the connection string or instrumentation key directly in your code, which will take priority over a set environment variable. +* Place your instrumentation key in a `connection string` and directly into your code. +* Alternatively, you can specify your `connection string` in an environment variable ``APPLICATIONINSIGHTS_CONNECTION_STRING``. .. code:: python @@ -35,15 +34,16 @@ This example shows how to send a warning level log to Azure Monitor. from opencensus.ext.azure.log_exporter import AzureLogHandler logger = logging.getLogger(__name__) - logger.addHandler(AzureLogHandler()) + logger.addHandler(AzureLogHandler(connection_string='InstrumentationKey=')) logger.warning('Hello, World!') +* Alternatively, you can specify your `connection string` in an environment variable ``APPLICATIONINSIGHTS_CONNECTION_STRING``. + You can enrich the logs with trace IDs and span IDs by using the `logging integration <../opencensus-ext-logging>`_. * Create an Azure Monitor resource and get the instrumentation key, more information can be found `here `_. -* Place your instrumentation key in a connection string and into ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable. -* You can also put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. -* Alternatively, you can specify either the connection string or instrumentation key directly in your code, which will take priority over a set environment variable. +* Place your instrumentation key in a `connection string` and directly into your code. +* Alternatively, you can specify your `connection string` in an environment variable ``APPLICATIONINSIGHTS_CONNECTION_STRING``. .. code:: python @@ -59,16 +59,19 @@ You can enrich the logs with trace IDs and span IDs by using the `logging integr logger = logging.getLogger(__name__) - handler = AzureLogHandler() + handler = AzureLogHandler(connection_string='InstrumentationKey=') handler.setFormatter(logging.Formatter('%(traceId)s %(spanId)s %(message)s')) logger.addHandler(handler) - tracer = Tracer(exporter=AzureExporter(), sampler=ProbabilitySampler(1.0)) + tracer = Tracer( + exporter=AzureExporter(connection_string='InstrumentationKey='), + sampler=ProbabilitySampler(1.0) + ) logger.warning('Before the span') with tracer.span(name='test'): logger.warning('In the span') - logger.warning('After the span') + logger.warning('After the span')s Metrics ~~~~~~~ @@ -76,9 +79,8 @@ Metrics The **Azure Monitor Metrics Exporter** allows you to export metrics to `Azure Monitor`_. * Create an Azure Monitor resource and get the instrumentation key, more information can be found `here `_. -* Place your instrumentation key in a connection string and into ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable. -* You can also put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. -* Alternatively, you can specify either the connection string or instrumentation key directly in your code, which will take priority over a set environment variable. +* Place your instrumentation key in a `connection string` and directly into your code. +* Alternatively, you can specify your `connection string` in an environment variable ``APPLICATIONINSIGHTS_CONNECTION_STRING``. .. code:: python @@ -107,7 +109,7 @@ The **Azure Monitor Metrics Exporter** allows you to export metrics to `Azure Mo def main(): # Enable metrics # Set the interval in seconds in which you want to send metrics - exporter = metrics_exporter.new_metrics_exporter() + exporter = metrics_exporter.new_metrics_exporter(connection_string='InstrumentationKey=') view_manager.register_exporter(exporter) view_manager.register_view(CARROTS_VIEW) @@ -140,7 +142,7 @@ The exporter also includes a set of standard metrics that are exported to Azure # All you need is the next line. You can disable standard metrics by # passing in enable_standard_metrics=False into the constructor of # new_metrics_exporter() - _exporter = metrics_exporter.new_metrics_exporter() + _exporter = metrics_exporter.new_metrics_exporter(connection_string='InstrumentationKey=') for i in range(100): print(psutil.virtual_memory()) @@ -169,27 +171,31 @@ The **Azure Monitor Trace Exporter** allows you to export `OpenCensus`_ traces t This example shows how to send a span "hello" to Azure Monitor. * Create an Azure Monitor resource and get the instrumentation key, more information can be found `here `_. -* Place your instrumentation key in a connection string and into ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable. -* You can also put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. -* Alternatively, you can specify either the connection string or instrumentation key directly in your code, which will take priority over a set environment variable. +* Place your instrumentation key in a `connection string` and directly into your code. +* Alternatively, you can specify your `connection string` in an environment variable ``APPLICATIONINSIGHTS_CONNECTION_STRING``. -.. code:: python + .. code:: python from opencensus.ext.azure.trace_exporter import AzureExporter from opencensus.trace.samplers import ProbabilitySampler from opencensus.trace.tracer import Tracer - tracer = Tracer(exporter=AzureExporter(), sampler=ProbabilitySampler(1.0)) + tracer = Tracer( + exporter=AzureExporter(connection_string='InstrumentationKey='), + sampler=ProbabilitySampler(1.0) + ) with tracer.span(name='hello'): print('Hello, World!') -You can also specify the instrumentation key explicitly in the code. +OpenCensus also supports several [integrations](https://github.com/census-instrumentation/opencensus-python#integration) which allows OpenCensus to integrate with third party libraries. + +This example shows how to integrate with the [requests](https://2.python-requests.org/en/master/) library. * Create an Azure Monitor resource and get the instrumentation key, more information can be found `here `_. -* Place your instrumentation key in a connection string and into ``APPLICATIONINSIGHTS_CONNECTION_STRING`` environment variable. -* You can also put the instrumentation key in ``APPINSIGHTS_INSTRUMENTATIONKEY`` environment variable. -* Alternatively, you can specify either the connection string or instrumentation key directly in your code, which will take priority over a set environment variable. +* Install the `requests integration package <../opencensus-ext-requests>`_ using ``pip install opencensus-ext-requests``. +* Place your instrumentation key in a `connection string` and directly into your code. +* Alternatively, you can specify your `connection string` in an environment variable ``APPLICATIONINSIGHTS_CONNECTION_STRING``. .. code:: python diff --git a/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py b/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py index 55aab0f88..754e2a27c 100644 --- a/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py +++ b/contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py @@ -17,8 +17,8 @@ from opencensus.ext.azure.common.protocol import BaseObject -INGESTION_ENDPOINT = 'IngestionEndpoint' -INSTRUMENTATION_KEY = 'InstrumentationKey' +INGESTION_ENDPOINT = 'ingestionendpoint' +INSTRUMENTATION_KEY = 'instrumentationkey' def process_options(options): @@ -53,21 +53,23 @@ def parse_connection_string(connection_string): try: pairs = connection_string.split(';') result = dict(s.split('=') for s in pairs) + # Convert keys to lower-case due to case type-insensitive checking + result = {key.lower(): value for key, value in result.items()} except Exception: raise ValueError('Invalid connection string') # Validate authorization - auth = result.get('Authorization') + auth = result.get('authorization') if auth is not None and auth.lower() != 'ikey': raise ValueError('Invalid authorization mechanism') # Construct the ingestion endpoint if not passed in explicitly if result.get(INGESTION_ENDPOINT) is None: endpoint_suffix = '' location_prefix = '' - suffix = result.get('EndpointSuffix') + suffix = result.get('endpointsuffix') if suffix is not None: endpoint_suffix = suffix # Get regional information if provided - prefix = result.get('Location') + prefix = result.get('location') if prefix is not None: location_prefix = prefix + '.' endpoint = 'https://' + location_prefix + 'dc.' + endpoint_suffix From 81d573f8aebc001cc74558657edddd88fdb62302 Mon Sep 17 00:00:00 2001 From: Leighton Date: Tue, 24 Sep 2019 16:16:49 -0700 Subject: [PATCH 13/13] include tests --- contrib/opencensus-ext-azure/tests/test_options.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contrib/opencensus-ext-azure/tests/test_options.py b/contrib/opencensus-ext-azure/tests/test_options.py index 63afa3b8c..5c16d9c6c 100644 --- a/contrib/opencensus-ext-azure/tests/test_options.py +++ b/contrib/opencensus-ext-azure/tests/test_options.py @@ -104,7 +104,7 @@ def test_parse_connection_string_invalid(self): def test_parse_connection_string_default_auth(self): cs = 'InstrumentationKey=123' result = common.parse_connection_string(cs) - self.assertEqual(result['InstrumentationKey'], '123') + self.assertEqual(result['instrumentationkey'], '123') def test_parse_connection_string_invalid_auth(self): cs = 'Authorization=asd' @@ -116,23 +116,23 @@ def test_parse_connection_string_explicit_endpoint(self): 'Location=us;EndpointSuffix=suffix' result = common.parse_connection_string(cs) - self.assertEqual(result['IngestionEndpoint'], '123') + self.assertEqual(result['ingestionendpoint'], '123') def test_parse_connection_string_default(self): cs = 'Authorization=ikey;Location=us' result = common.parse_connection_string(cs) - self.assertEqual(result['IngestionEndpoint'], + self.assertEqual(result['ingestionendpoint'], None) def test_parse_connection_string_no_location(self): cs = 'Authorization=ikey;EndpointSuffix=suffix' result = common.parse_connection_string(cs) - self.assertEqual(result['IngestionEndpoint'], 'https://dc.suffix') + self.assertEqual(result['ingestionendpoint'], 'https://dc.suffix') def test_parse_connection_string_location(self): cs = 'Authorization=ikey;EndpointSuffix=suffix;Location=us' result = common.parse_connection_string(cs) - self.assertEqual(result['IngestionEndpoint'], 'https://us.dc.suffix') + self.assertEqual(result['ingestionendpoint'], 'https://us.dc.suffix')