From c8e0f6b8a97c53949839a232c59f384a63292005 Mon Sep 17 00:00:00 2001 From: Jackson Weber Date: Sun, 27 Apr 2025 22:29:21 -0700 Subject: [PATCH 1/2] Add support for filtering requests and dependencies. --- src/main.ts | 3 ++ src/shim/shim-config.ts | 8 ++++ src/traces/requestProcessor.ts | 44 +++++++++++++++++++ src/types.ts | 2 + test/unitTests/agent/aksLoader.tests.ts | 2 +- test/unitTests/main.tests.ts | 4 +- .../shim/applicationInsights.tests.ts | 9 ++++ 7 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 src/traces/requestProcessor.ts diff --git a/src/main.ts b/src/main.ts index 3fef3f90f..b2eda00e8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -17,6 +17,7 @@ import { AZURE_MONITOR_STATSBEAT_FEATURES, AzureMonitorOpenTelemetryOptions } fr import { ApplicationInsightsConfig } from "./shared/configuration/config"; import { LogApi } from "./shim/logsApi"; import { StatsbeatFeature, StatsbeatInstrumentation } from "./shim/types"; +import { RequestSpanProcessor } from "./traces/requestProcessor"; let autoCollectLogs: AutoCollectLogs; let exceptions: AutoCollectExceptions; @@ -31,6 +32,8 @@ export function useAzureMonitor(options?: AzureMonitorOpenTelemetryOptions) { instrumentation: StatsbeatInstrumentation.NONE, feature: StatsbeatFeature.SHIM }); + // Allows for full filtering of dependency/request spans + options.spanProcessors = [new RequestSpanProcessor(options.enableAutoCollectDependencies, options.enableAutoCollectRequests)]; distroUseAzureMonitor(options); const internalConfig = new ApplicationInsightsConfig(options); const logApi = new LogApi(logs.getLogger("ApplicationInsightsLogger")); diff --git a/src/shim/shim-config.ts b/src/shim/shim-config.ts index f099c164b..11f05ad70 100644 --- a/src/shim/shim-config.ts +++ b/src/shim/shim-config.ts @@ -186,6 +186,14 @@ class Config implements IConfig { options.enableAutoCollectExceptions = this.enableAutoCollectExceptions; } + if (typeof (this.enableAutoCollectRequests) === "boolean") { + options.enableAutoCollectRequests = this.enableAutoCollectRequests; + } + + if (typeof (this.enableAutoCollectDependencies) === "boolean") { + options.enableAutoCollectDependencies = this.enableAutoCollectDependencies; + } + if (this.enableAutoCollectDependencies === false && this.enableAutoCollectRequests === false) { options.instrumentationOptions.http.enabled = false; } diff --git a/src/traces/requestProcessor.ts b/src/traces/requestProcessor.ts new file mode 100644 index 000000000..8752530cb --- /dev/null +++ b/src/traces/requestProcessor.ts @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { Context, SpanKind, TraceFlags } from "@opentelemetry/api"; +import { ReadableSpan, Span, SpanProcessor } from "@opentelemetry/sdk-trace-base"; + +/** + * Azure Monitor Incoming & Outgoing Request Processor. + * @internal + */ +export class RequestSpanProcessor implements SpanProcessor { + private _enableDependencyTelemetry: boolean; + private _enableRequestTelemetry: boolean; + + constructor(enableDependencyTelemetry: boolean, enableRequestTelemetry: boolean) { + this._enableDependencyTelemetry = enableDependencyTelemetry; + this._enableRequestTelemetry = enableRequestTelemetry; + } + + forceFlush(): Promise { + return Promise.resolve(); + } + + onStart(span: Span, _context: Context): void { + return; + } + + onEnd(span: ReadableSpan): void { + if (this._enableDependencyTelemetry === false) { + if (span.kind === SpanKind.CLIENT || span.kind === SpanKind.PRODUCER) { + span.spanContext().traceFlags = TraceFlags.SAMPLED; + } + } + if (this._enableRequestTelemetry === false) { + if (span.kind === SpanKind.SERVER || span.kind === SpanKind.CONSUMER) { + span.spanContext().traceFlags = TraceFlags.SAMPLED; + } + } + } + + shutdown(): Promise { + return Promise.resolve(); + } +} \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index cf897c15d..8e3267ee4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -31,6 +31,8 @@ export interface AzureMonitorOpenTelemetryOptions extends DistroOptions { * if true performance counters will be collected every second and sent to Azure Monitor */ enableAutoCollectPerformance?: boolean; + enableAutoCollectDependencies?: boolean; + enableAutoCollectRequests?: boolean; } export interface InstrumentationOptions extends DistroInstrumentationOptions { diff --git a/test/unitTests/agent/aksLoader.tests.ts b/test/unitTests/agent/aksLoader.tests.ts index 52e844b44..b0faa3706 100644 --- a/test/unitTests/agent/aksLoader.tests.ts +++ b/test/unitTests/agent/aksLoader.tests.ts @@ -57,7 +57,7 @@ describe("agent/AKSLoader", () => { assert.equal(meterProvider["_sharedState"]["metricCollectors"][0]["_metricReader"]["_exporter"].constructor.name, "AzureMonitorMetricExporter"); let tracerProvider = ((trace.getTracerProvider() as ProxyTracerProvider).getDelegate()) as any; assert.equal(tracerProvider.constructor.name, "NodeTracerProvider"); - assert.equal(tracerProvider["_registeredSpanProcessors"][1]["_exporter"].constructor.name, "AzureMonitorTraceExporter"); + assert.equal(tracerProvider["_registeredSpanProcessors"][2]["_exporter"].constructor.name, "AzureMonitorTraceExporter"); let loggerProvider = logs.getLoggerProvider() as any; assert.equal(loggerProvider.constructor.name, "LoggerProvider"); assert.equal(loggerProvider["_sharedState"]["registeredLogRecordProcessors"][1]["_exporter"].constructor.name, "AzureMonitorLogExporter"); diff --git a/test/unitTests/main.tests.ts b/test/unitTests/main.tests.ts index cbc89e268..f3f616f3a 100644 --- a/test/unitTests/main.tests.ts +++ b/test/unitTests/main.tests.ts @@ -30,8 +30,8 @@ describe("ApplicationInsightsClient", () => { let tracerProvider = ((trace.getTracerProvider() as ProxyTracerProvider).getDelegate() as any); let spanProcessors = tracerProvider["_registeredSpanProcessors"]; - assert.ok(spanProcessors.length == 3, "wrong number of spanProcessors"); - otlpExporter = spanProcessors[2]["_exporter"]; + assert.ok(spanProcessors.length == 4, "wrong number of spanProcessors"); + otlpExporter = spanProcessors[3]["_exporter"]; assert.ok(otlpExporter instanceof OTLPTraceExporter, "wrong exporter"); let loggerProvider = ((logs.getLoggerProvider() as LoggerProvider) as any); diff --git a/test/unitTests/shim/applicationInsights.tests.ts b/test/unitTests/shim/applicationInsights.tests.ts index 71557cc63..ac794f270 100644 --- a/test/unitTests/shim/applicationInsights.tests.ts +++ b/test/unitTests/shim/applicationInsights.tests.ts @@ -93,6 +93,15 @@ describe("ApplicationInsights", () => { assert.equal(JSON.stringify((appInsights.defaultClient["_options"].instrumentationOptions as InstrumentationOptions).winston), JSON.stringify({ enabled: false })); }); + it("should set dependency and request collection", () => { + appInsights.setup(connString) + .setAutoCollectRequests(true) + .setAutoCollectDependencies(false) + appInsights.start(); + assert.equal(appInsights.defaultClient["_options"].enableAutoCollectDependencies, false); + assert.equal(appInsights.defaultClient["_options"].enableAutoCollectRequests, true); + }); + describe("#CorrelationContext", () => { it("should return context once AppInsights is intialized", () => { appInsights.setup(connString).start(); From b39bb2827e9383ec5249f19107f2c4ab186bb8b9 Mon Sep 17 00:00:00 2001 From: Jackson Weber Date: Mon, 19 May 2025 11:38:29 -0700 Subject: [PATCH 2/2] Move span processing logic. --- src/traces/requestProcessor.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/traces/requestProcessor.ts b/src/traces/requestProcessor.ts index 8752530cb..779d63963 100644 --- a/src/traces/requestProcessor.ts +++ b/src/traces/requestProcessor.ts @@ -22,10 +22,6 @@ export class RequestSpanProcessor implements SpanProcessor { } onStart(span: Span, _context: Context): void { - return; - } - - onEnd(span: ReadableSpan): void { if (this._enableDependencyTelemetry === false) { if (span.kind === SpanKind.CLIENT || span.kind === SpanKind.PRODUCER) { span.spanContext().traceFlags = TraceFlags.SAMPLED; @@ -38,6 +34,10 @@ export class RequestSpanProcessor implements SpanProcessor { } } + onEnd(span: ReadableSpan): void { + return; + } + shutdown(): Promise { return Promise.resolve(); }