Skip to content

Commit 843d8a3

Browse files
committed
respond to review feedback
1 parent 31069f8 commit 843d8a3

File tree

8 files changed

+124
-105
lines changed

8 files changed

+124
-105
lines changed

Diff for: src/Microsoft.Sbom.Api/Executors/ComponentDetectionBaseWalker.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ async Task Scan(string path)
108108
var cmdLineParams = configuration.ToComponentDetectorCommandLineParams(cliArgumentBuilder);
109109

110110
var scanSettings = cliArgumentBuilder.BuildScanSettingsFromParsedArgs(cmdLineParams);
111-
111+
// TODO: the MSBuild pathway needs a way to disable this setting, because the CG tools write directly to stdout (which is not kind).
112+
scanSettings.NoSummary = true;
112113
var scanResult = await componentDetector.ScanAsync(scanSettings);
113114

114115
if (scanResult.ResultCode != ProcessingResultCode.Success)

Diff for: src/Microsoft.Sbom.Extensions.DependencyInjection/MSBuildLogger.cs

-47
This file was deleted.

Diff for: src/Microsoft.Sbom.Extensions.DependencyInjection/Microsoft.Sbom.Extensions.DependencyInjection.csproj

-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
<ItemGroup>
2020
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" />
21-
<PackageReference Include="Microsoft.Build.Utilities.Core" />
2221
<PackageReference Include="Microsoft.Extensions.Hosting" />
2322
<PackageReference Include="Microsoft.Extensions.Http" />
2423
<PackageReference Include="Scrutor" />

Diff for: src/Microsoft.Sbom.Extensions.DependencyInjection/ServiceCollectionExtensions.cs

+36-34
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

44
using System.Collections.Concurrent;
5-
using Microsoft.Build.Utilities;
65
using Microsoft.ComponentDetection.Orchestrator;
76
using Microsoft.ComponentDetection.Orchestrator.Extensions;
87
using Microsoft.Extensions.DependencyInjection;
@@ -56,20 +55,29 @@ public static IServiceCollection AddSbomConfiguration(this IServiceCollection se
5655
inputConfiguration.ToConfiguration();
5756
return inputConfiguration;
5857
})
59-
.AddSbomTool(logLevel);
58+
.AddSbomTool();
6059
return services;
6160
}
6261

