-
Notifications
You must be signed in to change notification settings - Fork 91
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#2228 Create --kind and --level arguments for more granular control #2241
Changes from 5 commits
d8fbaee
dd2a148
5bccb3d
ceb56ae
c52e91a
1b16fb8
ec37c1e
c3d8e91
2e07ac5
d8fc5e4
35247a6
e54cf2c
de5f2b9
81fa57e
662c140
af84726
0f12856
3d7fab4
3092114
f068b44
cd8a99f
130b1fa
508ae6b
0b081e3
eef88e2
4cbed39
a1c523c
6bc4b62
b2ad7af
7440769
2b53283
ba0c58b
908e5ae
a49b35d
66e51f2
1720e89
0f0bee9
1a1c3fe
ea67391
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -96,5 +96,19 @@ public abstract class AnalyzeOptionsBase : CommonOptionsBase | |
"should be emitted to the console and log file (if appropriate). " + | ||
"Valid values: ScanTime.")] | ||
public virtual IEnumerable<string> Traces { get; set; } | ||
|
||
[Option( | ||
"level", | ||
Separator = ';', | ||
Default = new FailureLevel[] { FailureLevel.Error, FailureLevel.Warning }, | ||
HelpText = "Filter results. Valid values: None, Note, Warning, and Error.")] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
"Filter output of scan results to one or more failure levels. Valid values: Error, Warning and Note." #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
public IEnumerable<FailureLevel> Level { get; set; } | ||
|
||
[Option( | ||
"kind", | ||
Separator = ';', | ||
Default = new ResultKind[] { ResultKind.Fail }, | ||
HelpText = "Filter results. Valid values: None, NotApplicable, Pass, Facil, Review, Open, and Informational")] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
"Filter output one or more result kinds. Valid values: Fail (for literal scan results), Pass, Review, Open, NotApplicable and Informational." #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
public IEnumerable<ResultKind> Kind { get; set; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// Copyright (c) Microsoft. All rights reserved. | ||
// Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
|
||
namespace Microsoft.CodeAnalysis.Sarif.Writers | ||
{ | ||
public class BaseLogger | ||
{ | ||
private readonly List<FailureLevel> failureLevels; | ||
private readonly List<ResultKind> resultKinds; | ||
|
||
public BaseLogger(IEnumerable<FailureLevel> failureLevels, | ||
IEnumerable<ResultKind> resultKinds) | ||
{ | ||
this.failureLevels = failureLevels?.ToList() ?? new List<FailureLevel> { FailureLevel.Error, FailureLevel.Warning }; | ||
this.resultKinds = resultKinds?.ToList() ?? new List<ResultKind> { ResultKind.Fail }; | ||
|
||
ValidateParameters(); | ||
} | ||
|
||
private void ValidateParameters() | ||
{ | ||
if (resultKinds.Any(kind => kind != ResultKind.Fail)) | ||
{ | ||
if (failureLevels.Contains(FailureLevel.Error)) | ||
{ | ||
throw new ArgumentException("Invalid kind & level combination"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This validation is not correct. It's fine to configure a mixture of things. The levels are only relevant if kind == Fail. And so your actual validation here is if Kind does not include Fail, then Level must be zero. #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
} | ||
} | ||
|
||
public bool ShouldLog(Notification notification) | ||
{ | ||
return failureLevels.Contains(notification.Level); | ||
} | ||
|
||
public bool ShouldLog(Result result) | ||
{ | ||
// If resultKinds is the default value (Fail), we should just filter based on failureLevels | ||
if (resultKinds.Count == 1 && resultKinds[0] == ResultKind.Fail) | ||
{ | ||
return failureLevels.Contains(result.Level); | ||
} | ||
|
||
return resultKinds.Contains(result.Kind) && failureLevels.Contains(result.Level); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,17 +15,16 @@ namespace Microsoft.CodeAnalysis.Sarif.Writers | |
/// of simply repeating the analysis. This can result in significant performance gains, when that analysis | ||
/// is expensive (such as in the case of a binary analysis that retrieves and crawls binary PDBs). | ||
/// </summary> | ||
public class CacheByFileHashLogger : IAnalysisLogger | ||
public class CacheByFileHashLogger : BaseLogger, IAnalysisLogger | ||
{ | ||
private bool cacheLoggingData; | ||
private string currentFileHash; | ||
|
||
public Dictionary<string, List<Notification>> HashToNotificationsMap { get; private set; } | ||
public Dictionary<string, List<Tuple<ReportingDescriptor, Result>>> HashToResultsMap { get; private set; } | ||
|
||
public CacheByFileHashLogger(bool verbose) | ||
public CacheByFileHashLogger(IEnumerable<FailureLevel> level, IEnumerable<ResultKind> kind) : base(level, kind) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
s/be plural names #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
{ | ||
Verbose = verbose; | ||
} | ||
|
||
public void AnalysisStarted() | ||
|
@@ -34,8 +33,6 @@ public void AnalysisStarted() | |
HashToResultsMap = new Dictionary<string, List<Tuple<ReportingDescriptor, Result>>>(); | ||
} | ||
|
||
public bool Verbose { get; private set; } | ||
|
||
public void AnalysisStopped(RuntimeConditions runtimeConditions) | ||
{ | ||
} | ||
|
@@ -63,32 +60,12 @@ public void Log(ReportingDescriptor rule, Result result) | |
{ | ||
if (!cacheLoggingData) { return; } | ||
|
||
switch (result.Level) | ||
if (!ShouldLog(result)) | ||
{ | ||
// These result types are optionally emitted. | ||
case FailureLevel.None: | ||
case FailureLevel.Note: | ||
{ | ||
if (Verbose) | ||
{ | ||
CacheResult(rule, result); | ||
} | ||
break; | ||
} | ||
|
||
// These result types are always emitted. | ||
case FailureLevel.Error: | ||
case FailureLevel.Warning: | ||
{ | ||
CacheResult(rule, result); | ||
break; | ||
} | ||
|
||
default: | ||
{ | ||
throw new InvalidOperationException(); | ||
} | ||
return; | ||
} | ||
|
||
CacheResult(rule, result); | ||
} | ||
|
||
private void CacheResult(ReportingDescriptor rule, Result result) | ||
|
@@ -111,10 +88,6 @@ public void LogConfigurationNotification(Notification notification) | |
notifications.Add(notification); | ||
} | ||
|
||
public void LogMessage(bool verbose, string message) | ||
{ | ||
} | ||
|
||
public void LogToolNotification(Notification notification) | ||
{ | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,15 +2,16 @@ | |
// Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Globalization; | ||
using System.Linq; | ||
using System.Text; | ||
|
||
namespace Microsoft.CodeAnalysis.Sarif.Writers | ||
{ | ||
public class ConsoleLogger : IAnalysisLogger | ||
public class ConsoleLogger : BaseLogger, IAnalysisLogger | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Note James: our console output should emit results in an ordered and deterministic way! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
{ | ||
public ConsoleLogger(bool verbose, string toolName) | ||
public ConsoleLogger(bool verbose, string toolName, IEnumerable<FailureLevel> level = null, IEnumerable<ResultKind> kind = null) : base(level, kind) | ||
{ | ||
Verbose = verbose; | ||
_toolName = toolName.ToUpperInvariant(); | ||
|
@@ -89,14 +90,6 @@ public void AnalyzingTarget(IAnalysisContext context) | |
} | ||
} | ||
|
||
public void LogMessage(bool verbose, string message) | ||
{ | ||
if (Verbose) | ||
{ | ||
WriteLineToConsole(message); | ||
} | ||
} | ||
|
||
public void Log(ReportingDescriptor rule, Result result) | ||
{ | ||
if (result == null) | ||
|
@@ -110,59 +103,12 @@ public void Log(ReportingDescriptor rule, Result result) | |
// Note that we can potentially emit many messages from a single result. | ||
PhysicalLocation physicalLocation = result.Locations?.First().PhysicalLocation; | ||
|
||
WriteToConsole( | ||
result.Kind, | ||
result.Level, | ||
physicalLocation?.ArtifactLocation?.Uri, | ||
physicalLocation?.Region, | ||
result.RuleId, | ||
message); | ||
} | ||
|
||
private void WriteToConsole(ResultKind kind, FailureLevel level, Uri uri, Region region, string ruleId, string message) | ||
{ | ||
ValidateKindAndLevel(kind, level); | ||
|
||
switch (level) | ||
if (!ShouldLog(result)) | ||
{ | ||
// These result types are optionally emitted. | ||
case FailureLevel.None: | ||
case FailureLevel.Note: | ||
{ | ||
if (Verbose) | ||
{ | ||
WriteLineToConsole(GetMessageText(_toolName, uri, region, ruleId, message, kind, level)); | ||
} | ||
break; | ||
} | ||
|
||
// These result types are always emitted. | ||
case FailureLevel.Error: | ||
case FailureLevel.Warning: | ||
{ | ||
WriteLineToConsole(GetMessageText(_toolName, uri, region, ruleId, message, kind, level)); | ||
break; | ||
} | ||
|
||
default: | ||
{ | ||
throw new InvalidOperationException(); | ||
} | ||
} | ||
} | ||
|
||
private static void ValidateKindAndLevel(ResultKind kind, FailureLevel level) | ||
{ | ||
if (level != FailureLevel.None && kind != ResultKind.Fail) | ||
{ | ||
throw new ArgumentException("Level indicated a failure but kind was not set to 'Fail'."); | ||
return; | ||
} | ||
|
||
if (level == FailureLevel.None && kind == ResultKind.Fail) | ||
{ | ||
throw new ArgumentException("Level did not indicate a failure but kind was set to 'Fail'."); | ||
} | ||
return; | ||
WriteLineToConsole(GetMessageText(_toolName, physicalLocation?.ArtifactLocation?.Uri, physicalLocation?.Region, result.RuleId, message, result.Kind, result.Level)); | ||
} | ||
|
||
private static string GetMessageText( | ||
|
@@ -174,11 +120,7 @@ private static string GetMessageText( | |
ResultKind kind, | ||
FailureLevel level) | ||
{ | ||
string path = null; | ||
|
||
ValidateKindAndLevel(kind, level); | ||
|
||
path = ConstructPathFromUri(uri); | ||
string path = ConstructPathFromUri(uri); | ||
|
||
string issueType = null; | ||
|
||
|
@@ -217,13 +159,10 @@ private static string GetMessageText( | |
location = region.FormatForVisualStudio(); | ||
} | ||
|
||
string messageText = | ||
(path != null ? (path + location) : toolName) + ": " + | ||
issueType + " " + | ||
(!string.IsNullOrEmpty(ruleId) ? (ruleId + ": ") : "") + | ||
detailedDiagnosis; | ||
|
||
return messageText; | ||
return (path != null ? (path + location) : toolName) | ||
+ $": {issueType} " | ||
+ (!string.IsNullOrEmpty(ruleId) ? (ruleId + ": ") : "") | ||
+ detailedDiagnosis; | ||
} | ||
|
||
public static string NormalizeMessage(string message, bool enquote) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should remove analyzeOptions.Verbose from the ConsoleLogger constructor signature, in preparation for that Verbose's eventual retirement? Instead, if verbose is passed, we could set Level and Kind to the maximum level. We do something similar with "options.ComputeFileHashes". We could also do the opposite with analyzeOptions.Quiet. #Resolved