diff --git a/src/main.ts b/src/main.ts index 3fef3f90..b2eda00e 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 f099c164..11f05ad7 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 00000000..779d6396 --- /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 { + 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; + } + } + } + + onEnd(span: ReadableSpan): void { + return; + } + + shutdown(): Promise { + return Promise.resolve(); + } +} \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index cf897c15..8e3267ee 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 52e844b4..b0faa370 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 cbc89e26..f3f616f3 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 71557cc6..ac794f27 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();