From b87df1d7f57dd1768c210a812559ecc3dd41c9f6 Mon Sep 17 00:00:00 2001 From: Fernando Rojo Date: Thu, 9 Jul 2026 18:32:39 -0700 Subject: [PATCH 01/13] Reduce Telemetry by only logging certain events when diagnostics are enabled --- .../Records/BaseDetectionTelemetryRecord.cs | 9 +++++- .../CommandLineInvocationTelemetryRecord.cs | 22 +++++++++++++- .../DependencyGraphTranslationRecord.cs | 2 ++ .../Records/DetectedComponentScopeRecord.cs | 2 ++ .../DetectorExecutionTelemetryRecord.cs | 29 ++++++++++++++++++- 5 files changed, 61 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs index be3402694..9d0777cab 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs @@ -6,6 +6,8 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; public abstract class BaseDetectionTelemetryRecord : IDetectionTelemetryRecord { + private static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("agent.diagnostic"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("System.Debug"), "True", StringComparison.OrdinalIgnoreCase); + private readonly Stopwatch stopwatch = new Stopwatch(); private bool disposedValue; @@ -14,6 +16,8 @@ public abstract class BaseDetectionTelemetryRecord : IDetectionTelemetryRecord public abstract string RecordName { get; } + public virtual bool IsDiagnostic { get; } + [Metric] public TimeSpan? ExecutionTime { get; protected set; } @@ -39,7 +43,10 @@ protected virtual void Dispose(bool disposing) if (disposing) { this.StopExecutionTimer(); - TelemetryRelay.Instance.PostTelemetryRecord(this); + if (!this.IsDiagnostic || DiagnosticEnabled) + { + TelemetryRelay.Instance.PostTelemetryRecord(this); + } } this.disposedValue = true; diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs index c5730d20c..24bbb2227 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs @@ -1,10 +1,13 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; using System; +using System.Linq; using Microsoft.ComponentDetection.Contracts; internal class CommandLineInvocationTelemetryRecord : BaseDetectionTelemetryRecord { + private static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("agent.diagnostic"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("System.Debug"), "True", StringComparison.OrdinalIgnoreCase); + public override string RecordName => "CommandLineInvocation"; public string? PathThatWasRan { get; set; } @@ -20,7 +23,8 @@ internal class CommandLineInvocationTelemetryRecord : BaseDetectionTelemetryReco internal void Track(CommandLineExecutionResult result, string path, string parameters) { this.ExitCode = result.ExitCode; - this.StandardError = result.StdErr?.RemoveSensitiveInformation(); + var sanitizedError = result.StdErr?.RemoveSensitiveInformation(); + this.StandardError = DiagnosticEnabled ? sanitizedError : this.TruncateStandardErrorTo10Lines(sanitizedError); this.TrackCommon(path, parameters); } @@ -37,4 +41,20 @@ private void TrackCommon(string path, string parameters) this.Parameters = parameters?.RemoveSensitiveInformation(); this.StopExecutionTimer(); } + + private string? TruncateStandardErrorTo10Lines(string? error) + { + if (string.IsNullOrEmpty(error)) + { + return error; + } + + var lines = error.Split(["\r\n", "\r", "\n"], StringSplitOptions.None); + if (lines.Length <= 10) + { + return error; + } + + return string.Join(Environment.NewLine, lines.TakeLast(10)); + } } diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DependencyGraphTranslationRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DependencyGraphTranslationRecord.cs index 782dd3022..168f08973 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DependencyGraphTranslationRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DependencyGraphTranslationRecord.cs @@ -4,6 +4,8 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; internal class DependencyGraphTranslationRecord : BaseDetectionTelemetryRecord { + public override bool IsDiagnostic => true; + public override string RecordName => "DependencyGraphTranslationRecord"; public string? DetectorId { get; set; } diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectedComponentScopeRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectedComponentScopeRecord.cs index 4ec418c07..8e78f5b54 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectedComponentScopeRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectedComponentScopeRecord.cs @@ -4,6 +4,8 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; internal class DetectedComponentScopeRecord : BaseDetectionTelemetryRecord { + public override bool IsDiagnostic => true; + public override string RecordName => "ComponentScopeRecord"; public int? MavenProvidedScopeCount { get; set; } = 0; diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs index d2e1f47b4..74dc8c73b 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs @@ -1,7 +1,14 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; +using System; +using System.Linq; + internal class DetectorExecutionTelemetryRecord : BaseDetectionTelemetryRecord { + private static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("agent.diagnostic"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("System.Debug"), "True", StringComparison.OrdinalIgnoreCase); + + private string? experimentalInformation; + public override string RecordName => "DetectorExecution"; public string? DetectorId { get; set; } @@ -14,7 +21,27 @@ internal class DetectorExecutionTelemetryRecord : BaseDetectionTelemetryRecord public bool IsExperimental { get; set; } - public string? ExperimentalInformation { get; set; } + public string? ExperimentalInformation + { + get => this.experimentalInformation; + set => this.experimentalInformation = DiagnosticEnabled ? value : this.TruncateToLast10Lines(value); + } public string? AdditionalTelemetryDetails { get; set; } + + private string? TruncateToLast10Lines(string? text) + { + if (string.IsNullOrEmpty(text)) + { + return text; + } + + var lines = text.Split(["\r\n", "\r", "\n"], StringSplitOptions.None); + if (lines.Length <= 10) + { + return text; + } + + return string.Join(Environment.NewLine, lines.TakeLast(10)); + } } From a5ba55d2fa5194f12eb35b47488f902613e1af75 Mon Sep 17 00:00:00 2001 From: Fernando Rojo Date: Thu, 9 Jul 2026 19:15:53 -0700 Subject: [PATCH 02/13] take first 10 when truncating --- .../Telemetry/Records/CommandLineInvocationTelemetryRecord.cs | 2 +- .../Telemetry/Records/DetectorExecutionTelemetryRecord.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs index 24bbb2227..98836e13a 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs @@ -55,6 +55,6 @@ private void TrackCommon(string path, string parameters) return error; } - return string.Join(Environment.NewLine, lines.TakeLast(10)); + return string.Join(Environment.NewLine, lines.Take(10)); } } diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs index 74dc8c73b..66729b44e 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs @@ -42,6 +42,6 @@ public string? ExperimentalInformation return text; } - return string.Join(Environment.NewLine, lines.TakeLast(10)); + return string.Join(Environment.NewLine, lines.Take(10)); } } From f25b88a9a3546af332c8e628bb24efa6afc93f07 Mon Sep 17 00:00:00 2001 From: Fernando Rojo Date: Thu, 9 Jul 2026 19:32:28 -0700 Subject: [PATCH 03/13] Fix variable names --- .../Telemetry/Records/BaseDetectionTelemetryRecord.cs | 2 +- .../Telemetry/Records/CommandLineInvocationTelemetryRecord.cs | 2 +- .../Telemetry/Records/DetectorExecutionTelemetryRecord.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs index 9d0777cab..61b7fe293 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs @@ -6,7 +6,7 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; public abstract class BaseDetectionTelemetryRecord : IDetectionTelemetryRecord { - private static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("agent.diagnostic"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("System.Debug"), "True", StringComparison.OrdinalIgnoreCase); + private static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("agent_diagnostic"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("System_Debug"), "True", StringComparison.OrdinalIgnoreCase); private readonly Stopwatch stopwatch = new Stopwatch(); diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs index 98836e13a..b01c33aca 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs @@ -6,7 +6,7 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; internal class CommandLineInvocationTelemetryRecord : BaseDetectionTelemetryRecord { - private static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("agent.diagnostic"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("System.Debug"), "True", StringComparison.OrdinalIgnoreCase); + private static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("agent_diagnostic"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("System_Debug"), "True", StringComparison.OrdinalIgnoreCase); public override string RecordName => "CommandLineInvocation"; diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs index 66729b44e..3fb1308f7 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs @@ -5,7 +5,7 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; internal class DetectorExecutionTelemetryRecord : BaseDetectionTelemetryRecord { - private static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("agent.diagnostic"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("System.Debug"), "True", StringComparison.OrdinalIgnoreCase); + private static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("agent_diagnostic"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("System_Debug"), "True", StringComparison.OrdinalIgnoreCase); private string? experimentalInformation; From aa2f1a4ee6e98a0cf5b399c14f2c689d80d30e77 Mon Sep 17 00:00:00 2001 From: Fernando Rojo Date: Thu, 9 Jul 2026 19:39:07 -0700 Subject: [PATCH 04/13] Match variable casing --- .../Telemetry/Records/BaseDetectionTelemetryRecord.cs | 2 +- .../Telemetry/Records/CommandLineInvocationTelemetryRecord.cs | 2 +- .../Telemetry/Records/DetectorExecutionTelemetryRecord.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs index 61b7fe293..362968e7b 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs @@ -6,7 +6,7 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; public abstract class BaseDetectionTelemetryRecord : IDetectionTelemetryRecord { - private static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("agent_diagnostic"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("System_Debug"), "True", StringComparison.OrdinalIgnoreCase); + private static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("AGENT_DIAGNOSTIC"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("SYSTEM_DEBUG"), "True", StringComparison.OrdinalIgnoreCase); private readonly Stopwatch stopwatch = new Stopwatch(); diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs index b01c33aca..b9f36f7f8 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs @@ -6,7 +6,7 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; internal class CommandLineInvocationTelemetryRecord : BaseDetectionTelemetryRecord { - private static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("agent_diagnostic"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("System_Debug"), "True", StringComparison.OrdinalIgnoreCase); + private static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("AGENT_DIAGNOSTIC"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("SYSTEM_DEBUG"), "True", StringComparison.OrdinalIgnoreCase); public override string RecordName => "CommandLineInvocation"; diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs index 3fb1308f7..47e613b18 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs @@ -5,7 +5,7 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; internal class DetectorExecutionTelemetryRecord : BaseDetectionTelemetryRecord { - private static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("agent_diagnostic"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("System_Debug"), "True", StringComparison.OrdinalIgnoreCase); + private static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("AGENT_DIAGNOSTIC"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("SYSTEM_DEBUG"), "True", StringComparison.OrdinalIgnoreCase); private string? experimentalInformation; From 404bb70b989bdbf8e516ec9ea27fa927a84f71cf Mon Sep 17 00:00:00 2001 From: Fernando Rojo Date: Thu, 9 Jul 2026 19:43:23 -0700 Subject: [PATCH 05/13] deduplicate DiagnosticEnabled logic --- .../Telemetry/Records/BaseDetectionTelemetryRecord.cs | 2 +- .../Telemetry/Records/CommandLineInvocationTelemetryRecord.cs | 2 -- .../Telemetry/Records/DetectorExecutionTelemetryRecord.cs | 2 -- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs index 362968e7b..546643cdf 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs @@ -6,7 +6,7 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; public abstract class BaseDetectionTelemetryRecord : IDetectionTelemetryRecord { - private static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("AGENT_DIAGNOSTIC"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("SYSTEM_DEBUG"), "True", StringComparison.OrdinalIgnoreCase); + protected static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("AGENT_DIAGNOSTIC"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("SYSTEM_DEBUG"), "True", StringComparison.OrdinalIgnoreCase); private readonly Stopwatch stopwatch = new Stopwatch(); diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs index b9f36f7f8..71e231f5c 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs @@ -6,8 +6,6 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; internal class CommandLineInvocationTelemetryRecord : BaseDetectionTelemetryRecord { - private static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("AGENT_DIAGNOSTIC"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("SYSTEM_DEBUG"), "True", StringComparison.OrdinalIgnoreCase); - public override string RecordName => "CommandLineInvocation"; public string? PathThatWasRan { get; set; } diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs index 47e613b18..1ca695959 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs @@ -5,8 +5,6 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; internal class DetectorExecutionTelemetryRecord : BaseDetectionTelemetryRecord { - private static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("AGENT_DIAGNOSTIC"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("SYSTEM_DEBUG"), "True", StringComparison.OrdinalIgnoreCase); - private string? experimentalInformation; public override string RecordName => "DetectorExecution"; From 113397c2bc8f4a48b94c4e637dec245244b24c22 Mon Sep 17 00:00:00 2001 From: Fernando Rojo Date: Thu, 9 Jul 2026 19:49:01 -0700 Subject: [PATCH 06/13] method rename --- .../Telemetry/Records/DetectorExecutionTelemetryRecord.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs index 1ca695959..c00ddc812 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs @@ -22,12 +22,12 @@ internal class DetectorExecutionTelemetryRecord : BaseDetectionTelemetryRecord public string? ExperimentalInformation { get => this.experimentalInformation; - set => this.experimentalInformation = DiagnosticEnabled ? value : this.TruncateToLast10Lines(value); + set => this.experimentalInformation = DiagnosticEnabled ? value : this.TruncateToFirst10Lines(value); } public string? AdditionalTelemetryDetails { get; set; } - private string? TruncateToLast10Lines(string? text) + private string? TruncateToFirst10Lines(string? text) { if (string.IsNullOrEmpty(text)) { From 88952aed2d0211f2616c17fb835efac8cd1bad8a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Jul 2026 02:53:22 +0000 Subject: [PATCH 07/13] test: add unit tests for diagnostic record gating in BaseDetectionTelemetryRecord --- .../BaseDetectionTelemetryRecordTests.cs | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/test/Microsoft.ComponentDetection.Common.Tests/BaseDetectionTelemetryRecordTests.cs b/test/Microsoft.ComponentDetection.Common.Tests/BaseDetectionTelemetryRecordTests.cs index d5635ad80..b2fb5984d 100644 --- a/test/Microsoft.ComponentDetection.Common.Tests/BaseDetectionTelemetryRecordTests.cs +++ b/test/Microsoft.ComponentDetection.Common.Tests/BaseDetectionTelemetryRecordTests.cs @@ -8,8 +8,10 @@ namespace Microsoft.ComponentDetection.Common.Tests; using System.Reflection; using System.Runtime.Serialization; using AwesomeAssertions; +using Microsoft.ComponentDetection.Common.Telemetry; using Microsoft.ComponentDetection.Common.Telemetry.Records; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; [TestClass] [TestCategory("Governance/All")] @@ -75,4 +77,73 @@ public void SerializableProperties() } } } + + [TestMethod] + public void NonDiagnosticRecord_IsAlwaysPosted_WhenDisposed() + { + var telemetryServiceMock = new Mock(); + var postedRecords = new List(); + telemetryServiceMock.Setup(x => x.PostRecord(It.IsAny())) + .Callback(postedRecords.Add); + TelemetryRelay.Instance.Init([telemetryServiceMock.Object]); + + try + { + using (new NonDiagnosticTestRecord()) + { + } + + postedRecords.Should().ContainSingle(); + } + finally + { + TelemetryRelay.Instance.Init([]); + } + } + + [TestMethod] + public void DiagnosticRecord_PostingIsGatedOnDiagnosticsFlag() + { + var telemetryServiceMock = new Mock(); + var postedRecords = new List(); + telemetryServiceMock.Setup(x => x.PostRecord(It.IsAny())) + .Callback(postedRecords.Add); + TelemetryRelay.Instance.Init([telemetryServiceMock.Object]); + + try + { + using (new DiagnosticTestRecord()) + { + } + + var diagnosticEnabled = (bool)typeof(BaseDetectionTelemetryRecord) + .GetField("DiagnosticEnabled", BindingFlags.NonPublic | BindingFlags.Static) + .GetValue(null); + + if (diagnosticEnabled) + { + postedRecords.Should().ContainSingle("diagnostic records should be posted when diagnostics are enabled"); + } + else + { + postedRecords.Should().BeEmpty("diagnostic records should be suppressed when diagnostics are disabled"); + } + } + finally + { + TelemetryRelay.Instance.Init([]); + } + } + + private sealed class NonDiagnosticTestRecord : BaseDetectionTelemetryRecord + { + public override string RecordName => "NonDiagnosticTestRecord"; + } + + private sealed class DiagnosticTestRecord : BaseDetectionTelemetryRecord + { + public override string RecordName => "DiagnosticTestRecord"; + + public override bool IsDiagnostic => true; + } } From 69552ba93dabd422cb23fa031f351c203fc7cd4f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Jul 2026 02:54:26 +0000 Subject: [PATCH 08/13] test: clarify empty using blocks in diagnostic telemetry tests --- .../BaseDetectionTelemetryRecordTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Microsoft.ComponentDetection.Common.Tests/BaseDetectionTelemetryRecordTests.cs b/test/Microsoft.ComponentDetection.Common.Tests/BaseDetectionTelemetryRecordTests.cs index b2fb5984d..51b31b944 100644 --- a/test/Microsoft.ComponentDetection.Common.Tests/BaseDetectionTelemetryRecordTests.cs +++ b/test/Microsoft.ComponentDetection.Common.Tests/BaseDetectionTelemetryRecordTests.cs @@ -89,6 +89,7 @@ public void NonDiagnosticRecord_IsAlwaysPosted_WhenDisposed() try { + // Disposal triggers the telemetry posting in BaseDetectionTelemetryRecord.Dispose() using (new NonDiagnosticTestRecord()) { } @@ -112,6 +113,7 @@ public void DiagnosticRecord_PostingIsGatedOnDiagnosticsFlag() try { + // Disposal triggers the telemetry posting in BaseDetectionTelemetryRecord.Dispose() using (new DiagnosticTestRecord()) { } From 5a3d5416d04283eda3ed479a56f2b394d8c4edc4 Mon Sep 17 00:00:00 2001 From: Fernando Rojo Date: Thu, 9 Jul 2026 20:29:33 -0700 Subject: [PATCH 09/13] Add tests and improve line truncation --- .../Records/BaseDetectionTelemetryRecord.cs | 6 +- .../CommandLineInvocationTelemetryRecord.cs | 21 +- .../DetectorExecutionTelemetryRecord.cs | 21 +- .../TelemetryDiagnosticTests.cs | 211 ++++++++++++++++++ 4 files changed, 247 insertions(+), 12 deletions(-) create mode 100644 test/Microsoft.ComponentDetection.Common.Tests/TelemetryDiagnosticTests.cs diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs index 546643cdf..265e3d63b 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs @@ -2,20 +2,22 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; using System; using System.Diagnostics; +using System.Text.Json.Serialization; using Microsoft.ComponentDetection.Common.Telemetry.Attributes; public abstract class BaseDetectionTelemetryRecord : IDetectionTelemetryRecord { - protected static readonly bool DiagnosticEnabled = string.Equals(Environment.GetEnvironmentVariable("AGENT_DIAGNOSTIC"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("SYSTEM_DEBUG"), "True", StringComparison.OrdinalIgnoreCase); - private readonly Stopwatch stopwatch = new Stopwatch(); private bool disposedValue; protected BaseDetectionTelemetryRecord() => this.stopwatch.Start(); + internal static bool DiagnosticEnabled { get; set; } = string.Equals(Environment.GetEnvironmentVariable("AGENT_DIAGNOSTIC"), "True", StringComparison.OrdinalIgnoreCase) || string.Equals(Environment.GetEnvironmentVariable("SYSTEM_DEBUG"), "True", StringComparison.OrdinalIgnoreCase); + public abstract string RecordName { get; } + [JsonIgnore] public virtual bool IsDiagnostic { get; } [Metric] diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs index 71e231f5c..d5ab37c1d 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs @@ -1,7 +1,7 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; using System; -using System.Linq; +using System.IO; using Microsoft.ComponentDetection.Contracts; internal class CommandLineInvocationTelemetryRecord : BaseDetectionTelemetryRecord @@ -47,12 +47,23 @@ private void TrackCommon(string path, string parameters) return error; } - var lines = error.Split(["\r\n", "\r", "\n"], StringSplitOptions.None); - if (lines.Length <= 10) + var lines = new System.Collections.Generic.List(); + using (var reader = new StringReader(error)) { - return error; + string? line; + while ((line = reader.ReadLine()) != null) + { + if (lines.Count >= 10) + { + // More than 10 lines exist, truncate + return string.Join(Environment.NewLine, lines); + } + + lines.Add(line); + } } - return string.Join(Environment.NewLine, lines.Take(10)); + // EOF reached with <= 10 lines, return original + return error; } } diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs index c00ddc812..087a94996 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs @@ -1,7 +1,7 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; using System; -using System.Linq; +using System.IO; internal class DetectorExecutionTelemetryRecord : BaseDetectionTelemetryRecord { @@ -34,12 +34,23 @@ public string? ExperimentalInformation return text; } - var lines = text.Split(["\r\n", "\r", "\n"], StringSplitOptions.None); - if (lines.Length <= 10) + var lines = new System.Collections.Generic.List(); + using (var reader = new StringReader(text)) { - return text; + string? line; + while ((line = reader.ReadLine()) != null) + { + if (lines.Count >= 10) + { + // More than 10 lines exist, truncate + return string.Join(Environment.NewLine, lines); + } + + lines.Add(line); + } } - return string.Join(Environment.NewLine, lines.Take(10)); + // EOF reached with <= 10 lines, return original + return text; } } diff --git a/test/Microsoft.ComponentDetection.Common.Tests/TelemetryDiagnosticTests.cs b/test/Microsoft.ComponentDetection.Common.Tests/TelemetryDiagnosticTests.cs new file mode 100644 index 000000000..1e220482e --- /dev/null +++ b/test/Microsoft.ComponentDetection.Common.Tests/TelemetryDiagnosticTests.cs @@ -0,0 +1,211 @@ +#nullable disable +namespace Microsoft.ComponentDetection.Common.Tests; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text.Json.Serialization; +using AwesomeAssertions; +using Microsoft.ComponentDetection.Common.Telemetry; +using Microsoft.ComponentDetection.Common.Telemetry.Records; +using Microsoft.ComponentDetection.Contracts; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +[TestClass] +[TestCategory("Governance/All")] +[TestCategory("Governance/ComponentDetection")] +public class TelemetryDiagnosticTests +{ + private bool originalDiagnosticEnabled; + private Mock telemetryServiceMock; + private List publishedRecords; + + [TestInitialize] + public void TestInitialize() + { + this.originalDiagnosticEnabled = BaseDetectionTelemetryRecord.DiagnosticEnabled; + + this.publishedRecords = []; + this.telemetryServiceMock = new Mock(); + this.telemetryServiceMock + .Setup(s => s.PostRecord(It.IsAny())) + .Callback(r => this.publishedRecords.Add(r)); + + TelemetryRelay.Instance.Init([this.telemetryServiceMock.Object]); + } + + [TestCleanup] + public void TestCleanup() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = this.originalDiagnosticEnabled; + TelemetryRelay.Instance.Init([]); + } + + [TestMethod] + public void IsDiagnostic_DefaultsToFalse() + { + new CommandLineInvocationTelemetryRecord().IsDiagnostic.Should().BeFalse(); + new DetectorExecutionTelemetryRecord().IsDiagnostic.Should().BeFalse(); + } + + [TestMethod] + public void IsDiagnostic_HasJsonIgnoreAttribute() + { + var property = typeof(BaseDetectionTelemetryRecord).GetProperty(nameof(BaseDetectionTelemetryRecord.IsDiagnostic)); + + property.GetCustomAttribute().Should().NotBeNull( + "IsDiagnostic should not be serialized into telemetry output"); + } + + [TestMethod] + public void RegularRecord_Published_WhenDiagnosticsDisabled() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = false; + + using (new CommandLineInvocationTelemetryRecord()) + { + } + + this.publishedRecords.Should().HaveCount(1, "regular records are always published regardless of DiagnosticEnabled"); + } + + [TestMethod] + public void RegularRecord_Published_WhenDiagnosticsEnabled() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = true; + + using (new CommandLineInvocationTelemetryRecord()) + { + } + + this.publishedRecords.Should().HaveCount(1, "regular records are always published regardless of DiagnosticEnabled"); + } + + [TestMethod] + public void DiagnosticRecord_NotPublished_WhenDiagnosticsDisabled() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = false; + + using (new DiagnosticTestRecord()) + { + } + + this.publishedRecords.Should().BeEmpty("diagnostic records should not be published when DiagnosticEnabled=false"); + } + + [TestMethod] + public void DiagnosticRecord_Published_WhenDiagnosticsEnabled() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = true; + + using (new DiagnosticTestRecord()) + { + } + + this.publishedRecords.Should().HaveCount(1, "diagnostic records should be published when DiagnosticEnabled=true"); + } + + [TestMethod] + public void CommandLineInvocation_StandardError_TruncatedTo10Lines_WhenDiagnosticsDisabled() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = false; + + var record = new CommandLineInvocationTelemetryRecord(); + record.Track(new CommandLineExecutionResult { ExitCode = 1, StdErr = this.BuildLines(50) }, "/cmd", string.Empty); + + record.StandardError.Split(Environment.NewLine).Should().HaveCount(10); + record.StandardError.Should().Contain("Line 1"); + record.StandardError.Should().Contain("Line 10"); + record.StandardError.Should().NotContain("Line 11"); + } + + [TestMethod] + public void CommandLineInvocation_StandardError_NotTruncated_WhenDiagnosticsEnabled() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = true; + + var record = new CommandLineInvocationTelemetryRecord(); + record.Track(new CommandLineExecutionResult { ExitCode = 1, StdErr = this.BuildLines(50) }, "/cmd", string.Empty); + + record.StandardError.Split(Environment.NewLine).Should().HaveCount(50); + } + + [TestMethod] + public void CommandLineInvocation_StandardError_NotTruncated_WhenUnder10Lines() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = false; + + var fiveLines = this.BuildLines(5); + var record = new CommandLineInvocationTelemetryRecord(); + record.Track(new CommandLineExecutionResult { ExitCode = 1, StdErr = fiveLines }, "/cmd", string.Empty); + + record.StandardError.Should().Be(fiveLines); + } + + [TestMethod] + public void CommandLineInvocation_StandardError_NullHandled() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = false; + + var record = new CommandLineInvocationTelemetryRecord(); + record.Track(new CommandLineExecutionResult { ExitCode = 0, StdErr = null }, "/cmd", string.Empty); + + record.StandardError.Should().BeNull(); + } + + [TestMethod] + public void DetectorExecution_ExperimentalInformation_TruncatedTo10Lines_WhenDiagnosticsDisabled() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = false; + + var record = new DetectorExecutionTelemetryRecord { ExperimentalInformation = this.BuildLines(50) }; + + record.ExperimentalInformation.Split(Environment.NewLine).Should().HaveCount(10); + record.ExperimentalInformation.Should().Contain("Line 1"); + record.ExperimentalInformation.Should().Contain("Line 10"); + record.ExperimentalInformation.Should().NotContain("Line 11"); + } + + [TestMethod] + public void DetectorExecution_ExperimentalInformation_NotTruncated_WhenDiagnosticsEnabled() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = true; + + var record = new DetectorExecutionTelemetryRecord { ExperimentalInformation = this.BuildLines(50) }; + + record.ExperimentalInformation.Split(Environment.NewLine).Should().HaveCount(50); + } + + [TestMethod] + public void DetectorExecution_ExperimentalInformation_NotTruncated_WhenUnder10Lines() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = false; + + var fiveLines = this.BuildLines(5); + var record = new DetectorExecutionTelemetryRecord { ExperimentalInformation = fiveLines }; + + record.ExperimentalInformation.Should().Be(fiveLines); + } + + [TestMethod] + public void DetectorExecution_ExperimentalInformation_NullHandled() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = false; + + var record = new DetectorExecutionTelemetryRecord { ExperimentalInformation = null }; + + record.ExperimentalInformation.Should().BeNull(); + } + + private string BuildLines(int count) => + string.Join(Environment.NewLine, Enumerable.Range(1, count).Select(i => $"Line {i}")); + + private sealed class DiagnosticTestRecord : BaseDetectionTelemetryRecord + { + public override string RecordName => "DiagnosticTest"; + + public override bool IsDiagnostic => true; + } +} From 60535929ae4a65c3b77b3dc02b10aa0e2f93f3c2 Mon Sep 17 00:00:00 2001 From: Fernando Rojo Date: Thu, 9 Jul 2026 20:34:55 -0700 Subject: [PATCH 10/13] Update tests --- .../BaseDetectionTelemetryRecordTests.cs | 207 ++++++++++++++---- 1 file changed, 167 insertions(+), 40 deletions(-) diff --git a/test/Microsoft.ComponentDetection.Common.Tests/BaseDetectionTelemetryRecordTests.cs b/test/Microsoft.ComponentDetection.Common.Tests/BaseDetectionTelemetryRecordTests.cs index 51b31b944..51187ef16 100644 --- a/test/Microsoft.ComponentDetection.Common.Tests/BaseDetectionTelemetryRecordTests.cs +++ b/test/Microsoft.ComponentDetection.Common.Tests/BaseDetectionTelemetryRecordTests.cs @@ -7,9 +7,11 @@ namespace Microsoft.ComponentDetection.Common.Tests; using System.Net; using System.Reflection; using System.Runtime.Serialization; +using System.Text.Json.Serialization; using AwesomeAssertions; using Microsoft.ComponentDetection.Common.Telemetry; using Microsoft.ComponentDetection.Common.Telemetry.Records; +using Microsoft.ComponentDetection.Contracts; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -19,6 +21,9 @@ namespace Microsoft.ComponentDetection.Common.Tests; public class BaseDetectionTelemetryRecordTests { private Type[] recordTypes; + private bool originalDiagnosticEnabled; + private Mock telemetryServiceMock; + private List publishedRecords; [TestInitialize] public void Initialize() @@ -28,6 +33,23 @@ public void Initialize() .Where(type => typeof(BaseDetectionTelemetryRecord).IsAssignableFrom(type)) .Where(type => !type.IsAbstract) .ToArray(); + + this.originalDiagnosticEnabled = BaseDetectionTelemetryRecord.DiagnosticEnabled; + + this.publishedRecords = []; + this.telemetryServiceMock = new Mock(); + this.telemetryServiceMock + .Setup(x => x.PostRecord(It.IsAny())) + .Callback(this.publishedRecords.Add); + + TelemetryRelay.Instance.Init([this.telemetryServiceMock.Object]); + } + + [TestCleanup] + public void Cleanup() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = this.originalDiagnosticEnabled; + TelemetryRelay.Instance.Init([]); } [TestMethod] @@ -68,6 +90,11 @@ public void SerializableProperties() { foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { + if (property.GetCustomAttribute() != null) + { + continue; + } + if (!serializableTypes.Contains(property.PropertyType)) { Attribute.GetCustomAttribute(property.PropertyType, typeof(DataContractAttribute)).Should().NotBeNull( @@ -79,64 +106,164 @@ public void SerializableProperties() } [TestMethod] - public void NonDiagnosticRecord_IsAlwaysPosted_WhenDisposed() + public void IsDiagnostic_DefaultsToFalse() { - var telemetryServiceMock = new Mock(); - var postedRecords = new List(); - telemetryServiceMock.Setup(x => x.PostRecord(It.IsAny())) - .Callback(postedRecords.Add); - TelemetryRelay.Instance.Init([telemetryServiceMock.Object]); + new CommandLineInvocationTelemetryRecord().IsDiagnostic.Should().BeFalse(); + new DetectorExecutionTelemetryRecord().IsDiagnostic.Should().BeFalse(); + } - try - { - // Disposal triggers the telemetry posting in BaseDetectionTelemetryRecord.Dispose() - using (new NonDiagnosticTestRecord()) - { - } + [TestMethod] + public void IsDiagnostic_HasJsonIgnoreAttribute() + { + var property = typeof(BaseDetectionTelemetryRecord).GetProperty(nameof(BaseDetectionTelemetryRecord.IsDiagnostic)); - postedRecords.Should().ContainSingle(); - } - finally + property.GetCustomAttribute().Should().NotBeNull( + "IsDiagnostic should not be serialized into telemetry output"); + } + + [TestMethod] + public void NonDiagnosticRecord_IsPosted_WhenDiagnosticsDisabled() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = false; + + using (new NonDiagnosticTestRecord()) { - TelemetryRelay.Instance.Init([]); } + + this.publishedRecords.Should().ContainSingle("regular records are always published regardless of DiagnosticEnabled"); } [TestMethod] - public void DiagnosticRecord_PostingIsGatedOnDiagnosticsFlag() + public void NonDiagnosticRecord_IsPosted_WhenDiagnosticsEnabled() { - var telemetryServiceMock = new Mock(); - var postedRecords = new List(); - telemetryServiceMock.Setup(x => x.PostRecord(It.IsAny())) - .Callback(postedRecords.Add); - TelemetryRelay.Instance.Init([telemetryServiceMock.Object]); + BaseDetectionTelemetryRecord.DiagnosticEnabled = true; - try + using (new NonDiagnosticTestRecord()) { - // Disposal triggers the telemetry posting in BaseDetectionTelemetryRecord.Dispose() - using (new DiagnosticTestRecord()) - { - } + } - var diagnosticEnabled = (bool)typeof(BaseDetectionTelemetryRecord) - .GetField("DiagnosticEnabled", BindingFlags.NonPublic | BindingFlags.Static) - .GetValue(null); + this.publishedRecords.Should().ContainSingle("regular records are always published regardless of DiagnosticEnabled"); + } - if (diagnosticEnabled) - { - postedRecords.Should().ContainSingle("diagnostic records should be posted when diagnostics are enabled"); - } - else - { - postedRecords.Should().BeEmpty("diagnostic records should be suppressed when diagnostics are disabled"); - } + [TestMethod] + public void DiagnosticRecord_NotPosted_WhenDiagnosticsDisabled() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = false; + + using (new DiagnosticTestRecord()) + { } - finally + + this.publishedRecords.Should().BeEmpty("diagnostic records should not be published when DiagnosticEnabled=false"); + } + + [TestMethod] + public void DiagnosticRecord_IsPosted_WhenDiagnosticsEnabled() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = true; + + using (new DiagnosticTestRecord()) { - TelemetryRelay.Instance.Init([]); } + + this.publishedRecords.Should().ContainSingle("diagnostic records should be published when DiagnosticEnabled=true"); + } + + [TestMethod] + public void CommandLineInvocation_StandardError_TruncatedTo10Lines_WhenDiagnosticsDisabled() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = false; + + var record = new CommandLineInvocationTelemetryRecord(); + record.Track(new CommandLineExecutionResult { ExitCode = 1, StdErr = this.BuildLines(50) }, "/cmd", string.Empty); + + record.StandardError.Split(Environment.NewLine).Should().HaveCount(10); + record.StandardError.Should().Contain("Line 1"); + record.StandardError.Should().Contain("Line 10"); + record.StandardError.Should().NotContain("Line 11"); + } + + [TestMethod] + public void CommandLineInvocation_StandardError_NotTruncated_WhenDiagnosticsEnabled() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = true; + + var record = new CommandLineInvocationTelemetryRecord(); + record.Track(new CommandLineExecutionResult { ExitCode = 1, StdErr = this.BuildLines(50) }, "/cmd", string.Empty); + + record.StandardError.Split(Environment.NewLine).Should().HaveCount(50); } + [TestMethod] + public void CommandLineInvocation_StandardError_Unchanged_WhenUnder10Lines() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = false; + + var fiveLines = this.BuildLines(5); + var record = new CommandLineInvocationTelemetryRecord(); + record.Track(new CommandLineExecutionResult { ExitCode = 1, StdErr = fiveLines }, "/cmd", string.Empty); + + record.StandardError.Should().Be(fiveLines); + } + + [TestMethod] + public void CommandLineInvocation_StandardError_NullHandled() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = false; + + var record = new CommandLineInvocationTelemetryRecord(); + record.Track(new CommandLineExecutionResult { ExitCode = 0, StdErr = null }, "/cmd", string.Empty); + + record.StandardError.Should().BeNull(); + } + + [TestMethod] + public void DetectorExecution_ExperimentalInformation_TruncatedTo10Lines_WhenDiagnosticsDisabled() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = false; + + var record = new DetectorExecutionTelemetryRecord { ExperimentalInformation = this.BuildLines(50) }; + + record.ExperimentalInformation.Split(Environment.NewLine).Should().HaveCount(10); + record.ExperimentalInformation.Should().Contain("Line 1"); + record.ExperimentalInformation.Should().Contain("Line 10"); + record.ExperimentalInformation.Should().NotContain("Line 11"); + } + + [TestMethod] + public void DetectorExecution_ExperimentalInformation_NotTruncated_WhenDiagnosticsEnabled() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = true; + + var record = new DetectorExecutionTelemetryRecord { ExperimentalInformation = this.BuildLines(50) }; + + record.ExperimentalInformation.Split(Environment.NewLine).Should().HaveCount(50); + } + + [TestMethod] + public void DetectorExecution_ExperimentalInformation_Unchanged_WhenUnder10Lines() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = false; + + var fiveLines = this.BuildLines(5); + var record = new DetectorExecutionTelemetryRecord { ExperimentalInformation = fiveLines }; + + record.ExperimentalInformation.Should().Be(fiveLines); + } + + [TestMethod] + public void DetectorExecution_ExperimentalInformation_NullHandled() + { + BaseDetectionTelemetryRecord.DiagnosticEnabled = false; + + var record = new DetectorExecutionTelemetryRecord { ExperimentalInformation = null }; + + record.ExperimentalInformation.Should().BeNull(); + } + + private string BuildLines(int count) => + string.Join(Environment.NewLine, Enumerable.Range(1, count).Select(i => $"Line {i}")); + private sealed class NonDiagnosticTestRecord : BaseDetectionTelemetryRecord { public override string RecordName => "NonDiagnosticTestRecord"; From 145d97b06726b02eb86cd9b626db4abee8e19f2f Mon Sep 17 00:00:00 2001 From: Fernando Rojo Date: Thu, 9 Jul 2026 20:40:13 -0700 Subject: [PATCH 11/13] Consolidate duplicated code and remove magic number --- .../Records/BaseDetectionTelemetryRecord.cs | 29 +++ .../CommandLineInvocationTelemetryRecord.cs | 29 +-- .../DetectorExecutionTelemetryRecord.cs | 29 +-- .../TelemetryDiagnosticTests.cs | 211 ------------------ 4 files changed, 31 insertions(+), 267 deletions(-) delete mode 100644 test/Microsoft.ComponentDetection.Common.Tests/TelemetryDiagnosticTests.cs diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs index 265e3d63b..645056141 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs @@ -1,12 +1,16 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; using System; +using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Text.Json.Serialization; using Microsoft.ComponentDetection.Common.Telemetry.Attributes; public abstract class BaseDetectionTelemetryRecord : IDetectionTelemetryRecord { + internal const int MaxNonDiagnosticLines = 10; + private readonly Stopwatch stopwatch = new Stopwatch(); private bool disposedValue; @@ -54,4 +58,29 @@ protected virtual void Dispose(bool disposing) this.disposedValue = true; } } + + protected static string? TruncateToMaxLines(string? text) + { + if (string.IsNullOrEmpty(text)) + { + return text; + } + + var lines = new List(); + using (var reader = new StringReader(text)) + { + string? line; + while ((line = reader.ReadLine()) != null) + { + if (lines.Count >= MaxNonDiagnosticLines) + { + return string.Join(Environment.NewLine, lines); + } + + lines.Add(line); + } + } + + return text; + } } diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs index d5ab37c1d..bdd98273a 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs @@ -1,7 +1,6 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; using System; -using System.IO; using Microsoft.ComponentDetection.Contracts; internal class CommandLineInvocationTelemetryRecord : BaseDetectionTelemetryRecord @@ -22,7 +21,7 @@ internal void Track(CommandLineExecutionResult result, string path, string param { this.ExitCode = result.ExitCode; var sanitizedError = result.StdErr?.RemoveSensitiveInformation(); - this.StandardError = DiagnosticEnabled ? sanitizedError : this.TruncateStandardErrorTo10Lines(sanitizedError); + this.StandardError = DiagnosticEnabled ? sanitizedError : this.TruncateToMaxLines(sanitizedError); this.TrackCommon(path, parameters); } @@ -40,30 +39,4 @@ private void TrackCommon(string path, string parameters) this.StopExecutionTimer(); } - private string? TruncateStandardErrorTo10Lines(string? error) - { - if (string.IsNullOrEmpty(error)) - { - return error; - } - - var lines = new System.Collections.Generic.List(); - using (var reader = new StringReader(error)) - { - string? line; - while ((line = reader.ReadLine()) != null) - { - if (lines.Count >= 10) - { - // More than 10 lines exist, truncate - return string.Join(Environment.NewLine, lines); - } - - lines.Add(line); - } - } - - // EOF reached with <= 10 lines, return original - return error; - } } diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs index 087a94996..60a157d17 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs @@ -1,7 +1,6 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; using System; -using System.IO; internal class DetectorExecutionTelemetryRecord : BaseDetectionTelemetryRecord { @@ -22,35 +21,9 @@ internal class DetectorExecutionTelemetryRecord : BaseDetectionTelemetryRecord public string? ExperimentalInformation { get => this.experimentalInformation; - set => this.experimentalInformation = DiagnosticEnabled ? value : this.TruncateToFirst10Lines(value); + set => this.experimentalInformation = DiagnosticEnabled ? value : this.TruncateToMaxLines(value); } public string? AdditionalTelemetryDetails { get; set; } - private string? TruncateToFirst10Lines(string? text) - { - if (string.IsNullOrEmpty(text)) - { - return text; - } - - var lines = new System.Collections.Generic.List(); - using (var reader = new StringReader(text)) - { - string? line; - while ((line = reader.ReadLine()) != null) - { - if (lines.Count >= 10) - { - // More than 10 lines exist, truncate - return string.Join(Environment.NewLine, lines); - } - - lines.Add(line); - } - } - - // EOF reached with <= 10 lines, return original - return text; - } } diff --git a/test/Microsoft.ComponentDetection.Common.Tests/TelemetryDiagnosticTests.cs b/test/Microsoft.ComponentDetection.Common.Tests/TelemetryDiagnosticTests.cs deleted file mode 100644 index 1e220482e..000000000 --- a/test/Microsoft.ComponentDetection.Common.Tests/TelemetryDiagnosticTests.cs +++ /dev/null @@ -1,211 +0,0 @@ -#nullable disable -namespace Microsoft.ComponentDetection.Common.Tests; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text.Json.Serialization; -using AwesomeAssertions; -using Microsoft.ComponentDetection.Common.Telemetry; -using Microsoft.ComponentDetection.Common.Telemetry.Records; -using Microsoft.ComponentDetection.Contracts; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; - -[TestClass] -[TestCategory("Governance/All")] -[TestCategory("Governance/ComponentDetection")] -public class TelemetryDiagnosticTests -{ - private bool originalDiagnosticEnabled; - private Mock telemetryServiceMock; - private List publishedRecords; - - [TestInitialize] - public void TestInitialize() - { - this.originalDiagnosticEnabled = BaseDetectionTelemetryRecord.DiagnosticEnabled; - - this.publishedRecords = []; - this.telemetryServiceMock = new Mock(); - this.telemetryServiceMock - .Setup(s => s.PostRecord(It.IsAny())) - .Callback(r => this.publishedRecords.Add(r)); - - TelemetryRelay.Instance.Init([this.telemetryServiceMock.Object]); - } - - [TestCleanup] - public void TestCleanup() - { - BaseDetectionTelemetryRecord.DiagnosticEnabled = this.originalDiagnosticEnabled; - TelemetryRelay.Instance.Init([]); - } - - [TestMethod] - public void IsDiagnostic_DefaultsToFalse() - { - new CommandLineInvocationTelemetryRecord().IsDiagnostic.Should().BeFalse(); - new DetectorExecutionTelemetryRecord().IsDiagnostic.Should().BeFalse(); - } - - [TestMethod] - public void IsDiagnostic_HasJsonIgnoreAttribute() - { - var property = typeof(BaseDetectionTelemetryRecord).GetProperty(nameof(BaseDetectionTelemetryRecord.IsDiagnostic)); - - property.GetCustomAttribute().Should().NotBeNull( - "IsDiagnostic should not be serialized into telemetry output"); - } - - [TestMethod] - public void RegularRecord_Published_WhenDiagnosticsDisabled() - { - BaseDetectionTelemetryRecord.DiagnosticEnabled = false; - - using (new CommandLineInvocationTelemetryRecord()) - { - } - - this.publishedRecords.Should().HaveCount(1, "regular records are always published regardless of DiagnosticEnabled"); - } - - [TestMethod] - public void RegularRecord_Published_WhenDiagnosticsEnabled() - { - BaseDetectionTelemetryRecord.DiagnosticEnabled = true; - - using (new CommandLineInvocationTelemetryRecord()) - { - } - - this.publishedRecords.Should().HaveCount(1, "regular records are always published regardless of DiagnosticEnabled"); - } - - [TestMethod] - public void DiagnosticRecord_NotPublished_WhenDiagnosticsDisabled() - { - BaseDetectionTelemetryRecord.DiagnosticEnabled = false; - - using (new DiagnosticTestRecord()) - { - } - - this.publishedRecords.Should().BeEmpty("diagnostic records should not be published when DiagnosticEnabled=false"); - } - - [TestMethod] - public void DiagnosticRecord_Published_WhenDiagnosticsEnabled() - { - BaseDetectionTelemetryRecord.DiagnosticEnabled = true; - - using (new DiagnosticTestRecord()) - { - } - - this.publishedRecords.Should().HaveCount(1, "diagnostic records should be published when DiagnosticEnabled=true"); - } - - [TestMethod] - public void CommandLineInvocation_StandardError_TruncatedTo10Lines_WhenDiagnosticsDisabled() - { - BaseDetectionTelemetryRecord.DiagnosticEnabled = false; - - var record = new CommandLineInvocationTelemetryRecord(); - record.Track(new CommandLineExecutionResult { ExitCode = 1, StdErr = this.BuildLines(50) }, "/cmd", string.Empty); - - record.StandardError.Split(Environment.NewLine).Should().HaveCount(10); - record.StandardError.Should().Contain("Line 1"); - record.StandardError.Should().Contain("Line 10"); - record.StandardError.Should().NotContain("Line 11"); - } - - [TestMethod] - public void CommandLineInvocation_StandardError_NotTruncated_WhenDiagnosticsEnabled() - { - BaseDetectionTelemetryRecord.DiagnosticEnabled = true; - - var record = new CommandLineInvocationTelemetryRecord(); - record.Track(new CommandLineExecutionResult { ExitCode = 1, StdErr = this.BuildLines(50) }, "/cmd", string.Empty); - - record.StandardError.Split(Environment.NewLine).Should().HaveCount(50); - } - - [TestMethod] - public void CommandLineInvocation_StandardError_NotTruncated_WhenUnder10Lines() - { - BaseDetectionTelemetryRecord.DiagnosticEnabled = false; - - var fiveLines = this.BuildLines(5); - var record = new CommandLineInvocationTelemetryRecord(); - record.Track(new CommandLineExecutionResult { ExitCode = 1, StdErr = fiveLines }, "/cmd", string.Empty); - - record.StandardError.Should().Be(fiveLines); - } - - [TestMethod] - public void CommandLineInvocation_StandardError_NullHandled() - { - BaseDetectionTelemetryRecord.DiagnosticEnabled = false; - - var record = new CommandLineInvocationTelemetryRecord(); - record.Track(new CommandLineExecutionResult { ExitCode = 0, StdErr = null }, "/cmd", string.Empty); - - record.StandardError.Should().BeNull(); - } - - [TestMethod] - public void DetectorExecution_ExperimentalInformation_TruncatedTo10Lines_WhenDiagnosticsDisabled() - { - BaseDetectionTelemetryRecord.DiagnosticEnabled = false; - - var record = new DetectorExecutionTelemetryRecord { ExperimentalInformation = this.BuildLines(50) }; - - record.ExperimentalInformation.Split(Environment.NewLine).Should().HaveCount(10); - record.ExperimentalInformation.Should().Contain("Line 1"); - record.ExperimentalInformation.Should().Contain("Line 10"); - record.ExperimentalInformation.Should().NotContain("Line 11"); - } - - [TestMethod] - public void DetectorExecution_ExperimentalInformation_NotTruncated_WhenDiagnosticsEnabled() - { - BaseDetectionTelemetryRecord.DiagnosticEnabled = true; - - var record = new DetectorExecutionTelemetryRecord { ExperimentalInformation = this.BuildLines(50) }; - - record.ExperimentalInformation.Split(Environment.NewLine).Should().HaveCount(50); - } - - [TestMethod] - public void DetectorExecution_ExperimentalInformation_NotTruncated_WhenUnder10Lines() - { - BaseDetectionTelemetryRecord.DiagnosticEnabled = false; - - var fiveLines = this.BuildLines(5); - var record = new DetectorExecutionTelemetryRecord { ExperimentalInformation = fiveLines }; - - record.ExperimentalInformation.Should().Be(fiveLines); - } - - [TestMethod] - public void DetectorExecution_ExperimentalInformation_NullHandled() - { - BaseDetectionTelemetryRecord.DiagnosticEnabled = false; - - var record = new DetectorExecutionTelemetryRecord { ExperimentalInformation = null }; - - record.ExperimentalInformation.Should().BeNull(); - } - - private string BuildLines(int count) => - string.Join(Environment.NewLine, Enumerable.Range(1, count).Select(i => $"Line {i}")); - - private sealed class DiagnosticTestRecord : BaseDetectionTelemetryRecord - { - public override string RecordName => "DiagnosticTest"; - - public override bool IsDiagnostic => true; - } -} From cc4f1af8034e3404dc2bc6afc05afe8d1a5f09a4 Mon Sep 17 00:00:00 2001 From: Fernando Rojo Date: Thu, 9 Jul 2026 21:13:56 -0700 Subject: [PATCH 12/13] Centralize truncation logic and add tests --- .vscode/launch.json | 4 +- .../Records/BaseDetectionTelemetryRecord.cs | 46 +++++++++---------- .../CommandLineInvocationTelemetryRecord.cs | 3 +- .../DetectorExecutionTelemetryRecord.cs | 5 +- 4 files changed, 27 insertions(+), 31 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index e7af7be00..5243a425b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,8 +10,8 @@ "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/src/Microsoft.ComponentDetection/bin/Debug/netcoreapp3.1/Microsoft.ComponentDetection.dll", - "args": ["scan", "--Debug", "--Verbosity", "Verbose", "--Output", "${workspaceFolder}/scan-output", "--SourceDirectory", "${workspaceFolder}"], + "program": "${workspaceFolder}/src/Microsoft.ComponentDetection/bin/Debug/net8.0/Microsoft.ComponentDetection.dll", + "args": ["scan", "--Debug", "--Verbosity", "Verbose", "--Output", "${workspaceFolder}/scan-output", "--SourceDirectory", "${workspaceFolder}", "--DebugTelemetry"], "cwd": "${workspaceFolder}/src/Microsoft.ComponentDetection", // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console "console": "internalConsole", diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs index 645056141..da489a632 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/BaseDetectionTelemetryRecord.cs @@ -36,29 +36,6 @@ public void StopExecutionTimer() } } - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (!this.disposedValue) - { - if (disposing) - { - this.StopExecutionTimer(); - if (!this.IsDiagnostic || DiagnosticEnabled) - { - TelemetryRelay.Instance.PostTelemetryRecord(this); - } - } - - this.disposedValue = true; - } - } - protected static string? TruncateToMaxLines(string? text) { if (string.IsNullOrEmpty(text)) @@ -83,4 +60,27 @@ protected virtual void Dispose(bool disposing) return text; } + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!this.disposedValue) + { + if (disposing) + { + this.StopExecutionTimer(); + if (!this.IsDiagnostic || DiagnosticEnabled) + { + TelemetryRelay.Instance.PostTelemetryRecord(this); + } + } + + this.disposedValue = true; + } + } } diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs index bdd98273a..47adc6c52 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/CommandLineInvocationTelemetryRecord.cs @@ -21,7 +21,7 @@ internal void Track(CommandLineExecutionResult result, string path, string param { this.ExitCode = result.ExitCode; var sanitizedError = result.StdErr?.RemoveSensitiveInformation(); - this.StandardError = DiagnosticEnabled ? sanitizedError : this.TruncateToMaxLines(sanitizedError); + this.StandardError = DiagnosticEnabled ? sanitizedError : TruncateToMaxLines(sanitizedError); this.TrackCommon(path, parameters); } @@ -38,5 +38,4 @@ private void TrackCommon(string path, string parameters) this.Parameters = parameters?.RemoveSensitiveInformation(); this.StopExecutionTimer(); } - } diff --git a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs index 60a157d17..45e815f03 100644 --- a/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs +++ b/src/Microsoft.ComponentDetection.Common/Telemetry/Records/DetectorExecutionTelemetryRecord.cs @@ -1,7 +1,5 @@ namespace Microsoft.ComponentDetection.Common.Telemetry.Records; -using System; - internal class DetectorExecutionTelemetryRecord : BaseDetectionTelemetryRecord { private string? experimentalInformation; @@ -21,9 +19,8 @@ internal class DetectorExecutionTelemetryRecord : BaseDetectionTelemetryRecord public string? ExperimentalInformation { get => this.experimentalInformation; - set => this.experimentalInformation = DiagnosticEnabled ? value : this.TruncateToMaxLines(value); + set => this.experimentalInformation = DiagnosticEnabled ? value : TruncateToMaxLines(value); } public string? AdditionalTelemetryDetails { get; set; } - } From a71ddba51d528ab298ad6896267db40717274bb6 Mon Sep 17 00:00:00 2001 From: Fernando Rojo Date: Thu, 9 Jul 2026 22:11:00 -0700 Subject: [PATCH 13/13] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .vscode/launch.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 5243a425b..0b877dbe5 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,7 +11,7 @@ "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. "program": "${workspaceFolder}/src/Microsoft.ComponentDetection/bin/Debug/net8.0/Microsoft.ComponentDetection.dll", - "args": ["scan", "--Debug", "--Verbosity", "Verbose", "--Output", "${workspaceFolder}/scan-output", "--SourceDirectory", "${workspaceFolder}", "--DebugTelemetry"], + "args": ["scan", "--Debug", "--LogLevel", "Verbose", "--Output", "${workspaceFolder}/scan-output", "--SourceDirectory", "${workspaceFolder}", "--DebugTelemetry"], "cwd": "${workspaceFolder}/src/Microsoft.ComponentDetection", // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console "console": "internalConsole",