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 all 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 @@ -22,6 +22,9 @@ import type { SpanExporter } from '@opentelemetry/sdk-trace-base';
import type { SpanKind } from '@opentelemetry/api';
import type { TokenCredential } from '@azure/core-auth';

// @public
export const AI_OPERATION_NAME = "ai.operation.name";

// @public
export interface ApplicationInsightsClientOptionalParams extends coreClient.ServiceClientOptions {
endpoint?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ export enum ServiceApiVersion {
V2 = "2020-09-15_Preview",
}

/**
* Operation Name attribute name.
*/
export const AI_OPERATION_NAME = "ai.operation.name";

/**
* Default Breeze endpoint.
* @internal
Expand Down
1 change: 1 addition & 0 deletions sdk/monitor/monitor-opentelemetry-exporter/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export { AzureMonitorLogExporter } from "./export/log.js";
export { AzureMonitorExporterOptions } from "./config.js";
export { ServiceApiVersion } from "./Declarations/Constants.js";
export { ApplicationInsightsClientOptionalParams } from "./generated/models/index.js";
export { AI_OPERATION_NAME } from "./Declarations/Constants.js";
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ function createTagsFromLog(log: ReadableLogRecord): Tags {
if (log.spanContext?.spanId) {
tags[KnownContextTagKeys.AiOperationParentId] = log.spanContext.spanId;
}
if (log.attributes[KnownContextTagKeys.AiOperationName]) {
tags[KnownContextTagKeys.AiOperationName] = log.attributes[
KnownContextTagKeys.AiOperationName
] as string;
}
return tags;
}

Expand All @@ -144,7 +149,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
54 changes: 54 additions & 0 deletions sdk/monitor/monitor-opentelemetry/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,60 @@ const options: AzureMonitorOpenTelemetryOptions = {
useAzureMonitor(options);
```

#### Add Operation Name to Traces and Logs

Use a custom span processor and log record processor in order to attach and correlate operation name from requests to dependencies and logs.

```typescript
import { useAzureMonitor, AzureMonitorOpenTelemetryOptions } from "@azure/monitor-opentelemetry";
import { ReadableSpan, Span, SpanProcessor } from "@opentelemetry/sdk-trace-base";
import { LogRecordProcessor } from "@opentelemetry/sdk-logs";
import { SemanticAttributes } from "@opentelemetry/semantic-conventions";
import { AI_OPERATION_NAME } from "@azure/monitor-opentelemetry-exporter";
import { Context, trace } from "@opentelemetry/api";

class SpanEnrichingProcessor implements SpanProcessor {
forceFlush(): Promise<void> {
return Promise.resolve();
}
shutdown(): Promise<void> {
return Promise.resolve();
}
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 is a ReadableSpan and cast it.
_span.attributes[AI_OPERATION_NAME] = (parentSpan as unknown as ReadableSpan).name
}
}
onEnd(span: ReadableSpan) {}
}

class LogRecordEnrichingProcessor implements LogRecordProcessor {
forceFlush(): Promise<void> {
return Promise.resolve();
}
shutdown(): Promise<void> {
return Promise.resolve();
}
onEmit(_logRecord, _context): void {
const parentSpan = trace.getSpan(_context);
if (parentSpan && "name" in parentSpan) {
// If the parent span has a name we can assume it is a ReadableSpan and cast it.
_logRecord.attributes[AI_OPERATION_NAME] = (parentSpan as unknown as ReadableSpan).name;
}
}
}

// Enable Azure Monitor integration.
const options: AzureMonitorOpenTelemetryOptions = {
// Add the SpanEnrichingProcessor
spanProcessors: [new SpanEnrichingProcessor()],
logRecordProcessors: [new LogRecordEnrichingProcessor()]
}
useAzureMonitor(options);
```

### Filter telemetry

You might use the following ways to filter out telemetry before it leaves your application.
Expand Down
Loading