Skip to content

Commit 7272ca8

Browse files
fix: Update Azure Functions Detector Collected Attributes (#2233)
* Update azure functions detector collected attributes. * Clean up comments. * Fix lint. * Add tests for cloud resource id parsing. * Add comment to explain usage of the functionVersion env var. --------- Co-authored-by: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com>
1 parent 0078d0c commit 7272ca8

File tree

5 files changed

+94
-40
lines changed

5 files changed

+94
-40
lines changed

detectors/node/opentelemetry-resource-detector-azure/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,9 @@ This package implements Semantic Convention [Version 1.19.0](https://github.com/
6767
| cloud.region | The Azure region where the Azure Function is hosted, e.g., "East US", "West Europe", etc. Value of Process Environment Variable `REGION_NAME`. |
6868
| faas.instance | The specific instance of the Azure App Service, useful in a scaled-out configuration. Value from Process Environment Variable `WEBSITE_INSTANCE_ID`. |
6969
| faas.max_memory | The amount of memory available to the Azure Function expressed in MiB. value from Process Environment Variable `WEBSITE_MEMORY_LIMIT_MB`. |
70-
| faas.name | The name of the Azure App Service. Value from Process Environment Variable `WEBSITE_SITE_NAME`. |
71-
| faas.version | The version of the Azure Function being executed, e.g., "~4". value from Process Environment Variable `FUNCTIONS_EXTENSION_VERSION`. |
70+
| service.name | The name of the service the Azure Functions runs within. Value from Process Environment Variable `WEBSITE_SITE_NAME`. |
71+
| cloud.resource_id | The Azure Resource Manager URI uniquely identifying the Azure Virtual Machine. It typically follows this format: /subscriptions/{subscriptionId}/resourceGroups/{groupName}/providers/Microsoft.Compute/virtualMachines/{vmName}. Value from resourceId key on /metadata/instance/compute request. |
72+
| process.pid | The process ID collected from the running process. |
7273

7374
## Useful links
7475

detectors/node/opentelemetry-resource-detector-azure/src/detectors/AzureAppServiceDetector.ts

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ import {
2121
WEBSITE_HOME_STAMPNAME,
2222
WEBSITE_HOSTNAME,
2323
WEBSITE_INSTANCE_ID,
24-
WEBSITE_OWNER_NAME,
25-
WEBSITE_RESOURCE_GROUP,
2624
WEBSITE_SITE_NAME,
2725
WEBSITE_SLOT_NAME,
2826
CLOUD_RESOURCE_ID_RESOURCE_ATTRIBUTE,
@@ -39,6 +37,7 @@ import {
3937
CLOUDPROVIDERVALUES_AZURE,
4038
CLOUDPLATFORMVALUES_AZURE_APP_SERVICE,
4139
} from '@opentelemetry/semantic-conventions';
40+
import { getAzureResourceUri } from '../utils';
4241

4342
const APP_SERVICE_ATTRIBUTE_ENV_VARS = {
4443
[SEMRESATTRS_CLOUD_REGION]: REGION_NAME,
@@ -71,7 +70,7 @@ class AzureAppServiceDetector implements DetectorSync {
7170
[SEMRESATTRS_CLOUD_PLATFORM]: CLOUDPLATFORMVALUES_AZURE_APP_SERVICE,
7271
};
7372

74-
const azureResourceUri = this.getAzureResourceUri(websiteSiteName);
73+
const azureResourceUri = getAzureResourceUri(websiteSiteName);
7574
if (azureResourceUri) {
7675
attributes = {
7776
...attributes,
@@ -90,22 +89,6 @@ class AzureAppServiceDetector implements DetectorSync {
9089
}
9190
return new Resource(attributes);
9291
}
93-
94-
private getAzureResourceUri(websiteSiteName: string): string | undefined {
95-
const websiteResourceGroup = process.env[WEBSITE_RESOURCE_GROUP];
96-
const websiteOwnerName = process.env[WEBSITE_OWNER_NAME];
97-
98-
let subscriptionId = websiteOwnerName;
99-
if (websiteOwnerName && websiteOwnerName.indexOf('+') !== -1) {
100-
subscriptionId = websiteOwnerName.split('+')[0];
101-
}
102-
103-
if (!subscriptionId && !websiteOwnerName) {
104-
return undefined;
105-
}
106-
107-
return `/subscriptions/${subscriptionId}/resourceGroups/${websiteResourceGroup}/providers/Microsoft.Web/sites/${websiteSiteName}`;
108-
}
10992
}
11093

11194
export const azureAppServiceDetector = new AzureAppServiceDetector();

detectors/node/opentelemetry-resource-detector-azure/src/detectors/AzureFunctionsDetector.ts

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,28 @@
1717
import { DetectorSync, IResource, Resource } from '@opentelemetry/resources';
1818

1919
import {
20-
SEMRESATTRS_FAAS_NAME,
21-
SEMRESATTRS_FAAS_VERSION,
2220
SEMRESATTRS_FAAS_MAX_MEMORY,
2321
SEMRESATTRS_FAAS_INSTANCE,
2422
SEMRESATTRS_CLOUD_PROVIDER,
2523
SEMRESATTRS_CLOUD_PLATFORM,
2624
SEMRESATTRS_CLOUD_REGION,
2725
CLOUDPROVIDERVALUES_AZURE,
2826
CLOUDPLATFORMVALUES_AZURE_FUNCTIONS,
27+
SEMRESATTRS_SERVICE_NAME,
28+
SEMRESATTRS_PROCESS_PID,
2929
} from '@opentelemetry/semantic-conventions';
3030
import {
3131
WEBSITE_SITE_NAME,
3232
FUNCTIONS_VERSION,
3333
WEBSITE_INSTANCE_ID,
3434
FUNCTIONS_MEM_LIMIT,
3535
REGION_NAME,
36+
CLOUD_RESOURCE_ID_RESOURCE_ATTRIBUTE,
3637
} from '../types';
38+
import { getAzureResourceUri } from '../utils';
3739

3840
const AZURE_FUNCTIONS_ATTRIBUTE_ENV_VARS = {
39-
[SEMRESATTRS_FAAS_NAME]: WEBSITE_SITE_NAME,
40-
[SEMRESATTRS_FAAS_VERSION]: FUNCTIONS_VERSION,
41+
[SEMRESATTRS_SERVICE_NAME]: WEBSITE_SITE_NAME,
4142
[SEMRESATTRS_FAAS_INSTANCE]: WEBSITE_INSTANCE_ID,
4243
[SEMRESATTRS_FAAS_MAX_MEMORY]: FUNCTIONS_MEM_LIMIT,
4344
};
@@ -49,28 +50,28 @@ const AZURE_FUNCTIONS_ATTRIBUTE_ENV_VARS = {
4950
class AzureFunctionsDetector implements DetectorSync {
5051
detect(): IResource {
5152
let attributes = {};
52-
const functionName = process.env[WEBSITE_SITE_NAME];
53+
const serviceName = process.env[WEBSITE_SITE_NAME];
5354
const functionVersion = process.env[FUNCTIONS_VERSION];
54-
if (functionName && functionVersion) {
55+
56+
/**
57+
* Checks that we are operating within an Azure Function using the function version since WEBSITE_SITE_NAME
58+
* will exist in Azure App Service as well and detectors should be mutually exclusive.
59+
*/
60+
if (serviceName && functionVersion) {
5561
const functionInstance = process.env[WEBSITE_INSTANCE_ID];
5662
const functionMemLimit = process.env[FUNCTIONS_MEM_LIMIT];
5763

5864
attributes = {
5965
[SEMRESATTRS_CLOUD_PROVIDER]: CLOUDPROVIDERVALUES_AZURE,
6066
[SEMRESATTRS_CLOUD_PLATFORM]: CLOUDPLATFORMVALUES_AZURE_FUNCTIONS,
6167
[SEMRESATTRS_CLOUD_REGION]: process.env[REGION_NAME],
68+
[SEMRESATTRS_PROCESS_PID]: process.pid,
6269
};
6370

64-
if (functionName) {
65-
attributes = {
66-
...attributes,
67-
[SEMRESATTRS_FAAS_NAME]: functionName,
68-
};
69-
}
70-
if (functionVersion) {
71+
if (serviceName) {
7172
attributes = {
7273
...attributes,
73-
[SEMRESATTRS_FAAS_VERSION]: functionVersion,
74+
[SEMRESATTRS_SERVICE_NAME]: serviceName,
7475
};
7576
}
7677
if (functionInstance) {
@@ -85,6 +86,13 @@ class AzureFunctionsDetector implements DetectorSync {
8586
[SEMRESATTRS_FAAS_MAX_MEMORY]: functionMemLimit,
8687
};
8788
}
89+
const azureResourceUri = getAzureResourceUri(serviceName);
90+
if (azureResourceUri) {
91+
attributes = {
92+
...attributes,
93+
...{ [CLOUD_RESOURCE_ID_RESOURCE_ATTRIBUTE]: azureResourceUri },
94+
};
95+
}
8896

8997
for (const [key, value] of Object.entries(
9098
AZURE_FUNCTIONS_ATTRIBUTE_ENV_VARS
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { WEBSITE_OWNER_NAME, WEBSITE_RESOURCE_GROUP } from './types';
18+
19+
export function getAzureResourceUri(
20+
websiteSiteName: string
21+
): string | undefined {
22+
const websiteResourceGroup = process.env[WEBSITE_RESOURCE_GROUP];
23+
const websiteOwnerName = process.env[WEBSITE_OWNER_NAME];
24+
25+
let subscriptionId = websiteOwnerName;
26+
if (websiteOwnerName && websiteOwnerName.indexOf('+') !== -1) {
27+
subscriptionId = websiteOwnerName.split('+')[0];
28+
}
29+
30+
if (!subscriptionId && !websiteOwnerName) {
31+
return undefined;
32+
}
33+
34+
return `/subscriptions/${subscriptionId}/resourceGroups/${websiteResourceGroup}/providers/Microsoft.Web/sites/${websiteSiteName}`;
35+
}

detectors/node/opentelemetry-resource-detector-azure/test/detectors/AzureFunctionsDetector.test.ts

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ import {
2323
SEMRESATTRS_CLOUD_REGION,
2424
SEMRESATTRS_FAAS_INSTANCE,
2525
SEMRESATTRS_FAAS_MAX_MEMORY,
26-
SEMRESATTRS_FAAS_NAME,
27-
SEMRESATTRS_FAAS_VERSION,
26+
SEMRESATTRS_PROCESS_PID,
2827
SEMRESATTRS_SERVICE_INSTANCE_ID,
28+
SEMRESATTRS_SERVICE_NAME,
2929
} from '@opentelemetry/semantic-conventions';
3030
import { detectResourcesSync } from '@opentelemetry/resources';
3131
import { AZURE_APP_SERVICE_STAMP_RESOURCE_ATTRIBUTE } from '../../src/types';
@@ -41,18 +41,20 @@ describe('AzureFunctionsDetector', () => {
4141
});
4242

4343
it('should test functions values', () => {
44-
process.env.WEBSITE_SITE_NAME = 'test-function';
44+
process.env.WEBSITE_SITE_NAME = 'test-service';
4545
process.env.REGION_NAME = 'test-region';
4646
process.env.WEBSITE_INSTANCE_ID = 'test-instance-id';
4747
process.env.FUNCTIONS_EXTENSION_VERSION = '~4';
4848
process.env.WEBSITE_MEMORY_LIMIT_MB = '1000';
49+
process.env.WEBSITE_OWNER_NAME = 'test-owner-name';
50+
process.env.WEBSITE_RESOURCE_GROUP = 'test-resource-group';
4951

5052
const resource = detectResourcesSync({
5153
detectors: [azureFunctionsDetector, azureAppServiceDetector],
5254
});
5355
assert.ok(resource);
5456
const attributes = resource.attributes;
55-
assert.strictEqual(attributes[SEMRESATTRS_FAAS_NAME], 'test-function');
57+
assert.strictEqual(attributes[SEMRESATTRS_SERVICE_NAME], 'test-service');
5658
assert.strictEqual(attributes[SEMRESATTRS_CLOUD_PROVIDER], 'azure');
5759
assert.strictEqual(
5860
attributes[SEMRESATTRS_CLOUD_PLATFORM],
@@ -64,14 +66,39 @@ describe('AzureFunctionsDetector', () => {
6466
'test-instance-id'
6567
);
6668
assert.strictEqual(attributes[SEMRESATTRS_FAAS_MAX_MEMORY], '1000');
67-
assert.strictEqual(attributes[SEMRESATTRS_FAAS_VERSION], '~4');
6869

6970
// Should not detect app service values
7071
assert.strictEqual(attributes[SEMRESATTRS_SERVICE_INSTANCE_ID], undefined);
72+
assert.strictEqual(attributes[SEMRESATTRS_PROCESS_PID], process.pid);
7173

74+
assert.strictEqual(
75+
attributes['cloud.resource_id'],
76+
`/subscriptions/${process.env.WEBSITE_OWNER_NAME}/resourceGroups/${process.env.WEBSITE_RESOURCE_GROUP}/providers/Microsoft.Web/sites/${process.env.WEBSITE_SITE_NAME}`
77+
);
7278
assert.strictEqual(
7379
attributes[AZURE_APP_SERVICE_STAMP_RESOURCE_ATTRIBUTE],
7480
undefined
7581
);
7682
});
83+
84+
it('should get the correct cloud resource id when WEBSITE_OWNER_NAME has a +', () => {
85+
process.env.WEBSITE_SITE_NAME = 'test-service';
86+
process.env.REGION_NAME = 'test-region';
87+
process.env.WEBSITE_INSTANCE_ID = 'test-instance-id';
88+
process.env.FUNCTIONS_EXTENSION_VERSION = '~4';
89+
process.env.WEBSITE_MEMORY_LIMIT_MB = '1000';
90+
process.env.WEBSITE_OWNER_NAME = 'test-owner-name+test-subscription-id';
91+
process.env.WEBSITE_RESOURCE_GROUP = 'test-resource-group';
92+
93+
const expectedWebsiteOwnerName = 'test-owner-name';
94+
const resource = detectResourcesSync({
95+
detectors: [azureFunctionsDetector, azureAppServiceDetector],
96+
});
97+
assert.ok(resource);
98+
const attributes = resource.attributes;
99+
assert.strictEqual(
100+
attributes['cloud.resource_id'],
101+
`/subscriptions/${expectedWebsiteOwnerName}/resourceGroups/${process.env.WEBSITE_RESOURCE_GROUP}/providers/Microsoft.Web/sites/${process.env.WEBSITE_SITE_NAME}`
102+
);
103+
});
77104
});

0 commit comments

Comments
 (0)