From 2424b74f029273677f62433f28dd1390806f682c Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Tue, 15 Dec 2020 11:52:51 -0800 Subject: [PATCH] OpenTelemetry Exporter using Resources API to get service properties (#12893) * OT Exporter use Resources API to get service properties * Test * Addung pnpm-lock * Addressing comments * Updating pnpm --- common/config/rush/pnpm-lock.yaml | 2 ++ .../package.json | 1 + .../src/platform/nodejs/context/context.ts | 4 --- .../utils/constants/applicationinsights.ts | 2 ++ .../src/utils/spanUtils.ts | 20 +++++++++++++ .../test/unit/utils/spanUtils.test.ts | 30 +++++++++++++++---- 6 files changed, 50 insertions(+), 9 deletions(-) diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 941971e7a58d..e887e9d4b967 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -9669,6 +9669,7 @@ packages: '@microsoft/api-extractor': 7.7.11 '@opentelemetry/api': 0.10.2 '@opentelemetry/core': 0.10.2 + '@opentelemetry/resources': 0.10.2 '@opentelemetry/semantic-conventions': 0.10.2 '@opentelemetry/tracing': 0.10.2 '@types/mocha': 7.0.2 @@ -10495,6 +10496,7 @@ packages: integrity: sha512-o7zYyw6/QJHBg2/e3mbbeOe+ohKdRse2FU1aX4i7AdO+1337l44mWy1DwVWhcFUy5aSzahd194/6nrgp7Sj6JQ== tarball: 'file:projects/testhub.tgz' version: 0.0.0 +registry: '' specifiers: '@rush-temp/abort-controller': 'file:./projects/abort-controller.tgz' '@rush-temp/ai-anomaly-detector': 'file:./projects/ai-anomaly-detector.tgz' diff --git a/sdk/monitor/opentelemetry-exporter-azure-monitor/package.json b/sdk/monitor/opentelemetry-exporter-azure-monitor/package.json index b2b94b2d1854..20accf9d07d5 100644 --- a/sdk/monitor/opentelemetry-exporter-azure-monitor/package.json +++ b/sdk/monitor/opentelemetry-exporter-azure-monitor/package.json @@ -82,6 +82,7 @@ "@azure/core-http": "^1.2.0", "@opentelemetry/api": "^0.10.2", "@opentelemetry/core": "^0.10.2", + "@opentelemetry/resources": "^0.10.2", "@opentelemetry/semantic-conventions": "^0.10.2", "@opentelemetry/tracing": "^0.10.2" }, diff --git a/sdk/monitor/opentelemetry-exporter-azure-monitor/src/platform/nodejs/context/context.ts b/sdk/monitor/opentelemetry-exporter-azure-monitor/src/platform/nodejs/context/context.ts index 1ac4ac5a084e..3b65b21bdcaa 100644 --- a/sdk/monitor/opentelemetry-exporter-azure-monitor/src/platform/nodejs/context/context.ts +++ b/sdk/monitor/opentelemetry-exporter-azure-monitor/src/platform/nodejs/context/context.ts @@ -17,8 +17,6 @@ let instance: Context | null = null; export class Context { public tags: Tags; - public static DefaultRoleName: string = "Node.js"; - public static appVersion: { [path: string]: string } = {}; public static sdkVersion: string | null = null; @@ -98,8 +96,6 @@ export class Context { private _loadDeviceContext(): void { this.tags["ai.device.id"] = ""; - this.tags["ai.cloud.role"] = Context.DefaultRoleName; - this.tags["ai.cloud.roleInstance"] = os && os.hostname(); this.tags["ai.device.osVersion"] = os && `${os.type()} ${os.release()}`; // not yet supported tags diff --git a/sdk/monitor/opentelemetry-exporter-azure-monitor/src/utils/constants/applicationinsights.ts b/sdk/monitor/opentelemetry-exporter-azure-monitor/src/utils/constants/applicationinsights.ts index eff3b92ddc00..031aea14aa6c 100644 --- a/sdk/monitor/opentelemetry-exporter-azure-monitor/src/utils/constants/applicationinsights.ts +++ b/sdk/monitor/opentelemetry-exporter-azure-monitor/src/utils/constants/applicationinsights.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +export const AI_CLOUD_ROLE = "ai.cloud.role"; +export const AI_CLOUD_ROLE_INSTACE = "ai.cloud.roleInstance"; export const AI_OPERATION_ID = "ai.operation.id"; export const AI_OPERATION_PARENT_ID = "ai.operation.parentId"; export const AI_OPERATION_NAME = "ai.operation.name"; diff --git a/sdk/monitor/opentelemetry-exporter-azure-monitor/src/utils/spanUtils.ts b/sdk/monitor/opentelemetry-exporter-azure-monitor/src/utils/spanUtils.ts index de99dd664ec9..73f244232c43 100644 --- a/sdk/monitor/opentelemetry-exporter-azure-monitor/src/utils/spanUtils.ts +++ b/sdk/monitor/opentelemetry-exporter-azure-monitor/src/utils/spanUtils.ts @@ -5,6 +5,7 @@ import { URL } from "url"; import { ReadableSpan } from "@opentelemetry/tracing"; import { hrTimeToMilliseconds } from "@opentelemetry/core"; import { SpanKind, Logger, CanonicalCode, Link } from "@opentelemetry/api"; +import { SERVICE_RESOURCE } from "@opentelemetry/resources"; import { Tags, Properties, MSLink, Measurements } from "../types"; import { HTTP_METHOD, @@ -13,6 +14,8 @@ import { HTTP_STATUS_CODE } from "./constants/span/httpAttributes"; import { + AI_CLOUD_ROLE, + AI_CLOUD_ROLE_INSTACE, AI_OPERATION_ID, AI_OPERATION_PARENT_ID, AI_OPERATION_NAME, @@ -35,10 +38,27 @@ import { RemoteDependencyData, RequestData, TelemetryItem as Envelope } from ".. function createTagsFromSpan(span: ReadableSpan): Tags { const context = getInstance(); const tags: Tags = { ...context.tags }; + tags[AI_OPERATION_ID] = span.spanContext.traceId; if (span.parentSpanId) { tags[AI_OPERATION_PARENT_ID] = span.parentSpanId; } + if (span.resource && span.resource.labels) { + const serviceName = span.resource.labels[SERVICE_RESOURCE.NAME]; + const serviceNamespace = span.resource.labels[SERVICE_RESOURCE.NAMESPACE]; + const serviceInstanceId = span.resource.labels[SERVICE_RESOURCE.INSTANCE_ID]; + if (serviceName) { + if (serviceNamespace) { + tags[AI_CLOUD_ROLE] = `${serviceNamespace}.${serviceName}`; + } else { + tags[AI_CLOUD_ROLE] = String(serviceName); + } + } + if (serviceInstanceId) { + tags[AI_CLOUD_ROLE_INSTACE] = String(serviceInstanceId); + } + } + // @todo: is this for RequestData only? if ( (span.kind === SpanKind.SERVER || span.kind === SpanKind.CONSUMER) && diff --git a/sdk/monitor/opentelemetry-exporter-azure-monitor/test/unit/utils/spanUtils.test.ts b/sdk/monitor/opentelemetry-exporter-azure-monitor/test/unit/utils/spanUtils.test.ts index 34c79e3029b8..b57545b35faf 100644 --- a/sdk/monitor/opentelemetry-exporter-azure-monitor/test/unit/utils/spanUtils.test.ts +++ b/sdk/monitor/opentelemetry-exporter-azure-monitor/test/unit/utils/spanUtils.test.ts @@ -1,12 +1,17 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { Span, BasicTracerProvider } from "@opentelemetry/tracing"; +import { Span, BasicTracerProvider, TracerConfig } from "@opentelemetry/tracing"; import { SpanKind, CanonicalCode } from "@opentelemetry/api"; import * as assert from "assert"; import { NoopLogger, hrTimeToMilliseconds } from "@opentelemetry/core"; +import { Resource, SERVICE_RESOURCE } from "@opentelemetry/resources"; import { Tags, Properties, Measurements } from "../../../src/types"; +import { + AI_CLOUD_ROLE, + AI_CLOUD_ROLE_INSTACE +} from "../../../src/utils/constants/applicationinsights"; import * as http from "../../../src/utils/constants/span/httpAttributes"; import * as grpc from "../../../src/utils/constants/span/grpcAttributes"; import * as ai from "../../../src/utils/constants/applicationinsights"; @@ -18,9 +23,16 @@ import { TelemetryItem as Envelope } from "../../../src/generated"; const context = getInstance(undefined, "./", "../../"); -const tracer = new BasicTracerProvider({ - logger: new NoopLogger() -}).getTracer("default"); +const tracerProviderConfig: TracerConfig = { + logger: new NoopLogger(), + resource: new Resource({ + [SERVICE_RESOURCE.INSTANCE_ID]: "testServiceInstanceID", + [SERVICE_RESOURCE.NAME]: "testServiceName", + [SERVICE_RESOURCE.NAMESPACE]: "testServiceNamespace" + }) +}; + +const tracer = new BasicTracerProvider(tracerProviderConfig).getTracer("default"); function assertEnvelope( envelope: Envelope, @@ -49,7 +61,15 @@ function assertEnvelope( assert.deepStrictEqual(envelope.time, expectedTime); } - assert.deepStrictEqual(envelope.tags, { ...context.tags, ...expectedTags }); + const expectedServiceTags: Tags = { + [AI_CLOUD_ROLE]: "testServiceNamespace.testServiceName", + [AI_CLOUD_ROLE_INSTACE]: "testServiceInstanceID" + }; + assert.deepStrictEqual(envelope.tags, { + ...context.tags, + ...expectedServiceTags, + ...expectedTags + }); assert.deepStrictEqual((envelope?.data?.baseData as RequestData).properties, expectedProperties); assert.deepStrictEqual( (envelope?.data?.baseData as RequestData).measurements,