63-
public static IServiceCollection AddSbomTool(this IServiceCollection services, LogEventLevel logLevel = LogEventLevel.Information, TaskLoggingHelper? taskLoggingHelper = null)
62+
public static IServiceCollection AddSbomTool(this IServiceCollection services, ILogger? logger = null)
6463
{
6564
services
6665
.AddSingleton<IConfiguration, Configuration>()
67-
.AddTransient(_ => FileSystemUtilsProvider.CreateInstance(CreateLogger(logLevel, taskLoggingHelper)))
68-
.AddTransient(x =>
66+
.AddSingleton(x =>
6967
{
70-
logLevel = x.GetService<InputConfiguration>()?.Verbosity?.Value ?? logLevel;
71-
return Log.Logger = CreateLogger(logLevel, taskLoggingHelper);
68+
if (logger != null)
69+
{
70+
return logger;
71+
}
72+
else
73+
{
74+
var level = x.GetService<InputConfiguration>()?.Verbosity?.Value ?? LogEventLevel.Information;
75+
var defaultLogger = CreateDefaultLogger(level);
76+
Log.Logger = defaultLogger;
77+
return defaultLogger;
78+
}
7279
})
80+
.AddTransient(x => FileSystemUtilsProvider.CreateInstance(x.GetRequiredService<ILogger>()))
7381
.AddTransient<IWorkflow<SbomParserBasedValidationWorkflow>, SbomParserBasedValidationWorkflow>()
7482
.AddTransient<IWorkflow<SbomGenerationWorkflow>, SbomGenerationWorkflow>()
7583
.AddTransient<IWorkflow<SbomRedactionWorkflow>, SbomRedactionWorkflow>()
@@ -126,7 +134,8 @@ public static IServiceCollection AddSbomTool(this IServiceCollection services, L
126134
.AddSingleton<IFileTypeUtils, FileTypeUtils>()
127135
.AddSingleton<ISignValidationProvider, SignValidationProvider>()
128136
.AddSingleton<IManifestParserProvider, ManifestParserProvider>()
129-
.AddSingleton(x => {
137+
.AddSingleton(x =>
138+
{
130139
var comparer = x.GetRequiredService<IOSUtils>().GetFileSystemStringComparer();
131140
return new FileHashesDictionary(new ConcurrentDictionary<string, FileHashes>(comparer));
132141
})
@@ -201,33 +210,26 @@ public static IServiceCollection ConfigureLoggingProviders(this IServiceCollecti
201210
return services;
202211
}
203212

204-
private static ILogger CreateLogger(LogEventLevel logLevel, TaskLoggingHelper? taskLoggingHelper = null)
213+
private static ILogger CreateDefaultLogger(LogEventLevel logLevel = LogEventLevel.Information)
205214
{
206-
if (taskLoggingHelper == null)
207-
{
208-
return new RemapComponentDetectionErrorsToWarningsLogger(
209-
new LoggerConfiguration()
210-
.MinimumLevel.ControlledBy(new LoggingLevelSwitch { MinimumLevel = logLevel })
211-
.Filter.ByExcluding(Matching.FromSource("System.Net.Http.HttpClient"))
212-
.Enrich.With<LoggingEnricher>()
213-
.Enrich.FromLogContext()
214-
.WriteTo.Map(
215-
LoggingEnricher.LogFilePathPropertyName,
216-
(logFilePath, wt) => wt.Async(x => x.File($"{logFilePath}")),
217-
1) // sinkMapCountLimit
218-
.WriteTo.Map<bool>(
219-
LoggingEnricher.PrintStderrPropertyName,
220-
(printLogsToStderr, wt) => wt.Logger(lc => lc
221-
.WriteTo.Console(outputTemplate: Constants.LoggerTemplate, standardErrorFromLevel: printLogsToStderr ? LogEventLevel.Debug : null)
215+
return new RemapComponentDetectionErrorsToWarningsLogger(
216+
new LoggerConfiguration()
217+
.MinimumLevel.ControlledBy(new LoggingLevelSwitch { MinimumLevel = logLevel })
218+
.Filter.ByExcluding(Matching.FromSource("System.Net.Http.HttpClient"))
219+
.Enrich.With<LoggingEnricher>()
220+
.Enrich.FromLogContext()
221+
.WriteTo.Map(
222+
LoggingEnricher.LogFilePathPropertyName,
223+
(logFilePath, wt) => wt.Async(x => x.File($"{logFilePath}")),
224+
1) // sinkMapCountLimit
225+
.WriteTo.Map<bool>(
226+
LoggingEnricher.PrintStderrPropertyName,
227+
(printLogsToStderr, wt) => wt.Logger(lc => lc
228+
.WriteTo.Console(outputTemplate: Constants.LoggerTemplate, standardErrorFromLevel: printLogsToStderr ? LogEventLevel.Debug : null)
222229

223-
// Don't write the detection times table from DetectorProcessingService to the console, only the log file
224-
.Filter.ByExcluding(Matching.WithProperty<string>("DetectionTimeLine", x => !string.IsNullOrEmpty(x)))),
225-
1) // sinkMapCountLimit
226-
.CreateLogger());
227-
}
228-
else
229-
{
230-
return new MSBuildLogger(taskLoggingHelper);
231-
}
230+
// Don't write the detection times table from DetectorProcessingService to the console, only the log file
231+
.Filter.ByExcluding(Matching.WithProperty<string>("DetectionTimeLine", x => !string.IsNullOrEmpty(x)))),
232+
1) // sinkMapCountLimit
233+
.CreateLogger());
232234
}
233235
}

Diff for: src/Microsoft.Sbom.Targets/GenerateSbomTask.cs

+25-19
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ namespace Microsoft.Sbom.Targets;
55

66
using System;
77
using System.Collections.Generic;
8-
using System.Diagnostics.Tracing;
8+
using System.Threading;
9+
using Microsoft.Build.Framework;
910
using Microsoft.Build.Utilities;
1011
using Microsoft.Extensions.DependencyInjection;
1112
using Microsoft.Extensions.Hosting;
@@ -15,26 +16,30 @@ namespace Microsoft.Sbom.Targets;
1516
using Microsoft.Sbom.Api.Providers.ExternalDocumentReferenceProviders;
1617
using Microsoft.Sbom.Api.Providers.FilesProviders;
1718
using Microsoft.Sbom.Api.Providers.PackagesProviders;
19+
using Microsoft.Sbom.Common.Config;
1820
using Microsoft.Sbom.Contracts;
1921
using Microsoft.Sbom.Contracts.Entities;
2022
using Microsoft.Sbom.Contracts.Interfaces;
2123
using Microsoft.Sbom.Extensions;
2224
using Microsoft.Sbom.Extensions.DependencyInjection;
2325
using Microsoft.Sbom.Parsers.Spdx22SbomParser;
24-
using Serilog.Events;
2526

2627
/// <summary>
2728
/// MSBuild task for generating SBOMs from build output.
2829
/// </summary>
29-
public partial class GenerateSbom : Task
30+
public partial class GenerateSbom : Task, ICancelableTask
3031
{
32+
private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
33+
3134
/// <summary>
3235
/// Constructor for the GenerateSbomTask.
3336
/// </summary>
3437
public GenerateSbom()
3538
{
3639
}
3740

41+
public void Cancel() => cancellationTokenSource.Cancel();
42+
3843
/// <inheritdoc/>
3944
public override bool Execute()
4045
{
@@ -49,12 +54,13 @@ public override bool Execute()
4954
}
5055

5156
var logVerbosity = ValidateAndAssignVerbosity();
52-
var serilogLogVerbosity = MapSerilogLevel(logVerbosity);
53-
57+
var msbuildLogger = new MSBuildLogger(taskLoggingHelper);
58+
Serilog.Log.Logger = msbuildLogger;
5459
var taskHost = Host.CreateDefaultBuilder()
5560
.ConfigureServices((host, services) =>
5661
services
57-
.AddSbomTool(serilogLogVerbosity, taskLoggingHelper)
62+
.AddSingleton<IConfiguration, Configuration>()
63+
.AddSbomTool(msbuildLogger)
5864
/* Manually adding some dependencies since `AddSbomTool()` does not add them when
5965
* running the MSBuild Task from another project.
6066
*/
@@ -101,29 +107,29 @@ public override bool Execute()
101107
componentPath: this.BuildComponentPath,
102108
runtimeConfiguration: runtimeConfiguration,
103109
specifications: ValidateAndAssignSpecifications(),
104-
externalDocumentReferenceListFile: this.ExternalDocumentListFile)).GetAwaiter().GetResult();
110+
externalDocumentReferenceListFile: this.ExternalDocumentListFile),
111+
this.cancellationTokenSource.Token).GetAwaiter().GetResult();
105112
#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits
106113

114+
if (!result.IsSuccessful)
115+
{
116+
Log.LogError("SBOM generation failed. Check the build log for details.");
117+
}
118+
119+
foreach (var error in result.Errors)
120+
{
121+
Log.LogMessage(MessageImportance.Normal, "{0}({1}) - {2} - {3}", error.Entity.Id, error.Entity.EntityType, error.ErrorType, error.Details);
122+
}
123+
107124
return result.IsSuccessful;
108125
}
109126
catch (Exception e)
110127
{
111128
Log.LogError($"SBOM generation failed: {e.Message}");
112-
return false;
129+
return Log.HasLoggedErrors;
113130
}
114131
}
115132

