Skip to content

Commit

Permalink
[Monitor OpenTelemetry][Monitor OpenTelemetry Exporter] Add Operation…
Browse files Browse the repository at this point in the history
… Name Propagation for Dependencies and Logs (#31778)

### Packages impacted by this PR
@azure/monitor-opentelemetry
@azure/monitor-opentelemetry-exporter

### Describe the problem that is addressed by this PR
We should support operation name propagation for dependencies and logs.
The operation name is sourced from the parent request span and
propagates to any child dependencies or logs.

### Are there test cases added in this PR? _(If not, why?)_
Yes

### Checklists
- [x] Added impacted package name to the issue description
- [ ] Does this PR needs any fixes in the SDK Generator?** _(If so,
create an Issue in the
[Autorest/typescript](https://github.com/Azure/autorest.typescript)
repository and link it here)_
- [x] Added a changelog (if necessary)
  • Loading branch information
JacksonWeber authored Dec 2, 2024
1 parent 1350c15 commit 8841687
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 3 deletions.
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

0 comments on commit 8841687

Please sign in to comment.