From b4d4d50865b5e8c6f6da69ceb4d54626fa95cdf4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 May 2025 22:25:16 +0000 Subject: [PATCH 1/6] Initial plan for issue From 577965775780ee77867c865f766cc2ea505d9fc9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 May 2025 22:43:24 +0000 Subject: [PATCH 2/6] Add support for sessionId and ai-session-id headers for frontend-backend correlation Co-authored-by: JacksonWeber <47067795+JacksonWeber@users.noreply.github.com> --- README.md | 19 +++++++++++ src/shim/correlationContextManager.ts | 33 ++++++++++++++++--- .../shim/correlationContextManger.tests.ts | 31 +++++++++++++++++ 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8cb2f78a..f3c3a68c 100644 --- a/README.md +++ b/README.md @@ -354,6 +354,25 @@ The following configurations are set using either environment variables, setting | extendedMetricDisablers | Will not have any effect as extended/native metrics are not supported in the Application Insights 3.X SDK or Azure Monitor OpenTelemetry. | | correlationHeaderExcludedDomains | Not supported in the Application Insights 3.X SDK or Azure Monitor OpenTelemetry. | +## Distributed Tracing and Correlation + +The SDK automatically correlates telemetry associated with a request by using a W3C trace context. When a request is processed, the SDK creates a context for that operation, including a unique identifier that helps correlate all telemetry related to that operation. + +### Frontend to Backend Correlation + +When correlating between Frontend (e.g., using `@microsoft/applicationinsights-web`) and Backend (using this SDK), the following headers are supported: + +| Header | Description | Required | +|--------|-------------|----------| +| `traceparent` | W3C trace context header with format `00-traceId-spanId-flags` | Yes | +| `tracestate` | W3C trace state header with vendor-specific data | No | +| `request-id` | Legacy Application Insights header | No, fallback if traceparent is not provided | +| `sessionId` or `ai-session-id` | Session ID for correlation | No | + +The Backend will automatically read these headers from incoming requests and establish the correlation context. No additional code is needed to support this correlation. + +For more information about W3C Trace Context, see the [W3C Trace Context specification](https://www.w3.org/TR/trace-context/). + The following methods are part of the `TelemetryClient` class. They can be called using `applicationinsights.defaultClient.()`. |Property | Support Status | diff --git a/src/shim/correlationContextManager.ts b/src/shim/correlationContextManager.ts index 7eeef3f9..bdfaf79a 100644 --- a/src/shim/correlationContextManager.ts +++ b/src/shim/correlationContextManager.ts @@ -69,6 +69,7 @@ export class CorrelationContextManager { * @param operationName Human readable name of the span * @param traceparent Context conveying string in the format version-traceId-spanId-traceFlag * @param tracestate String of key value pairs for additional trace context + * @param sessionId Optional sessionId for correlation between Frontend and Backend * @returns ICorrelationContext object */ public static generateContextObject( @@ -76,13 +77,20 @@ export class CorrelationContextManager { parentId?: string, operationName?: string, traceparent?: ITraceparent, - tracestate?: TraceState + tracestate?: TraceState, + sessionId?: string ): ICorrelationContext { // Cast OpenTelemetry TraceState object to ITracestate object const ITraceState: ITracestate = { fieldmap: tracestate?.serialize()?.split(",") }; + // Create a properties object that stores sessionId if provided + const properties: { [key: string]: string } = {}; + if (sessionId) { + properties["sessionId"] = sessionId; + } + return { operation: { name: operationName, @@ -93,8 +101,8 @@ export class CorrelationContextManager { }, // Headers are not being used so custom properties will always be stubbed out customProperties: { - getProperty(prop: string) { return "" }, - setProperty(prop: string) { return "" }, + getProperty(prop: string) { return properties[prop] || ""; }, + setProperty(prop: string, value: string) { properties[prop] = value; return ""; } } as ICustomProperties, } } @@ -169,6 +177,12 @@ export class CorrelationContextManager { * @param input Any kind of object we can extract context information from * @param request HTTP request we can pull context information from in the form of the request's headers * @returns IcorrelationContext object + * + * For correlation between Frontend and Backend, the following headers are supported: + * - traceparent: W3C trace context header (required for distributed tracing) + * - tracestate: W3C trace state header (optional) + * - request-id: Legacy Application Insights header (optional, fallback if traceparent is not provided) + * - sessionId or ai-session-id: Session ID for correlation (optional) */ public static startOperation( input: AzureFnContext | (http.IncomingMessage | AzureFnRequest) | SpanContext | Span, @@ -227,12 +241,20 @@ export class CorrelationContextManager { // If headers is defined instead of traceContext, use the headers to set the traceparent and tracestate // If headers is not an instance of Headers, we use the old programming model, otherwise use the old v3 values + let sessionId: string = null; if (headers && (headers as HttpRequestHeaders).traceparent) { traceparent = (headers as HttpRequestHeaders).traceparent ? (headers as HttpRequestHeaders).traceparent.toString() : null; tracestate = (headers as HttpRequestHeaders).tracestate ? (headers as HttpRequestHeaders).tracestate.toString() : tracestate; - } else if (headers && headers.get) { + // Check for sessionId in headers for correlation between Frontend and Backend + sessionId = (headers as HttpRequestHeaders).sessionId ? (headers as HttpRequestHeaders).sessionId.toString() : + (headers as HttpRequestHeaders)["ai-session-id"] ? (headers as HttpRequestHeaders)["ai-session-id"].toString() : null; + diag.debug("Found session ID in headers:", sessionId); + } else if (headers && (headers as any).get && typeof (headers as any).get === 'function') { traceparent = (headers as any).get("traceparent") || (headers as any).get("request-id"); tracestate = (headers as any).get("tracestate"); + // Check for sessionId in headers for correlation between Frontend and Backend + sessionId = (headers as any).get("sessionId") || (headers as any).get("ai-session-id"); + diag.debug("Found session ID in headers with get():", sessionId); } const traceArray: string[] = traceparent?.split("-"); @@ -255,7 +277,8 @@ export class CorrelationContextManager { traceId: traceArray[1], version: "00", }, - tracestateObj + tracestateObj, + sessionId ); } catch (error) { diag.warn("Error creating context object", Util.getInstance().dumpObj(error)); diff --git a/test/unitTests/shim/correlationContextManger.tests.ts b/test/unitTests/shim/correlationContextManger.tests.ts index 5b81b581..d31de648 100644 --- a/test/unitTests/shim/correlationContextManger.tests.ts +++ b/test/unitTests/shim/correlationContextManger.tests.ts @@ -297,6 +297,37 @@ describe("#startOperation()", () => { testFunctionTraceContext.traceparent ); }); + + it("should include sessionId in customProperties when provided", () => { + // Create a test request with sessionId + const requestWithSessionId = { + ...testRequest, + headers: { + ...testRequest.headers, + sessionId: "test-session-id" + } + }; + + const context = CorrelationContextManager.startOperation(requestWithSessionId, "GET /test"); + + assert.ok(context.operation); + assert.deepEqual(context.customProperties.getProperty("sessionId"), "test-session-id"); + }); + + it("should include ai-session-id in customProperties when provided", () => { + // Use an object with direct header properties + const requestWithSessionIdHeader = { + headers: { + traceparent: testFunctionContext.traceContext.traceparent, + "ai-session-id": "test-ai-session-id" + } + }; + + const context = CorrelationContextManager.startOperation(requestWithSessionIdHeader as any, "GET /test"); + + assert.ok(context.operation); + assert.strictEqual(context.customProperties.getProperty("sessionId"), "test-ai-session-id"); + }); }); /** From cbb5892a2eb5347c17fc10e6c7398db6122e89a1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 May 2025 22:49:25 +0000 Subject: [PATCH 3/6] Enhance documentation for frontend-backend correlation with detailed examples Co-authored-by: JacksonWeber <47067795+JacksonWeber@users.noreply.github.com> --- README.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f3c3a68c..9651eeba 100644 --- a/README.md +++ b/README.md @@ -369,7 +369,30 @@ When correlating between Frontend (e.g., using `@microsoft/applicationinsights-w | `request-id` | Legacy Application Insights header | No, fallback if traceparent is not provided | | `sessionId` or `ai-session-id` | Session ID for correlation | No | -The Backend will automatically read these headers from incoming requests and establish the correlation context. No additional code is needed to support this correlation. +#### How Frontend-Backend Correlation Works + +1. **Frontend Setup**: The frontend application (using `@microsoft/applicationinsights-web`) automatically includes these headers in outgoing HTTP requests when properly configured with `enableCorsCorrelation: true`. + + ```javascript + // Example of frontend setup with Application Insights + const appInsights = new ApplicationInsights({ + config: { + connectionString: "your-connection-string", + enableCorsCorrelation: true // Important for cross-domain correlation + } + }); + appInsights.loadAppInsights(); + ``` + +2. **Automatic Header Processing**: When the backend receives a request, this SDK automatically: + - Extracts the correlation headers (`traceparent`, `tracestate`, etc.) from the incoming request + - Creates a correlation context based on these headers + - Associates this context with the current execution + - All telemetry sent during request processing is automatically linked to this context + +3. **Session Correlation**: If `sessionId` or `ai-session-id` headers are present, they are automatically stored in the correlation context's custom properties and will be included in telemetry. + +**No manual code is required** in your backend application to maintain this correlation. The SDK handles all the correlation context management internally. For more information about W3C Trace Context, see the [W3C Trace Context specification](https://www.w3.org/TR/trace-context/). From 8e4b74bd1c1a712bb6cf57daf44d053003b45e5b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 May 2025 22:53:14 +0000 Subject: [PATCH 4/6] Remove sessionId from README.md documentation Co-authored-by: JacksonWeber <47067795+JacksonWeber@users.noreply.github.com> --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9651eeba..b7dde5cf 100644 --- a/README.md +++ b/README.md @@ -367,7 +367,7 @@ When correlating between Frontend (e.g., using `@microsoft/applicationinsights-w | `traceparent` | W3C trace context header with format `00-traceId-spanId-flags` | Yes | | `tracestate` | W3C trace state header with vendor-specific data | No | | `request-id` | Legacy Application Insights header | No, fallback if traceparent is not provided | -| `sessionId` or `ai-session-id` | Session ID for correlation | No | +| `ai-session-id` | Session ID for correlation | No | #### How Frontend-Backend Correlation Works @@ -390,7 +390,7 @@ When correlating between Frontend (e.g., using `@microsoft/applicationinsights-w - Associates this context with the current execution - All telemetry sent during request processing is automatically linked to this context -3. **Session Correlation**: If `sessionId` or `ai-session-id` headers are present, they are automatically stored in the correlation context's custom properties and will be included in telemetry. +3. **Session Correlation**: If `ai-session-id` headers are present, they are automatically stored in the correlation context's custom properties and will be included in telemetry. **No manual code is required** in your backend application to maintain this correlation. The SDK handles all the correlation context management internally. From 567287355a0b8cd3f80db4779c94834b1d1ca516 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 May 2025 02:10:41 +0000 Subject: [PATCH 5/6] Revert changes to correlationContextManager.ts Co-authored-by: JacksonWeber <47067795+JacksonWeber@users.noreply.github.com> --- src/shim/correlationContextManager.ts | 33 ++++----------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/src/shim/correlationContextManager.ts b/src/shim/correlationContextManager.ts index bdfaf79a..7eeef3f9 100644 --- a/src/shim/correlationContextManager.ts +++ b/src/shim/correlationContextManager.ts @@ -69,7 +69,6 @@ export class CorrelationContextManager { * @param operationName Human readable name of the span * @param traceparent Context conveying string in the format version-traceId-spanId-traceFlag * @param tracestate String of key value pairs for additional trace context - * @param sessionId Optional sessionId for correlation between Frontend and Backend * @returns ICorrelationContext object */ public static generateContextObject( @@ -77,20 +76,13 @@ export class CorrelationContextManager { parentId?: string, operationName?: string, traceparent?: ITraceparent, - tracestate?: TraceState, - sessionId?: string + tracestate?: TraceState ): ICorrelationContext { // Cast OpenTelemetry TraceState object to ITracestate object const ITraceState: ITracestate = { fieldmap: tracestate?.serialize()?.split(",") }; - // Create a properties object that stores sessionId if provided - const properties: { [key: string]: string } = {}; - if (sessionId) { - properties["sessionId"] = sessionId; - } - return { operation: { name: operationName, @@ -101,8 +93,8 @@ export class CorrelationContextManager { }, // Headers are not being used so custom properties will always be stubbed out customProperties: { - getProperty(prop: string) { return properties[prop] || ""; }, - setProperty(prop: string, value: string) { properties[prop] = value; return ""; } + getProperty(prop: string) { return "" }, + setProperty(prop: string) { return "" }, } as ICustomProperties, } } @@ -177,12 +169,6 @@ export class CorrelationContextManager { * @param input Any kind of object we can extract context information from * @param request HTTP request we can pull context information from in the form of the request's headers * @returns IcorrelationContext object - * - * For correlation between Frontend and Backend, the following headers are supported: - * - traceparent: W3C trace context header (required for distributed tracing) - * - tracestate: W3C trace state header (optional) - * - request-id: Legacy Application Insights header (optional, fallback if traceparent is not provided) - * - sessionId or ai-session-id: Session ID for correlation (optional) */ public static startOperation( input: AzureFnContext | (http.IncomingMessage | AzureFnRequest) | SpanContext | Span, @@ -241,20 +227,12 @@ export class CorrelationContextManager { // If headers is defined instead of traceContext, use the headers to set the traceparent and tracestate // If headers is not an instance of Headers, we use the old programming model, otherwise use the old v3 values - let sessionId: string = null; if (headers && (headers as HttpRequestHeaders).traceparent) { traceparent = (headers as HttpRequestHeaders).traceparent ? (headers as HttpRequestHeaders).traceparent.toString() : null; tracestate = (headers as HttpRequestHeaders).tracestate ? (headers as HttpRequestHeaders).tracestate.toString() : tracestate; - // Check for sessionId in headers for correlation between Frontend and Backend - sessionId = (headers as HttpRequestHeaders).sessionId ? (headers as HttpRequestHeaders).sessionId.toString() : - (headers as HttpRequestHeaders)["ai-session-id"] ? (headers as HttpRequestHeaders)["ai-session-id"].toString() : null; - diag.debug("Found session ID in headers:", sessionId); - } else if (headers && (headers as any).get && typeof (headers as any).get === 'function') { + } else if (headers && headers.get) { traceparent = (headers as any).get("traceparent") || (headers as any).get("request-id"); tracestate = (headers as any).get("tracestate"); - // Check for sessionId in headers for correlation between Frontend and Backend - sessionId = (headers as any).get("sessionId") || (headers as any).get("ai-session-id"); - diag.debug("Found session ID in headers with get():", sessionId); } const traceArray: string[] = traceparent?.split("-"); @@ -277,8 +255,7 @@ export class CorrelationContextManager { traceId: traceArray[1], version: "00", }, - tracestateObj, - sessionId + tracestateObj ); } catch (error) { diag.warn("Error creating context object", Util.getInstance().dumpObj(error)); From a0a019625e88306336169e4c07492fe33c8469bf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 May 2025 02:17:22 +0000 Subject: [PATCH 6/6] Remove references to request-id and ai-session-id from README.md Co-authored-by: JacksonWeber <47067795+JacksonWeber@users.noreply.github.com> --- README.md | 6 +--- .../shim/correlationContextManger.tests.ts | 31 ------------------- 2 files changed, 1 insertion(+), 36 deletions(-) diff --git a/README.md b/README.md index b7dde5cf..e9343a93 100644 --- a/README.md +++ b/README.md @@ -366,8 +366,6 @@ When correlating between Frontend (e.g., using `@microsoft/applicationinsights-w |--------|-------------|----------| | `traceparent` | W3C trace context header with format `00-traceId-spanId-flags` | Yes | | `tracestate` | W3C trace state header with vendor-specific data | No | -| `request-id` | Legacy Application Insights header | No, fallback if traceparent is not provided | -| `ai-session-id` | Session ID for correlation | No | #### How Frontend-Backend Correlation Works @@ -385,13 +383,11 @@ When correlating between Frontend (e.g., using `@microsoft/applicationinsights-w ``` 2. **Automatic Header Processing**: When the backend receives a request, this SDK automatically: - - Extracts the correlation headers (`traceparent`, `tracestate`, etc.) from the incoming request + - Extracts the correlation headers (`traceparent`, `tracestate`) from the incoming request - Creates a correlation context based on these headers - Associates this context with the current execution - All telemetry sent during request processing is automatically linked to this context -3. **Session Correlation**: If `ai-session-id` headers are present, they are automatically stored in the correlation context's custom properties and will be included in telemetry. - **No manual code is required** in your backend application to maintain this correlation. The SDK handles all the correlation context management internally. For more information about W3C Trace Context, see the [W3C Trace Context specification](https://www.w3.org/TR/trace-context/). diff --git a/test/unitTests/shim/correlationContextManger.tests.ts b/test/unitTests/shim/correlationContextManger.tests.ts index d31de648..5b81b581 100644 --- a/test/unitTests/shim/correlationContextManger.tests.ts +++ b/test/unitTests/shim/correlationContextManger.tests.ts @@ -297,37 +297,6 @@ describe("#startOperation()", () => { testFunctionTraceContext.traceparent ); }); - - it("should include sessionId in customProperties when provided", () => { - // Create a test request with sessionId - const requestWithSessionId = { - ...testRequest, - headers: { - ...testRequest.headers, - sessionId: "test-session-id" - } - }; - - const context = CorrelationContextManager.startOperation(requestWithSessionId, "GET /test"); - - assert.ok(context.operation); - assert.deepEqual(context.customProperties.getProperty("sessionId"), "test-session-id"); - }); - - it("should include ai-session-id in customProperties when provided", () => { - // Use an object with direct header properties - const requestWithSessionIdHeader = { - headers: { - traceparent: testFunctionContext.traceContext.traceparent, - "ai-session-id": "test-ai-session-id" - } - }; - - const context = CorrelationContextManager.startOperation(requestWithSessionIdHeader as any, "GET /test"); - - assert.ok(context.operation); - assert.strictEqual(context.customProperties.getProperty("sessionId"), "test-ai-session-id"); - }); }); /**