116-
private LogEventLevel MapSerilogLevel(EventLevel logVerbosity) =>
117-
logVerbosity switch
118-
{
119-
EventLevel.Critical => LogEventLevel.Fatal,
120-
EventLevel.Error => LogEventLevel.Error,
121-
EventLevel.Warning => LogEventLevel.Warning,
122-
EventLevel.Informational => LogEventLevel.Information,
123-
EventLevel.Verbose => LogEventLevel.Verbose,
124-
_ => LogEventLevel.Information,
125-
};
126-
127133
/// <summary>
128134
/// Check for ManifestInfo and create an SbomSpecification accordingly.
129135
/// </summary>

Diff for: src/Microsoft.Sbom.Targets/MSBuildLogger.cs

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
namespace Microsoft.Sbom.Targets;
5+
6+
using System;
7+
using Microsoft.Build.Framework;
8+
using Microsoft.Build.Utilities;
9+
using Serilog.Events;
10+
using ILogger = Serilog.ILogger;
11+
12+
/// <summary>
13+
/// A class to remap Errors logged from ComponentDetection assemblies to Warnings or Errors in MSBuild from Serilog to MSBuild's native logging facilities. This class interprets a few kinds of properties from the
14+
/// logged Serilog message:
15+
/// <list type="bullet">
16+
/// <item><description><c>msbuild.code</c> becomes the <c>code</c> for the <see cref="TaskLoggingHelper.LogWarning(string, string, string, string, string, int, int, int, int, string, object[])" /> or <see cref="TaskLoggingHelper.LogError(string, string, string, string, string, int, int, int, int, string, object[])"/> methods for Serilog <see cref="LogEventLevel.Warning"/>, <see cref="LogEventLevel.Error"/>, and <see cref="LogEventLevel.Fatal"/> events.</description></item>
17+
/// <item><description><c>msbuild.importance</c> becomes the <c>importance</c> for the <see cref="TaskLoggingHelper.LogMessage(MessageImportance, string, object[])" /> method for Serilog <see cref="LogEventLevel.Debug"/> and <see cref="LogEventLevel.Information"/> events.</description></item>
18+
/// </list>
19+
/// </summary>
20+
public class MSBuildLogger : ILogger
21+
{
22+
private readonly TaskLoggingHelper loggingHelper;
23+
24+
public MSBuildLogger(TaskLoggingHelper loggingHelperToWrap)
25+
{
26+
loggingHelper = loggingHelperToWrap;
27+
}
28+
29+
public void Write(LogEvent logEvent)
30+
{
31+
var logLevel = logEvent.Level;
32+
switch (logLevel)
33+
{
34+
case LogEventLevel.Debug:
35+
logEvent.Properties.TryGetValue("msbuild.importance", out var debugImportance);
36+
loggingHelper.LogMessage(TryParseImportance(debugImportance) ?? MessageImportance.Low, logEvent.RenderMessage());
37+
break;
38+
case LogEventLevel.Information:
39+
logEvent.Properties.TryGetValue("msbuild.importance", out var infoImportance);
40+
loggingHelper.LogMessage(TryParseImportance(infoImportance) ?? MessageImportance.Normal, logEvent.RenderMessage());
41+
break;
42+
// warnings and errors can have codes, etc - if the message has a code then use it
43+
case LogEventLevel.Warning:
44+
logEvent.Properties.TryGetValue("msbuild.code", out var warningCode);
45+
loggingHelper.LogWarning(subcategory: null, warningCode: warningCode?.ToString(), helpKeyword: null, helpLink: null, file: null, lineNumber: 0, columnNumber: 0, endLineNumber: 0, endColumnNumber: 0, message: logEvent.RenderMessage());
46+
break;
47+
case LogEventLevel.Error:
48+
case LogEventLevel.Fatal:
49+
logEvent.Properties.TryGetValue("msbuild.code", out var errorCode);
50+
loggingHelper.LogError(subcategory: null, errorCode: errorCode?.ToString(), helpKeyword: null, helpLink: null, file: null, lineNumber: 0, columnNumber: 0, endLineNumber: 0, endColumnNumber: 0, message: logEvent.RenderMessage());
51+
break;
52+
default:
53+
break;
54+
}
55+
}
56+
57+
private MessageImportance? TryParseImportance(LogEventPropertyValue? infoImportance) =>
58+
infoImportance != null && Enum.TryParse<MessageImportance>(infoImportance.ToString(), out var msbuildImportance) ? msbuildImportance : null;
59+
}

