diff --git a/NuKeeper.Inspection/Logging/ConfigurableLogger.cs b/NuKeeper.Inspection/Logging/ConfigurableLogger.cs index d9b4a6af3..92e1e691b 100644 --- a/NuKeeper.Inspection/Logging/ConfigurableLogger.cs +++ b/NuKeeper.Inspection/Logging/ConfigurableLogger.cs @@ -6,12 +6,8 @@ public class ConfigurableLogger : INuKeeperLogger, IConfigureLogger { private IInternalLogger _inner; - public void Initialise(LogLevel logLevel, string filePath) + public void Initialise(LogLevel logLevel, LogDestination destination, string filePath) { - var destination = string.IsNullOrWhiteSpace(filePath) ? - LogDestination.Console : - LogDestination.File; - _inner = CreateLogger(logLevel, destination, filePath); } @@ -59,6 +55,9 @@ private static IInternalLogger CreateLogger( case LogDestination.File: return new FileLogger(filePath, logLevel); + case LogDestination.Off: + return new NullLogger(); + default: throw new Exception($"Unknown log destination {destination}"); } diff --git a/NuKeeper.Inspection/Logging/FileLogger.cs b/NuKeeper.Inspection/Logging/FileLogger.cs index e767de954..640a22e02 100644 --- a/NuKeeper.Inspection/Logging/FileLogger.cs +++ b/NuKeeper.Inspection/Logging/FileLogger.cs @@ -10,6 +10,11 @@ public class FileLogger : IInternalLogger public FileLogger(string filePath, LogLevel logLevel) { + if (string.IsNullOrWhiteSpace(filePath)) + { + throw new ArgumentException("File logger has no file path", nameof(filePath)); + } + _filePath = filePath; _logLevel = logLevel; } diff --git a/NuKeeper.Inspection/Logging/IConfigureLogger.cs b/NuKeeper.Inspection/Logging/IConfigureLogger.cs index 259c116b0..a1cf06900 100644 --- a/NuKeeper.Inspection/Logging/IConfigureLogger.cs +++ b/NuKeeper.Inspection/Logging/IConfigureLogger.cs @@ -1,8 +1,7 @@ namespace NuKeeper.Inspection.Logging { - public interface IConfigureLogger { - void Initialise(LogLevel logLevel, string filePath); + void Initialise(LogLevel logLevel, LogDestination dest, string filePath); } } diff --git a/NuKeeper.Inspection/Logging/LogDestination.cs b/NuKeeper.Inspection/Logging/LogDestination.cs index fb657ab87..d2977d949 100644 --- a/NuKeeper.Inspection/Logging/LogDestination.cs +++ b/NuKeeper.Inspection/Logging/LogDestination.cs @@ -1,8 +1,9 @@ -namespace NuKeeper.Inspection.Logging +namespace NuKeeper.Inspection.Logging { public enum LogDestination { Console, - File + File, + Off } } diff --git a/NuKeeper.Inspection/Logging/NullLogger.cs b/NuKeeper.Inspection/Logging/NullLogger.cs new file mode 100644 index 000000000..6b911fcee --- /dev/null +++ b/NuKeeper.Inspection/Logging/NullLogger.cs @@ -0,0 +1,15 @@ +using System; + +namespace NuKeeper.Inspection.Logging +{ + public class NullLogger : IInternalLogger + { + public void Log(LogLevel level, string message) + { + } + + public void LogError(string message, Exception ex) + { + } + } +} diff --git a/NuKeeper.Inspection/Report/OutputFormat.cs b/NuKeeper.Inspection/Report/OutputFormat.cs index f2a32ad36..18cf73508 100644 --- a/NuKeeper.Inspection/Report/OutputFormat.cs +++ b/NuKeeper.Inspection/Report/OutputFormat.cs @@ -2,7 +2,7 @@ namespace NuKeeper.Inspection.Report { public enum OutputFormat { - None, + Off, Text, Csv, Metrics, diff --git a/NuKeeper.Inspection/Report/Reporter.cs b/NuKeeper.Inspection/Report/Reporter.cs index 8526f8de4..34c04d541 100644 --- a/NuKeeper.Inspection/Report/Reporter.cs +++ b/NuKeeper.Inspection/Report/Reporter.cs @@ -43,7 +43,7 @@ private static IReportFormat MakeReporter( { switch (format) { - case OutputFormat.None: + case OutputFormat.Off: return new NullReportFormat(); case OutputFormat.Text: diff --git a/NuKeeper.Tests/Commands/InspectCommandTests.cs b/NuKeeper.Tests/Commands/InspectCommandTests.cs index f1285fc8b..e6ae85683 100644 --- a/NuKeeper.Tests/Commands/InspectCommandTests.cs +++ b/NuKeeper.Tests/Commands/InspectCommandTests.cs @@ -116,7 +116,7 @@ public async Task LogLevelIsNormalByDefault() logger .Received(1) - .Initialise(LogLevel.Normal, null); + .Initialise(LogLevel.Normal, LogDestination.Console, Arg.Any()); } [Test] @@ -135,7 +135,7 @@ public async Task ShouldSetLogLevelFromCommand() logger .Received(1) - .Initialise(LogLevel.Minimal, null); + .Initialise(LogLevel.Minimal, LogDestination.Console, Arg.Any()); } [Test] @@ -158,7 +158,7 @@ public async Task ShouldSetLogLevelFromFile() logger .Received(1) - .Initialise(LogLevel.Detailed, null); + .Initialise(LogLevel.Detailed, LogDestination.Console, Arg.Any()); } [Test] @@ -182,7 +182,49 @@ public async Task CommandLineLogLevelOverridesFile() logger .Received(1) - .Initialise(LogLevel.Minimal, null); + .Initialise(LogLevel.Minimal, LogDestination.Console, Arg.Any()); + } + + [Test] + public async Task LogToFileBySettingFileName() + { + var engine = Substitute.For(); + var logger = Substitute.For(); + var fileSettings = Substitute.For(); + + var settings = FileSettings.Empty(); + + fileSettings.GetSettings().Returns(settings); + + var command = new InspectCommand(engine, logger, fileSettings); + command.LogFile = "somefile.log"; + + await command.OnExecute(); + + logger + .Received(1) + .Initialise(LogLevel.Normal, LogDestination.File, "somefile.log"); + } + + [Test] + public async Task LogToFileBySettingLogDestination() + { + var engine = Substitute.For(); + var logger = Substitute.For(); + var fileSettings = Substitute.For(); + + var settings = FileSettings.Empty(); + + fileSettings.GetSettings().Returns(settings); + + var command = new InspectCommand(engine, logger, fileSettings); + command.LogDestination = LogDestination.File; + + await command.OnExecute(); + + logger + .Received(1) + .Initialise(LogLevel.Normal, LogDestination.File, "nukeeper.log"); } [Test] diff --git a/NuKeeper.Tests/Configuration/FileSettingsReaderTests.cs b/NuKeeper.Tests/Configuration/FileSettingsReaderTests.cs index 5cb217214..a7fd05977 100644 --- a/NuKeeper.Tests/Configuration/FileSettingsReaderTests.cs +++ b/NuKeeper.Tests/Configuration/FileSettingsReaderTests.cs @@ -34,6 +34,7 @@ public void MissingFileReturnsNoSettings() Assert.That(data.OutputDestination, Is.Null); Assert.That(data.OutputFormat, Is.Null); Assert.That(data.OutputFileName, Is.Null); + Assert.That(data.LogDestination, Is.Null); } [Test] @@ -58,6 +59,7 @@ public void EmptyConfigReturnsNoSettings() Assert.That(data.OutputDestination, Is.Null); Assert.That(data.OutputFormat, Is.Null); Assert.That(data.OutputFileName, Is.Null); + Assert.That(data.LogDestination, Is.Null); } private const string FullFileData = @"{ @@ -75,7 +77,8 @@ public void EmptyConfigReturnsNoSettings() ""Change"": ""Minor"", ""OutputFormat"": ""Text"", ""OutputDestination"": ""Console"", - ""OutputFileName"" : ""out_42.txt"" + ""OutputFileName"" : ""out_42.txt"", + ""LogDestination"" : ""file"" }"; [Test] @@ -138,8 +141,11 @@ public void PopulatedConfigReturnsEnumSettings() var data = fsr.Read(path); - Assert.That(data.Verbosity, Is.EqualTo(LogLevel.Detailed)); Assert.That(data.Change, Is.EqualTo(VersionChange.Minor)); + + Assert.That(data.Verbosity, Is.EqualTo(LogLevel.Detailed)); + Assert.That(data.LogDestination, Is.EqualTo(LogDestination.File)); + Assert.That(data.OutputDestination, Is.EqualTo(OutputDestination.Console)); Assert.That(data.OutputFormat, Is.EqualTo(OutputFormat.Text)); } diff --git a/NuKeeper/Commands/CommandBase.cs b/NuKeeper/Commands/CommandBase.cs index dd1ca3ec8..f26ba0ecc 100644 --- a/NuKeeper/Commands/CommandBase.cs +++ b/NuKeeper/Commands/CommandBase.cs @@ -28,15 +28,7 @@ internal abstract class CommandBase // ReSharper disable once MemberCanBePrivate.Global protected string[] Source { get; } - protected NuGetSources NuGetSources => Source == null? null : new NuGetSources(Source); - - [Option(CommandOptionType.SingleValue, ShortName = "v", LongName = "verbosity", - Description = "Sets the verbosity level of the command. Allowed values are q[uiet], m[inimal], n[ormal], d[etailed].")] - public LogLevel? Verbosity { get; set; } - - [Option(CommandOptionType.SingleValue, ShortName = "lf", LongName = "logfile", - Description = "Log to the named file")] - public string LogFile { get; set; } + protected NuGetSources NuGetSources => Source == null ? null : new NuGetSources(Source); [Option(CommandOptionType.SingleValue, ShortName = "a", LongName = "age", Description = "Exclude updates that do not meet a minimum age, in order to not consume packages immediately after they are released. Examples: 0 = zero, 12h = 12 hours, 3d = 3 days, 2w = two weeks. The default is 7 days.")] @@ -52,6 +44,18 @@ internal abstract class CommandBase Description = "Do not consider packages matching this regex pattern.")] public string Exclude { get; set; } + [Option(CommandOptionType.SingleValue, ShortName = "v", LongName = "verbosity", + Description = "Sets the verbosity level of the command. Allowed values are q[uiet], m[inimal], n[ormal], d[etailed].")] + public LogLevel? Verbosity { get; set; } + + [Option(CommandOptionType.SingleValue, ShortName = "ld", LongName = "logdestination", + Description = "Destination for logging.")] + public LogDestination? LogDestination { get; set; } + + [Option(CommandOptionType.SingleValue, ShortName = "lf", LongName = "logfile", + Description = "Log to the named file.")] + public string LogFile { get; set; } + [Option(CommandOptionType.SingleValue, ShortName = "om", LongName = "outputformat", Description = "Format for output.")] public OutputFormat? OutputFormat { get; set; } @@ -89,11 +93,19 @@ public async Task OnExecute() private void InitialiseLogging() { - var fileSettings = FileSettingsCache.GetSettings(); - var logLevel = Concat.FirstValue(Verbosity, fileSettings.Verbosity, LogLevel.Normal); - var logFile = Concat.FirstValue(LogFile, fileSettings.LogFile); + var settingsFromFile = FileSettingsCache.GetSettings(); + + var defaultLogDestination = string.IsNullOrWhiteSpace(LogFile) + ? Inspection.Logging.LogDestination.Console + : Inspection.Logging.LogDestination.File; + + var logDest = Concat.FirstValue(LogDestination, settingsFromFile.LogDestination, + defaultLogDestination); + + var logLevel = Concat.FirstValue(Verbosity, settingsFromFile.Verbosity, LogLevel.Normal); + var logFile = Concat.FirstValue(LogFile, settingsFromFile.LogFile, "nukeeper.log"); - _configureLogger.Initialise(logLevel, logFile); + _configureLogger.Initialise(logLevel, logDest, logFile); } private SettingsContainer MakeSettings() diff --git a/NuKeeper/Configuration/FileSettings.cs b/NuKeeper/Configuration/FileSettings.cs index 35f18a156..8fd9ff96a 100644 --- a/NuKeeper/Configuration/FileSettings.cs +++ b/NuKeeper/Configuration/FileSettings.cs @@ -32,6 +32,8 @@ public class FileSettings public OutputDestination? OutputDestination { get; set; } public string OutputFileName { get; set; } + public LogDestination? LogDestination { get; set; } + public static FileSettings Empty() { return new FileSettings();