Skip to content

Commit c19a973

Browse files
zeitlingerheyams
andauthored
Azure resource providers (#1228)
Co-authored-by: heyams <[email protected]>
1 parent 1b00650 commit c19a973

21 files changed

+1371
-0
lines changed

.github/component_owners.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ components:
1919
aws-xray-propagator:
2020
- wangzlei
2121
- srprash
22+
azure-resources:
23+
- trask
24+
- zeitlinger
2225
baggage-processor:
2326
- mikegoldsmith
2427
- zeitlinger

azure-resources/README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Azure Resource Detectors for OpenTelemetry
2+
3+
This module provides Azure resource detectors for OpenTelemetry.
4+
5+
The following OpenTelemetry semantic conventions will be detected:
6+
7+
| Resource attribute | VM | Functions | App Service | Containers |
8+
|-------------------------|----------|-----------------|-------------------|----------------------|
9+
| cloud.platform | azure_vm | azure_functions | azure_app_service | azure_container_apps |
10+
| cloud.provider | azure | azure | azure | azure |
11+
| cloud.resource.id | auto | | auto | |
12+
| cloud.region | auto | auto | auto | |
13+
| deployment.environment | | | auto | |
14+
| host.id | auto | | auto | |
15+
| host.name | auto | | | |
16+
| host.type | auto | | | |
17+
| os.type | auto | | | |
18+
| os.version | auto | | | |
19+
| azure.vm.scaleset.name | auto | | | |
20+
| azure.vm.sku | auto | | | |
21+
| service.name | | | auto | auto |
22+
| service.version | | | | auto |
23+
| service.instance.id | | | auto | auto |
24+
| azure.app.service.stamp | | | auto | |
25+
| faas.name | | auto | | |
26+
| faas.version | | auto | | |
27+
| faas.instance | | auto | | |
28+
| faas.faas.max_memory | | auto | | |
29+
30+
## Component Owners
31+
32+
- [Trask Stalnaker](https://github.com/trask), Microsoft
33+
- [Gregor Zeitlinger](https://github.com/zeitlinger), Grafana
34+
35+
Learn more about component owners in [component_owners.yml](../.github/component_owners.yml).

azure-resources/build.gradle.kts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
plugins {
2+
id("otel.java-conventions")
3+
4+
id("otel.publish-conventions")
5+
id("maven-publish")
6+
}
7+
8+
description = "OpenTelemetry GCP Resources Support"
9+
otelJava.moduleName.set("io.opentelemetry.contrib.gcp.resource")
10+
11+
// enable publishing to maven local
12+
java {
13+
withSourcesJar()
14+
}
15+
16+
dependencies {
17+
api("io.opentelemetry:opentelemetry-api")
18+
api("io.opentelemetry:opentelemetry-sdk")
19+
20+
implementation("io.opentelemetry.semconv:opentelemetry-semconv")
21+
22+
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
23+
24+
implementation("com.fasterxml.jackson.core:jackson-core")
25+
implementation("com.squareup.okhttp3:okhttp")
26+
27+
testImplementation("io.opentelemetry.semconv:opentelemetry-semconv-incubating")
28+
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
29+
testImplementation("io.opentelemetry:opentelemetry-sdk-testing")
30+
31+
// testImplementation("org.mockito:mockito-core")
32+
testImplementation("com.google.guava:guava")
33+
34+
testImplementation("org.junit.jupiter:junit-jupiter-api")
35+
testImplementation("org.assertj:assertj-core")
36+
testImplementation("com.linecorp.armeria:armeria-junit5")
37+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.azure.resource;
7+
8+
import static io.opentelemetry.contrib.azure.resource.IncubatingAttributes.CloudPlatformIncubatingValues.AZURE_AKS;
9+
import static io.opentelemetry.contrib.azure.resource.IncubatingAttributes.K8S_CLUSTER_NAME;
10+
11+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
12+
import io.opentelemetry.sdk.resources.Resource;
13+
import java.util.HashMap;
14+
import java.util.Map;
15+
import java.util.Optional;
16+
import java.util.function.Supplier;
17+
18+
public class AzureAksResourceProvider extends CloudResourceProvider {
19+
20+
private static final Map<String, AzureVmResourceProvider.Entry> COMPUTE_MAPPING = new HashMap<>();
21+
22+
static {
23+
COMPUTE_MAPPING.put(
24+
"resourceGroupName",
25+
new AzureVmResourceProvider.Entry(
26+
K8S_CLUSTER_NAME, AzureAksResourceProvider::parseClusterName));
27+
}
28+
29+
// visible for testing
30+
static String parseClusterName(String resourceGroup) {
31+
// Code inspired by
32+
// https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/datadogexporter/internal/hostmetadata/internal/azure/provider.go#L36
33+
String[] splitAll = resourceGroup.split("_");
34+
if (splitAll.length == 4 && splitAll[0].equalsIgnoreCase("mc")) {
35+
return splitAll[splitAll.length - 2];
36+
}
37+
return resourceGroup;
38+
}
39+
40+
// Environment variable that is set when running on Kubernetes
41+
static final String KUBERNETES_SERVICE_HOST = "KUBERNETES_SERVICE_HOST";
42+
private final Supplier<Optional<String>> client;
43+
private final Map<String, String> environment;
44+
45+
// SPI
46+
public AzureAksResourceProvider() {
47+
this(AzureMetadataService.defaultClient(), System.getenv());
48+
}
49+
50+
// visible for testing
51+
AzureAksResourceProvider(Supplier<Optional<String>> client, Map<String, String> environment) {
52+
this.client = client;
53+
this.environment = environment;
54+
}
55+
56+
@Override
57+
public int order() {
58+
// run after the fast cloud resource providers that only check environment variables
59+
// and before the AKS provider
60+
return 100;
61+
}
62+
63+
@Override
64+
public Resource createResource(ConfigProperties configProperties) {
65+
if (environment.get(KUBERNETES_SERVICE_HOST) == null) {
66+
return Resource.empty();
67+
}
68+
return client
69+
.get()
70+
.map(body -> AzureVmResourceProvider.parseMetadata(body, COMPUTE_MAPPING, AZURE_AKS))
71+
.orElse(Resource.empty());
72+
}
73+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.azure.resource;
7+
8+
import static io.opentelemetry.contrib.azure.resource.IncubatingAttributes.CLOUD_REGION;
9+
import static io.opentelemetry.contrib.azure.resource.IncubatingAttributes.CLOUD_RESOURCE_ID;
10+
import static io.opentelemetry.contrib.azure.resource.IncubatingAttributes.CloudPlatformIncubatingValues.AZURE_APP_SERVICE;
11+
import static io.opentelemetry.contrib.azure.resource.IncubatingAttributes.DEPLOYMENT_ENVIRONMENT_NAME;
12+
import static io.opentelemetry.contrib.azure.resource.IncubatingAttributes.HOST_ID;
13+
import static io.opentelemetry.contrib.azure.resource.IncubatingAttributes.SERVICE_INSTANCE_ID;
14+
import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_NAME;
15+
16+
import io.opentelemetry.api.common.AttributeKey;
17+
import io.opentelemetry.api.common.Attributes;
18+
import io.opentelemetry.api.common.AttributesBuilder;
19+
import io.opentelemetry.api.internal.StringUtils;
20+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
21+
import io.opentelemetry.sdk.resources.Resource;
22+
import java.util.HashMap;
23+
import java.util.Map;
24+
import java.util.Objects;
25+
import javax.annotation.Nullable;
26+
27+
public class AzureAppServiceResourceProvider extends CloudResourceProvider {
28+
29+
static final AttributeKey<String> AZURE_APP_SERVICE_STAMP_RESOURCE_ATTRIBUTE =
30+
AttributeKey.stringKey("azure.app.service.stamp");
31+
static final String REGION_NAME = "REGION_NAME";
32+
private static final String WEBSITE_HOME_STAMPNAME = "WEBSITE_HOME_STAMPNAME";
33+
private static final String WEBSITE_HOSTNAME = "WEBSITE_HOSTNAME";
34+
static final String WEBSITE_INSTANCE_ID = "WEBSITE_INSTANCE_ID";
35+
private static final String WEBSITE_OWNER_NAME = "WEBSITE_OWNER_NAME";
36+
private static final String WEBSITE_RESOURCE_GROUP = "WEBSITE_RESOURCE_GROUP";
37+
static final String WEBSITE_SITE_NAME = "WEBSITE_SITE_NAME";
38+
private static final String WEBSITE_SLOT_NAME = "WEBSITE_SLOT_NAME";
39+
40+
private static final Map<AttributeKey<String>, String> ENV_VAR_MAPPING = new HashMap<>();
41+
42+
static {
43+
ENV_VAR_MAPPING.put(CLOUD_REGION, REGION_NAME);
44+
ENV_VAR_MAPPING.put(DEPLOYMENT_ENVIRONMENT_NAME, WEBSITE_SLOT_NAME);
45+
ENV_VAR_MAPPING.put(HOST_ID, WEBSITE_HOSTNAME);
46+
ENV_VAR_MAPPING.put(SERVICE_INSTANCE_ID, WEBSITE_INSTANCE_ID);
47+
ENV_VAR_MAPPING.put(AZURE_APP_SERVICE_STAMP_RESOURCE_ATTRIBUTE, WEBSITE_HOME_STAMPNAME);
48+
}
49+
50+
private final Map<String, String> env;
51+
52+
// SPI
53+
public AzureAppServiceResourceProvider() {
54+
this(System.getenv());
55+
}
56+
57+
// Visible for testing
58+
AzureAppServiceResourceProvider(Map<String, String> env) {
59+
this.env = env;
60+
}
61+
62+
@Override
63+
public Resource createResource(ConfigProperties config) {
64+
return Resource.create(getAttributes());
65+
}
66+
67+
public Attributes getAttributes() {
68+
AzureEnvVarPlatform detect = AzureEnvVarPlatform.detect(env);
69+
if (detect != AzureEnvVarPlatform.APP_SERVICE) {
70+
return Attributes.empty();
71+
}
72+
String name = Objects.requireNonNull(env.get(WEBSITE_SITE_NAME));
73+
AttributesBuilder builder = AzureVmResourceProvider.azureAttributeBuilder(AZURE_APP_SERVICE);
74+
builder.put(SERVICE_NAME, name);
75+
76+
String resourceUri = resourceUri(name);
77+
if (resourceUri != null) {
78+
builder.put(CLOUD_RESOURCE_ID, resourceUri);
79+
}
80+
81+
AzureEnvVarPlatform.addAttributesFromEnv(ENV_VAR_MAPPING, env, builder);
82+
83+
return builder.build();
84+
}
85+
86+
@Nullable
87+
private String resourceUri(String websiteName) {
88+
String websiteResourceGroup = env.get(WEBSITE_RESOURCE_GROUP);
89+
String websiteOwnerName = env.get(WEBSITE_OWNER_NAME);
90+
91+
String subscriptionId;
92+
if (websiteOwnerName != null && websiteOwnerName.contains("+")) {
93+
subscriptionId = websiteOwnerName.substring(0, websiteOwnerName.indexOf("+"));
94+
} else {
95+
subscriptionId = websiteOwnerName;
96+
}
97+
98+
if (StringUtils.isNullOrEmpty(websiteResourceGroup)
99+
|| StringUtils.isNullOrEmpty(subscriptionId)) {
100+
return null;
101+
}
102+
103+
return String.format(
104+
"/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Web/sites/%s",
105+
subscriptionId, websiteResourceGroup, websiteName);
106+
}
107+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.azure.resource;
7+
8+
import static io.opentelemetry.contrib.azure.resource.IncubatingAttributes.SERVICE_INSTANCE_ID;
9+
import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_NAME;
10+
import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_VERSION;
11+
12+
import io.opentelemetry.api.common.AttributeKey;
13+
import io.opentelemetry.api.common.Attributes;
14+
import io.opentelemetry.api.common.AttributesBuilder;
15+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
16+
import io.opentelemetry.sdk.resources.Resource;
17+
import java.util.HashMap;
18+
import java.util.Map;
19+
20+
public class AzureContainersResourceProvider extends CloudResourceProvider {
21+
22+
static final String CONTAINER_APP_NAME = "CONTAINER_APP_NAME";
23+
24+
private static final String CONTAINER_APP_REPLICA_NAME = "CONTAINER_APP_REPLICA_NAME";
25+
private static final String CONTAINER_APP_REVISION = "CONTAINER_APP_REVISION";
26+
27+
private static final Map<AttributeKey<String>, String> ENV_VAR_MAPPING = new HashMap<>();
28+
29+
static {
30+
ENV_VAR_MAPPING.put(SERVICE_NAME, CONTAINER_APP_NAME);
31+
ENV_VAR_MAPPING.put(SERVICE_INSTANCE_ID, CONTAINER_APP_REPLICA_NAME);
32+
ENV_VAR_MAPPING.put(SERVICE_VERSION, CONTAINER_APP_REVISION);
33+
}
34+
35+
private final Map<String, String> env;
36+
37+
// SPI
38+
public AzureContainersResourceProvider() {
39+
this(System.getenv());
40+
}
41+
42+
// Visible for testing
43+
AzureContainersResourceProvider(Map<String, String> env) {
44+
this.env = env;
45+
}
46+
47+
@Override
48+
public Resource createResource(ConfigProperties config) {
49+
return Resource.create(getAttributes());
50+
}
51+
52+
public Attributes getAttributes() {
53+
AzureEnvVarPlatform detect = AzureEnvVarPlatform.detect(env);
54+
if (detect != AzureEnvVarPlatform.CONTAINER_APP) {
55+
return Attributes.empty();
56+
}
57+
58+
AttributesBuilder builder =
59+
AzureVmResourceProvider.azureAttributeBuilder("azure_container_apps");
60+
61+
AzureEnvVarPlatform.addAttributesFromEnv(ENV_VAR_MAPPING, env, builder);
62+
63+
return builder.build();
64+
}
65+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.azure.resource;
7+
8+
import io.opentelemetry.api.common.AttributeKey;
9+
import io.opentelemetry.api.common.AttributesBuilder;
10+
import java.util.Map;
11+
12+
public enum AzureEnvVarPlatform {
13+
APP_SERVICE,
14+
FUNCTIONS,
15+
CONTAINER_APP,
16+
NONE;
17+
18+
public static AzureEnvVarPlatform detect(Map<String, String> env) {
19+
String appName = env.get(AzureContainersResourceProvider.CONTAINER_APP_NAME);
20+
if (appName != null) {
21+
return CONTAINER_APP;
22+
}
23+
String name = env.get(AzureAppServiceResourceProvider.WEBSITE_SITE_NAME);
24+
if (name == null) {
25+
return NONE;
26+
}
27+
if (env.get(AzureFunctionsResourceProvider.FUNCTIONS_VERSION) != null) {
28+
return FUNCTIONS;
29+
}
30+
return APP_SERVICE;
31+
}
32+
33+
static void addAttributesFromEnv(
34+
Map<AttributeKey<String>, String> mapping,
35+
Map<String, String> env,
36+
AttributesBuilder builder) {
37+
mapping.forEach(
38+
(key, value) -> {
39+
String envValue = env.get(value);
40+
if (envValue != null) {
41+
builder.put(key, envValue);
42+
}
43+
});
44+
}
45+
}

0 commit comments

Comments
 (0)