Skip to content

Commit f8758c6

Browse files
lzchenjeremydvoss
andauthored
Implement functions resource detector (#2523)
* Update .pylintrc * fn * Update CHANGELOG.md * commments * Add deployment.environment to functions detector * Revert "Add deployment.environment to functions detector" This reverts commit 5411759. * Remove deployment.environment from readme * Release 0.1.5 --------- Co-authored-by: jeremydvoss <[email protected]>
1 parent 460fc33 commit f8758c6

File tree

11 files changed

+274
-29
lines changed

11 files changed

+274
-29
lines changed

resource/opentelemetry-resource-detector-azure/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## Unreleased
8+
## Version 0.1.5 (2024-05-16)
99

1010
- Ignore vm detector if already in other rps
1111
([#2456](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2456))
12+
- Implement functions resource detector
13+
([#2523](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2523))
1214

1315
## Version 0.1.4 (2024-04-05)
1416

resource/opentelemetry-resource-detector-azure/README.rst

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,17 @@ The Azure App Service Resource Detector sets the following Resource Attributes:
6060
* ``service.instance.id`` set to the value of the ``WEBSITE_INSTANCE_ID`` environment variable.
6161
* ``azure.app.service.stamp`` set to the value of the ``WEBSITE_HOME_STAMPNAME`` environment variable.
6262

63-
The Azure VM Resource Detector sets the following Resource Attributes according to the response from the `Azure Metadata Service <https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service?tabs=windows>`_:
63+
The Azure Functions Resource Detector sets the following Resource Attributes:
64+
* ``service.name`` set to the value of the ``WEBSITE_SITE_NAME`` environment variable.
65+
* ``process.id`` set to the process ID collected from the running process.
66+
* ``cloud.platform`` set to ``azure_functions``.
67+
* ``cloud.provider`` set to ``azure``.
68+
* ``cloud.resource_id`` set using the ``WEBSITE_RESOURCE_GROUP``, ``WEBSITE_OWNER_NAME``, and ``WEBSITE_SITE_NAME`` environment variables.
69+
* ``cloud.region`` set to the value of the ``REGION_NAME`` environment variable.
70+
* ``faas.instance`` set to the value of the ``WEBSITE_INSTANCE_ID`` environment variable.
71+
* ``faas.max_memory`` set to the value of the ``WEBSITE_MEMORY_LIMIT_MB`` environment variable.
72+
73+
The Azure VM Resource Detector sets the following Resource Attributes according to the response from the `Azure Metadata Service <https://learn.microsoft.com/azure/virtual-machines/instance-metadata-service?tabs=windows>`_:
6474
* ``azure.vm.scaleset.name`` set to the value of the ``vmScaleSetName`` field.
6575
* ``azure.vm.sku`` set to the value of the ``sku`` field.
6676
* ``cloud.platform`` set to the value of the ``azure_vm``.

resource/opentelemetry-resource-detector-azure/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ dependencies = [
2929

3030
[project.entry-points.opentelemetry_resource_detector]
3131
azure_app_service = "opentelemetry.resource.detector.azure.app_service:AzureAppServiceResourceDetector"
32+
azure_functions = "opentelemetry.resource.detector.azure.functions:AzureFunctionsResourceDetector"
3233
azure_vm = "opentelemetry.resource.detector.azure.vm:AzureVMResourceDetector"
3334

3435
[project.urls]

resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
# pylint: disable=import-error
1616

1717
from .app_service import AzureAppServiceResourceDetector
18+
from .functions import AzureFunctionsResourceDetector
1819
from .version import __version__
1920
from .vm import AzureVMResourceDetector
2021

2122
__all__ = [
2223
"AzureAppServiceResourceDetector",
24+
"AzureFunctionsResourceDetector",
2325
"AzureVMResourceDetector",
2426
"__version__",
2527
]

resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/_constants.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@
4343
# Functions
4444

4545
_FUNCTIONS_WORKER_RUNTIME = "FUNCTIONS_WORKER_RUNTIME"
46+
_WEBSITE_MEMORY_LIMIT_MB = "WEBSITE_MEMORY_LIMIT_MB"
47+
48+
_FUNCTIONS_ATTRIBUTE_ENV_VARS = {
49+
ResourceAttributes.FAAS_INSTANCE: _WEBSITE_INSTANCE_ID,
50+
ResourceAttributes.FAAS_MAX_MEMORY: _WEBSITE_MEMORY_LIMIT_MB,
51+
}
4652

4753
# Vm
4854

resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/_utils.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,44 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
15-
import os
14+
from os import environ
15+
from typing import Optional
1616

1717
from ._constants import (
1818
_AKS_ARM_NAMESPACE_ID,
1919
_FUNCTIONS_WORKER_RUNTIME,
20+
_WEBSITE_OWNER_NAME,
21+
_WEBSITE_RESOURCE_GROUP,
2022
_WEBSITE_SITE_NAME,
2123
)
2224

2325

2426
def _is_on_aks() -> bool:
25-
return os.environ.get(_AKS_ARM_NAMESPACE_ID) is not None
27+
return environ.get(_AKS_ARM_NAMESPACE_ID) is not None
2628

2729

2830
def _is_on_app_service() -> bool:
29-
return os.environ.get(_WEBSITE_SITE_NAME) is not None
31+
return environ.get(_WEBSITE_SITE_NAME) is not None
3032

3133

3234
def _is_on_functions() -> bool:
33-
return os.environ.get(_FUNCTIONS_WORKER_RUNTIME) is not None
35+
return environ.get(_FUNCTIONS_WORKER_RUNTIME) is not None
3436

3537

3638
def _can_ignore_vm_detect() -> bool:
3739
return _is_on_aks() or _is_on_app_service() or _is_on_functions()
40+
41+
42+
def _get_azure_resource_uri() -> Optional[str]:
43+
website_site_name = environ.get(_WEBSITE_SITE_NAME)
44+
website_resource_group = environ.get(_WEBSITE_RESOURCE_GROUP)
45+
website_owner_name = environ.get(_WEBSITE_OWNER_NAME)
46+
47+
subscription_id = website_owner_name
48+
if website_owner_name and "+" in website_owner_name:
49+
subscription_id = website_owner_name[0 : website_owner_name.index("+")]
50+
51+
if not (website_site_name and website_resource_group and subscription_id):
52+
return None
53+
54+
return f"/subscriptions/{subscription_id}/resourceGroups/{website_resource_group}/providers/Microsoft.Web/sites/{website_site_name}"

resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/app_service.py

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
from typing import Optional
1516
from os import environ
1617

1718
from opentelemetry.sdk.resources import Resource, ResourceDetector
@@ -20,29 +21,32 @@
2021
CloudProviderValues,
2122
ResourceAttributes,
2223
)
24+
from opentelemetry.resource.detector.azure._utils import _get_azure_resource_uri
2325

2426
from ._constants import (
2527
_APP_SERVICE_ATTRIBUTE_ENV_VARS,
26-
_WEBSITE_OWNER_NAME,
27-
_WEBSITE_RESOURCE_GROUP,
2828
_WEBSITE_SITE_NAME,
2929
)
3030

31+
from opentelemetry.resource.detector.azure._utils import _is_on_functions
32+
3133

3234
class AzureAppServiceResourceDetector(ResourceDetector):
3335
def detect(self) -> Resource:
3436
attributes = {}
3537
website_site_name = environ.get(_WEBSITE_SITE_NAME)
3638
if website_site_name:
37-
attributes[ResourceAttributes.SERVICE_NAME] = website_site_name
39+
# Functions resource detector takes priority with `service.name` and `cloud.platform`
40+
if not _is_on_functions():
41+
attributes[ResourceAttributes.SERVICE_NAME] = website_site_name
42+
attributes[ResourceAttributes.CLOUD_PLATFORM] = (
43+
CloudPlatformValues.AZURE_APP_SERVICE.value
44+
)
3845
attributes[ResourceAttributes.CLOUD_PROVIDER] = (
3946
CloudProviderValues.AZURE.value
4047
)
41-
attributes[ResourceAttributes.CLOUD_PLATFORM] = (
42-
CloudPlatformValues.AZURE_APP_SERVICE.value
43-
)
4448

45-
azure_resource_uri = _get_azure_resource_uri(website_site_name)
49+
azure_resource_uri = _get_azure_resource_uri()
4650
if azure_resource_uri:
4751
attributes[ResourceAttributes.CLOUD_RESOURCE_ID] = (
4852
azure_resource_uri
@@ -53,17 +57,3 @@ def detect(self) -> Resource:
5357
attributes[key] = value
5458

5559
return Resource(attributes)
56-
57-
58-
def _get_azure_resource_uri(website_site_name):
59-
website_resource_group = environ.get(_WEBSITE_RESOURCE_GROUP)
60-
website_owner_name = environ.get(_WEBSITE_OWNER_NAME)
61-
62-
subscription_id = website_owner_name
63-
if website_owner_name and "+" in website_owner_name:
64-
subscription_id = website_owner_name[0 : website_owner_name.index("+")]
65-
66-
if not (website_resource_group and subscription_id):
67-
return None
68-
69-
return f"/subscriptions/{subscription_id}/resourceGroups/{website_resource_group}/providers/Microsoft.Web/sites/{website_site_name}"
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from os import environ, getpid
16+
17+
from opentelemetry.sdk.resources import Resource, ResourceDetector
18+
from opentelemetry.semconv.resource import (
19+
CloudPlatformValues,
20+
CloudProviderValues,
21+
ResourceAttributes,
22+
)
23+
24+
from ._constants import (
25+
_FUNCTIONS_ATTRIBUTE_ENV_VARS,
26+
_REGION_NAME,
27+
_WEBSITE_SITE_NAME,
28+
)
29+
from opentelemetry.resource.detector.azure._utils import (
30+
_get_azure_resource_uri,
31+
_is_on_functions,
32+
)
33+
34+
35+
class AzureFunctionsResourceDetector(ResourceDetector):
36+
def detect(self) -> Resource:
37+
attributes = {}
38+
if _is_on_functions():
39+
website_site_name = environ.get(_WEBSITE_SITE_NAME)
40+
if website_site_name:
41+
attributes[ResourceAttributes.SERVICE_NAME] = website_site_name
42+
attributes[ResourceAttributes.PROCESS_PID] = getpid()
43+
attributes[ResourceAttributes.CLOUD_PROVIDER] = (
44+
CloudProviderValues.AZURE.value
45+
)
46+
attributes[ResourceAttributes.CLOUD_PLATFORM] = (
47+
CloudPlatformValues.AZURE_FUNCTIONS.value
48+
)
49+
cloud_region = environ.get(_REGION_NAME)
50+
if cloud_region:
51+
attributes[ResourceAttributes.CLOUD_REGION] = cloud_region
52+
azure_resource_uri = _get_azure_resource_uri()
53+
if azure_resource_uri:
54+
attributes[ResourceAttributes.CLOUD_RESOURCE_ID] = (
55+
azure_resource_uri
56+
)
57+
for key, env_var in _FUNCTIONS_ATTRIBUTE_ENV_VARS.items():
58+
value = environ.get(env_var)
59+
if value:
60+
if key == ResourceAttributes.FAAS_MAX_MEMORY:
61+
try:
62+
value = int(value)
63+
except ValueError:
64+
continue
65+
attributes[key] = value
66+
67+
return Resource(attributes)
68+

resource/opentelemetry-resource-detector-azure/src/opentelemetry/resource/detector/azure/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
__version__ = "0.1.4"
15+
__version__ = "0.1.5"

resource/opentelemetry-resource-detector-azure/tests/test_app_service.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,45 @@ def test_on_app_service(self):
6868
self.assertEqual(
6969
attributes["azure.app.service.stamp"], TEST_WEBSITE_HOME_STAMPNAME
7070
)
71+
72+
@patch.dict(
73+
"os.environ",
74+
{
75+
"FUNCTIONS_WORKER_RUNTIME": "1",
76+
"WEBSITE_SITE_NAME": TEST_WEBSITE_SITE_NAME,
77+
"REGION_NAME": TEST_REGION_NAME,
78+
"WEBSITE_SLOT_NAME": TEST_WEBSITE_SLOT_NAME,
79+
"WEBSITE_HOSTNAME": TEST_WEBSITE_HOSTNAME,
80+
"WEBSITE_INSTANCE_ID": TEST_WEBSITE_INSTANCE_ID,
81+
"WEBSITE_HOME_STAMPNAME": TEST_WEBSITE_HOME_STAMPNAME,
82+
"WEBSITE_RESOURCE_GROUP": TEST_WEBSITE_RESOURCE_GROUP,
83+
"WEBSITE_OWNER_NAME": TEST_WEBSITE_OWNER_NAME,
84+
},
85+
clear=True,
86+
)
87+
def test_on_app_service_with_functions(self):
88+
resource = AzureAppServiceResourceDetector().detect()
89+
attributes = resource.attributes
90+
self.assertIsNone(attributes.get("service.name"))
91+
self.assertEqual(attributes["cloud.provider"], "azure")
92+
self.assertIsNone(attributes.get("cloud.platform"))
93+
94+
self.assertEqual(
95+
attributes["cloud.resource_id"],
96+
f"/subscriptions/{TEST_WEBSITE_OWNER_NAME}/resourceGroups/{TEST_WEBSITE_RESOURCE_GROUP}/providers/Microsoft.Web/sites/{TEST_WEBSITE_SITE_NAME}",
97+
)
98+
99+
self.assertEqual(attributes["cloud.region"], TEST_REGION_NAME)
100+
self.assertEqual(
101+
attributes["deployment.environment"], TEST_WEBSITE_SLOT_NAME
102+
)
103+
self.assertEqual(attributes["host.id"], TEST_WEBSITE_HOSTNAME)
104+
self.assertEqual(
105+
attributes["service.instance.id"], TEST_WEBSITE_INSTANCE_ID
106+
)
107+
self.assertEqual(
108+
attributes["azure.app.service.stamp"], TEST_WEBSITE_HOME_STAMPNAME
109+
)
71110

72111
@patch.dict(
73112
"os.environ",

0 commit comments

Comments
 (0)