Skip to content

Commit

Permalink
Add aks to statsbeat and prefix (#34427)
Browse files Browse the repository at this point in the history
* Add aks to statsbeat and prefix

* Fixed tests to run on vms
  • Loading branch information
jeremydvoss authored Feb 23, 2024
1 parent 9cda4c0 commit 56c346a
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 47 deletions.
2 changes: 2 additions & 0 deletions sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

- Add attachType character to sdkVersion prefix
([#34226](https://github.com/Azure/azure-sdk-for-python/pull/34226))
- Add AKS scenarios to statsbeat metric and sdkVersion prefix
([#34427](https://github.com/Azure/azure-sdk-for-python/pull/34427))

## 1.0.0b22 (2024-02-01)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@
# Licensed under the MIT License.
# cSpell:disable

from opentelemetry.semconv.metrics import MetricInstruments

# Environment variables

_APPLICATIONINSIGHTS_STATSBEAT_DISABLED_ALL = "APPLICATIONINSIGHTS_STATSBEAT_DISABLED_ALL"
_APPLICATIONINSIGHTS_OPENTELEMETRY_RESOURCE_METRIC_DISABLED = \
"APPLICATIONINSIGHTS_OPENTELEMETRY_RESOURCE_METRIC_DISABLED"
_WEBSITE_SITE_NAME = "WEBSITE_SITE_NAME"
_WEBSITE_HOME_STAMPNAME = "WEBSITE_HOME_STAMPNAME"
_WEBSITE_HOSTNAME = "WEBSITE_HOSTNAME"
_FUNCTIONS_WORKER_RUNTIME = "FUNCTIONS_WORKER_RUNTIME"
_AKS_ARM_NAMESPACE_ID = "AKS_ARM_NAMESPACE_ID"

# Network

Expand Down Expand Up @@ -132,15 +139,14 @@
# Standard metrics

# List of metric instrument names that are autocollected from instrumentations
# TODO: switch to semconv constants
_AUTOCOLLECTED_INSTRUMENT_NAMES = (
"http.server.duration",
"http.server.request.size",
"http.server.response.size",
"http.server.active_requests",
"http.client.duration",
"http.client.request.size",
"http.client.response.size",
MetricInstruments.HTTP_SERVER_DURATION,
MetricInstruments.HTTP_SERVER_REQUEST_SIZE,
MetricInstruments.HTTP_SERVER_RESPONSE_SIZE,
MetricInstruments.HTTP_SERVER_ACTIVE_REQUESTS,
MetricInstruments.HTTP_CLIENT_DURATION,
MetricInstruments.HTTP_CLIENT_REQUEST_SIZE,
MetricInstruments.HTTP_CLIENT_RESPONSE_SIZE,
)

# Temporary solution for checking which instrumentations support metric collection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@

from azure.monitor.opentelemetry.exporter._generated.models import ContextTagKeys, TelemetryItem
from azure.monitor.opentelemetry.exporter._version import VERSION as ext_version
from azure.monitor.opentelemetry.exporter._constants import _INSTRUMENTATIONS_BIT_MAP
from azure.monitor.opentelemetry.exporter._constants import (
_INSTRUMENTATIONS_BIT_MAP,
_WEBSITE_SITE_NAME,
_FUNCTIONS_WORKER_RUNTIME,
_AKS_ARM_NAMESPACE_ID,
)


opentelemetry_version = ""
Expand All @@ -35,16 +40,24 @@
).version


# Azure App Service

def _is_on_app_service():
return environ.get("WEBSITE_SITE_NAME") is not None
return environ.get(_WEBSITE_SITE_NAME) is not None

def _is_on_functions():
return environ.get("FUNCTIONS_WORKER_RUNTIME") is not None
return environ.get(_FUNCTIONS_WORKER_RUNTIME) is not None

def _is_attach_enabled():
return isdir("/agents/python/")


# AKS

def _is_on_aks():
return _AKS_ARM_NAMESPACE_ID in environ


def _get_sdk_version_prefix():
sdk_version_prefix = ''
rp = 'u'
Expand All @@ -55,9 +68,8 @@ def _get_sdk_version_prefix():
# TODO: Add VM scenario outside statsbeat
# elif _is_on_vm():
# rp = 'v'
# TODO: Add AKS scenario
# elif _is_on_aks():
# rp = 'k'
elif _is_on_aks():
rp = 'k'

os = 'u'
system = platform.system()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
from enum import Enum
import json
import os
import platform
Expand All @@ -23,6 +24,10 @@
_REQ_RETRY_NAME,
_REQ_SUCCESS_NAME,
_REQ_THROTTLE_NAME,
_WEBSITE_HOME_STAMPNAME,
_WEBSITE_HOSTNAME,
_WEBSITE_SITE_NAME,
_AKS_ARM_NAMESPACE_ID,
)
from azure.monitor.opentelemetry.exporter.statsbeat._state import (
_REQUESTS_MAP_LOCK,
Expand All @@ -38,7 +43,12 @@
_AIMS_FORMAT = "format=json"

_ENDPOINT_TYPES = ["breeze"]
_RP_NAMES = ["appsvc", "functions", "vm", "unknown"]
class _RP_Names(Enum):
APP_SERVICE = "appsvc"
FUNCTIONS = "functions"
AKS = "aks"
VM = "vm"
UNKNOWN = "unknown"

_HOST_PATTERN = re.compile('^https?://(?:www\\.)?([^/.]+)')

Expand Down Expand Up @@ -66,7 +76,7 @@ class _AttachTypes:
class _StatsbeatMetrics:

_COMMON_ATTRIBUTES: Dict[str, Any] = {
"rp": _RP_NAMES[3],
"rp": _RP_Names.UNKNOWN.value,
"attach": _AttachTypes.MANUAL,
"cikey": None,
"runtimeVersion": platform.python_version(),
Expand Down Expand Up @@ -161,27 +171,30 @@ def _get_attach_metric(self, options: CallbackOptions) -> Iterable[Observation]:
# rp, rpId
if _utils._is_on_app_service():
# Web apps
rp = _RP_NAMES[0]
rp = _RP_Names.APP_SERVICE.value
rpId = '{}/{}'.format(
os.environ.get("WEBSITE_SITE_NAME"),
os.environ.get("WEBSITE_HOME_STAMPNAME", '')
os.environ.get(_WEBSITE_SITE_NAME),
os.environ.get(_WEBSITE_HOME_STAMPNAME, '')
)
elif _utils._is_on_functions():
# Function apps
rp = _RP_NAMES[1]
rpId = os.environ.get("WEBSITE_HOSTNAME", '')
rp = _RP_Names.FUNCTIONS.value
rpId = os.environ.get(_WEBSITE_HOSTNAME, '')
elif _utils._is_on_aks():
# AKS
rp = _RP_Names.AKS.value
rpId = os.environ.get(_AKS_ARM_NAMESPACE_ID, '')
elif self._vm_retry and self._get_azure_compute_metadata():
# VM
rp = _RP_NAMES[2]
rp = _RP_Names.VM.value
rpId = '{}/{}'.format(
self._vm_data.get("vmId", ''),
self._vm_data.get("subscriptionId", ''))
os_type = self._vm_data.get("osType", '')
# TODO: add AKS scenario
else:
# Not in any rp or VM metadata failed
rp = _RP_NAMES[3]
rpId = _RP_NAMES[3]
rp = _RP_Names.UNKNOWN.value
rpId = _RP_Names.UNKNOWN.value

_StatsbeatMetrics._COMMON_ATTRIBUTES["rp"] = rp
_StatsbeatMetrics._COMMON_ATTRIBUTES["os"] = os_type or platform.system()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
_StatsbeatFeature,
_StatsbeatMetrics,
_AttachTypes,
_RP_NAMES,
_RP_Names,
)

class MockResponse(object):
Expand Down Expand Up @@ -334,7 +334,7 @@ def test_statsbeat_metric_init(self):
self.assertEqual(_StatsbeatMetrics._COMMON_ATTRIBUTES["cikey"], ikey)
self.assertEqual(_StatsbeatMetrics._COMMON_ATTRIBUTES["attach"], _AttachTypes.MANUAL)
self.assertEqual(_StatsbeatMetrics._NETWORK_ATTRIBUTES["host"], "westus-1")
self.assertEqual(_StatsbeatMetrics._COMMON_ATTRIBUTES["rp"], _RP_NAMES[3])
self.assertEqual(_StatsbeatMetrics._COMMON_ATTRIBUTES["rp"], _RP_Names.UNKNOWN.value)
self.assertEqual(_StatsbeatMetrics._FEATURE_ATTRIBUTES["feature"], 1)
self.assertEqual(_StatsbeatMetrics._FEATURE_ATTRIBUTES["type"], _FEATURE_TYPES.FEATURE)
self.assertEqual(metric._meter_provider, mp)
Expand Down Expand Up @@ -368,7 +368,7 @@ def test_statsbeat_metric_init_attach_enabled(self, attach_mock):
self.assertEqual(_StatsbeatMetrics._COMMON_ATTRIBUTES["cikey"], ikey)
self.assertEqual(_StatsbeatMetrics._COMMON_ATTRIBUTES["attach"], _AttachTypes.INTEGRATED)
self.assertEqual(_StatsbeatMetrics._NETWORK_ATTRIBUTES["host"], "westus-1")
self.assertEqual(_StatsbeatMetrics._COMMON_ATTRIBUTES["rp"], _RP_NAMES[3])
self.assertEqual(_StatsbeatMetrics._COMMON_ATTRIBUTES["rp"], _RP_Names.UNKNOWN.value)
self.assertEqual(_StatsbeatMetrics._FEATURE_ATTRIBUTES["feature"], 1)
self.assertEqual(_StatsbeatMetrics._FEATURE_ATTRIBUTES["type"], _FEATURE_TYPES.FEATURE)
self.assertEqual(metric._meter_provider, mp)
Expand Down Expand Up @@ -430,16 +430,17 @@ def test_get_attach_metric_does_not_meet_threshold(self):
"WEBSITE_HOME_STAMPNAME": "stamp_name",
}
)
def test_get_attach_metric_appsvc(self):
@mock.patch("azure.monitor.opentelemetry.exporter.statsbeat._statsbeat_metrics._StatsbeatMetrics._get_azure_compute_metadata", return_value=False)
def test_get_attach_metric_appsvc(self, metadata_mock):
attributes = dict(_StatsbeatMetrics._COMMON_ATTRIBUTES)
self.assertEqual(attributes["rp"], _RP_NAMES[3])
attributes["rp"] = _RP_NAMES[0]
self.assertEqual(attributes["rp"], _RP_Names.UNKNOWN.value)
attributes["rp"] = _RP_Names.APP_SERVICE.value
attributes["rpId"] = "site_name/stamp_name"
observations = self._metric._get_attach_metric(options=None)
for obs in observations:
self.assertEqual(obs.value, 1)
self.assertEqual(obs.attributes, attributes)
self.assertEqual(_StatsbeatMetrics._COMMON_ATTRIBUTES["rp"], _RP_NAMES[0])
self.assertEqual(_StatsbeatMetrics._COMMON_ATTRIBUTES["rp"], _RP_Names.APP_SERVICE.value)

# pylint: disable=protected-access
@mock.patch.dict(
Expand All @@ -449,18 +450,38 @@ def test_get_attach_metric_appsvc(self):
"WEBSITE_HOSTNAME": "host_name",
}
)
def test_get_attach_metric_functions(self):
@mock.patch("azure.monitor.opentelemetry.exporter.statsbeat._statsbeat_metrics._StatsbeatMetrics._get_azure_compute_metadata", return_value=False)
def test_get_attach_metric_functions(self, metadata_mock):
attributes = dict(_StatsbeatMetrics._COMMON_ATTRIBUTES)
self.assertEqual(attributes["rp"], _RP_NAMES[3])
attributes["rp"] = _RP_NAMES[1]
self.assertEqual(attributes["rp"], _RP_Names.UNKNOWN.value)
attributes["rp"] = _RP_Names.FUNCTIONS.value
attributes["rpId"] = "host_name"
observations = self._metric._get_attach_metric(options=None)
for obs in observations:
self.assertEqual(obs.value, 1)
self.assertEqual(obs.attributes, attributes)
self.assertEqual(_StatsbeatMetrics._COMMON_ATTRIBUTES["rp"], _RP_NAMES[1])
self.assertEqual(_StatsbeatMetrics._COMMON_ATTRIBUTES["rp"], _RP_Names.FUNCTIONS.value)

def test_get_attach_metric_vm(self):
# pylint: disable=protected-access
@mock.patch.dict(
os.environ,
{
"AKS_ARM_NAMESPACE_ID": "namespace_id",
}
)
def test_get_attach_metric_aks(self):
attributes = dict(_StatsbeatMetrics._COMMON_ATTRIBUTES)
self.assertEqual(attributes["rp"], _RP_Names.UNKNOWN.value)
attributes["rp"] = _RP_Names.AKS.value
attributes["rpId"] = "namespace_id"
observations = self._metric._get_attach_metric(options=None)
for obs in observations:
self.assertEqual(obs.value, 1)
self.assertEqual(obs.attributes, attributes)
self.assertEqual(_StatsbeatMetrics._COMMON_ATTRIBUTES["rp"], _RP_Names.AKS.value)

@mock.patch("azure.monitor.opentelemetry.exporter.statsbeat._statsbeat_metrics._StatsbeatMetrics._get_azure_compute_metadata")
def test_get_attach_metric_vm(self, metadata_mock):
mp = MeterProvider()
ikey = "1aa11111-bbbb-1ccc-8ddd-eeeeffff3334"
endpoint = "https://westus-1.in.applicationinsights.azure.com/"
Expand All @@ -478,23 +499,22 @@ def test_get_attach_metric_vm(self):
_vm_data["osType"] = "test_os"
metric._vm_data = _vm_data
metric._vm_retry = True
metadata_mock = mock.Mock()
metadata_mock.return_value = True
metric._get_azure_compute_metadata = metadata_mock
attributes = dict(_StatsbeatMetrics._COMMON_ATTRIBUTES)
self.assertEqual(attributes["rp"], _RP_NAMES[3])
self.assertEqual(attributes["rp"], _RP_Names.UNKNOWN.value)
self.assertEqual(attributes["os"], platform.system())
attributes["rp"] = _RP_NAMES[2]
attributes["rp"] = _RP_Names.VM.value
attributes["rpId"] = "123/sub123"
attributes["os"] = "test_os"
observations = metric._get_attach_metric(options=None)
for obs in observations:
self.assertEqual(obs.value, 1)
self.assertEqual(obs.attributes, attributes)
self.assertEqual(_StatsbeatMetrics._COMMON_ATTRIBUTES["rp"], _RP_NAMES[2])
self.assertEqual(_StatsbeatMetrics._COMMON_ATTRIBUTES["rp"], _RP_Names.VM.value)
self.assertEqual(_StatsbeatMetrics._COMMON_ATTRIBUTES["os"], "test_os")

def test_get_attach_metric_vm_no_os(self):
@mock.patch("azure.monitor.opentelemetry.exporter.statsbeat._statsbeat_metrics._StatsbeatMetrics._get_azure_compute_metadata", return_value=False)
def test_get_attach_metric_vm_no_os(self, metadata_mock):
mp = MeterProvider()
ikey = "1aa11111-bbbb-1ccc-8ddd-eeeeffff3334"
endpoint = "https://westus-1.in.applicationinsights.azure.com/"
Expand All @@ -512,10 +532,8 @@ def test_get_attach_metric_vm_no_os(self):
_vm_data["osType"] = None
metric._vm_data = _vm_data
metric._vm_retry = True
metadata_mock = mock.Mock()
metadata_mock.return_value = True
self.assertEqual(_StatsbeatMetrics._COMMON_ATTRIBUTES["os"], platform.system())
metric._get_azure_compute_metadata = metadata_mock
observations = metric._get_attach_metric(options=None)
for obs in observations:
self.assertEqual(obs.value, 1)
Expand All @@ -536,11 +554,11 @@ def test_get_attach_metric_unknown(self):
)
metric._vm_retry = False
attributes = dict(_StatsbeatMetrics._COMMON_ATTRIBUTES)
self.assertEqual(attributes["rp"], _RP_NAMES[3])
self.assertEqual(attributes["rp"], _RP_Names.UNKNOWN.value)
observations = metric._get_attach_metric(options=None)
for obs in observations:
self.assertEqual(obs.value, 1)
self.assertEqual(obs.attributes["rp"], _RP_NAMES[3])
self.assertEqual(obs.attributes["rp"], _RP_Names.UNKNOWN.value)

def test_get_azure_compute_metadata(self):
mp = MeterProvider()
Expand Down

0 comments on commit 56c346a

Please sign in to comment.