diff --git a/src/Shared.CLI/BaseTool.cs b/src/Shared.CLI/BaseTool.cs index c7a11589..020afee3 100644 --- a/src/Shared.CLI/BaseTool.cs +++ b/src/Shared.CLI/BaseTool.cs @@ -127,6 +127,72 @@ protected void SelectOutput(string outputFile) } } + protected void ConfigureLogging(BaseToolOptions options) + { + var config = NLog.LogManager.Configuration; + var rule = config.LoggingRules.FirstOrDefault(); + + if (rule != null) + { + NLog.LogLevel? targetLevel = ParseLogLevel(options.LogLevel); + + if (targetLevel != null) + { + rule.SetLoggingLevels(targetLevel, NLog.LogLevel.Fatal); + NLog.LogManager.ReconfigExistingLoggers(); + Logger.Debug("Log level set to: {0}", targetLevel); + } + else + { + Logger.Warn("Invalid log level '{0}'. Using default (Info). Valid values: Trace(0), Debug(1), Info(2), Warn(3), Error(4), Fatal(5), Off(6)", options.LogLevel); + } + } + } + + /// + /// Parses a log level from either a string name or numeric value. + /// Supports NLog log levels: Trace, Debug, Info, Warn, Error, Fatal, Off (case-insensitive). + /// Also supports numeric values: 0=Trace, 1=Debug, 2=Info, 3=Warn, 4=Error, 5=Fatal, 6=Off. + /// + /// The log level as a string or number. + /// The corresponding NLog.LogLevel, or null if invalid. + private NLog.LogLevel? ParseLogLevel(string logLevel) + { + if (string.IsNullOrWhiteSpace(logLevel)) + { + return null; + } + + // Try parsing as a number first + if (int.TryParse(logLevel, out int numericLevel)) + { + return numericLevel switch + { + 0 => NLog.LogLevel.Trace, + 1 => NLog.LogLevel.Debug, + 2 => NLog.LogLevel.Info, + 3 => NLog.LogLevel.Warn, + 4 => NLog.LogLevel.Error, + 5 => NLog.LogLevel.Fatal, + 6 => NLog.LogLevel.Off, + _ => null + }; + } + + // Try parsing as a string (case-insensitive) + return logLevel.ToLowerInvariant() switch + { + "trace" => NLog.LogLevel.Trace, + "debug" => NLog.LogLevel.Debug, + "info" => NLog.LogLevel.Info, + "warn" or "warning" => NLog.LogLevel.Warn, + "error" => NLog.LogLevel.Error, + "fatal" => NLog.LogLevel.Fatal, + "off" or "none" => NLog.LogLevel.Off, + _ => null + }; + } + private bool redirectConsole = false; } } diff --git a/src/Shared.CLI/Options/BaseToolOptions.cs b/src/Shared.CLI/Options/BaseToolOptions.cs index fe9de91d..f1ca1554 100644 --- a/src/Shared.CLI/Options/BaseToolOptions.cs +++ b/src/Shared.CLI/Options/BaseToolOptions.cs @@ -8,4 +8,7 @@ namespace Microsoft.CST.OpenSource.OssGadget.Options; public class BaseToolOptions { + [Option('l', "log-level", Required = false, Default = "Info", + HelpText = "Set the logging level (Trace=0, Debug=1, Info=2, Warn=3, Error=4, Fatal=5, Off=6). Can use name or number.")] + public string LogLevel { get; set; } = "Info"; } diff --git a/src/Shared.CLI/Tools/DownloadTool.cs b/src/Shared.CLI/Tools/DownloadTool.cs index d42db957..2431d8ca 100644 --- a/src/Shared.CLI/Tools/DownloadTool.cs +++ b/src/Shared.CLI/Tools/DownloadTool.cs @@ -27,6 +27,8 @@ public DownloadTool(ProjectManagerFactory projectManagerFactory) public override async Task RunAsync(DownloadToolOptions options) { + ConfigureLogging(options); + if (options.Targets is IEnumerable targetList && targetList.Any()) { foreach (string? target in targetList) diff --git a/src/Shared/PackageManagers/BaseProjectManager.cs b/src/Shared/PackageManagers/BaseProjectManager.cs index d9da3e53..b0975cba 100644 --- a/src/Shared/PackageManagers/BaseProjectManager.cs +++ b/src/Shared/PackageManagers/BaseProjectManager.cs @@ -475,6 +475,23 @@ public async Task> IdentifySourceRepositoryAsync( /// protected static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger(); + /// + /// Logs the download operation with package details and download URL. + /// + /// The PackageURL being downloaded. + /// The actual download URL. + protected static void LogDownload(PackageURL? purl, string? downloadUrl) + { + if (string.IsNullOrWhiteSpace(downloadUrl)) + { + Logger.Debug("Downloading {0}...", purl?.ToString()); + } + else + { + Logger.Debug("Downloading {0} from {1}", purl?.ToString(), downloadUrl); + } + } + /// /// Rank the source repo entry candidates by their edit distance. /// diff --git a/src/Shared/PackageManagers/CPANProjectManager.cs b/src/Shared/PackageManagers/CPANProjectManager.cs index 85b77d0a..e057e570 100644 --- a/src/Shared/PackageManagers/CPANProjectManager.cs +++ b/src/Shared/PackageManagers/CPANProjectManager.cs @@ -84,9 +84,10 @@ public override async Task> DownloadVersionAsync(PackageURL string binaryUrl = root.GetProperty("download_url").GetString(); + LogDownload(purl, binaryUrl); + HttpResponseMessage result = await httpClient.GetAsync(binaryUrl); result.EnsureSuccessStatusCode(); - Logger.Debug("Downloading {0}...", purl); string targetName = $"cpan-{packageName}@{packageVersion}"; string extractionPath = Path.Combine(TopLevelExtractionDirectory, targetName); diff --git a/src/Shared/PackageManagers/CRANProjectManager.cs b/src/Shared/PackageManagers/CRANProjectManager.cs index 98d24b5b..c022c2ca 100644 --- a/src/Shared/PackageManagers/CRANProjectManager.cs +++ b/src/Shared/PackageManagers/CRANProjectManager.cs @@ -61,7 +61,7 @@ public override async Task> DownloadVersionAsync(PackageURL string url = $"{ENV_CRAN_ENDPOINT}/src/contrib/{packageName}_{packageVersion}.tar.gz"; System.Net.Http.HttpResponseMessage? result = await httpClient.GetAsync(url); result.EnsureSuccessStatusCode(); - Logger.Debug("Downloading {0}...", purl); + LogDownload(purl, url); string targetName = $"cran-{packageName}@{packageVersion}"; string extractionPath = Path.Combine(TopLevelExtractionDirectory, targetName); @@ -96,7 +96,7 @@ public override async Task> DownloadVersionAsync(PackageURL string url = $"{ENV_CRAN_ENDPOINT}/src/contrib/Archive/{packageName}/{packageName}_{packageVersion}.tar.gz"; System.Net.Http.HttpResponseMessage? result = await httpClient.GetAsync(url); result.EnsureSuccessStatusCode(); - Logger.Debug("Downloading {0}...", purl); + LogDownload(purl, url); string targetName = $"cran-{packageName}@{packageVersion}"; string extractionPath = Path.Combine(TopLevelExtractionDirectory, targetName); diff --git a/src/Shared/PackageManagers/CargoProjectManager.cs b/src/Shared/PackageManagers/CargoProjectManager.cs index f551bc98..0558f5d1 100644 --- a/src/Shared/PackageManagers/CargoProjectManager.cs +++ b/src/Shared/PackageManagers/CargoProjectManager.cs @@ -116,7 +116,7 @@ await retryPolicy.ExecuteAsync(async () => downloadedPaths.Add(extractionPath); return; } - Logger.Debug("Downloading {0}", url); + LogDownload(purl, url.ToString()); HttpClient httpClient = CreateHttpClient(); diff --git a/src/Shared/PackageManagers/CocoapodsProjectManager.cs b/src/Shared/PackageManagers/CocoapodsProjectManager.cs index 29093360..863e86c4 100644 --- a/src/Shared/PackageManagers/CocoapodsProjectManager.cs +++ b/src/Shared/PackageManagers/CocoapodsProjectManager.cs @@ -98,7 +98,8 @@ public override async Task> DownloadVersionAsync(PackageURL if (url != null) { - Logger.Debug("Downloading {0}...", purl); + //Logger.Debug("Downloading {0}...", purl); + LogDownload(purl, url); System.Net.Http.HttpResponseMessage result = await httpClient.GetAsync(url); result.EnsureSuccessStatusCode(); diff --git a/src/Shared/PackageManagers/ComposerProjectManager.cs b/src/Shared/PackageManagers/ComposerProjectManager.cs index 5d13b157..014c44e2 100644 --- a/src/Shared/PackageManagers/ComposerProjectManager.cs +++ b/src/Shared/PackageManagers/ComposerProjectManager.cs @@ -70,7 +70,7 @@ public override async Task> DownloadVersionAsync(PackageURL string? url = versionObject.Value.GetProperty("dist").GetProperty("url").GetString(); System.Net.Http.HttpResponseMessage? result = await httpClient.GetAsync(url); result.EnsureSuccessStatusCode(); - Logger.Debug("Downloading {0}...", purl); + LogDownload(purl, url); string fsNamespace = OssUtilities.NormalizeStringForFileSystem(packageNamespace); string fsName = OssUtilities.NormalizeStringForFileSystem(packageName); diff --git a/src/Shared/PackageManagers/GemProjectManager.cs b/src/Shared/PackageManagers/GemProjectManager.cs index 85a0ba0a..51b14cd9 100644 --- a/src/Shared/PackageManagers/GemProjectManager.cs +++ b/src/Shared/PackageManagers/GemProjectManager.cs @@ -61,7 +61,7 @@ public override async Task> DownloadVersionAsync(PackageURL System.Net.Http.HttpResponseMessage result = await httpClient.GetAsync(url); result.EnsureSuccessStatusCode(); - Logger.Debug("Downloading {0}...", purl); + LogDownload(purl, url); string targetName = $"rubygems-{packageName}@{packageVersion}"; string extractionPath = Path.Combine(TopLevelExtractionDirectory, targetName); diff --git a/src/Shared/PackageManagers/GolangProjectManager.cs b/src/Shared/PackageManagers/GolangProjectManager.cs index 7067adb9..b1ca943e 100644 --- a/src/Shared/PackageManagers/GolangProjectManager.cs +++ b/src/Shared/PackageManagers/GolangProjectManager.cs @@ -92,7 +92,7 @@ public override async Task> DownloadVersionAsync(PackageURL System.Net.Http.HttpResponseMessage result = await httpClient.GetAsync(url); result.EnsureSuccessStatusCode(); - Logger.Debug("Downloading {0}...", purl); + LogDownload(purl, url.ToString()); string targetName = $"golang-{packageNamespace}-{packageName}-{packageSubpath}@{packageVersion}"; string extractionPath = Path.Combine(TopLevelExtractionDirectory, targetName); diff --git a/src/Shared/PackageManagers/HackageProjectManager.cs b/src/Shared/PackageManagers/HackageProjectManager.cs index 9eda543e..e1fc5399 100644 --- a/src/Shared/PackageManagers/HackageProjectManager.cs +++ b/src/Shared/PackageManagers/HackageProjectManager.cs @@ -62,7 +62,7 @@ public override async Task> DownloadVersionAsync(PackageURL System.Net.Http.HttpResponseMessage result = await httpClient.GetAsync(url); result.EnsureSuccessStatusCode(); - Logger.Debug("Downloading {0}...", purl.ToString()); + LogDownload(purl, url); string targetName = $"hackage-{packageName}@{packageVersion}"; string extractionPath = Path.Combine(TopLevelExtractionDirectory, targetName); diff --git a/src/Shared/PackageManagers/MavenProjectManager.cs b/src/Shared/PackageManagers/MavenProjectManager.cs index cf480235..064a309a 100644 --- a/src/Shared/PackageManagers/MavenProjectManager.cs +++ b/src/Shared/PackageManagers/MavenProjectManager.cs @@ -111,7 +111,8 @@ public override async Task> DownloadVersionAsync(PackageURL System.Net.Http.HttpResponseMessage result = await httpClient.GetAsync(artifact.Uri); result.EnsureSuccessStatusCode(); - Logger.Debug($"Downloading {purl}..."); + LogDownload(purl, artifact.Uri.ToString()); + string targetName = $"maven-{packageNamespace}-{packageName}{artifact.Type}@{packageVersion}"; targetName = targetName.Replace('/', '-'); diff --git a/src/Shared/PackageManagers/NPMProjectManager.cs b/src/Shared/PackageManagers/NPMProjectManager.cs index 520d2e24..0e8c74b4 100644 --- a/src/Shared/PackageManagers/NPMProjectManager.cs +++ b/src/Shared/PackageManagers/NPMProjectManager.cs @@ -111,7 +111,8 @@ public override async Task> DownloadVersionAsync(PackageURL string? tarball = doc.RootElement.GetProperty("versions").GetProperty(packageVersion).GetProperty("dist").GetProperty("tarball").GetString(); HttpResponseMessage result = await httpClient.GetAsync(tarball); result.EnsureSuccessStatusCode(); - Logger.Debug("Downloading {0}...", purl?.ToString()); + LogDownload(purl, tarball); + //Logger.Debug("Downloading {0}...", purl?.ToString()); string targetName = $"npm-{packageName}@{packageVersion}"; string extractionPath = Path.Combine(TopLevelExtractionDirectory, targetName); if (doExtract && Directory.Exists(extractionPath) && cached == true) diff --git a/src/Shared/PackageManagers/PyPIProjectManager.cs b/src/Shared/PackageManagers/PyPIProjectManager.cs index 4178b447..e008b2a3 100644 --- a/src/Shared/PackageManagers/PyPIProjectManager.cs +++ b/src/Shared/PackageManagers/PyPIProjectManager.cs @@ -142,7 +142,10 @@ public override async Task> DownloadVersionAsync(PackageURL continue; // Missing a package type } - System.Net.Http.HttpResponseMessage result = await httpClient.GetAsync(release.GetProperty("url").GetString()); + string? downloadUrl = release.GetProperty("url").GetString(); + LogDownload(purl, downloadUrl); + + System.Net.Http.HttpResponseMessage result = await httpClient.GetAsync(downloadUrl); result.EnsureSuccessStatusCode(); string targetName = $"pypi-{packageType}-{packageName}@{packageVersion}"; string extension = ".tar.gz"; diff --git a/src/Shared/PackageManagers/VSMProjectManager.cs b/src/Shared/PackageManagers/VSMProjectManager.cs index 96638244..0df72170 100644 --- a/src/Shared/PackageManagers/VSMProjectManager.cs +++ b/src/Shared/PackageManagers/VSMProjectManager.cs @@ -134,7 +134,10 @@ public override async Task> DownloadVersionAsync(PackageURL try { - HttpResponseMessage downloadResult = await httpClient.GetAsync(source.GetString()); + string? downloadUrl = source.GetString(); + LogDownload(purl, downloadUrl); + + HttpResponseMessage downloadResult = await httpClient.GetAsync(downloadUrl); downloadResult.EnsureSuccessStatusCode(); string? targetName = $"vsm-{packageName}@{packageVersion}-{assetType}"; string extractionPath = Path.Combine(TopLevelExtractionDirectory, targetName);