Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Monitor OpenTelemetry][Monitor OpenTelemetry Exporter] Add Operation Name Propagation for Dependencies and Logs #31778

Merged
merged 16 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions sdk/monitor/monitor-opentelemetry-exporter/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Release History

## 1.0.0-beta.28 ()

### Features Added

- Added support for operation name on dependencies and logs.

## 1.0.0-beta.27 (2024-10-23)

### Other Changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ export function logToEnvelope(log: ReadableLogRecord, ikey: string): Envelope |
const sampleRate = 100;
const instrumentationKey = ikey;
const tags = createTagsFromLog(log);
if (log.attributes[KnownContextTagKeys.AiOperationName]) {
tags[KnownContextTagKeys.AiOperationName] = log.attributes[
KnownContextTagKeys.AiOperationName
] as string;
}
// eslint-disable-next-line prefer-const
let [properties, measurements] = createPropertiesFromLog(log);
let name: string;
Expand Down Expand Up @@ -141,7 +146,8 @@ function createPropertiesFromLog(log: ReadableLogRecord): [Properties, Measureme
key.startsWith("_MS.") ||
key === ATTR_EXCEPTION_TYPE ||
key === ATTR_EXCEPTION_MESSAGE ||
key === ATTR_EXCEPTION_STACKTRACE
key === ATTR_EXCEPTION_STACKTRACE ||
key === (KnownContextTagKeys.AiOperationName as string)
)
) {
properties[key] = serializeAttribute(log.attributes[key]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,14 @@ function createTagsFromSpan(span: ReadableSpan): Tags {
tags[KnownContextTagKeys.AiLocationIp] = String(netPeerIp);
}
}
} else {
if (span.attributes[KnownContextTagKeys.AiOperationName]) {
tags[KnownContextTagKeys.AiOperationName] = span.attributes[
KnownContextTagKeys.AiOperationName
] as string;
}
}
// TODO: Operation Name and Location IP TBD for non server spans
// TODO: Location IP TBD for non server spans

return tags;
}
Expand Down Expand Up @@ -141,7 +147,8 @@ function createPropertiesFromSpanAttributes(attributes?: Attributes): {
key === SEMATTRS_RPC_GRPC_STATUS_CODE ||
key === SEMATTRS_EXCEPTION_TYPE ||
key === SEMATTRS_EXCEPTION_MESSAGE ||
key === SEMATTRS_EXCEPTION_STACKTRACE
key === SEMATTRS_EXCEPTION_STACKTRACE ||
key === (KnownContextTagKeys.AiOperationName as string)
)
) {
properties[key] = serializeAttribute(attributes[key]);
Expand Down
2 changes: 1 addition & 1 deletion sdk/monitor/monitor-opentelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

### Other Changes

- Update to using the logRecordProcessors property.
- Update to using the logRecordProcessors property.

## 1.7.1 (2024-09-13)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { Context, trace } from "@opentelemetry/api";
import type { MetricHandler } from "../metrics/handler";
import type { LogRecord, LogRecordProcessor } from "@opentelemetry/sdk-logs";
import { ReadableSpan } from "@opentelemetry/sdk-trace-base";
import { AI_OPERATION_NAME } from "../types";

/**
* Azure Monitor LogRecord Processor.
Expand All @@ -15,7 +18,12 @@ export class AzureLogRecordProcessor implements LogRecordProcessor {
this._metricHandler = metricHandler;
}

public onEmit(logRecord: LogRecord): void {
public onEmit(logRecord: LogRecord, context: Context): void {
const parentSpan = trace.getSpan(context);
if (parentSpan && "name" in parentSpan) {
// If the parent span has a name we can assume it's a ReadableSpan and cast it as such
logRecord.attributes[AI_OPERATION_NAME] = (parentSpan as unknown as ReadableSpan).name;
}
this._metricHandler.recordLog(logRecord);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import type { Context } from "@opentelemetry/api";
import { trace, type Context } from "@opentelemetry/api";
import type { ReadableSpan, Span, SpanProcessor } from "@opentelemetry/sdk-trace-base";
import type { MetricHandler } from "../metrics";
import { AI_OPERATION_NAME } from "../types";

/**
* Azure Monitor Span Processor.
Expand All @@ -21,6 +22,11 @@ export class AzureMonitorSpanProcessor implements SpanProcessor {
}

onStart(span: Span, _context: Context): void {
const parentSpan = trace.getSpan(_context);
if (parentSpan && "name" in parentSpan) {
// If the parent span has a name we can assume it's a ReadableSpan and cast it as such
span.attributes[AI_OPERATION_NAME] = (parentSpan as unknown as ReadableSpan).name;
}
this._metricHandler.markSpanAsProcessed(span);
}

Expand Down
5 changes: 5 additions & 0 deletions sdk/monitor/monitor-opentelemetry/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,11 @@ export const DEFAULT_BREEZE_ENDPOINT = "https://dc.services.visualstudio.com";
* @internal
*/
export const DEFAULT_LIVEMETRICS_ENDPOINT = "https://global.livediagnostics.monitor.azure.com";
/**
* Operation Name attribute name.
* @internal
*/
export const AI_OPERATION_NAME = "ai.operation.name";

/**
* Internal attribute name for sample rate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type {
import type { ProxyTracerProvider, Span } from "@opentelemetry/api";
import { metrics, trace } from "@opentelemetry/api";
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
import { AI_OPERATION_NAME } from "../../../../src/types";

describe("Library/TraceHandler", () => {
let http: any = null;
Expand Down Expand Up @@ -350,5 +351,28 @@ describe("Library/TraceHandler", () => {
done(error);
});
});

it("Span processing propagates operation id", (done) => {
createHandler({ enabled: true });
makeHttpRequest()
.then(() => {
((trace.getTracerProvider() as ProxyTracerProvider).getDelegate() as NodeTracerProvider)
.forceFlush()
.then(() => {
assert.ok(exportStub.calledOnce, "Export called");
const spans = exportStub.args[0][0];
assert.deepStrictEqual(spans.length, 2);
// Outgoing request
assert.deepStrictEqual(spans[1].attributes[AI_OPERATION_NAME], "test");
done();
})
.catch((error) => {
done(error);
});
})
.catch((error) => {
done(error);
});
});
});
});