Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,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,11 @@
_REQ_RETRY_NAME,
_REQ_SUCCESS_NAME,
_REQ_THROTTLE_NAME,
_WEBSITE_HOME_STAMPNAME,
_WEBSITE_HOSTNAME,
_WEBSITE_SITE_NAME,
_FUNCTIONS_WORKER_RUNTIME,
_AKS_ARM_NAMESPACE_ID,
)
from azure.monitor.opentelemetry.exporter.statsbeat._state import (
_REQUESTS_MAP_LOCK,
Expand All @@ -38,7 +44,12 @@
_AIMS_FORMAT = "format=json"

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

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

Expand Down Expand Up @@ -66,7 +77,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 +172,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 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
elif _utils._is_on_aks():
# AKS
rp = _RP_Names.AKS.value
rpId = os.environ.get(_AKS_ARM_NAMESPACE_ID, '')
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 @@ -432,14 +432,14 @@ def test_get_attach_metric_does_not_meet_threshold(self):
)
def test_get_attach_metric_appsvc(self):
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 @@ -451,14 +451,14 @@ def test_get_attach_metric_appsvc(self):
)
def test_get_attach_metric_functions(self):
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):
mp = MeterProvider()
Expand All @@ -482,18 +482,36 @@ def test_get_attach_metric_vm(self):
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")

# 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)

def test_get_attach_metric_vm_no_os(self):
mp = MeterProvider()
ikey = "1aa11111-bbbb-1ccc-8ddd-eeeeffff3334"
Expand Down Expand Up @@ -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