Skip to content

Commit

Permalink
[Azure Monitor] OpenTelemetry Exporter Add support for Kubernetes res…
Browse files Browse the repository at this point in the history
…ource attributes (Azure#26104)

Use Kubernetes resource attributes when present, to support AKS
customers
  • Loading branch information
hectorhdzg authored Jun 7, 2023
1 parent 4dae449 commit 365c3f9
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 15 deletions.
1 change: 1 addition & 0 deletions sdk/monitor/monitor-opentelemetry-exporter/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- Add AiCloudRole and AiCloudRoleInstance to OTel Resource event.
- Add OTel resource metric envelope.
- Add OpenTelemetry Log Exporter
- Use Kubernetes resource attributes to populate cloud role and role instance.

## 1.0.0-beta.12 (2023-04-04)

Expand Down
86 changes: 71 additions & 15 deletions sdk/monitor/monitor-opentelemetry-exporter/src/utils/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,8 @@ export function createTagsFromResource(resource: Resource): Tags {
const context = getInstance();
const tags: Tags = { ...context.tags };
if (resource && resource.attributes) {
const serviceName = resource.attributes[SemanticResourceAttributes.SERVICE_NAME];
const serviceNamespace = resource.attributes[SemanticResourceAttributes.SERVICE_NAMESPACE];
if (serviceName) {
if (serviceNamespace) {
tags[KnownContextTagKeys.AiCloudRole] = `${serviceNamespace}.${serviceName}`;
} else {
tags[KnownContextTagKeys.AiCloudRole] = String(serviceName);
}
}
const serviceInstanceId = resource.attributes[SemanticResourceAttributes.SERVICE_INSTANCE_ID];
if (serviceInstanceId) {
tags[KnownContextTagKeys.AiCloudRoleInstance] = String(serviceInstanceId);
} else {
tags[KnownContextTagKeys.AiCloudRoleInstance] = os && os.hostname();
}
tags[KnownContextTagKeys.AiCloudRole] = getCloudRole(resource);
tags[KnownContextTagKeys.AiCloudRoleInstance] = getCloudRoleInstance(resource);
const endUserId = resource.attributes[SemanticAttributes.ENDUSER_ID];
if (endUserId) {
tags[KnownContextTagKeys.AiUserId] = String(endUserId);
Expand All @@ -40,6 +27,75 @@ export function createTagsFromResource(resource: Resource): Tags {
return tags;
}

function getCloudRole(resource: Resource): string {
let cloudRole = "";
// Service attributes
const serviceName = resource.attributes[SemanticResourceAttributes.SERVICE_NAME];
const serviceNamespace = resource.attributes[SemanticResourceAttributes.SERVICE_NAMESPACE];
if (serviceName) {
// Custom Service name provided by customer is highest precedence
if (!String(serviceName).startsWith("unknown_service")) {
if (serviceNamespace) {
return `${serviceNamespace}.${serviceName}`;
} else {
return String(serviceName);
}
} else {
// Service attributes will be only used if K8S attributes are not present
if (serviceNamespace) {
cloudRole = `${serviceNamespace}.${serviceName}`;
} else {
cloudRole = String(serviceName);
}
}
}
// Kubernetes attributes should take precedence
const kubernetesDeploymentName =
resource.attributes[SemanticResourceAttributes.K8S_DEPLOYMENT_NAME];
if (kubernetesDeploymentName) {
return String(kubernetesDeploymentName);
}
const kuberneteReplicasetName =
resource.attributes[SemanticResourceAttributes.K8S_REPLICASET_NAME];
if (kuberneteReplicasetName) {
return String(kuberneteReplicasetName);
}
const kubernetesStatefulSetName =
resource.attributes[SemanticResourceAttributes.K8S_STATEFULSET_NAME];
if (kubernetesStatefulSetName) {
return String(kubernetesStatefulSetName);
}
const kubernetesJobName = resource.attributes[SemanticResourceAttributes.K8S_JOB_NAME];
if (kubernetesJobName) {
return String(kubernetesJobName);
}
const kubernetesCronjobName = resource.attributes[SemanticResourceAttributes.K8S_CRONJOB_NAME];
if (kubernetesCronjobName) {
return String(kubernetesCronjobName);
}
const kubernetesDaemonsetName =
resource.attributes[SemanticResourceAttributes.K8S_DAEMONSET_NAME];
if (kubernetesDaemonsetName) {
return String(kubernetesDaemonsetName);
}
return cloudRole;
}

function getCloudRoleInstance(resource: Resource): string {
// Kubernetes attributes should take precedence
const kubernetesPodName = resource.attributes[SemanticResourceAttributes.K8S_POD_NAME];
if (kubernetesPodName) {
return String(kubernetesPodName);
}
// Service attributes
const serviceInstanceId = resource.attributes[SemanticResourceAttributes.SERVICE_INSTANCE_ID];
if (serviceInstanceId) {
return String(serviceInstanceId);
}
// Default
return os && os.hostname();
}

export function isSqlDB(dbSystem: string) {
return (
dbSystem === DbSystemValues.DB2 ||
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import os from "os";
import * as assert from "assert";
import { Resource } from "@opentelemetry/resources";
import { Tags } from "../../src/types";
import { createTagsFromResource } from "../../src/utils/common";

describe("commonUtils.ts", () => {
describe("#createTagsFromResource", () => {
it("default values", () => {
let resource: Resource = Resource.EMPTY;
const tags: Tags = createTagsFromResource(resource);
assert.strictEqual(tags["ai.cloud.role"], "");
assert.strictEqual(tags["ai.cloud.roleInstance"], os.hostname());
assert.strictEqual(tags["ai.user.id"], undefined);
});

it("should create Tags using custom Service attributes", () => {
let resource = new Resource({
"service.name": "testServiceName",
"service.namespace": "testServiceNamespace",
"service.instance.id": "testServiceInstanceId",
"k8s.deployment.name": "testK8sDeployment",
"k8s.replicaset.name": "testK8sReplicaset",
"k8s.statefulset.name": "testK8sStatefulSet",
"k8s.job.name": "testK8sJob",
"k8s.cronjob.name": "testK8sCronJob",
"k8s.daemonset.name": "testK8sDaemonset",
"k8s.pod.name": "testK8sPod",
});
let tags: Tags = createTagsFromResource(resource);
assert.strictEqual(tags["ai.cloud.role"], "testServiceNamespace.testServiceName");
assert.strictEqual(tags["ai.cloud.roleInstance"], "testK8sPod");

resource = new Resource({
"service.name": "testServiceName",
});
tags = createTagsFromResource(resource);
assert.strictEqual(tags["ai.cloud.role"], "testServiceName");
});

it("should create Tags using Kubernetes attributes if available", () => {
let resource = new Resource({
"k8s.deployment.name": "testK8sDeployment",
"k8s.replicaset.name": "testK8sReplicaset",
"k8s.statefulset.name": "testK8sStatefulSet",
"k8s.job.name": "testK8sJob",
"k8s.cronjob.name": "testK8sCronJob",
"k8s.daemonset.name": "testK8sDaemonset",
"k8s.pod.name": "testK8sPod",
});
let tags: Tags = createTagsFromResource(resource);
assert.strictEqual(tags["ai.cloud.role"], "testK8sDeployment");
assert.strictEqual(tags["ai.cloud.roleInstance"], "testK8sPod");

resource = new Resource({
"k8s.replicaset.name": "testK8sReplicaset",
"k8s.statefulset.name": "testK8sStatefulSet",
"k8s.job.name": "testK8sJob",
"k8s.cronjob.name": "testK8sCronJob",
"k8s.daemonset.name": "testK8sDaemonset",
});
tags = createTagsFromResource(resource);
assert.strictEqual(tags["ai.cloud.role"], "testK8sReplicaset");
resource = new Resource({
"k8s.statefulset.name": "testK8sStatefulSet",
"k8s.job.name": "testK8sJob",
"k8s.cronjob.name": "testK8sCronJob",
"k8s.daemonset.name": "testK8sDaemonset",
});
tags = createTagsFromResource(resource);
assert.strictEqual(tags["ai.cloud.role"], "testK8sStatefulSet");
resource = new Resource({
"k8s.job.name": "testK8sJob",
"k8s.cronjob.name": "testK8sCronJob",
"k8s.daemonset.name": "testK8sDaemonset",
});
tags = createTagsFromResource(resource);
assert.strictEqual(tags["ai.cloud.role"], "testK8sJob");
resource = new Resource({
"k8s.cronjob.name": "testK8sCronJob",
"k8s.daemonset.name": "testK8sDaemonset",
});
tags = createTagsFromResource(resource);
assert.strictEqual(tags["ai.cloud.role"], "testK8sCronJob");
resource = new Resource({
"k8s.daemonset.name": "testK8sDaemonset",
});
tags = createTagsFromResource(resource);
assert.strictEqual(tags["ai.cloud.role"], "testK8sDaemonset");
});

it("should create Tags using default Resource", () => {
let resource = Resource.default();
let tags: Tags = createTagsFromResource(resource);
assert.ok(tags["ai.cloud.role"].startsWith("unknown_service"), "wrong ai.cloud.role");
});
});
});

0 comments on commit 365c3f9

Please sign in to comment.