Diff for: src/Microsoft.Sbom.Targets/Microsoft.Sbom.Targets.csproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
1414
<SbomCLIToolTargetFramework>net8.0</SbomCLIToolTargetFramework>
1515
<DevelopmentDependency>true</DevelopmentDependency>
16-
1716
<!-- This target will run when MSBuild is collecting the files to be packaged, and we'll implement it below. This property controls the dependency list for this packaging process, so by adding our custom property we hook ourselves into the process in a supported way. -->
1817
<TargetsForTfmSpecificBuildOutput>
1918
$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage
@@ -54,7 +53,7 @@
5453
<Visible>true</Visible>
5554
</Content>
5655
</ItemGroup>
57-
</Target>
56+
</Target>
5857

5958
<ItemGroup>
6059
<!-- these lines pack the build props/targets files to the `build` folder in the generated package.
@@ -78,6 +77,7 @@
7877

7978
<ItemGroup Condition="'$(TargetFramework)' == 'net472'">
8079
<Compile Remove="GenerateSbomTask.cs" />
80+
<Compile Remove="MSBuildLogger.cs" />
8181
</ItemGroup>
8282

8383
<ItemGroup Condition="'$(TargetFramework)' != 'net472'">

Diff for: src/Microsoft.Sbom.Tool/Microsoft.Sbom.Tool.csproj

-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,5 @@
2424
<ItemGroup>
2525
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" />
2626
<PackageReference Include="Microsoft.Extensions.Hosting" />
27-
<PackageReference Include="Microsoft.Build.Utilities.Core" />
2827
</ItemGroup>
2928
</Project>

0 commit comments

Comments
 (0)