Skip to content
This repository was archived by the owner on Sep 17, 2025. It is now read-only.

Commit 4cb6f5a

Browse files
authored
Implement feature/instrumentation statsbeat (#1076)
1 parent ad0b2b8 commit 4cb6f5a

File tree

12 files changed

+250
-109
lines changed

12 files changed

+250
-109
lines changed

contrib/opencensus-ext-azure/CHANGELOG.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44

55
- Enable AAD authorization via TokenCredential
66
([#1021](https://github.com/census-instrumentation/opencensus-python/pull/1021))
7-
- Implement attach rate metrics via Statbeat
7+
- Implement attach rate metrics via Statsbeat
88
([#1053](https://github.com/census-instrumentation/opencensus-python/pull/1053))
9-
- Implement network metrics via Statbeat - Success count
9+
- Implement network metrics via Statsbeat - Success count
1010
([#1059](https://github.com/census-instrumentation/opencensus-python/pull/1059))
11-
- Implement network metrics via Statbeat - Others
11+
- Implement network metrics via Statsbeat - Others
1212
([#1062](https://github.com/census-instrumentation/opencensus-python/pull/1062))
13+
- Implement feature and instrumentation metrics via Statsbeat
14+
([#1076](https://github.com/census-instrumentation/opencensus-python/pull/1076))
1315

1416
## 1.0.8
1517
Released 2021-05-13

contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ def __init__(self, *args, **kwargs):
111111
credential=None, # Credential class used by AAD auth
112112
enable_local_storage=True,
113113
enable_standard_metrics=True, # Used by metrics exporter, True to send standard metrics # noqa: E501
114-
enable_stats_metrics=True, # True to send stats metrics
115114
endpoint='https://dc.services.visualstudio.com/v2/track',
116115
export_interval=15.0,
117116
grace_period=5.0,

contrib/opencensus-ext-azure/opencensus/ext/azure/common/transport.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import json
1616
import logging
17+
import os
1718
import threading
1819
import time
1920

@@ -29,8 +30,7 @@
2930

3031
class TransportMixin(object):
3132
def _check_stats_collection(self):
32-
return self.options.enable_stats_metrics and \
33-
(not hasattr(self, '_is_stats') or not self._is_stats)
33+
return not os.environ.get("APPLICATIONINSIGHTS_STATSBEAT_DISABLED_ALL") and (not hasattr(self, '_is_stats') or not self._is_stats) # noqa: E501
3434

3535
def _transmit_from_storage(self):
3636
if self.storage:

contrib/opencensus-ext-azure/opencensus/ext/azure/log_exporter/__init__.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414

1515
import logging
16+
import os
1617
import random
1718
import threading
1819
import time
@@ -63,10 +64,8 @@ def __init__(self, **options):
6364
self._worker = Worker(self._queue, self)
6465
self._worker.start()
6566
# start statsbeat on exporter instantiation
66-
if self.options.enable_stats_metrics:
67-
statsbeat_metrics.collect_statsbeat_metrics(
68-
self.options.instrumentation_key
69-
)
67+
if not os.environ.get("APPLICATIONINSIGHTS_STATSBEAT_DISABLED_ALL"):
68+
statsbeat_metrics.collect_statsbeat_metrics(self.options)
7069

7170
def _export(self, batch, event=None): # pragma: NO COVER
7271
try:

contrib/opencensus-ext-azure/opencensus/ext/azure/metrics_exporter/__init__.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import atexit
1616
import logging
17+
import os
1718

1819
from opencensus.common import utils as common_utils
1920
from opencensus.ext.azure.common import Options, utils
@@ -162,10 +163,8 @@ def new_metrics_exporter(**options):
162163
producers,
163164
exporter,
164165
interval=exporter.options.export_interval)
165-
if exporter.options.enable_stats_metrics:
166+
if not os.environ.get("APPLICATIONINSIGHTS_STATSBEAT_DISABLED_ALL"):
166167
from opencensus.ext.azure.metrics_exporter import statsbeat_metrics
167168
# Stats will track the user's ikey
168-
statsbeat_metrics.collect_statsbeat_metrics(
169-
exporter.options.instrumentation_key
170-
)
169+
statsbeat_metrics.collect_statsbeat_metrics(exporter.options)
171170
return exporter

contrib/opencensus-ext-azure/opencensus/ext/azure/metrics_exporter/statsbeat_metrics/__init__.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
_STATSBEAT_LOCK = threading.Lock()
2828

2929

30-
def collect_statsbeat_metrics(ikey):
30+
def collect_statsbeat_metrics(options):
3131
with _STATSBEAT_LOCK:
3232
# Only start statsbeat if did not exist before
3333
global _STATSBEAT_METRICS # pylint: disable=global-statement
@@ -39,9 +39,7 @@ def collect_statsbeat_metrics(ikey):
3939
export_interval=_STATS_SHORT_EXPORT_INTERVAL, # 15m by default
4040
)
4141
# The user's ikey is the one being tracked
42-
producer = _AzureStatsbeatMetricsProducer(
43-
instrumentation_key=ikey
44-
)
42+
producer = _AzureStatsbeatMetricsProducer(options)
4543
_STATSBEAT_METRICS = producer
4644
# Export some initial stats on program start
4745
exporter.export_metrics(_STATSBEAT_METRICS.get_initial_metrics())
@@ -54,10 +52,11 @@ def collect_statsbeat_metrics(ikey):
5452
class _AzureStatsbeatMetricsProducer(MetricProducer):
5553
"""Implementation of the producer of statsbeat metrics.
5654
57-
Includes Azure attach rate metrics, implemented using gauges.
55+
Includes Azure attach rate, network and feature metrics,
56+
implemented using gauges.
5857
"""
59-
def __init__(self, instrumentation_key):
60-
self._statsbeat = _StatsbeatMetrics(instrumentation_key)
58+
def __init__(self, options):
59+
self._statsbeat = _StatsbeatMetrics(options)
6160

6261
def get_metrics(self):
6362
return self._statsbeat.get_metrics()

contrib/opencensus-ext-azure/opencensus/ext/azure/metrics_exporter/statsbeat_metrics/statsbeat.py

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
)
3131
from opencensus.metrics.label_key import LabelKey
3232
from opencensus.metrics.label_value import LabelValue
33+
from opencensus.trace.integrations import get_integrations
3334

3435
_AIMS_URI = "http://169.254.169.254/metadata/instance/compute"
3536
_AIMS_API_VERSION = "api-version=2017-12-01"
@@ -40,18 +41,31 @@
4041
_DEFAULT_STATS_LONG_EXPORT_INTERVAL = 86400 # 24 hours
4142

4243
_ATTACH_METRIC_NAME = "Attach"
44+
_FEATURE_METRIC_NAME = "Feature"
4345
_REQ_SUC_COUNT_NAME = "Request Success Count"
4446
_REQ_FAIL_COUNT_NAME = "Request Failure Count"
4547
_REQ_DURATION_NAME = "Request Duration"
4648
_REQ_RETRY_NAME = "Request Retry Count"
4749
_REQ_THROTTLE_NAME = "Request Throttle Count"
4850
_REQ_EXCEPTION_NAME = "Request Exception Count"
4951

50-
_RP_NAMES = ["appsvc", "function", "vm", "unknown"]
52+
_ENDPOINT_TYPES = ["breeze"]
53+
_RP_NAMES = ["appsvc", "functions", "vm", "unknown"]
5154

5255
_logger = logging.getLogger(__name__)
5356

5457

58+
class _FEATURE_TYPES:
59+
FEATURE = 0
60+
INSTRUMENTATION = 1
61+
62+
63+
class _StatsbeatFeature:
64+
NONE = 0
65+
DISK_RETRY = 1
66+
AAD_HANDLING = 2
67+
68+
5569
def _get_stats_connection_string():
5670
cs_env = os.environ.get("APPLICATION_INSIGHTS_STATS_CONNECTION_STRING")
5771
if cs_env:
@@ -103,6 +117,15 @@ def _get_attach_properties():
103117

104118
def _get_network_properties():
105119
properties = _get_common_properties()
120+
properties.append(LabelKey("endpoint", "ingestion endpoint type"))
121+
properties.append(LabelKey("host", "destination of ingestion endpoint"))
122+
return properties
123+
124+
125+
def _get_feature_properties():
126+
properties = _get_common_properties()
127+
properties.insert(4, LabelKey("feature", 'represents enabled features'))
128+
properties.insert(4, LabelKey("type", 'type, either feature or instrumentation')) # noqa: E501
106129
return properties
107130

108131

@@ -162,8 +185,12 @@ def _get_exception_count_value():
162185

163186
class _StatsbeatMetrics:
164187

165-
def __init__(self, instrumentation_key):
166-
self._instrumentation_key = instrumentation_key
188+
def __init__(self, options):
189+
self._options = options
190+
self._instrumentation_key = options.instrumentation_key
191+
self._feature = _StatsbeatFeature.NONE
192+
if options.enable_local_storage:
193+
self._feature |= _StatsbeatFeature.DISK_RETRY
167194
self._stats_lock = threading.Lock()
168195
self._vm_data = {}
169196
self._vm_retry = True
@@ -218,6 +245,21 @@ def __init__(self, instrumentation_key):
218245
'count',
219246
_get_network_properties(),
220247
)
248+
# feature/instrumentation metrics
249+
# metrics related to what features and instrumentations are enabled
250+
self._feature_metric = LongGauge(
251+
_FEATURE_METRIC_NAME,
252+
'Statsbeat metric related to features enabled', # noqa: E501
253+
'count',
254+
_get_feature_properties(),
255+
)
256+
# Instrumentation metric uses same name/properties as feature
257+
self._instrumentation_metric = LongGauge(
258+
_FEATURE_METRIC_NAME,
259+
'Statsbeat metric related to instrumentations enabled', # noqa: E501
260+
'count',
261+
_get_feature_properties(),
262+
)
221263

222264
# Metrics that are sent on application start
223265
def get_initial_metrics(self):
@@ -226,6 +268,14 @@ def get_initial_metrics(self):
226268
attach_metric = self._get_attach_metric()
227269
if attach_metric:
228270
stats_metrics.append(attach_metric)
271+
if self._feature_metric:
272+
feature_metric = self._get_feature_metric()
273+
if feature_metric:
274+
stats_metrics.append(feature_metric)
275+
if self._instrumentation_metric:
276+
instr_metric = self._get_instrumentation_metric()
277+
if instr_metric:
278+
stats_metrics.append(instr_metric)
229279
return stats_metrics
230280

231281
# Metrics sent every statsbeat interval
@@ -248,6 +298,8 @@ def get_metrics(self):
248298

249299
def _get_network_metrics(self):
250300
properties = self._get_common_properties()
301+
properties.append(LabelValue(_ENDPOINT_TYPES[0])) # endpoint
302+
properties.append(LabelValue(self._options.endpoint)) # host
251303
metrics = []
252304
for fn, metric in self._network_metrics.items():
253305
# NOTE: A time series is a set of unique label values
@@ -260,6 +312,20 @@ def _get_network_metrics(self):
260312
metrics.append(stats_metric)
261313
return metrics
262314

315+
def _get_feature_metric(self):
316+
properties = self._get_common_properties()
317+
properties.insert(4, LabelValue(self._feature)) # feature long
318+
properties.insert(4, LabelValue(_FEATURE_TYPES.FEATURE)) # type
319+
self._feature_metric.get_or_create_time_series(properties)
320+
return self._feature_metric.get_metric(datetime.datetime.utcnow())
321+
322+
def _get_instrumentation_metric(self):
323+
properties = self._get_common_properties()
324+
properties.insert(4, LabelValue(get_integrations())) # instr long
325+
properties.insert(4, LabelValue(_FEATURE_TYPES.INSTRUMENTATION)) # type # noqa: E501
326+
self._instrumentation_metric.get_or_create_time_series(properties)
327+
return self._instrumentation_metric.get_metric(datetime.datetime.utcnow()) # noqa: E501
328+
263329
def _get_attach_metric(self):
264330
properties = []
265331
rp = ''
@@ -303,9 +369,7 @@ def _get_common_properties(self):
303369
properties.append(LabelValue(platform.python_version()))
304370
properties.append(LabelValue(self._os_type or platform.system())) # os
305371
properties.append(LabelValue("python")) # language
306-
# version
307-
properties.append(
308-
LabelValue(ext_version))
372+
properties.append(LabelValue(ext_version)) # version
309373
return properties
310374

311375
def _get_azure_compute_metadata(self):

contrib/opencensus-ext-azure/opencensus/ext/azure/trace_exporter/__init__.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import atexit
1616
import json
1717
import logging
18+
import os
1819

1920
from opencensus.common.schedule import QueueExitEvent
2021
from opencensus.ext.azure.common import Options, utils
@@ -74,10 +75,8 @@ def __init__(self, **options):
7475
super(AzureExporter, self).__init__(**options)
7576
atexit.register(self._stop, self.options.grace_period)
7677
# start statsbeat on exporter instantiation
77-
if self.options.enable_stats_metrics:
78-
statsbeat_metrics.collect_statsbeat_metrics(
79-
self.options.instrumentation_key
80-
)
78+
if not os.environ.get("APPLICATIONINSIGHTS_STATSBEAT_DISABLED_ALL"):
79+
statsbeat_metrics.collect_statsbeat_metrics(self.options)
8180

8281
def span_data_to_envelope(self, sd):
8382
envelope = Envelope(

0 commit comments

Comments
 (0)