Skip to content

Commit

Permalink
Add telemetry for which error codes are suppressed at restore time (#…
Browse files Browse the repository at this point in the history
…6290)

* Logged telemetry for suppressed warnings

* Added unit tests

* added telemetry test

* Update test/NuGet.Core.Tests/NuGet.Commands.Test/CollectorLoggerTests.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* spelling mistake

* add packageid to supressed warning message

* Revert "add packageid to supressed warning message"

This reverts commit ea47feb.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
  • Loading branch information
jgonz120 and Copilot authored Mar 3, 2025
1 parent 181b65d commit c74e61b
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public class RestoreCollectorLogger : LoggerBase, ICollectorLogger
{
private readonly ILogger _innerLogger;
private readonly ConcurrentQueue<IRestoreLogMessage> _errors;
private readonly ConcurrentQueue<IRestoreLogMessage> _suppressedWarnings;

private readonly bool _hideWarningsAndErrors;
private IEnumerable<RestoreTargetGraph> _restoreTargetGraphs;
private PackageSpec _projectSpec;
Expand All @@ -24,6 +26,8 @@ public class RestoreCollectorLogger : LoggerBase, ICollectorLogger
public string ProjectPath => _projectSpec?.RestoreMetadata?.ProjectPath;

public IEnumerable<IRestoreLogMessage> Errors => _errors.ToArray();
internal IEnumerable<IRestoreLogMessage> SuppressedWarnings => _suppressedWarnings.ToArray();


public WarningPropertiesCollection ProjectWarningPropertiesCollection { get; set; }

Expand Down Expand Up @@ -90,6 +94,7 @@ public RestoreCollectorLogger(ILogger innerLogger, LogLevel verbosity, bool hide
{
_innerLogger = innerLogger;
_errors = new ConcurrentQueue<IRestoreLogMessage>();
_suppressedWarnings = new ConcurrentQueue<IRestoreLogMessage>();
_hideWarningsAndErrors = hideWarningsAndErrors;
}

Expand Down Expand Up @@ -221,23 +226,29 @@ protected bool DisplayMessage(IRestoreLogMessage message)
/// <returns>bool indicating if the message should be suppressed.</returns>
private bool IsWarningSuppressed(IRestoreLogMessage message)
{
var isWarningSuppressed = false;
if (message.Level == LogLevel.Warning)
{
// If the ProjectWarningPropertiesCollection is present then test if the warning is suppressed in
// project wide no warn or package specific no warn
if (ProjectWarningPropertiesCollection?.ApplyNoWarnProperties(message) == true)
{
return true;
isWarningSuppressed = true;
}
else
{
// Use transitive warning properties only if the project does not suppress the warning
// In transitive warning properties look at only the package specific ones as all properties are per package reference.
return TransitiveWarningPropertiesCollection?.ApplyNoWarnProperties(message) == true;
isWarningSuppressed = TransitiveWarningPropertiesCollection?.ApplyNoWarnProperties(message) == true;
}
}

return false;
if (isWarningSuppressed)
{
_suppressedWarnings.Enqueue(message);
}

return isWarningSuppressed;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ private readonly Dictionary<RestoreTargetGraph, Dictionary<string, LibraryInclud
// status names for ProjectRestoreInformation
private const string ErrorCodes = nameof(ErrorCodes);
private const string WarningCodes = nameof(WarningCodes);
private const string SuppressedWarningCodes = nameof(SuppressedWarningCodes);
private const string RestoreSuccess = nameof(RestoreSuccess);
private const string ProjectFilePath = nameof(ProjectFilePath);
private const string IsCentralVersionManagementEnabled = nameof(IsCentralVersionManagementEnabled);
Expand Down Expand Up @@ -622,6 +623,7 @@ private async Task<IEnumerable<RestoreTargetGraph>> GenerateRestoreGraphsAsync(T

var errorCodes = ConcatAsString(new HashSet<NuGetLogCode>(logs.Where(l => l.Level == LogLevel.Error).Select(l => l.Code)));
var warningCodes = ConcatAsString(new HashSet<NuGetLogCode>(logs.Where(l => l.Level == LogLevel.Warning).Select(l => l.Code)));
var suppressedWarningCodes = ConcatAsString(new HashSet<NuGetLogCode>(_logger.SuppressedWarnings.Select(l => l.Code)));

if (!string.IsNullOrEmpty(errorCodes))
{
Expand All @@ -633,6 +635,11 @@ private async Task<IEnumerable<RestoreTargetGraph>> GenerateRestoreGraphsAsync(T
telemetry.TelemetryEvent[WarningCodes] = warningCodes;
}

if (!string.IsNullOrEmpty(suppressedWarningCodes))
{
telemetry.TelemetryEvent[SuppressedWarningCodes] = suppressedWarningCodes;
}

telemetry.TelemetryEvent[NewPackagesInstalledCount] = graphs.Where(g => !g.InConflict).SelectMany(g => g.Install).Distinct().Count();
telemetry.TelemetryEvent[RestoreSuccess] = _success;
}
Expand Down
86 changes: 86 additions & 0 deletions test/NuGet.Core.Tests/NuGet.Commands.Test/CollectorLoggerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,92 @@ public void CollectorLogger_DoesNotLogsWarningsForPackageSpecificNoWarnSetAndPro
VerifyInnerLoggerCalls(innerLogger, LogLevel.Error, "Error", Times.Once());
}

[Fact]
public void CollectorLogger_NoSuppressedWarnings_SuppressedWarningsEmpty()
{
// Arrange
var noWarnSet = new HashSet<NuGetLogCode> { };
var warnAsErrorSet = new HashSet<NuGetLogCode> { };
var warningsNotAsErrors = new HashSet<NuGetLogCode>();
var allWarningsAsErrors = false;
var innerLogger = new Mock<ILogger>();
var collector = new RestoreCollectorLogger(innerLogger.Object)
{
ProjectWarningPropertiesCollection = new WarningPropertiesCollection(
new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors, warningsNotAsErrors),
null,
null)
};

// Act
collector.Log(new RestoreLogMessage(LogLevel.Warning, NuGetLogCode.NU1500, "Warning") { ShouldDisplay = true });

// Assert
Assert.Equal(0, collector.SuppressedWarnings.Count());
}

[Fact]
public void CollectorLogger_PackageSpecificNoWarnSet_SuppressedWarningsTracked()
{
// Arrange
var libraryId = "test_library";
var frameworkString = "net45";
var targetFramework = NuGetFramework.Parse(frameworkString);
var noWarnSet = new HashSet<NuGetLogCode> { };
var warnAsErrorSet = new HashSet<NuGetLogCode> { };
var warningsNotAsErrors = new HashSet<NuGetLogCode>();
var allWarningsAsErrors = false;
var packageSpecificWarningProperties = new PackageSpecificWarningProperties();
packageSpecificWarningProperties.Add(NuGetLogCode.NU1500, libraryId, targetFramework);
packageSpecificWarningProperties.Add(NuGetLogCode.NU1601, libraryId, targetFramework);
packageSpecificWarningProperties.Add(NuGetLogCode.NU1605, libraryId, targetFramework);

var innerLogger = new Mock<ILogger>();
var collector = new RestoreCollectorLogger(innerLogger.Object)
{
ProjectWarningPropertiesCollection = new WarningPropertiesCollection(
new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors, warningsNotAsErrors),
packageSpecificWarningProperties,
null)
};

// Act
collector.Log(RestoreLogMessage.CreateWarning(NuGetLogCode.NU1500, "Warning", libraryId, frameworkString));
collector.Log(RestoreLogMessage.CreateWarning(NuGetLogCode.NU1601, "Warning", libraryId, frameworkString));
collector.Log(RestoreLogMessage.CreateWarning(NuGetLogCode.NU1605, "Warning", libraryId, frameworkString));

// Assert
Assert.Equal(3, collector.SuppressedWarnings.Count());
Assert.Contains(NuGetLogCode.NU1500, collector.SuppressedWarnings.Select(x => x.Code));
Assert.Contains(NuGetLogCode.NU1601, collector.SuppressedWarnings.Select(x => x.Code));
Assert.Contains(NuGetLogCode.NU1605, collector.SuppressedWarnings.Select(x => x.Code));
}

[Fact]
public void CollectorLogger_ProjectWideNoWarnSet_SuppressedWarningsTracked()
{
// Arrange
var noWarnSet = new HashSet<NuGetLogCode> { NuGetLogCode.NU1500 };
var warnAsErrorSet = new HashSet<NuGetLogCode> { };
var warningsNotAsErrors = new HashSet<NuGetLogCode>();
var allWarningsAsErrors = false;
var innerLogger = new Mock<ILogger>();
var collector = new RestoreCollectorLogger(innerLogger.Object)
{
ProjectWarningPropertiesCollection = new WarningPropertiesCollection(
new WarningProperties(warnAsErrorSet, noWarnSet, allWarningsAsErrors, warningsNotAsErrors),
null,
null)
};

// Act
collector.Log(new RestoreLogMessage(LogLevel.Warning, NuGetLogCode.NU1500, "Warning") { ShouldDisplay = true });

// Assert
Assert.Equal(1, collector.SuppressedWarnings.Count());
Assert.Contains(NuGetLogCode.NU1500, collector.SuppressedWarnings.Select(x => x.Code));
}

private void VerifyInnerLoggerCalls(Mock<ILogger> innerLogger, LogLevel messageLevel, string message, Times times, NuGetLogCode code = NuGetLogCode.Undefined, string filePath = null, string projectPath = null)
{
innerLogger.Verify(x => x.Log(It.Is<RestoreLogMessage>(l =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2930,6 +2930,43 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async(
}
}

[Fact]
public async Task ExecuteAsync_WithSuppressedWarning_PopulatesCorrectTelemetry()
{
// Arrange
using var pathContext = new SimpleTestPathContext();
var projectName = "TestProject";
var projectPath = Path.Combine(pathContext.SolutionRoot, projectName);
PackageSpec packageSpec = ProjectTestHelpers.GetPackageSpec(projectName, pathContext.SolutionRoot, "net472", "a");
packageSpec.RestoreMetadata.ProjectWideWarningProperties.NoWarn.Add(NuGetLogCode.NU1603);

await SimpleTestPackageUtility.CreateFolderFeedV3Async(
pathContext.PackageSource,
PackageSaveMode.Defaultv3,
new SimpleTestPackageContext("a", "1.5.0"));

var logger = new TestLogger();

// Set-up telemetry service - Important to set-up the service *after* the package source creation call as that emits telemetry!
var telemetryEvents = new ConcurrentQueue<TelemetryEvent>();
var _telemetryService = new Mock<INuGetTelemetryService>(MockBehavior.Loose);
_telemetryService
.Setup(x => x.EmitTelemetryEvent(It.IsAny<TelemetryEvent>()))
.Callback<TelemetryEvent>(x => telemetryEvents.Enqueue(x));

TelemetryActivity.NuGetTelemetryService = _telemetryService.Object;

//Act
var request = ProjectTestHelpers.CreateRestoreRequest(pathContext, logger, packageSpec);
var restoreCommand = new RestoreCommand(request);
RestoreResult result = await restoreCommand.ExecuteAsync();

// Assert
var projectInformationEvent = telemetryEvents.Single(e => e.Name.Equals("ProjectRestoreInformation"));
Assert.Equal("NU1603", projectInformationEvent["SuppressedWarningCodes"]);
Assert.Null(projectInformationEvent["WarningCodes"]);
}

[Fact]
public async Task ExecuteAsync_WithSinglePackage_WhenNoOping_PopulatesCorrectTelemetry()
{
Expand Down

0 comments on commit c74e61b

Please sign in to comment.