diff --git a/packages/agents-a365-observability/src/tracing/middleware/BaggageBuilder.ts b/packages/agents-a365-observability/src/tracing/middleware/BaggageBuilder.ts index 19b6f297..fc2e1345 100644 --- a/packages/agents-a365-observability/src/tracing/middleware/BaggageBuilder.ts +++ b/packages/agents-a365-observability/src/tracing/middleware/BaggageBuilder.ts @@ -99,6 +99,16 @@ export class BaggageBuilder { return this; } + /** + * Set the session ID baggage value. + * @param value The session ID + * @returns Self for method chaining + */ + sessionId(value: string): BaggageBuilder { + this.set(OpenTelemetryConstants.SESSION_ID_KEY, value); + return this; + } + /** * Set the caller ID baggage value. * @param value The caller ID @@ -267,7 +277,7 @@ export class BaggageBuilder { static setRequestContext( tenantId?: string | null, agentId?: string | null, - correlationId?: string | null + correlationId?: string | null, ): BaggageScope { return new BaggageBuilder() .tenantId(tenantId) diff --git a/packages/agents-a365-observability/src/tracing/processors/util.ts b/packages/agents-a365-observability/src/tracing/processors/util.ts index b9aa94c4..4b960dae 100644 --- a/packages/agents-a365-observability/src/tracing/processors/util.ts +++ b/packages/agents-a365-observability/src/tracing/processors/util.ts @@ -12,6 +12,7 @@ export const GENERIC_ATTRIBUTES: readonly string[] = [ consts.CUSTOM_PARENT_SPAN_ID_KEY, consts.CUSTOM_SPAN_NAME_KEY, consts.CORRELATION_ID_KEY, + consts.SESSION_ID_KEY, consts.GEN_AI_CONVERSATION_ID_KEY, consts.GEN_AI_CONVERSATION_ITEM_LINK_KEY, consts.GEN_AI_OPERATION_NAME_KEY, diff --git a/tests/observability/core/BaggageBuilder.test.ts b/tests/observability/core/BaggageBuilder.test.ts index 73ab2e41..e3afd04a 100644 --- a/tests/observability/core/BaggageBuilder.test.ts +++ b/tests/observability/core/BaggageBuilder.test.ts @@ -2,7 +2,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // ------------------------------------------------------------------------------ -import { context } from '@opentelemetry/api'; +import { context, propagation } from '@opentelemetry/api'; import { BaggageBuilder, BaggageScope } from '@microsoft/agents-a365-observability/dist/cjs/tracing/middleware/BaggageBuilder'; import { OpenTelemetryConstants } from '@microsoft/agents-a365-observability/dist/cjs/tracing/constants'; @@ -133,6 +133,27 @@ describe('BaggageBuilder', () => { }); }); + describe('sessionId support', () => { + it('should set sessionId via fluent API', () => { + const scope = new BaggageBuilder() + .tenantId('tenant-123') + .agentId('agent-456') + .correlationId('corr-789') + .sessionId('session-0001') + .build(); + const bag = propagation.getBaggage((scope as any).contextWithBaggage); + expect(bag?.getEntry(OpenTelemetryConstants.SESSION_ID_KEY)?.value).toBe('session-0001'); + }); + + it('should omit empty sessionId value', () => { + const scope = new BaggageBuilder() + .sessionId(' ') + .build(); + const bag = propagation.getBaggage((scope as any).contextWithBaggage); + expect(bag?.getEntry(OpenTelemetryConstants.SESSION_ID_KEY)).toBeUndefined(); + }); + }); + }); describe('BaggageScope', () => { diff --git a/tests/observability/core/SpanProcessor.test.ts b/tests/observability/core/SpanProcessor.test.ts index 32b75f8b..2b175904 100644 --- a/tests/observability/core/SpanProcessor.test.ts +++ b/tests/observability/core/SpanProcessor.test.ts @@ -27,7 +27,6 @@ describe('SpanProcessor', () => { describe('baggage to span attribute enrichment', () => { it('should copy generic attributes from baggage to span', () => { - // Set baggage const baggageEntries = { [OpenTelemetryConstants.TENANT_ID_KEY]: 'tenant-123', [OpenTelemetryConstants.CORRELATION_ID_KEY]: 'corr-456', @@ -41,7 +40,7 @@ describe('SpanProcessor', () => { const ctx = propagation.setBaggage(context.active(), baggage); - // Create a span in this context + // Create a span in this context (parentContext not passed so processor may no-op) const tracer = provider.getTracer('test'); let testSpan: Span | undefined; @@ -52,10 +51,23 @@ describe('SpanProcessor', () => { } }); - // The span processor should have copied baggage to attributes expect(testSpan).toBeDefined(); }); + + it('should copy sessionId from baggage to span', () => { + let baggage = propagation.createBaggage(); + baggage = baggage.setEntry(OpenTelemetryConstants.SESSION_ID_KEY, { value: 'session-abc' }); + + const ctx = propagation.setBaggage(context.active(), baggage); + const tracer = provider.getTracer('test'); + const testSpan = tracer.startSpan('test-span', { kind: SpanKind.CLIENT }, ctx as any); + testSpan.end(); + + const attrs = (testSpan as any)._attributes ?? (testSpan as any).attributes ?? {}; + expect(attrs[OpenTelemetryConstants.SESSION_ID_KEY]).toBe('session-abc'); + }); + it('should copy invoke agent attributes for invoke_agent operations', () => { // Set baggage with invoke agent specific fields const baggageEntries = { @@ -139,6 +151,7 @@ describe('SpanProcessor', () => { expect(GENERIC_ATTRIBUTES).toContain(OpenTelemetryConstants.TENANT_ID_KEY); expect(GENERIC_ATTRIBUTES).toContain(OpenTelemetryConstants.CORRELATION_ID_KEY); expect(GENERIC_ATTRIBUTES).toContain(OpenTelemetryConstants.GEN_AI_AGENT_ID_KEY); + expect(GENERIC_ATTRIBUTES).toContain(OpenTelemetryConstants.SESSION_ID_KEY); }); it('should apply invoke agent specific attributes', () => {