From 0081dfd89c3fa82f2914f5135da2ab725b0e4e37 Mon Sep 17 00:00:00 2001 From: Will Sugarman Date: Fri, 17 May 2024 19:11:17 -0700 Subject: [PATCH 01/16] Initial design --- .../OptionsDisplayStrings.Designer.cs | 9 ++ .../OptionsDisplayStrings.resx | 6 +- .../StorageOptions.cs | 5 ++ .../Controllers/DiagController.cs | 2 +- .../IDumpOperationFactory.cs | 7 +- .../Actions/CollectDumpAction.cs | 2 +- .../Dumps/DumpFilePathTemplate.cs | 89 +++++++++++++++++++ .../dotnet-monitor/Dumps/DumpOperation.cs | 20 ++--- .../Dumps/DumpOperationFactory.cs | 13 +-- .../StoragePostConfigureOptions.cs | 6 ++ src/Tools/dotnet-monitor/Strings.Designer.cs | 9 ++ src/Tools/dotnet-monitor/Strings.resx | 4 + 12 files changed, 147 insertions(+), 25 deletions(-) create mode 100644 src/Tools/dotnet-monitor/Dumps/DumpFilePathTemplate.cs diff --git a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs index 9b212eb9f5c..7317543d20b 100644 --- a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs +++ b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs @@ -1441,6 +1441,15 @@ public static string DisplayAttributeDescription_StorageOptions_DefaultSharedPat } } + /// + /// Looks up a localized string similar to The template used for the name of dump files. Defaults to a name based on the current time and OS.. + /// + public static string DisplayAttributeDescription_StorageOptions_DumpFileNameTemplate { + get { + return ResourceManager.GetString("DisplayAttributeDescription_StorageOptions_DumpFileNameTemplate", resourceCulture); + } + } + /// /// Looks up a localized string similar to The location for temporary dump files. Defaults to the temp folder.. /// diff --git a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx index f0abc10a457..101e476f962 100644 --- a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx +++ b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx @@ -773,4 +773,8 @@ The expected value of the 'iss' or Issuer field in the JWT (JSON Web Token). The description provided for the Issuer parameter on MonitorApiKeyOptions. - \ No newline at end of file + + The template used for the name of dump files. Defaults to a name based on the current time and OS. + The description provided for the DumpFileNameTemplate parameter on StorageOptions. + + diff --git a/src/Microsoft.Diagnostics.Monitoring.Options/StorageOptions.cs b/src/Microsoft.Diagnostics.Monitoring.Options/StorageOptions.cs index aaf9240cbbc..227369c698f 100644 --- a/src/Microsoft.Diagnostics.Monitoring.Options/StorageOptions.cs +++ b/src/Microsoft.Diagnostics.Monitoring.Options/StorageOptions.cs @@ -18,6 +18,11 @@ public class StorageOptions Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_StorageOptions_DumpTempFolder))] public string DumpTempFolder { get; set; } + [Display( + ResourceType = typeof(OptionsDisplayStrings), + Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_StorageOptions_DumpFileNameTemplate))] + public string DumpFileNameTemplate { get; set; } + [Experimental] [Display( ResourceType = typeof(OptionsDisplayStrings), diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagController.cs b/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagController.cs index a19229ec962..a3500cf6fe0 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagController.cs +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagController.cs @@ -227,7 +227,7 @@ public Task CaptureDump( processInfo => Result( Utilities.ArtifactType_Dump, egressProvider, - _dumpOperationFactory.Create(processInfo.EndpointInfo, type), + _dumpOperationFactory.Create(processInfo, type), processInfo, tags), processKey, diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/IDumpOperationFactory.cs b/src/Microsoft.Diagnostics.Monitoring.WebApi/IDumpOperationFactory.cs index 8c735976cb4..728024e316d 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/IDumpOperationFactory.cs +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/IDumpOperationFactory.cs @@ -13,8 +13,9 @@ internal interface IDumpOperationFactory /// /// Creates an operation that produces a dump artifact. /// - IArtifactOperation Create( - IEndpointInfo endpointInfo, - DumpType dumpType); + /// The target of the dump artifact. + /// Specifies the type of dump. + /// The corresponding operation responsible for producing the artifact. + IArtifactOperation Create(IProcessInfo processInfo, DumpType dumpType); } } diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectDumpAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectDumpAction.cs index de934fd8c2c..e9758fe46a6 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectDumpAction.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectDumpAction.cs @@ -52,7 +52,7 @@ protected override EgressOperation CreateArtifactOperation(CollectionRuleMetadat KeyValueLogScope scope = Utils.CreateArtifactScope(Utils.ArtifactType_Dump, EndpointInfo); - IArtifactOperation dumpOperation = _dumpOperationFactory.Create(EndpointInfo, dumpType); + IArtifactOperation dumpOperation = _dumpOperationFactory.Create(ProcessInfo, dumpType); EgressOperation egressOperation = new EgressOperation( dumpOperation, diff --git a/src/Tools/dotnet-monitor/Dumps/DumpFilePathTemplate.cs b/src/Tools/dotnet-monitor/Dumps/DumpFilePathTemplate.cs new file mode 100644 index 00000000000..63e9f326cb3 --- /dev/null +++ b/src/Tools/dotnet-monitor/Dumps/DumpFilePathTemplate.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Diagnostics.Monitoring.WebApi; +using System; +using System.Globalization; +using System.Net; +using System.Text; +using Utils = Microsoft.Diagnostics.Monitoring.WebApi.Utilities; + +namespace Microsoft.Diagnostics.Tools.Monitor +{ + internal sealed class DumpFilePathTemplate + { + private const char PercentSpecifier = '%'; + private const char ProcessIdSpecifier = 'p'; + private const char ExecutableSpecifier = 'e'; + private const char HostSpecifier = 'h'; + private const char TimeSpecifier = 't'; + + private const int PercentPosition = 0; + private const int ProcessIdPosition = 1; + private const int ExecutablePosition = 2; + private const int HostPosition = 3; + private const int TimePosition = 4; + + private readonly CompositeFormat _format; + + private DumpFilePathTemplate(CompositeFormat format) + { + _format = format; + } + + public static DumpFilePathTemplate Parse(string template) + { + StringBuilder builder = new(); + for (int i = 0; i < template.Length; i++) + { + switch (template[i]) + { + case '{': + builder.Append('{'); + goto default; + case '}': + builder.Append('}'); + goto default; + case '%': + if (i == template.Length - 1) + { + throw new FormatException(string.Format(CultureInfo.InvariantCulture, Strings.ErrorMessage_InvalidDumpFileTemplateSpecifier, '%')); + } + + char specifier = template[++i]; + int position = specifier switch + { + PercentSpecifier => PercentPosition, + ProcessIdSpecifier => ProcessIdPosition, + ExecutableSpecifier => ExecutablePosition, + HostSpecifier => HostPosition, + TimeSpecifier => TimePosition, + _ => throw new FormatException(string.Format(CultureInfo.InvariantCulture, Strings.ErrorMessage_InvalidDumpFileTemplateSpecifier, "%" + specifier)), + }; + + builder.Append('{'); + builder.Append(position); + builder.Append('}'); + break; + default: + builder.Append(template[i]); + break; + } + } + + return new DumpFilePathTemplate(CompositeFormat.Parse(builder.ToString())); + } + + public string ToString(IProcessInfo processInfo) + { + return string.Format( + CultureInfo.InvariantCulture, + _format, + '%', + processInfo.EndpointInfo.ProcessId, + processInfo.ProcessName, + Dns.GetHostName(), + Utils.GetFileNameTimeStampUtcNow()); + } + } +} diff --git a/src/Tools/dotnet-monitor/Dumps/DumpOperation.cs b/src/Tools/dotnet-monitor/Dumps/DumpOperation.cs index 42e46327bc9..48dbd3de7a0 100644 --- a/src/Tools/dotnet-monitor/Dumps/DumpOperation.cs +++ b/src/Tools/dotnet-monitor/Dumps/DumpOperation.cs @@ -4,13 +4,9 @@ using Microsoft.Diagnostics.Monitoring; using Microsoft.Diagnostics.Monitoring.WebApi; using Microsoft.Diagnostics.Monitoring.WebApi.Models; -using Microsoft.Extensions.Logging; -using System; using System.IO; -using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using Utils = Microsoft.Diagnostics.Monitoring.WebApi.Utilities; namespace Microsoft.Diagnostics.Tools.Monitor { @@ -18,30 +14,28 @@ internal sealed class DumpOperation : IArtifactOperation { private readonly IDumpService _dumpService; private readonly DumpType _dumpType; - private readonly IEndpointInfo _endpointInfo; - private readonly ILogger _logger; + private readonly IProcessInfo _processInfo; + private readonly DumpFilePathTemplate _filePathTemplate; private readonly TaskCompletionSource _startCompletionSource = new(TaskCreationOptions.RunContinuationsAsynchronously); - public DumpOperation(IEndpointInfo endpointInfo, IDumpService dumpService, DumpType dumpType, ILogger logger) + public DumpOperation(IProcessInfo processInfo, IDumpService dumpService, DumpType dumpType, DumpFilePathTemplate filePathTemplate) { _dumpService = dumpService; _dumpType = dumpType; - _endpointInfo = endpointInfo; - _logger = logger; + _processInfo = processInfo; + _filePathTemplate = filePathTemplate; } public string GenerateFileName() { - return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? - FormattableString.Invariant($"dump_{Utils.GetFileNameTimeStampUtcNow()}.dmp") : - FormattableString.Invariant($"core_{Utils.GetFileNameTimeStampUtcNow()}"); + return _filePathTemplate.ToString(_processInfo); } public async Task ExecuteAsync(Stream outputStream, CancellationToken token) { _startCompletionSource.TrySetResult(); - using Stream dumpStream = await _dumpService.DumpAsync(_endpointInfo, _dumpType, token); + using Stream dumpStream = await _dumpService.DumpAsync(_processInfo.EndpointInfo, _dumpType, token); await dumpStream.CopyToAsync(outputStream, token); } diff --git a/src/Tools/dotnet-monitor/Dumps/DumpOperationFactory.cs b/src/Tools/dotnet-monitor/Dumps/DumpOperationFactory.cs index 01763ce1ae9..c44ef06bb87 100644 --- a/src/Tools/dotnet-monitor/Dumps/DumpOperationFactory.cs +++ b/src/Tools/dotnet-monitor/Dumps/DumpOperationFactory.cs @@ -3,24 +3,25 @@ using Microsoft.Diagnostics.Monitoring.WebApi; using Microsoft.Diagnostics.Monitoring.WebApi.Models; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; namespace Microsoft.Diagnostics.Tools.Monitor { internal sealed class DumpOperationFactory : IDumpOperationFactory { private readonly IDumpService _dumpService; - private readonly ILogger _logger; + private readonly IOptionsMonitor _options; - public DumpOperationFactory(IDumpService dumpService, ILogger logger) + public DumpOperationFactory(IDumpService dumpService, IOptionsMonitor options) { _dumpService = dumpService; - _logger = logger; + _options = options; } - public IArtifactOperation Create(IEndpointInfo endpointInfo, DumpType dumpType) + public IArtifactOperation Create(IProcessInfo processInfo, DumpType dumpType) { - return new DumpOperation(endpointInfo, _dumpService, dumpType, _logger); + StorageOptions options = _options.CurrentValue; + return new DumpOperation(processInfo, _dumpService, dumpType, DumpFilePathTemplate.Parse(options.DumpFileNameTemplate)); } } } diff --git a/src/Tools/dotnet-monitor/StoragePostConfigureOptions.cs b/src/Tools/dotnet-monitor/StoragePostConfigureOptions.cs index bd603ac1d0b..a2f99857007 100644 --- a/src/Tools/dotnet-monitor/StoragePostConfigureOptions.cs +++ b/src/Tools/dotnet-monitor/StoragePostConfigureOptions.cs @@ -4,6 +4,7 @@ using Microsoft.Diagnostics.Monitoring.WebApi; using Microsoft.Extensions.Options; using System.IO; +using System.Runtime.InteropServices; namespace Microsoft.Diagnostics.Tools.Monitor { @@ -34,6 +35,11 @@ void IPostConfigureOptions.PostConfigure(string name, StorageOpt options.SharedLibraryPath = Path.Combine(options.DefaultSharedPath, DefaultSharedPathLibrariesFolderName); } } + + if (string.IsNullOrEmpty(options.DumpFileNameTemplate)) + { + options.DumpFileNameTemplate = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "dump_%t.dmp" : "core_%t"; + } } } } diff --git a/src/Tools/dotnet-monitor/Strings.Designer.cs b/src/Tools/dotnet-monitor/Strings.Designer.cs index e6468279b8f..3f16e79ee9c 100644 --- a/src/Tools/dotnet-monitor/Strings.Designer.cs +++ b/src/Tools/dotnet-monitor/Strings.Designer.cs @@ -366,6 +366,15 @@ internal static string ErrorMessage_InvalidAuthHeader { } } + /// + /// Looks up a localized string similar to DumpFileTemplate contains an invalid template specifier {0}.. + /// + internal static string ErrorMessage_InvalidDumpFileTemplateSpecifier { + get { + return ResourceManager.GetString("ErrorMessage_InvalidDumpFileTemplateSpecifier", resourceCulture); + } + } + /// /// Looks up a localized string similar to The configuration parameter {0} must be contain a valid jwk, the value '{1}' could not be parsed as a Json Web Key. The expected format is a Json Web Key written as JSON which is base64Url encoded. {2}. /// diff --git a/src/Tools/dotnet-monitor/Strings.resx b/src/Tools/dotnet-monitor/Strings.resx index 8616e168143..1ee0d8aed0b 100644 --- a/src/Tools/dotnet-monitor/Strings.resx +++ b/src/Tools/dotnet-monitor/Strings.resx @@ -949,4 +949,8 @@ The expiration time on or after the generated key will no longer be accepted. This is a time span offset (e.g. "7.00:00:00" for 7 days) that will be added to the current date time to create the expiration date time. Gets the string to display in help that explains what the '--expiration' option does. + + DumpFileTemplate contains an invalid template specifier {0}. + Gets the format string for an invalid specifier. + \ No newline at end of file From 1408cf6217cb89f45ed8fefa564722c289b4fdfd Mon Sep 17 00:00:00 2001 From: Will Sugarman Date: Fri, 17 May 2024 19:22:50 -0700 Subject: [PATCH 02/16] Add proper unix seconds support --- .../Utilities/Utilities.cs | 9 +++++++-- .../dotnet-monitor/Dumps/DumpFilePathTemplate.cs | 12 +++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/Utilities/Utilities.cs b/src/Microsoft.Diagnostics.Monitoring.WebApi/Utilities/Utilities.cs index 7d04ecbb8b3..a679591d58e 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/Utilities/Utilities.cs +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/Utilities/Utilities.cs @@ -26,10 +26,15 @@ public static TimeSpan ConvertSecondsToTimeSpan(int durationSeconds) TimeSpan.FromSeconds(durationSeconds); } - public static string GetFileNameTimeStampUtcNow() + public static string GetFileNameTimeStamp(DateTimeOffset dt) { // spell-checker:disable-next - return DateTime.UtcNow.ToString("yyyyMMdd_HHmmss_fff"); + return dt.ToString("yyyyMMdd_HHmmss_fff"); + } + + public static string GetFileNameTimeStampUtcNow() + { + return GetFileNameTimeStamp(DateTimeOffset.UtcNow); } public static KeyValueLogScope CreateArtifactScope(string artifactType, IEndpointInfo endpointInfo) diff --git a/src/Tools/dotnet-monitor/Dumps/DumpFilePathTemplate.cs b/src/Tools/dotnet-monitor/Dumps/DumpFilePathTemplate.cs index 63e9f326cb3..02d62087ad5 100644 --- a/src/Tools/dotnet-monitor/Dumps/DumpFilePathTemplate.cs +++ b/src/Tools/dotnet-monitor/Dumps/DumpFilePathTemplate.cs @@ -17,12 +17,14 @@ internal sealed class DumpFilePathTemplate private const char ExecutableSpecifier = 'e'; private const char HostSpecifier = 'h'; private const char TimeSpecifier = 't'; + private const char DateTimeSpecifier = 'd'; private const int PercentPosition = 0; private const int ProcessIdPosition = 1; private const int ExecutablePosition = 2; private const int HostPosition = 3; private const int TimePosition = 4; + private const int DateTimePosition = 5; private readonly CompositeFormat _format; @@ -58,6 +60,7 @@ public static DumpFilePathTemplate Parse(string template) ExecutableSpecifier => ExecutablePosition, HostSpecifier => HostPosition, TimeSpecifier => TimePosition, + DateTimeSpecifier => DateTimePosition, _ => throw new FormatException(string.Format(CultureInfo.InvariantCulture, Strings.ErrorMessage_InvalidDumpFileTemplateSpecifier, "%" + specifier)), }; @@ -76,6 +79,12 @@ public static DumpFilePathTemplate Parse(string template) public string ToString(IProcessInfo processInfo) { + return ToString(processInfo, TimeProvider.System); + } + + internal string ToString(IProcessInfo processInfo, TimeProvider timeProvider) + { + DateTimeOffset utcNow = timeProvider.GetUtcNow(); return string.Format( CultureInfo.InvariantCulture, _format, @@ -83,7 +92,8 @@ public string ToString(IProcessInfo processInfo) processInfo.EndpointInfo.ProcessId, processInfo.ProcessName, Dns.GetHostName(), - Utils.GetFileNameTimeStampUtcNow()); + utcNow.ToUnixTimeSeconds(), + Utils.GetFileNameTimeStamp(utcNow)); } } } From 80208458c46b9d714f1114968b1d46bac36ded15 Mon Sep 17 00:00:00 2001 From: Will Sugarman Date: Fri, 17 May 2024 19:30:53 -0700 Subject: [PATCH 03/16] Fix default --- src/Tools/dotnet-monitor/StoragePostConfigureOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tools/dotnet-monitor/StoragePostConfigureOptions.cs b/src/Tools/dotnet-monitor/StoragePostConfigureOptions.cs index a2f99857007..aab695ab32d 100644 --- a/src/Tools/dotnet-monitor/StoragePostConfigureOptions.cs +++ b/src/Tools/dotnet-monitor/StoragePostConfigureOptions.cs @@ -38,7 +38,7 @@ void IPostConfigureOptions.PostConfigure(string name, StorageOpt if (string.IsNullOrEmpty(options.DumpFileNameTemplate)) { - options.DumpFileNameTemplate = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "dump_%t.dmp" : "core_%t"; + options.DumpFileNameTemplate = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "dump_%d.dmp" : "core_%d"; } } } From 38c3071af10654727183bf7002a64646d7977bce Mon Sep 17 00:00:00 2001 From: Will Sugarman Date: Sun, 2 Jun 2024 14:13:59 -0700 Subject: [PATCH 04/16] Add host and time --- .../ActionOptionsDependencyAnalyzer.cs | 5 ++++- src/Tools/dotnet-monitor/ConfigurationTokenParser.cs | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Tools/dotnet-monitor/CollectionRules/ActionOptionsDependencyAnalyzer.cs b/src/Tools/dotnet-monitor/CollectionRules/ActionOptionsDependencyAnalyzer.cs index bc92579ed56..70c20640d7f 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/ActionOptionsDependencyAnalyzer.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/ActionOptionsDependencyAnalyzer.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Reflection; using System.Text; @@ -159,7 +160,9 @@ public object SubstituteOptionValues(IDictionary EnvironmentBlock { get; set; } = new Dictionary(); } @@ -35,15 +39,21 @@ internal sealed class ConfigurationTokenParser public const string Separator = "."; private const string ProcessInfoReference = "Process"; + private const string ActionReference = "Action"; + private const string RuntimeId = "RuntimeId"; private const string ProcessId = "ProcessId"; private const string ProcessName = "Name"; private const string CommandLine = "CommandLine"; + private const string Hostname = "Hostname"; + private const string UnixTime = "UnixTime"; public static readonly string RuntimeIdReference = CreateTokenReference(ProcessInfoReference, RuntimeId); public static readonly string ProcessIdReference = CreateTokenReference(ProcessInfoReference, ProcessId); public static readonly string ProcessNameReference = CreateTokenReference(ProcessInfoReference, ProcessName); public static readonly string CommandLineReference = CreateTokenReference(ProcessInfoReference, CommandLine); + public static readonly string HostnameReference = CreateTokenReference(ProcessInfoReference, Hostname); + public static readonly string UnixTimeReference = CreateTokenReference(ActionReference, UnixTime); public ConfigurationTokenParser(ILogger logger) { @@ -66,6 +76,8 @@ public object SubstituteOptionValues(object originalSettings, TokenContext conte replacement = replacement.Replace(ProcessIdReference, context.ProcessId.ToString(CultureInfo.InvariantCulture), StringComparison.Ordinal); replacement = replacement.Replace(ProcessNameReference, context.ProcessName, StringComparison.Ordinal); replacement = replacement.Replace(CommandLineReference, context.CommandLine, StringComparison.Ordinal); + replacement = replacement.Replace(HostnameReference, context.Hostname, StringComparison.Ordinal); + replacement = replacement.Replace(UnixTimeReference, context.Timestamp.ToUnixTimeSeconds().ToString(CultureInfo.InvariantCulture), StringComparison.Ordinal); if (!ReferenceEquals(replacement, originalPropertyValue)) { From 29fd72afdad64d43b03460ec24cc7c80f4118942 Mon Sep 17 00:00:00 2001 From: Will Sugarman Date: Sun, 2 Jun 2024 14:15:53 -0700 Subject: [PATCH 05/16] Revert "Fix default" This reverts commit 80208458c46b9d714f1114968b1d46bac36ded15. --- src/Tools/dotnet-monitor/StoragePostConfigureOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tools/dotnet-monitor/StoragePostConfigureOptions.cs b/src/Tools/dotnet-monitor/StoragePostConfigureOptions.cs index aab695ab32d..a2f99857007 100644 --- a/src/Tools/dotnet-monitor/StoragePostConfigureOptions.cs +++ b/src/Tools/dotnet-monitor/StoragePostConfigureOptions.cs @@ -38,7 +38,7 @@ void IPostConfigureOptions.PostConfigure(string name, StorageOpt if (string.IsNullOrEmpty(options.DumpFileNameTemplate)) { - options.DumpFileNameTemplate = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "dump_%d.dmp" : "core_%d"; + options.DumpFileNameTemplate = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "dump_%t.dmp" : "core_%t"; } } } From 4b46fde44f1dcfcf4902713713fb0d1235810b52 Mon Sep 17 00:00:00 2001 From: Will Sugarman Date: Sun, 2 Jun 2024 14:16:20 -0700 Subject: [PATCH 06/16] Revert "Add proper unix seconds support" This reverts commit 1408cf6217cb89f45ed8fefa564722c289b4fdfd. --- .../Utilities/Utilities.cs | 9 ++------- .../dotnet-monitor/Dumps/DumpFilePathTemplate.cs | 12 +----------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/Utilities/Utilities.cs b/src/Microsoft.Diagnostics.Monitoring.WebApi/Utilities/Utilities.cs index a679591d58e..7d04ecbb8b3 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/Utilities/Utilities.cs +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/Utilities/Utilities.cs @@ -26,15 +26,10 @@ public static TimeSpan ConvertSecondsToTimeSpan(int durationSeconds) TimeSpan.FromSeconds(durationSeconds); } - public static string GetFileNameTimeStamp(DateTimeOffset dt) - { - // spell-checker:disable-next - return dt.ToString("yyyyMMdd_HHmmss_fff"); - } - public static string GetFileNameTimeStampUtcNow() { - return GetFileNameTimeStamp(DateTimeOffset.UtcNow); + // spell-checker:disable-next + return DateTime.UtcNow.ToString("yyyyMMdd_HHmmss_fff"); } public static KeyValueLogScope CreateArtifactScope(string artifactType, IEndpointInfo endpointInfo) diff --git a/src/Tools/dotnet-monitor/Dumps/DumpFilePathTemplate.cs b/src/Tools/dotnet-monitor/Dumps/DumpFilePathTemplate.cs index 02d62087ad5..63e9f326cb3 100644 --- a/src/Tools/dotnet-monitor/Dumps/DumpFilePathTemplate.cs +++ b/src/Tools/dotnet-monitor/Dumps/DumpFilePathTemplate.cs @@ -17,14 +17,12 @@ internal sealed class DumpFilePathTemplate private const char ExecutableSpecifier = 'e'; private const char HostSpecifier = 'h'; private const char TimeSpecifier = 't'; - private const char DateTimeSpecifier = 'd'; private const int PercentPosition = 0; private const int ProcessIdPosition = 1; private const int ExecutablePosition = 2; private const int HostPosition = 3; private const int TimePosition = 4; - private const int DateTimePosition = 5; private readonly CompositeFormat _format; @@ -60,7 +58,6 @@ public static DumpFilePathTemplate Parse(string template) ExecutableSpecifier => ExecutablePosition, HostSpecifier => HostPosition, TimeSpecifier => TimePosition, - DateTimeSpecifier => DateTimePosition, _ => throw new FormatException(string.Format(CultureInfo.InvariantCulture, Strings.ErrorMessage_InvalidDumpFileTemplateSpecifier, "%" + specifier)), }; @@ -79,12 +76,6 @@ public static DumpFilePathTemplate Parse(string template) public string ToString(IProcessInfo processInfo) { - return ToString(processInfo, TimeProvider.System); - } - - internal string ToString(IProcessInfo processInfo, TimeProvider timeProvider) - { - DateTimeOffset utcNow = timeProvider.GetUtcNow(); return string.Format( CultureInfo.InvariantCulture, _format, @@ -92,8 +83,7 @@ internal string ToString(IProcessInfo processInfo, TimeProvider timeProvider) processInfo.EndpointInfo.ProcessId, processInfo.ProcessName, Dns.GetHostName(), - utcNow.ToUnixTimeSeconds(), - Utils.GetFileNameTimeStamp(utcNow)); + Utils.GetFileNameTimeStampUtcNow()); } } } From 40024fbb13efef65f4458c39deab73669b129b0b Mon Sep 17 00:00:00 2001 From: Will Sugarman Date: Sun, 2 Jun 2024 14:16:43 -0700 Subject: [PATCH 07/16] Revert "Initial design" This reverts commit 0081dfd89c3fa82f2914f5135da2ab725b0e4e37. --- .../OptionsDisplayStrings.Designer.cs | 9 -- .../OptionsDisplayStrings.resx | 6 +- .../StorageOptions.cs | 5 -- .../Controllers/DiagController.cs | 2 +- .../IDumpOperationFactory.cs | 7 +- .../Actions/CollectDumpAction.cs | 2 +- .../Dumps/DumpFilePathTemplate.cs | 89 ------------------- .../dotnet-monitor/Dumps/DumpOperation.cs | 20 +++-- .../Dumps/DumpOperationFactory.cs | 13 ++- .../StoragePostConfigureOptions.cs | 6 -- src/Tools/dotnet-monitor/Strings.Designer.cs | 9 -- src/Tools/dotnet-monitor/Strings.resx | 4 - 12 files changed, 25 insertions(+), 147 deletions(-) delete mode 100644 src/Tools/dotnet-monitor/Dumps/DumpFilePathTemplate.cs diff --git a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs index 7317543d20b..9b212eb9f5c 100644 --- a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs +++ b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs @@ -1441,15 +1441,6 @@ public static string DisplayAttributeDescription_StorageOptions_DefaultSharedPat } } - /// - /// Looks up a localized string similar to The template used for the name of dump files. Defaults to a name based on the current time and OS.. - /// - public static string DisplayAttributeDescription_StorageOptions_DumpFileNameTemplate { - get { - return ResourceManager.GetString("DisplayAttributeDescription_StorageOptions_DumpFileNameTemplate", resourceCulture); - } - } - /// /// Looks up a localized string similar to The location for temporary dump files. Defaults to the temp folder.. /// diff --git a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx index 101e476f962..f0abc10a457 100644 --- a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx +++ b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx @@ -773,8 +773,4 @@ The expected value of the 'iss' or Issuer field in the JWT (JSON Web Token). The description provided for the Issuer parameter on MonitorApiKeyOptions. - - The template used for the name of dump files. Defaults to a name based on the current time and OS. - The description provided for the DumpFileNameTemplate parameter on StorageOptions. - - + \ No newline at end of file diff --git a/src/Microsoft.Diagnostics.Monitoring.Options/StorageOptions.cs b/src/Microsoft.Diagnostics.Monitoring.Options/StorageOptions.cs index 227369c698f..aaf9240cbbc 100644 --- a/src/Microsoft.Diagnostics.Monitoring.Options/StorageOptions.cs +++ b/src/Microsoft.Diagnostics.Monitoring.Options/StorageOptions.cs @@ -18,11 +18,6 @@ public class StorageOptions Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_StorageOptions_DumpTempFolder))] public string DumpTempFolder { get; set; } - [Display( - ResourceType = typeof(OptionsDisplayStrings), - Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_StorageOptions_DumpFileNameTemplate))] - public string DumpFileNameTemplate { get; set; } - [Experimental] [Display( ResourceType = typeof(OptionsDisplayStrings), diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagController.cs b/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagController.cs index 34f3571eff4..b6dcd12e890 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagController.cs +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagController.cs @@ -227,7 +227,7 @@ public Task CaptureDump( processInfo => Result( Utilities.ArtifactType_Dump, egressProvider, - _dumpOperationFactory.Create(processInfo, type), + _dumpOperationFactory.Create(processInfo.EndpointInfo, type), processInfo, tags), processKey, diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/IDumpOperationFactory.cs b/src/Microsoft.Diagnostics.Monitoring.WebApi/IDumpOperationFactory.cs index 728024e316d..8c735976cb4 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/IDumpOperationFactory.cs +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/IDumpOperationFactory.cs @@ -13,9 +13,8 @@ internal interface IDumpOperationFactory /// /// Creates an operation that produces a dump artifact. /// - /// The target of the dump artifact. - /// Specifies the type of dump. - /// The corresponding operation responsible for producing the artifact. - IArtifactOperation Create(IProcessInfo processInfo, DumpType dumpType); + IArtifactOperation Create( + IEndpointInfo endpointInfo, + DumpType dumpType); } } diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectDumpAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectDumpAction.cs index e9758fe46a6..de934fd8c2c 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectDumpAction.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectDumpAction.cs @@ -52,7 +52,7 @@ protected override EgressOperation CreateArtifactOperation(CollectionRuleMetadat KeyValueLogScope scope = Utils.CreateArtifactScope(Utils.ArtifactType_Dump, EndpointInfo); - IArtifactOperation dumpOperation = _dumpOperationFactory.Create(ProcessInfo, dumpType); + IArtifactOperation dumpOperation = _dumpOperationFactory.Create(EndpointInfo, dumpType); EgressOperation egressOperation = new EgressOperation( dumpOperation, diff --git a/src/Tools/dotnet-monitor/Dumps/DumpFilePathTemplate.cs b/src/Tools/dotnet-monitor/Dumps/DumpFilePathTemplate.cs deleted file mode 100644 index 63e9f326cb3..00000000000 --- a/src/Tools/dotnet-monitor/Dumps/DumpFilePathTemplate.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Diagnostics.Monitoring.WebApi; -using System; -using System.Globalization; -using System.Net; -using System.Text; -using Utils = Microsoft.Diagnostics.Monitoring.WebApi.Utilities; - -namespace Microsoft.Diagnostics.Tools.Monitor -{ - internal sealed class DumpFilePathTemplate - { - private const char PercentSpecifier = '%'; - private const char ProcessIdSpecifier = 'p'; - private const char ExecutableSpecifier = 'e'; - private const char HostSpecifier = 'h'; - private const char TimeSpecifier = 't'; - - private const int PercentPosition = 0; - private const int ProcessIdPosition = 1; - private const int ExecutablePosition = 2; - private const int HostPosition = 3; - private const int TimePosition = 4; - - private readonly CompositeFormat _format; - - private DumpFilePathTemplate(CompositeFormat format) - { - _format = format; - } - - public static DumpFilePathTemplate Parse(string template) - { - StringBuilder builder = new(); - for (int i = 0; i < template.Length; i++) - { - switch (template[i]) - { - case '{': - builder.Append('{'); - goto default; - case '}': - builder.Append('}'); - goto default; - case '%': - if (i == template.Length - 1) - { - throw new FormatException(string.Format(CultureInfo.InvariantCulture, Strings.ErrorMessage_InvalidDumpFileTemplateSpecifier, '%')); - } - - char specifier = template[++i]; - int position = specifier switch - { - PercentSpecifier => PercentPosition, - ProcessIdSpecifier => ProcessIdPosition, - ExecutableSpecifier => ExecutablePosition, - HostSpecifier => HostPosition, - TimeSpecifier => TimePosition, - _ => throw new FormatException(string.Format(CultureInfo.InvariantCulture, Strings.ErrorMessage_InvalidDumpFileTemplateSpecifier, "%" + specifier)), - }; - - builder.Append('{'); - builder.Append(position); - builder.Append('}'); - break; - default: - builder.Append(template[i]); - break; - } - } - - return new DumpFilePathTemplate(CompositeFormat.Parse(builder.ToString())); - } - - public string ToString(IProcessInfo processInfo) - { - return string.Format( - CultureInfo.InvariantCulture, - _format, - '%', - processInfo.EndpointInfo.ProcessId, - processInfo.ProcessName, - Dns.GetHostName(), - Utils.GetFileNameTimeStampUtcNow()); - } - } -} diff --git a/src/Tools/dotnet-monitor/Dumps/DumpOperation.cs b/src/Tools/dotnet-monitor/Dumps/DumpOperation.cs index 48dbd3de7a0..42e46327bc9 100644 --- a/src/Tools/dotnet-monitor/Dumps/DumpOperation.cs +++ b/src/Tools/dotnet-monitor/Dumps/DumpOperation.cs @@ -4,9 +4,13 @@ using Microsoft.Diagnostics.Monitoring; using Microsoft.Diagnostics.Monitoring.WebApi; using Microsoft.Diagnostics.Monitoring.WebApi.Models; +using Microsoft.Extensions.Logging; +using System; using System.IO; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using Utils = Microsoft.Diagnostics.Monitoring.WebApi.Utilities; namespace Microsoft.Diagnostics.Tools.Monitor { @@ -14,28 +18,30 @@ internal sealed class DumpOperation : IArtifactOperation { private readonly IDumpService _dumpService; private readonly DumpType _dumpType; - private readonly IProcessInfo _processInfo; - private readonly DumpFilePathTemplate _filePathTemplate; + private readonly IEndpointInfo _endpointInfo; + private readonly ILogger _logger; private readonly TaskCompletionSource _startCompletionSource = new(TaskCreationOptions.RunContinuationsAsynchronously); - public DumpOperation(IProcessInfo processInfo, IDumpService dumpService, DumpType dumpType, DumpFilePathTemplate filePathTemplate) + public DumpOperation(IEndpointInfo endpointInfo, IDumpService dumpService, DumpType dumpType, ILogger logger) { _dumpService = dumpService; _dumpType = dumpType; - _processInfo = processInfo; - _filePathTemplate = filePathTemplate; + _endpointInfo = endpointInfo; + _logger = logger; } public string GenerateFileName() { - return _filePathTemplate.ToString(_processInfo); + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? + FormattableString.Invariant($"dump_{Utils.GetFileNameTimeStampUtcNow()}.dmp") : + FormattableString.Invariant($"core_{Utils.GetFileNameTimeStampUtcNow()}"); } public async Task ExecuteAsync(Stream outputStream, CancellationToken token) { _startCompletionSource.TrySetResult(); - using Stream dumpStream = await _dumpService.DumpAsync(_processInfo.EndpointInfo, _dumpType, token); + using Stream dumpStream = await _dumpService.DumpAsync(_endpointInfo, _dumpType, token); await dumpStream.CopyToAsync(outputStream, token); } diff --git a/src/Tools/dotnet-monitor/Dumps/DumpOperationFactory.cs b/src/Tools/dotnet-monitor/Dumps/DumpOperationFactory.cs index c44ef06bb87..01763ce1ae9 100644 --- a/src/Tools/dotnet-monitor/Dumps/DumpOperationFactory.cs +++ b/src/Tools/dotnet-monitor/Dumps/DumpOperationFactory.cs @@ -3,25 +3,24 @@ using Microsoft.Diagnostics.Monitoring.WebApi; using Microsoft.Diagnostics.Monitoring.WebApi.Models; -using Microsoft.Extensions.Options; +using Microsoft.Extensions.Logging; namespace Microsoft.Diagnostics.Tools.Monitor { internal sealed class DumpOperationFactory : IDumpOperationFactory { private readonly IDumpService _dumpService; - private readonly IOptionsMonitor _options; + private readonly ILogger _logger; - public DumpOperationFactory(IDumpService dumpService, IOptionsMonitor options) + public DumpOperationFactory(IDumpService dumpService, ILogger logger) { _dumpService = dumpService; - _options = options; + _logger = logger; } - public IArtifactOperation Create(IProcessInfo processInfo, DumpType dumpType) + public IArtifactOperation Create(IEndpointInfo endpointInfo, DumpType dumpType) { - StorageOptions options = _options.CurrentValue; - return new DumpOperation(processInfo, _dumpService, dumpType, DumpFilePathTemplate.Parse(options.DumpFileNameTemplate)); + return new DumpOperation(endpointInfo, _dumpService, dumpType, _logger); } } } diff --git a/src/Tools/dotnet-monitor/StoragePostConfigureOptions.cs b/src/Tools/dotnet-monitor/StoragePostConfigureOptions.cs index a2f99857007..bd603ac1d0b 100644 --- a/src/Tools/dotnet-monitor/StoragePostConfigureOptions.cs +++ b/src/Tools/dotnet-monitor/StoragePostConfigureOptions.cs @@ -4,7 +4,6 @@ using Microsoft.Diagnostics.Monitoring.WebApi; using Microsoft.Extensions.Options; using System.IO; -using System.Runtime.InteropServices; namespace Microsoft.Diagnostics.Tools.Monitor { @@ -35,11 +34,6 @@ void IPostConfigureOptions.PostConfigure(string name, StorageOpt options.SharedLibraryPath = Path.Combine(options.DefaultSharedPath, DefaultSharedPathLibrariesFolderName); } } - - if (string.IsNullOrEmpty(options.DumpFileNameTemplate)) - { - options.DumpFileNameTemplate = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "dump_%t.dmp" : "core_%t"; - } } } } diff --git a/src/Tools/dotnet-monitor/Strings.Designer.cs b/src/Tools/dotnet-monitor/Strings.Designer.cs index 3f16e79ee9c..e6468279b8f 100644 --- a/src/Tools/dotnet-monitor/Strings.Designer.cs +++ b/src/Tools/dotnet-monitor/Strings.Designer.cs @@ -366,15 +366,6 @@ internal static string ErrorMessage_InvalidAuthHeader { } } - /// - /// Looks up a localized string similar to DumpFileTemplate contains an invalid template specifier {0}.. - /// - internal static string ErrorMessage_InvalidDumpFileTemplateSpecifier { - get { - return ResourceManager.GetString("ErrorMessage_InvalidDumpFileTemplateSpecifier", resourceCulture); - } - } - /// /// Looks up a localized string similar to The configuration parameter {0} must be contain a valid jwk, the value '{1}' could not be parsed as a Json Web Key. The expected format is a Json Web Key written as JSON which is base64Url encoded. {2}. /// diff --git a/src/Tools/dotnet-monitor/Strings.resx b/src/Tools/dotnet-monitor/Strings.resx index 1ee0d8aed0b..8616e168143 100644 --- a/src/Tools/dotnet-monitor/Strings.resx +++ b/src/Tools/dotnet-monitor/Strings.resx @@ -949,8 +949,4 @@ The expiration time on or after the generated key will no longer be accepted. This is a time span offset (e.g. "7.00:00:00" for 7 days) that will be added to the current date time to create the expiration date time. Gets the string to display in help that explains what the '--expiration' option does. - - DumpFileTemplate contains an invalid template specifier {0}. - Gets the format string for an invalid specifier. - \ No newline at end of file From 680381ee9225bc968473ae53f9f2de83af7af2a3 Mon Sep 17 00:00:00 2001 From: Will Sugarman Date: Sun, 2 Jun 2024 17:07:21 -0700 Subject: [PATCH 08/16] Change pattern --- .../OptionsDisplayStrings.Designer.cs | 9 +++++++++ .../OptionsDisplayStrings.resx | 3 +++ .../Operation/EgressOperation.cs | 4 ++-- .../CollectionRules/Actions/CollectDumpAction.cs | 4 ++-- .../CollectionRules/Actions/CollectExceptionsAction.cs | 1 + .../CollectionRules/Actions/CollectGCDumpAction.cs | 1 + .../CollectionRules/Actions/CollectLiveMetricsAction.cs | 1 + .../CollectionRules/Actions/CollectLogsAction.cs | 4 ++-- .../CollectionRules/Actions/CollectStacksAction.cs | 1 + .../CollectionRules/Actions/CollectTraceAction.cs | 4 ++-- .../Options/Actions/CollectDumpOptions.cs | 5 +++++ .../Options/Actions/CollectExceptionsOptions.cs | 5 +++++ .../Options/Actions/CollectGCDumpOptions.cs | 5 +++++ .../Options/Actions/CollectLiveMetricsOptions.cs | 5 +++++ .../Options/Actions/CollectLogsOptions.cs | 5 +++++ .../Options/Actions/CollectStacksOptions.cs | 5 +++++ .../Options/Actions/CollectTraceOptions.cs | 5 +++++ 17 files changed, 59 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs index 9b212eb9f5c..b093a72e400 100644 --- a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs +++ b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs @@ -258,6 +258,15 @@ public static string DisplayAttributeDescription_CallStacksOptions_Enabled { } } + /// + /// Looks up a localized string similar to The name of the created artifact.. + /// + public static string DisplayAttributeDescription_CollectArtifactOptions_ArtifactName { + get { + return ResourceManager.GetString("DisplayAttributeDescription_CollectArtifactOptions_ArtifactName", resourceCulture); + } + } + /// /// Looks up a localized string similar to The duration of time in which the artifact is collected.. /// diff --git a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx index f0abc10a457..6323d5b504e 100644 --- a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx +++ b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx @@ -773,4 +773,7 @@ The expected value of the 'iss' or Issuer field in the JWT (JSON Web Token). The description provided for the Issuer parameter on MonitorApiKeyOptions. + + The name of the created artifact. + \ No newline at end of file diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/Operation/EgressOperation.cs b/src/Microsoft.Diagnostics.Monitoring.WebApi/Operation/EgressOperation.cs index e7429f0cb40..855a05943bc 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/Operation/EgressOperation.cs +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/Operation/EgressOperation.cs @@ -23,12 +23,12 @@ internal class EgressOperation : IEgressOperation private readonly IArtifactOperation _operation; - public EgressOperation(IArtifactOperation operation, string endpointName, IProcessInfo processInfo, KeyValueLogScope scope, string tags, CollectionRuleMetadata collectionRuleMetadata = null) + public EgressOperation(IArtifactOperation operation, string endpointName, string artifactName, IProcessInfo processInfo, KeyValueLogScope scope, string tags, CollectionRuleMetadata collectionRuleMetadata = null) { _egress = (service, token) => service.EgressAsync( endpointName, operation.ExecuteAsync, - operation.GenerateFileName(), + artifactName ?? operation.GenerateFileName(), operation.ContentType, processInfo.EndpointInfo, collectionRuleMetadata, diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectDumpAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectDumpAction.cs index de934fd8c2c..440b558ebc2 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectDumpAction.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectDumpAction.cs @@ -48,7 +48,6 @@ public CollectDumpAction(IServiceProvider serviceProvider, IProcessInfo processI protected override EgressOperation CreateArtifactOperation(CollectionRuleMetadata collectionRuleMetadata) { DumpType dumpType = Options.Type.GetValueOrDefault(CollectDumpOptionsDefaults.Type); - string egressProvider = Options.Egress; KeyValueLogScope scope = Utils.CreateArtifactScope(Utils.ArtifactType_Dump, EndpointInfo); @@ -56,7 +55,8 @@ protected override EgressOperation CreateArtifactOperation(CollectionRuleMetadat EgressOperation egressOperation = new EgressOperation( dumpOperation, - egressProvider, + Options.Egress, + Options.ArtifactName, ProcessInfo, scope, tags: null, diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectExceptionsAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectExceptionsAction.cs index b8ab6730571..5959a01770f 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectExceptionsAction.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectExceptionsAction.cs @@ -46,6 +46,7 @@ protected override EgressOperation CreateArtifactOperation(CollectionRuleMetadat EgressOperation egressOperation = new EgressOperation( operation, Options.Egress, + Options.ArtifactName, ProcessInfo, scope, tags: null, diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectGCDumpAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectGCDumpAction.cs index 41faa666084..fca58488e30 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectGCDumpAction.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectGCDumpAction.cs @@ -51,6 +51,7 @@ protected override EgressOperation CreateArtifactOperation(CollectionRuleMetadat return new EgressOperation( _operationFactory.Create(ProcessInfo.EndpointInfo), Options.Egress, + Options.ArtifactName, ProcessInfo, scope, tags: null, diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectLiveMetricsAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectLiveMetricsAction.cs index 2e89eed7e61..59e72cc4496 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectLiveMetricsAction.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectLiveMetricsAction.cs @@ -85,6 +85,7 @@ protected override EgressOperation CreateArtifactOperation(CollectionRuleMetadat EgressOperation egressOperation = new EgressOperation( operation, Options.Egress, + Options.ArtifactName, ProcessInfo, scope, null, diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectLogsAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectLogsAction.cs index 2be6414a6be..7e1e2d282dd 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectLogsAction.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectLogsAction.cs @@ -51,7 +51,6 @@ protected override EgressOperation CreateArtifactOperation(CollectionRuleMetadat bool useAppFilters = Options.UseAppFilters.GetValueOrDefault(CollectLogsOptionsDefaults.UseAppFilters); LogLevel defaultLevel = Options.DefaultLevel.GetValueOrDefault(CollectLogsOptionsDefaults.DefaultLevel); Dictionary filterSpecs = Options.FilterSpecs; - string egressProvider = Options.Egress; LogFormat logFormat = Options.Format.GetValueOrDefault(CollectLogsOptionsDefaults.Format); var settings = new EventLogsPipelineSettings() @@ -73,7 +72,8 @@ protected override EgressOperation CreateArtifactOperation(CollectionRuleMetadat EgressOperation egressOperation = new EgressOperation( operation, - egressProvider, + Options.Egress, + Options.ArtifactName, ProcessInfo, scope, null, diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectStacksAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectStacksAction.cs index 9caba1a1339..3f90becfcee 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectStacksAction.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectStacksAction.cs @@ -53,6 +53,7 @@ protected override EgressOperation CreateArtifactOperation(CollectionRuleMetadat EgressOperation egressOperation = new EgressOperation( stacksOperation, Options.Egress, + Options.ArtifactName, ProcessInfo, scope, tags: null, diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectTraceAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectTraceAction.cs index 9eff259cf4f..cfbb01feb0f 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectTraceAction.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectTraceAction.cs @@ -50,7 +50,6 @@ public CollectTraceAction(IServiceProvider serviceProvider, IProcessInfo process protected override EgressOperation CreateArtifactOperation(CollectionRuleMetadata collectionRuleMetadata) { TimeSpan duration = Options.Duration.GetValueOrDefault(TimeSpan.Parse(CollectTraceOptionsDefaults.Duration)); - string egressProvider = Options.Egress; MonitoringSourceConfiguration configuration; @@ -95,7 +94,8 @@ protected override EgressOperation CreateArtifactOperation(CollectionRuleMetadat EgressOperation egressOperation = new EgressOperation( operation, - egressProvider, + Options.Egress, + Options.ArtifactName, ProcessInfo, scope, null, diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectDumpOptions.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectDumpOptions.cs index 666b0ada60c..c8c68e61bd0 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectDumpOptions.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectDumpOptions.cs @@ -36,5 +36,10 @@ internal sealed partial record class CollectDumpOptions : BaseRecordOptions, IEg [ValidateEgressProvider] #endif public string Egress { get; set; } + + [Display( + ResourceType = typeof(OptionsDisplayStrings), + Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_CollectArtifactOptions_ArtifactName))] + public string ArtifactName { get; set; } } } diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectExceptionsOptions.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectExceptionsOptions.cs index b35bc2f22e7..2dca3f4f6f7 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectExceptionsOptions.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectExceptionsOptions.cs @@ -37,5 +37,10 @@ internal sealed partial record class CollectExceptionsOptions : BaseRecordOption ResourceType = typeof(OptionsDisplayStrings), Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_CollectExceptionsOptions_Filters))] public ExceptionsConfiguration Filters { get; set; } + + [Display( + ResourceType = typeof(OptionsDisplayStrings), + Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_CollectArtifactOptions_ArtifactName))] + public string ArtifactName { get; set; } } } diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectGCDumpOptions.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectGCDumpOptions.cs index 00754715914..4d39577d000 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectGCDumpOptions.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectGCDumpOptions.cs @@ -27,5 +27,10 @@ internal sealed partial record class CollectGCDumpOptions : BaseRecordOptions, I [ValidateEgressProvider] #endif public string Egress { get; set; } + + [Display( + ResourceType = typeof(OptionsDisplayStrings), + Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_CollectArtifactOptions_ArtifactName))] + public string ArtifactName { get; set; } } } diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectLiveMetricsOptions.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectLiveMetricsOptions.cs index 5803078acc3..bff3a1a3a5b 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectLiveMetricsOptions.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectLiveMetricsOptions.cs @@ -53,5 +53,10 @@ internal sealed partial record class CollectLiveMetricsOptions : BaseRecordOptio [ValidateEgressProvider] #endif public string Egress { get; set; } + + [Display( + ResourceType = typeof(OptionsDisplayStrings), + Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_CollectArtifactOptions_ArtifactName))] + public string ArtifactName { get; set; } } } diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectLogsOptions.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectLogsOptions.cs index c10c5820eb8..b3834ab10a6 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectLogsOptions.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectLogsOptions.cs @@ -64,5 +64,10 @@ internal sealed partial record class CollectLogsOptions : BaseRecordOptions, IEg [DefaultValue(CollectLogsOptionsDefaults.Format)] [EnumDataType(typeof(LogFormat))] public LogFormat? Format { get; set; } + + [Display( + ResourceType = typeof(OptionsDisplayStrings), + Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_CollectArtifactOptions_ArtifactName))] + public string ArtifactName { get; set; } } } diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectStacksOptions.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectStacksOptions.cs index dd1aeda5a35..aa197d10b40 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectStacksOptions.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectStacksOptions.cs @@ -38,5 +38,10 @@ internal sealed partial record class CollectStacksOptions : BaseRecordOptions, I Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_CollectStacksOptions_Format))] [DefaultValue(CallStackFormat.Json)] public CallStackFormat? Format { get; set; } + + [Display( + ResourceType = typeof(OptionsDisplayStrings), + Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_CollectArtifactOptions_ArtifactName))] + public string ArtifactName { get; set; } } } diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectTraceOptions.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectTraceOptions.cs index dfe4c50a888..90029bc1b7a 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectTraceOptions.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectTraceOptions.cs @@ -67,5 +67,10 @@ internal sealed partial record class CollectTraceOptions : BaseRecordOptions, IE ResourceType = typeof(OptionsDisplayStrings), Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_CollectTraceOptions_StoppingEvent))] public TraceEventFilter StoppingEvent { get; set; } + + [Display( + ResourceType = typeof(OptionsDisplayStrings), + Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_CollectArtifactOptions_ArtifactName))] + public string ArtifactName { get; set; } } } From ccad5c33f0069262d2b4c016fa39fea51b848c5f Mon Sep 17 00:00:00 2001 From: Will Sugarman Date: Sun, 2 Jun 2024 17:55:50 -0700 Subject: [PATCH 09/16] Use default artifact name for API --- .../Controllers/DiagnosticsControllerBase.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagnosticsControllerBase.cs b/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagnosticsControllerBase.cs index e73ea81359b..a6f590af416 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagnosticsControllerBase.cs +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagnosticsControllerBase.cs @@ -96,6 +96,7 @@ protected async Task Result( return await SendToEgress(new EgressOperation( operation, providerName, + null, // Use the default artifact name processInfo, scope, tags), From 562d0b6e90498b1f3692d95c71ffb83c8e6ce50d Mon Sep 17 00:00:00 2001 From: Will Sugarman Date: Fri, 5 Jul 2024 14:09:35 -0700 Subject: [PATCH 10/16] Create HostInfo --- .../ActionListExecutorTests.cs | 6 +-- .../ActionDependencyAnalyzerTests.cs | 47 +++++++++++++++++-- .../CollectionRulePipelineTestsHelper.cs | 2 +- .../ActionOptionsDependencyAnalyzer.cs | 9 +--- .../CollectionRuleContainer.cs | 6 +-- .../CollectionRules/CollectionRuleContext.cs | 8 ++-- .../CollectionRules/CollectionRulePipeline.cs | 6 +-- .../CollectionRules/CollectionRuleService.cs | 2 +- .../CollectionRules/HostInfo.cs | 27 +++++++++++ .../ConfigurationTokenParser.cs | 13 ++--- 10 files changed, 93 insertions(+), 33 deletions(-) create mode 100644 src/Tools/dotnet-monitor/CollectionRules/HostInfo.cs diff --git a/src/Tests/CollectionRuleActions.UnitTests/ActionListExecutorTests.cs b/src/Tests/CollectionRuleActions.UnitTests/ActionListExecutorTests.cs index f5632195bfe..e6db3496c6d 100644 --- a/src/Tests/CollectionRuleActions.UnitTests/ActionListExecutorTests.cs +++ b/src/Tests/CollectionRuleActions.UnitTests/ActionListExecutorTests.cs @@ -54,7 +54,7 @@ await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions => ILogger logger = host.Services.GetRequiredService>(); TimeProvider timeProvider = host.Services.GetRequiredService(); - CollectionRuleContext context = new(DefaultRuleName, ruleOptions, null, logger, timeProvider); + CollectionRuleContext context = new(DefaultRuleName, ruleOptions, null, HostInfo.GetCurrent(timeProvider), logger); int callbackCount = 0; Action startCallback = () => callbackCount++; @@ -95,7 +95,7 @@ await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions => ILogger logger = host.Services.GetRequiredService>(); TimeProvider timeProvider = host.Services.GetRequiredService(); - CollectionRuleContext context = new(DefaultRuleName, ruleOptions, null, logger, timeProvider); + CollectionRuleContext context = new(DefaultRuleName, ruleOptions, null, HostInfo.GetCurrent(timeProvider), logger); int callbackCount = 0; Action startCallback = () => callbackCount++; @@ -141,7 +141,7 @@ await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions => ILogger logger = host.Services.GetRequiredService>(); TimeProvider timeProvider = host.Services.GetRequiredService(); - CollectionRuleContext context = new(DefaultRuleName, ruleOptions, null, logger, timeProvider); + CollectionRuleContext context = new(DefaultRuleName, ruleOptions, null, HostInfo.GetCurrent(timeProvider), logger); int callbackCount = 0; Action startCallback = () => callbackCount++; diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/ActionDependencyAnalyzerTests.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/ActionDependencyAnalyzerTests.cs index 017df523328..9a11418a7ca 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/ActionDependencyAnalyzerTests.cs +++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/ActionDependencyAnalyzerTests.cs @@ -107,7 +107,7 @@ await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions => ILogger logger = host.Services.GetRequiredService>(); TimeProvider timeProvider = host.Services.GetRequiredService(); - CollectionRuleContext context = new(DefaultRuleName, ruleOptions, null, logger, timeProvider); + CollectionRuleContext context = new(DefaultRuleName, ruleOptions, null, HostInfo.GetCurrent(timeProvider), logger); int callbackCount = 0; Action startCallback = () => callbackCount++; @@ -160,7 +160,7 @@ await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions => string commandLine = FormattableString.Invariant($"{processName} arg1"); Guid instanceId = Guid.NewGuid(); - CollectionRuleContext context = new(DefaultRuleName, ruleOptions, new TestProcessInfo(instanceId, processId: processId, commandLine: commandLine), logger, timeProvider); + CollectionRuleContext context = new(DefaultRuleName, ruleOptions, new TestProcessInfo(instanceId, processId: processId, commandLine: commandLine), HostInfo.GetCurrent(timeProvider), logger); ActionOptionsDependencyAnalyzer analyzer = ActionOptionsDependencyAnalyzer.Create(context); PassThroughOptions newSettings = (PassThroughOptions)analyzer.SubstituteOptionValues(new Dictionary(), 1, settings); @@ -175,6 +175,43 @@ await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions => }); } + [Fact] + public async Task HostInfoTest() + { + PassThroughOptions settings = null; + await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions => + { + CollectionRuleOptions options = rootOptions.CreateCollectionRule(DefaultRuleName) + .AddPassThroughAction("a1", ConfigurationTokenParser.HostNameReference, ConfigurationTokenParser.UnixTimeReference, "test") + .SetStartupTrigger(); + + settings = (PassThroughOptions)options.Actions.Last().Settings; + }, host => + { + using CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TimeoutMs); + + CollectionRuleOptions ruleOptions = host.Services.GetRequiredService>().Get(DefaultRuleName); + ILogger logger = host.Services.GetRequiredService>(); + MockTimeProvider timeProvider = host.Services.GetRequiredService() as MockTimeProvider; + + const string hostName = "exampleHost"; + Guid instanceId = Guid.NewGuid(); + HostInfo hostInfo = new HostInfo(hostName, timeProvider); + CollectionRuleContext context = new(DefaultRuleName, ruleOptions, new TestProcessInfo(instanceId), hostInfo, logger); + + ActionOptionsDependencyAnalyzer analyzer = ActionOptionsDependencyAnalyzer.Create(context); + PassThroughOptions newSettings = (PassThroughOptions)analyzer.SubstituteOptionValues(new Dictionary(), 1, settings); + + Assert.Equal(hostName, newSettings.Input1); + Assert.Equal(hostInfo.TimeProvider.GetUtcNow().ToUnixTimeSeconds().ToString(CultureInfo.InvariantCulture), newSettings.Input2); + + }, serviceCollection => + { + serviceCollection.AddSingleton(); + serviceCollection.RegisterCollectionRuleAction(nameof(PassThroughAction)); + }); + } + [Fact] public async Task InvalidTokenReferenceTest() { @@ -200,7 +237,7 @@ await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions => ILogger logger = host.Services.GetRequiredService>(); TimeProvider timeProvider = host.Services.GetRequiredService(); - CollectionRuleContext context = new(DefaultRuleName, ruleOptions, null, logger, timeProvider); + CollectionRuleContext context = new(DefaultRuleName, ruleOptions, null, HostInfo.GetCurrent(timeProvider), logger); ActionOptionsDependencyAnalyzer analyzer = ActionOptionsDependencyAnalyzer.Create(context); analyzer.GetActionDependencies(1); @@ -240,7 +277,7 @@ await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions => TimeProvider timeProvider = host.Services.GetRequiredService(); Guid instanceId = Guid.NewGuid(); - CollectionRuleContext context = new(DefaultRuleName, ruleOptions, new TestProcessInfo(instanceId), logger, timeProvider); + CollectionRuleContext context = new(DefaultRuleName, ruleOptions, new TestProcessInfo(instanceId), HostInfo.GetCurrent(timeProvider), logger); ActionOptionsDependencyAnalyzer analyzer = ActionOptionsDependencyAnalyzer.Create(context); PassThroughOptions newSettings = (PassThroughOptions)analyzer.SubstituteOptionValues(new Dictionary(), 1, settings); @@ -286,7 +323,7 @@ await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions => Guid instanceId = Guid.NewGuid(); - CollectionRuleContext context = new(DefaultRuleName, ruleOptions, new TestProcessInfo(instanceId), logger, timeProvider); + CollectionRuleContext context = new(DefaultRuleName, ruleOptions, new TestProcessInfo(instanceId), HostInfo.GetCurrent(timeProvider), logger); int callbackCount = 0; Action startCallback = () => callbackCount++; diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRulePipelineTestsHelper.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRulePipelineTestsHelper.cs index 4baccf6d413..dfec441dda1 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRulePipelineTestsHelper.cs +++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRulePipelineTestsHelper.cs @@ -70,8 +70,8 @@ await TestHostHelper.CreateCollectionRulesHost( collectionRuleName, optionsMonitor.Get(collectionRuleName), processInfo, + HostInfo.GetCurrent(timeProvider), logger, - timeProvider, callbacks.NotifyActionsThrottled); await using CollectionRulePipeline pipeline = new( diff --git a/src/Tools/dotnet-monitor/CollectionRules/ActionOptionsDependencyAnalyzer.cs b/src/Tools/dotnet-monitor/CollectionRules/ActionOptionsDependencyAnalyzer.cs index 70c20640d7f..b13848fb8bf 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/ActionOptionsDependencyAnalyzer.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/ActionOptionsDependencyAnalyzer.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net; using System.Reflection; using System.Text; @@ -161,15 +160,13 @@ public object SubstituteOptionValues(IDictionary GetDependencyPropertiesFromSettings(Col { return ConfigurationTokenParser.GetPropertiesFromSettings(options.Settings, p => p.GetCustomAttributes(typeof(ActionOptionsDependencyPropertyAttribute), inherit: true).Any()); } - - } } diff --git a/src/Tools/dotnet-monitor/CollectionRules/CollectionRuleContainer.cs b/src/Tools/dotnet-monitor/CollectionRules/CollectionRuleContainer.cs index 8bbbea62655..3272cb818c2 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/CollectionRuleContainer.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/CollectionRuleContainer.cs @@ -25,9 +25,9 @@ internal class CollectionRuleContainer : IAsyncDisposable private readonly ActionListExecutor _actionListExecutor; private readonly ILogger _logger; private readonly IProcessInfo _processInfo; + private readonly HostInfo _hostInfo; private readonly IOptionsMonitor _optionsMonitor; private readonly List _runTasks = new(); - private readonly TimeProvider _timeProvider; private readonly ICollectionRuleTriggerOperations _triggerOperations; public List Pipelines { get; set; } = new(); @@ -48,9 +48,9 @@ public CollectionRuleContainer( _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _processInfo = processInfo ?? throw new ArgumentNullException(nameof(processInfo)); + _hostInfo = HostInfo.GetCurrent(serviceProvider.GetRequiredService()); _actionListExecutor = serviceProvider.GetRequiredService(); _optionsMonitor = serviceProvider.GetRequiredService>(); - _timeProvider = serviceProvider.GetRequiredService(); _triggerOperations = serviceProvider.GetRequiredService(); } @@ -199,7 +199,7 @@ private async Task RunRuleAsync( _logger.CollectionRuleStarted(ruleName); - CollectionRuleContext context = new(ruleName, options, _processInfo, _logger, _timeProvider); + CollectionRuleContext context = new(ruleName, options, _processInfo, _hostInfo, _logger); await using CollectionRulePipeline pipeline = new( _actionListExecutor, diff --git a/src/Tools/dotnet-monitor/CollectionRules/CollectionRuleContext.cs b/src/Tools/dotnet-monitor/CollectionRules/CollectionRuleContext.cs index cce2107656c..a8ef127b13d 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/CollectionRuleContext.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/CollectionRuleContext.cs @@ -10,7 +10,7 @@ namespace Microsoft.Diagnostics.Tools.Monitor.CollectionRules { internal class CollectionRuleContext { - public CollectionRuleContext(string name, CollectionRuleOptions options, IProcessInfo processInfo, ILogger logger, TimeProvider timeProvider, Action throttledCallback = null) + public CollectionRuleContext(string name, CollectionRuleOptions options, IProcessInfo processInfo, HostInfo hostInfo, ILogger logger, Action throttledCallback = null) { // TODO: Allow null processInfo to allow tests to pass, but this should be provided by // tests since it will be required by all aspects in the future. For example, the ActionListExecutor @@ -18,19 +18,19 @@ public CollectionRuleContext(string name, CollectionRuleOptions options, IProces // the actions property bag used for token replacement. //ProcessInfo = processInfo ?? throw new ArgumentNullException(nameof(processInfo)); ProcessInfo = processInfo; + HostInfo = hostInfo ?? throw new ArgumentNullException(nameof(hostInfo)); Logger = logger ?? throw new ArgumentNullException(nameof(logger)); Options = options ?? throw new ArgumentNullException(nameof(options)); Name = name ?? throw new ArgumentNullException(nameof(name)); - TimeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider)); ThrottledCallback = throttledCallback; } - public TimeProvider TimeProvider { get; } - public IProcessInfo ProcessInfo { get; } public IEndpointInfo EndpointInfo => ProcessInfo?.EndpointInfo; + public HostInfo HostInfo { get; } + public ILogger Logger { get; } public CollectionRuleOptions Options { get; } diff --git a/src/Tools/dotnet-monitor/CollectionRules/CollectionRulePipeline.cs b/src/Tools/dotnet-monitor/CollectionRules/CollectionRulePipeline.cs index f7873ba79fc..5f2085e34f8 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/CollectionRulePipeline.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/CollectionRulePipeline.cs @@ -69,7 +69,7 @@ protected override async Task OnRun(CancellationToken token) (Context.Options.Limits?.ActionCount).GetValueOrDefault(CollectionRuleLimitsOptionsDefaults.ActionCount), Context.Options.Limits?.ActionCountSlidingWindowDuration, Context.Options.Limits?.RuleDuration, - Context.TimeProvider.GetUtcNow().UtcDateTime); + Context.HostInfo.TimeProvider.GetUtcNow().UtcDateTime); // Start cancellation timer for graceful stop of the collection rule // when the rule duration has been specified. Conditionally enable this @@ -137,7 +137,7 @@ protected override async Task OnRun(CancellationToken token) } } - DateTime currentTimestamp = Context.TimeProvider.GetUtcNow().UtcDateTime; + DateTime currentTimestamp = Context.HostInfo.TimeProvider.GetUtcNow().UtcDateTime; if (_stateHolder.BeginActionExecution(currentTimestamp)) { @@ -233,7 +233,7 @@ public CollectionRulePipelineState GetPipelineState() { CollectionRulePipelineState pipelineStateCopy = new CollectionRulePipelineState(_stateHolder); - _ = pipelineStateCopy.CheckForThrottling(Context.TimeProvider.GetUtcNow().UtcDateTime); + _ = pipelineStateCopy.CheckForThrottling(Context.HostInfo.TimeProvider.GetUtcNow().UtcDateTime); return pipelineStateCopy; } diff --git a/src/Tools/dotnet-monitor/CollectionRules/CollectionRuleService.cs b/src/Tools/dotnet-monitor/CollectionRules/CollectionRuleService.cs index 3344fd4a18d..03c8855e369 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/CollectionRuleService.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/CollectionRuleService.cs @@ -317,7 +317,7 @@ internal static CollectionRuleDetailedDescription GetCollectionRuleDetailedDescr if (description.State != CollectionRuleState.Finished) { - DateTime currentTime = pipeline.Context.TimeProvider.GetUtcNow().UtcDateTime; + DateTime currentTime = pipeline.Context.HostInfo.TimeProvider.GetUtcNow().UtcDateTime; description.SlidingWindowDurationCountdown = GetSWDCountdown(pipelineState.ExecutionTimestamps, description.ActionCountSlidingWindowDurationLimit, description.ActionCountLimit, currentTime); description.RuleFinishedCountdown = GetRuleFinishedCountdown(pipelineState.PipelineStartTime, pipelineState.RuleDuration, currentTime); diff --git a/src/Tools/dotnet-monitor/CollectionRules/HostInfo.cs b/src/Tools/dotnet-monitor/CollectionRules/HostInfo.cs new file mode 100644 index 00000000000..af2c737c133 --- /dev/null +++ b/src/Tools/dotnet-monitor/CollectionRules/HostInfo.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Net; + +namespace Microsoft.Diagnostics.Tools.Monitor.CollectionRules +{ + internal sealed class HostInfo + { + public HostInfo(string hostname, TimeProvider timeProvider) + { + ArgumentException.ThrowIfNullOrWhiteSpace(hostname); + ArgumentNullException.ThrowIfNull(timeProvider); + + HostName = hostname; + TimeProvider = timeProvider; + } + + public string HostName { get; } + + public TimeProvider TimeProvider { get; } + + public static HostInfo GetCurrent(TimeProvider timeProvider = null) => + new HostInfo(Dns.GetHostName(), timeProvider ?? TimeProvider.System); + } +} diff --git a/src/Tools/dotnet-monitor/ConfigurationTokenParser.cs b/src/Tools/dotnet-monitor/ConfigurationTokenParser.cs index 77bbf9f78ad..7cb39298123 100644 --- a/src/Tools/dotnet-monitor/ConfigurationTokenParser.cs +++ b/src/Tools/dotnet-monitor/ConfigurationTokenParser.cs @@ -23,7 +23,7 @@ internal sealed class TokenContext public string CommandLine { get; set; } = string.Empty; - public string Hostname { get; set; } = string.Empty; + public string MonitorHostName { get; set; } = string.Empty; public DateTimeOffset Timestamp { get; set; } @@ -38,22 +38,23 @@ internal sealed class ConfigurationTokenParser public const string SubstitutionSuffix = ")"; public const string Separator = "."; + private const string MonitorInfoReference = "Monitor"; private const string ProcessInfoReference = "Process"; - private const string ActionReference = "Action"; + private const string TriggerInfoReference = "Trigger"; private const string RuntimeId = "RuntimeId"; private const string ProcessId = "ProcessId"; private const string ProcessName = "Name"; private const string CommandLine = "CommandLine"; - private const string Hostname = "Hostname"; + private const string HostName = "HostName"; private const string UnixTime = "UnixTime"; public static readonly string RuntimeIdReference = CreateTokenReference(ProcessInfoReference, RuntimeId); public static readonly string ProcessIdReference = CreateTokenReference(ProcessInfoReference, ProcessId); public static readonly string ProcessNameReference = CreateTokenReference(ProcessInfoReference, ProcessName); public static readonly string CommandLineReference = CreateTokenReference(ProcessInfoReference, CommandLine); - public static readonly string HostnameReference = CreateTokenReference(ProcessInfoReference, Hostname); - public static readonly string UnixTimeReference = CreateTokenReference(ActionReference, UnixTime); + public static readonly string HostNameReference = CreateTokenReference(MonitorInfoReference, HostName); + public static readonly string UnixTimeReference = CreateTokenReference(TriggerInfoReference, UnixTime); public ConfigurationTokenParser(ILogger logger) { @@ -76,7 +77,7 @@ public object SubstituteOptionValues(object originalSettings, TokenContext conte replacement = replacement.Replace(ProcessIdReference, context.ProcessId.ToString(CultureInfo.InvariantCulture), StringComparison.Ordinal); replacement = replacement.Replace(ProcessNameReference, context.ProcessName, StringComparison.Ordinal); replacement = replacement.Replace(CommandLineReference, context.CommandLine, StringComparison.Ordinal); - replacement = replacement.Replace(HostnameReference, context.Hostname, StringComparison.Ordinal); + replacement = replacement.Replace(HostNameReference, context.MonitorHostName, StringComparison.Ordinal); replacement = replacement.Replace(UnixTimeReference, context.Timestamp.ToUnixTimeSeconds().ToString(CultureInfo.InvariantCulture), StringComparison.Ordinal); if (!ReferenceEquals(replacement, originalPropertyValue)) From 501f6c253889656e842cf72e9e0a8f540f3667b2 Mon Sep 17 00:00:00 2001 From: Will Sugarman Date: Sat, 6 Jul 2024 09:13:04 -0700 Subject: [PATCH 11/16] Fix nullability --- .../Controllers/DiagnosticsControllerBase.cs | 2 +- .../Operation/EgressOperation.cs | 2 +- .../CollectionRules/Options/Actions/CollectDumpOptions.cs | 2 ++ .../CollectionRules/Options/Actions/CollectGCDumpOptions.cs | 2 ++ .../CollectionRules/Options/Actions/CollectLogsOptions.cs | 2 +- .../CollectionRules/Options/Actions/CollectStacksOptions.cs | 4 +++- 6 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagnosticsControllerBase.cs b/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagnosticsControllerBase.cs index 05e2052b69f..4d7876ecb02 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagnosticsControllerBase.cs +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagnosticsControllerBase.cs @@ -96,7 +96,7 @@ protected async Task Result( return await SendToEgress(new EgressOperation( operation, providerName, - null, // Use the default artifact name + default, // Use default artifact name processInfo, scope, tags), diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/Operation/EgressOperation.cs b/src/Microsoft.Diagnostics.Monitoring.WebApi/Operation/EgressOperation.cs index 73f0c4f038b..fb5aa47680f 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/Operation/EgressOperation.cs +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/Operation/EgressOperation.cs @@ -23,7 +23,7 @@ internal class EgressOperation : IEgressOperation private readonly IArtifactOperation _operation; - public EgressOperation(IArtifactOperation operation, string endpointName, IProcessInfo processInfo, KeyValueLogScope scope, string? artifactName, string? tags, CollectionRuleMetadata? collectionRuleMetadata = null) + public EgressOperation(IArtifactOperation operation, string endpointName, string? artifactName, IProcessInfo processInfo, KeyValueLogScope scope, string? tags, CollectionRuleMetadata? collectionRuleMetadata = null) { _egress = (service, token) => service.EgressAsync( endpointName, diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectDumpOptions.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectDumpOptions.cs index ba060c14c19..cb8c950fcff 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectDumpOptions.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectDumpOptions.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable enable + using Microsoft.Diagnostics.Monitoring.WebApi; using Microsoft.Diagnostics.Monitoring.WebApi.Models; using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.CollectionRuleDefaultsInterfaces; diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectGCDumpOptions.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectGCDumpOptions.cs index 227b4ed2793..bdc0c61c1b2 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectGCDumpOptions.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectGCDumpOptions.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable enable + using Microsoft.Diagnostics.Monitoring.WebApi; using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.CollectionRuleDefaultsInterfaces; using System.ComponentModel.DataAnnotations; diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectLogsOptions.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectLogsOptions.cs index c9882869118..ed0a8ba62fb 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectLogsOptions.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectLogsOptions.cs @@ -70,6 +70,6 @@ internal sealed partial record class CollectLogsOptions : BaseRecordOptions, IEg [Display( ResourceType = typeof(OptionsDisplayStrings), Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_CollectArtifactOptions_ArtifactName))] - public string ArtifactName { get; set; } + public string? ArtifactName { get; set; } } } diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectStacksOptions.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectStacksOptions.cs index 783f8e17606..e1958403170 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectStacksOptions.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectStacksOptions.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable enable + using Microsoft.Diagnostics.Monitoring.WebApi; using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.CollectionRuleDefaultsInterfaces; using System.ComponentModel; @@ -42,6 +44,6 @@ internal sealed partial record class CollectStacksOptions : BaseRecordOptions, I [Display( ResourceType = typeof(OptionsDisplayStrings), Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_CollectArtifactOptions_ArtifactName))] - public string ArtifactName { get; set; } + public string? ArtifactName { get; set; } } } From adb22efcad88f63ded907c32493d5e75ca084023 Mon Sep 17 00:00:00 2001 From: Will Sugarman Date: Sun, 7 Jul 2024 11:47:44 -0700 Subject: [PATCH 12/16] Add E2E tests for most actions --- .../CollectDumpActionTests.cs | 23 ++++++-- .../CollectGCDumpActionTests.cs | 17 +++++- .../CollectLiveMetricsActionTests.cs | 19 ++++-- .../CollectTraceActionTests.cs | 33 ++++++++--- .../StacksTests.cs | 2 +- .../ActionTestsHelper.cs | 18 +++--- .../CollectionRuleOptionsExtensions.cs | 58 +++++++++---------- .../CollectionRuleOptionsTests.cs | 20 ++++--- .../IEgressProviderProperties.cs | 4 ++ .../ConfigurationTokenParser.cs | 3 +- 10 files changed, 128 insertions(+), 69 deletions(-) diff --git a/src/Tests/CollectionRuleActions.UnitTests/CollectDumpActionTests.cs b/src/Tests/CollectionRuleActions.UnitTests/CollectDumpActionTests.cs index 0b3b042f712..f853d5e7208 100644 --- a/src/Tests/CollectionRuleActions.UnitTests/CollectDumpActionTests.cs +++ b/src/Tests/CollectionRuleActions.UnitTests/CollectDumpActionTests.cs @@ -8,6 +8,7 @@ using Microsoft.Diagnostics.Monitoring.WebApi.Models; using Microsoft.Diagnostics.Tools.Monitor.CollectionRules; using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Actions; +using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options; using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.Actions; using Microsoft.Extensions.DependencyInjection; using System; @@ -45,7 +46,17 @@ public Task CollectDumpAction_Success(TargetFrameworkMoniker tfm, DumpType dumpT outputHelper: _outputHelper); } - private async Task CollectDumpAction_SuccessCore(TargetFrameworkMoniker tfm, DumpType dumpType) + [Fact] + public Task CollectDumpAction_CustomArtifactName() + { + // Code path should be unchanged between TFM and dump type + return RetryUtilities.RetryAsync( + func: () => CollectDumpAction_SuccessCore(TargetFrameworkMoniker.Current, DumpType.Mini, artifactName: Guid.NewGuid().ToString("n")), + shouldRetry: (Exception ex) => ex is TaskCanceledException, + outputHelper: _outputHelper); + } + + private async Task CollectDumpAction_SuccessCore(TargetFrameworkMoniker tfm, DumpType dumpType, string artifactName = null) { // MacOS dumps inconsistently segfault the runtime on .NET 5: https://github.com/dotnet/dotnet-monitor/issues/174 if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && tfm == TargetFrameworkMoniker.Net50) @@ -59,8 +70,12 @@ await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions => { rootOptions.AddFileSystemEgress(ActionTestsConstants.ExpectedEgressProvider, tempDirectory.FullName); - rootOptions.CreateCollectionRule(DefaultRuleName) - .AddCollectDumpAction(ActionTestsConstants.ExpectedEgressProvider, dumpType) + CollectionRuleOptions options = rootOptions.CreateCollectionRule(DefaultRuleName) + .AddCollectDumpAction(ActionTestsConstants.ExpectedEgressProvider, o => + { + o.ArtifactName = artifactName; + o.Type = dumpType; + }) .SetStartupTrigger(); }, async host => { @@ -84,7 +99,7 @@ await runner.ExecuteAsync(async () => CollectionRuleActionResult result = await ActionTestsHelper.ExecuteAndDisposeAsync(action, CommonTestTimeouts.DumpTimeout); - string egressPath = ActionTestsHelper.ValidateEgressPath(result); + string egressPath = ActionTestsHelper.ValidateEgressPath(result, artifactName); using FileStream dumpStream = new(egressPath, FileMode.Open, FileAccess.Read); Assert.NotNull(dumpStream); diff --git a/src/Tests/CollectionRuleActions.UnitTests/CollectGCDumpActionTests.cs b/src/Tests/CollectionRuleActions.UnitTests/CollectGCDumpActionTests.cs index fba07f21552..16903391c50 100644 --- a/src/Tests/CollectionRuleActions.UnitTests/CollectGCDumpActionTests.cs +++ b/src/Tests/CollectionRuleActions.UnitTests/CollectGCDumpActionTests.cs @@ -51,7 +51,18 @@ ex is TaskCanceledException || outputHelper: _outputHelper); } - private async Task CollectGCDumpAction_SuccessCore(TargetFrameworkMoniker tfm) + [Fact] + public Task CollectGCDumpAction_CustomArtifactName() + { + return RetryUtilities.RetryAsync( + func: () => CollectGCDumpAction_SuccessCore(TargetFrameworkMoniker.Current, artifactName: Guid.NewGuid().ToString("n")), + shouldRetry: (Exception ex) => ( + ex is TaskCanceledException || + (ex is CollectionRuleActionException && ex.InnerException is InvalidOperationException)), + outputHelper: _outputHelper); + } + + private async Task CollectGCDumpAction_SuccessCore(TargetFrameworkMoniker tfm, string artifactName = null) { using TemporaryDirectory tempDirectory = new(_outputHelper); @@ -60,7 +71,7 @@ await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions => rootOptions.AddFileSystemEgress(ActionTestsConstants.ExpectedEgressProvider, tempDirectory.FullName); rootOptions.CreateCollectionRule(DefaultRuleName) - .AddCollectGCDumpAction(ActionTestsConstants.ExpectedEgressProvider) + .AddCollectGCDumpAction(ActionTestsConstants.ExpectedEgressProvider, o => o.ArtifactName = artifactName) .SetStartupTrigger(); }, async host => { @@ -84,7 +95,7 @@ await runner.ExecuteAsync(async () => CollectionRuleActionResult result = await ActionTestsHelper.ExecuteAndDisposeAsync(action, CommonTestTimeouts.GCDumpTimeout); - string egressPath = ActionTestsHelper.ValidateEgressPath(result); + string egressPath = ActionTestsHelper.ValidateEgressPath(result, artifactName); using FileStream gcdumpStream = new(egressPath, FileMode.Open, FileAccess.Read); Assert.NotNull(gcdumpStream); diff --git a/src/Tests/CollectionRuleActions.UnitTests/CollectLiveMetricsActionTests.cs b/src/Tests/CollectionRuleActions.UnitTests/CollectLiveMetricsActionTests.cs index 19895fd8be4..454f9c48bda 100644 --- a/src/Tests/CollectionRuleActions.UnitTests/CollectLiveMetricsActionTests.cs +++ b/src/Tests/CollectionRuleActions.UnitTests/CollectLiveMetricsActionTests.cs @@ -38,7 +38,15 @@ public CollectLiveMetricsActionTests(ITestOutputHelper outputHelper) [Theory] [MemberData(nameof(ActionTestsHelper.GetTfms), MemberType = typeof(ActionTestsHelper))] - public async Task CollectLiveMetricsAction_CustomProviders(TargetFrameworkMoniker tfm) + public Task CollectLiveMetricsAction_CustomProviders(TargetFrameworkMoniker tfm) => + CollectLiveMetricsAction_CustomProvidersCore(tfm); + + [Theory] + [MemberData(nameof(ActionTestsHelper.GetTfms), MemberType = typeof(ActionTestsHelper))] + public Task CollectLiveMetricsAction_CustomArtifactName(TargetFrameworkMoniker tfm) => + CollectLiveMetricsAction_CustomProvidersCore(tfm, artifactName: Guid.NewGuid().ToString("n")); + + private async Task CollectLiveMetricsAction_CustomProvidersCore(TargetFrameworkMoniker tfm, string artifactName = null) { using TemporaryDirectory tempDirectory = new(_outputHelper); @@ -55,16 +63,17 @@ await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions => rootOptions.CreateCollectionRule(DefaultRuleName) .AddCollectLiveMetricsAction(ActionTestsConstants.ExpectedEgressProvider, options => { + options.ArtifactName = artifactName; options.Duration = TimeSpan.FromSeconds(CommonTestTimeouts.LiveMetricsDurationSeconds); options.IncludeDefaultProviders = false; - options.Providers = new[] - { + options.Providers = + [ new EventMetricsProvider { ProviderName = providerName, CounterNames = counterNames, } - }; + ]; }) .SetStartupTrigger(); }, async host => @@ -89,7 +98,7 @@ await runner.ExecuteAsync(async () => CollectionRuleActionResult result = await ActionTestsHelper.ExecuteAndDisposeAsync(action, CommonTestTimeouts.LiveMetricsTimeout); - string egressPath = ActionTestsHelper.ValidateEgressPath(result); + string egressPath = ActionTestsHelper.ValidateEgressPath(result, artifactName); using FileStream liveMetricsStream = new(egressPath, FileMode.Open, FileAccess.Read); Assert.NotNull(liveMetricsStream); diff --git a/src/Tests/CollectionRuleActions.UnitTests/CollectTraceActionTests.cs b/src/Tests/CollectionRuleActions.UnitTests/CollectTraceActionTests.cs index 03ac3df43ef..7f27357ca24 100644 --- a/src/Tests/CollectionRuleActions.UnitTests/CollectTraceActionTests.cs +++ b/src/Tests/CollectionRuleActions.UnitTests/CollectTraceActionTests.cs @@ -62,10 +62,10 @@ await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions => [MemberData(nameof(ActionTestsHelper.GetTfms), MemberType = typeof(ActionTestsHelper))] public async Task CollectTraceAction_ProvidersSuccess(TargetFrameworkMoniker tfm) { - List ExpectedProviders = new() - { + List ExpectedProviders = + [ new() { Name = "Microsoft-Extensions-Logging" } - }; + ]; using TemporaryDirectory tempDirectory = new(_outputHelper); @@ -79,13 +79,30 @@ await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions => options.Duration = TimeSpan.FromSeconds(2); }) .SetStartupTrigger(); - }, async host => + }, host => PerformTrace(host, tfm)); + } + + [Fact] + public async Task CollectTraceAction_CustomArtifactName() + { + string artifactName = Guid.NewGuid().ToString("n"); + using TemporaryDirectory tempDirectory = new(_outputHelper); + + await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions => { - await PerformTrace(host, tfm); - }); + rootOptions.AddFileSystemEgress(ActionTestsConstants.ExpectedEgressProvider, tempDirectory.FullName); + + rootOptions.CreateCollectionRule(DefaultRuleName) + .AddCollectTraceAction(TraceProfile.Logs, ActionTestsConstants.ExpectedEgressProvider, options => + { + options.ArtifactName = artifactName; + options.Duration = TimeSpan.FromSeconds(2); + }) + .SetStartupTrigger(); + }, host => PerformTrace(host, TargetFrameworkMoniker.Current, artifactName)); } - private async Task PerformTrace(IHost host, TargetFrameworkMoniker tfm) + private async Task PerformTrace(IHost host, TargetFrameworkMoniker tfm, string artifactName = null) { CollectTraceOptions options = ActionTestsHelper.GetActionOptions(host, DefaultRuleName); @@ -107,7 +124,7 @@ await runner.ExecuteAsync(async () => CollectionRuleActionResult result = await ActionTestsHelper.ExecuteAndDisposeAsync(action, CommonTestTimeouts.TraceTimeout); - string egressPath = ActionTestsHelper.ValidateEgressPath(result); + string egressPath = ActionTestsHelper.ValidateEgressPath(result, artifactName); using FileStream traceStream = new(egressPath, FileMode.Open, FileAccess.Read); Assert.NotNull(traceStream); diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/StacksTests.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/StacksTests.cs index 0a8631cbcae..de5cae7a5b4 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/StacksTests.cs +++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/StacksTests.cs @@ -380,7 +380,7 @@ private void CollectStacksActionConfigureTool(MonitorCollectRunner runner, out T options.GreaterThan = 0.0; options.SlidingWindowDuration = TimeSpan.FromSeconds(5); }) - .AddCollectStacksAction(fileEgress, Tools.Monitor.CollectionRules.Options.Actions.CallStackFormat.Json); + .AddCollectStacksAction(fileEgress, o => o.Format = Tools.Monitor.CollectionRules.Options.Actions.CallStackFormat.Json); ruleCompletedTask = runner.WaitForCollectionRuleActionsCompletedAsync("StacksCounterRule"); } diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/ActionTestsHelper.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/ActionTestsHelper.cs index 90ba2712d56..9b5d5107b92 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/ActionTestsHelper.cs +++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/ActionTestsHelper.cs @@ -20,18 +20,19 @@ namespace Microsoft.Diagnostics.Monitoring.TestCommon { internal static class ActionTestsHelper { - public static TargetFrameworkMoniker[] tfmsToTest = new TargetFrameworkMoniker[] - { + private static TargetFrameworkMoniker[] tfmsToTest = + [ TargetFrameworkMoniker.Net60, TargetFrameworkMoniker.Net70, TargetFrameworkMoniker.Net80 - }; - public static TargetFrameworkMoniker[] tfms6PlusToTest = new TargetFrameworkMoniker[] - { + ]; + + private static TargetFrameworkMoniker[] tfms6PlusToTest = + [ TargetFrameworkMoniker.Net60, TargetFrameworkMoniker.Net70, TargetFrameworkMoniker.Net80 - }; + ]; public static IEnumerable GetTfms() { @@ -115,12 +116,15 @@ static void AddTestCases(List arguments, Architecture architecture) } } - internal static string ValidateEgressPath(CollectionRuleActionResult result) + internal static string ValidateEgressPath(CollectionRuleActionResult result, string expectedArtifactName = null) { Assert.NotNull(result.OutputValues); Assert.True(result.OutputValues.TryGetValue(CollectionRuleActionConstants.EgressPathOutputValueName, out string egressPath)); Assert.True(File.Exists(egressPath)); + if (expectedArtifactName != null) + Assert.Equal(expectedArtifactName, Path.GetFileName(egressPath)); + return egressPath; } diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/Options/CollectionRuleOptionsExtensions.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/Options/CollectionRuleOptionsExtensions.cs index 752df683d5a..dda40b261e5 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/Options/CollectionRuleOptionsExtensions.cs +++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTestCommon/Options/CollectionRuleOptionsExtensions.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.Diagnostics.Monitoring.Options; using Microsoft.Diagnostics.Monitoring.WebApi; using Microsoft.Diagnostics.Monitoring.WebApi.Models; using Microsoft.Diagnostics.Tools.Monitor.CollectionRules; @@ -55,28 +54,27 @@ public static CollectionRuleOptions AddAction(this CollectionRuleOptions options return options; } - public static CollectionRuleOptions AddCollectDumpAction(this CollectionRuleOptions options, string egress = null, DumpType? type = null) + public static CollectionRuleOptions AddCollectDumpAction(this CollectionRuleOptions options, string egress = null, Action callback = null) { return options.AddAction( KnownCollectionRuleActions.CollectDump, actionOptions => { - CollectDumpOptions collectDumpOptions = new(); - collectDumpOptions.Egress = egress; - collectDumpOptions.Type = type; + CollectDumpOptions collectDumpOptions = new() { Egress = egress }; + callback?.Invoke(collectDumpOptions); actionOptions.Settings = collectDumpOptions; }); } - public static CollectionRuleOptions AddCollectGCDumpAction(this CollectionRuleOptions options, string egress) + public static CollectionRuleOptions AddCollectGCDumpAction(this CollectionRuleOptions options, string egress, Action callback = null) { return options.AddAction( KnownCollectionRuleActions.CollectGCDump, actionOptions => { - CollectGCDumpOptions collectGCDumpOptions = new(); - collectGCDumpOptions.Egress = egress; + CollectGCDumpOptions collectGCDumpOptions = new() { Egress = egress }; + callback?.Invoke(collectGCDumpOptions); actionOptions.Settings = collectGCDumpOptions; }); @@ -88,9 +86,7 @@ public static CollectionRuleOptions AddCollectLogsAction(this CollectionRuleOpti KnownCollectionRuleActions.CollectLogs, actionOptions => { - CollectLogsOptions collectLogsOptions = new(); - collectLogsOptions.Egress = egress; - + CollectLogsOptions collectLogsOptions = new() { Egress = egress }; callback?.Invoke(collectLogsOptions); actionOptions.Settings = collectLogsOptions; @@ -103,9 +99,11 @@ public static CollectionRuleOptions AddCollectTraceAction(this CollectionRuleOpt KnownCollectionRuleActions.CollectTrace, actionOptions => { - CollectTraceOptions collectTraceOptions = new(); - collectTraceOptions.Profile = profile; - collectTraceOptions.Egress = egress; + CollectTraceOptions collectTraceOptions = new() + { + Egress = egress, + Profile = profile, + }; callback?.Invoke(collectTraceOptions); @@ -119,9 +117,11 @@ public static CollectionRuleOptions AddCollectTraceAction(this CollectionRuleOpt KnownCollectionRuleActions.CollectTrace, actionOptions => { - CollectTraceOptions collectTraceOptions = new(); - collectTraceOptions.Providers = new List(providers); - collectTraceOptions.Egress = egress; + CollectTraceOptions collectTraceOptions = new() + { + Egress = egress, + Providers = new List(providers), + }; callback?.Invoke(collectTraceOptions); @@ -135,24 +135,22 @@ public static CollectionRuleOptions AddCollectLiveMetricsAction(this CollectionR KnownCollectionRuleActions.CollectLiveMetrics, actionOptions => { - CollectLiveMetricsOptions collectLiveMetricsOptions = new(); - collectLiveMetricsOptions.Egress = egress; - + CollectLiveMetricsOptions collectLiveMetricsOptions = new() { Egress = egress }; callback?.Invoke(collectLiveMetricsOptions); actionOptions.Settings = collectLiveMetricsOptions; }); } - public static CollectionRuleOptions AddCollectStacksAction(this CollectionRuleOptions options, string egress, CallStackFormat? format = null) + public static CollectionRuleOptions AddCollectStacksAction(this CollectionRuleOptions options, string egress, Action callback = null) { return options.AddAction( KnownCollectionRuleActions.CollectStacks, actionOptions => { - CollectStacksOptions collectStacksOptions = new(); - collectStacksOptions.Egress = egress; - collectStacksOptions.Format = format; + CollectStacksOptions collectStacksOptions = new() { Egress = egress }; + callback?.Invoke(collectStacksOptions); + actionOptions.Settings = collectStacksOptions; }); } @@ -186,14 +184,14 @@ public static CollectionRuleOptions AddExecuteActionAppAction(this CollectionRul return options; } - public static CollectionRuleOptions AddLoadProfilerAction(this CollectionRuleOptions options, Action configureOptions) + public static CollectionRuleOptions AddLoadProfilerAction(this CollectionRuleOptions options, Action callback = null) { return options.AddAction( KnownCollectionRuleActions.LoadProfiler, callback: actionOptions => { LoadProfilerOptions loadProfilerOptions = new(); - configureOptions?.Invoke(loadProfilerOptions); + callback?.Invoke(loadProfilerOptions); actionOptions.Settings = loadProfilerOptions; }); } @@ -227,16 +225,14 @@ public static CollectionRuleOptions AddGetEnvironmentVariableAction(this Collect }); } - public static CollectionRuleOptions AddCollectExceptionsAction(this CollectionRuleOptions options, string egress, ExceptionFormat? format = null, ExceptionsConfiguration filters = null) + public static CollectionRuleOptions AddCollectExceptionsAction(this CollectionRuleOptions options, string egress, Action callback = null) { return options.AddAction( KnownCollectionRuleActions.CollectExceptions, actionOptions => { - CollectExceptionsOptions collectExceptionsOptions = new(); - collectExceptionsOptions.Egress = egress; - collectExceptionsOptions.Format = format; - collectExceptionsOptions.Filters = filters; + CollectExceptionsOptions collectExceptionsOptions = new() { Egress = egress }; + callback?.Invoke(collectExceptionsOptions); actionOptions.Settings = collectExceptionsOptions; }); diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRuleOptionsTests.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRuleOptionsTests.cs index 8efc02c294e..31cbb996d67 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRuleOptionsTests.cs +++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRuleOptionsTests.cs @@ -945,7 +945,7 @@ public Task CollectionRuleOptions_CollectDumpAction_RoundTrip() { rootOptions.CreateCollectionRule(DefaultRuleName) .SetStartupTrigger() - .AddCollectDumpAction(ExpectedEgressProvider, ExpectedDumpType); + .AddCollectDumpAction(ExpectedEgressProvider, o => o.Type = ExpectedDumpType); rootOptions.AddFileSystemEgress(ExpectedEgressProvider, "/tmp"); }, ruleOptions => @@ -962,7 +962,7 @@ public Task CollectionRuleOptions_CollectDumpAction_PropertyValidation() { rootOptions.CreateCollectionRule(DefaultRuleName) .SetStartupTrigger() - .AddCollectDumpAction(UnknownEgressName, (DumpType)20); + .AddCollectDumpAction(UnknownEgressName, o => o.Type = (DumpType)20); }, ex => { @@ -1661,7 +1661,7 @@ public Task CollectionRuleOptions_LoadProfilerAction_RoundTrip() rootOptions.CreateCollectionRule(DefaultRuleName) .SetStartupTrigger() .AddLoadProfilerAction( - configureOptions: opts => + callback: opts => { opts.Path = ExpectedTargetPath; opts.Clsid = ExpectedClsid; @@ -1684,7 +1684,7 @@ await ValidateFailure( rootOptions.CreateCollectionRule(DefaultRuleName) .SetStartupTrigger() .AddLoadProfilerAction( - configureOptions: opts => + callback: opts => { opts.Path = null; opts.Clsid = ExpectedClsid; @@ -1703,7 +1703,7 @@ await ValidateFailure( rootOptions.CreateCollectionRule(DefaultRuleName) .SetStartupTrigger() .AddLoadProfilerAction( - configureOptions: opts => + callback: opts => { opts.Path = string.Empty; opts.Clsid = ExpectedClsid; @@ -1722,7 +1722,7 @@ await ValidateFailure( rootOptions.CreateCollectionRule(DefaultRuleName) .SetStartupTrigger() .AddLoadProfilerAction( - configureOptions: opts => + callback: opts => { opts.Path = " "; // White space is not allowed by the [Required] Attribute opts.Clsid = ExpectedClsid; @@ -1746,7 +1746,7 @@ public Task CollectionRuleOptions_LoadProfilerAction_ClsidPropertyValidation() rootOptions.CreateCollectionRule(DefaultRuleName) .SetStartupTrigger() .AddLoadProfilerAction( - configureOptions: opts => + callback: opts => { opts.Path = ExpectedTargetPath; opts.Clsid = Guid.Empty; @@ -2056,7 +2056,11 @@ public Task CollectionRuleOptions_CollectExceptionsAction_RoundTrip() { rootOptions.CreateCollectionRule(DefaultRuleName) .SetStartupTrigger() - .AddCollectExceptionsAction(ExpectedEgressProvider, ExpectedFormat, ExpectedFilters); + .AddCollectExceptionsAction(ExpectedEgressProvider, o => + { + o.Filters = ExpectedFilters; + o.Format = ExpectedFormat; + }); rootOptions.AddFileSystemEgress(ExpectedEgressProvider, "/tmp") .EnableExceptions(); }, diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/CollectionRuleDefaultsInterfaces/IEgressProviderProperties.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/CollectionRuleDefaultsInterfaces/IEgressProviderProperties.cs index 56c906eed69..ba170d259af 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Options/CollectionRuleDefaultsInterfaces/IEgressProviderProperties.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Options/CollectionRuleDefaultsInterfaces/IEgressProviderProperties.cs @@ -1,10 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable enable + namespace Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.CollectionRuleDefaultsInterfaces { internal interface IEgressProviderProperties { public string Egress { get; set; } + + public string? ArtifactName { get; set; } } } diff --git a/src/Tools/dotnet-monitor/ConfigurationTokenParser.cs b/src/Tools/dotnet-monitor/ConfigurationTokenParser.cs index 7cb39298123..a3b3c8dc6a4 100644 --- a/src/Tools/dotnet-monitor/ConfigurationTokenParser.cs +++ b/src/Tools/dotnet-monitor/ConfigurationTokenParser.cs @@ -40,7 +40,6 @@ internal sealed class ConfigurationTokenParser private const string MonitorInfoReference = "Monitor"; private const string ProcessInfoReference = "Process"; - private const string TriggerInfoReference = "Trigger"; private const string RuntimeId = "RuntimeId"; private const string ProcessId = "ProcessId"; @@ -54,7 +53,7 @@ internal sealed class ConfigurationTokenParser public static readonly string ProcessNameReference = CreateTokenReference(ProcessInfoReference, ProcessName); public static readonly string CommandLineReference = CreateTokenReference(ProcessInfoReference, CommandLine); public static readonly string HostNameReference = CreateTokenReference(MonitorInfoReference, HostName); - public static readonly string UnixTimeReference = CreateTokenReference(TriggerInfoReference, UnixTime); + public static readonly string UnixTimeReference = CreateTokenReference(MonitorInfoReference, UnixTime); public ConfigurationTokenParser(ILogger logger) { From 463aa7cc4a97068da3b5cc4f7948d38dc290926d Mon Sep 17 00:00:00 2001 From: Will Sugarman Date: Sun, 7 Jul 2024 12:49:12 -0700 Subject: [PATCH 13/16] Nit --- .../OptionsDisplayStrings.Designer.cs | 2 +- .../OptionsDisplayStrings.resx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs index b093a72e400..4ec22b771fd 100644 --- a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs +++ b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs @@ -259,7 +259,7 @@ public static string DisplayAttributeDescription_CallStacksOptions_Enabled { } /// - /// Looks up a localized string similar to The name of the created artifact.. + /// Looks up a localized string similar to The name of the generated artifact.. /// public static string DisplayAttributeDescription_CollectArtifactOptions_ArtifactName { get { diff --git a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx index 6323d5b504e..23907f6752f 100644 --- a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx +++ b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx @@ -774,6 +774,6 @@ The description provided for the Issuer parameter on MonitorApiKeyOptions. - The name of the created artifact. + The name of the generated artifact. - \ No newline at end of file + From c23a94fd03f2c2857fb2d3d426ba3aff9c5c86d4 Mon Sep 17 00:00:00 2001 From: Will Sugarman Date: Sat, 27 Jul 2024 16:55:10 -0700 Subject: [PATCH 14/16] Update schema.json --- documentation/schema.json | 49 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/documentation/schema.json b/documentation/schema.json index 36cff6187fc..fc88415a895 100644 --- a/documentation/schema.json +++ b/documentation/schema.json @@ -1700,6 +1700,13 @@ "type": "string", "description": "The name of the egress provider to which the artifact is egressed.", "minLength": 1 + }, + "ArtifactName": { + "type": [ + "null", + "string" + ], + "description": "The name of the generated artifact." } } }, @@ -1750,6 +1757,13 @@ "$ref": "#/definitions/ExceptionsConfiguration" } ] + }, + "ArtifactName": { + "type": [ + "null", + "string" + ], + "description": "The name of the generated artifact." } } }, @@ -1776,6 +1790,13 @@ "type": "string", "description": "The name of the egress provider to which the artifact is egressed.", "minLength": 1 + }, + "ArtifactName": { + "type": [ + "null", + "string" + ], + "description": "The name of the generated artifact." } } }, @@ -1825,6 +1846,13 @@ "type": "string", "description": "The name of the egress provider to which the artifact is egressed.", "minLength": 1 + }, + "ArtifactName": { + "type": [ + "null", + "string" + ], + "description": "The name of the generated artifact." } } }, @@ -1939,6 +1967,13 @@ "$ref": "#/definitions/LogFormat" } ] + }, + "ArtifactName": { + "type": [ + "null", + "string" + ], + "description": "The name of the generated artifact." } } }, @@ -1976,6 +2011,13 @@ "$ref": "#/definitions/CallStackFormat" } ] + }, + "ArtifactName": { + "type": [ + "null", + "string" + ], + "description": "The name of the generated artifact." } } }, @@ -2062,6 +2104,13 @@ "$ref": "#/definitions/TraceEventFilter" } ] + }, + "ArtifactName": { + "type": [ + "null", + "string" + ], + "description": "The name of the generated artifact." } } }, From b6a769335419bdd34322e8dd749441b5ef000689 Mon Sep 17 00:00:00 2001 From: Will Sugarman Date: Sat, 27 Jul 2024 16:57:10 -0700 Subject: [PATCH 15/16] Nit --- .../CollectLiveMetricsActionTests.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Tests/CollectionRuleActions.UnitTests/CollectLiveMetricsActionTests.cs b/src/Tests/CollectionRuleActions.UnitTests/CollectLiveMetricsActionTests.cs index 454f9c48bda..a82a33f43c0 100644 --- a/src/Tests/CollectionRuleActions.UnitTests/CollectLiveMetricsActionTests.cs +++ b/src/Tests/CollectionRuleActions.UnitTests/CollectLiveMetricsActionTests.cs @@ -41,10 +41,9 @@ public CollectLiveMetricsActionTests(ITestOutputHelper outputHelper) public Task CollectLiveMetricsAction_CustomProviders(TargetFrameworkMoniker tfm) => CollectLiveMetricsAction_CustomProvidersCore(tfm); - [Theory] - [MemberData(nameof(ActionTestsHelper.GetTfms), MemberType = typeof(ActionTestsHelper))] - public Task CollectLiveMetricsAction_CustomArtifactName(TargetFrameworkMoniker tfm) => - CollectLiveMetricsAction_CustomProvidersCore(tfm, artifactName: Guid.NewGuid().ToString("n")); + [Fact] + public Task CollectLiveMetricsAction_CustomArtifactName() => + CollectLiveMetricsAction_CustomProvidersCore(TargetFrameworkMoniker.Current, artifactName: Guid.NewGuid().ToString("n")); private async Task CollectLiveMetricsAction_CustomProvidersCore(TargetFrameworkMoniker tfm, string artifactName = null) { From 092c607d1345dff9575c246847558286b80cc3a0 Mon Sep 17 00:00:00 2001 From: Will Sugarman Date: Thu, 1 Aug 2024 06:54:00 -0700 Subject: [PATCH 16/16] nit: spelling mistake --- .../StacksTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/StacksTests.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/StacksTests.cs index de5cae7a5b4..a78f444cdd2 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/StacksTests.cs +++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/StacksTests.cs @@ -48,7 +48,7 @@ private static MethodInfo GetMethodInfo(string methodName) methodName = methodName[..methodName.IndexOf('[')]; } - // Return null on psuedo frames (e.g. [NativeFrame]) + // Return null on pseudo frames (e.g. [NativeFrame]) if (methodName.Length == 0) { return null;