Skip to content

[feature/10.0] Replace reflection-based options mapping logic #8233

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: feature/10.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
<Compile Include="..\..\Tools\dotnet-monitor\CollectionRules\Options\Triggers\EventMeterOptions.cs" Link="Options\CollectionRules\Triggers\EventMeterOptions.cs" />
<Compile Include="..\..\Tools\dotnet-monitor\CollectionRules\Options\Triggers\IAspNetActionPathFilters.cs" Link="Options\CollectionRules\Triggers\IAspNetActionPathFilters.cs" />
<Compile Include="..\..\Tools\dotnet-monitor\CollectionRules\Options\Triggers\TriggerOptionsConstants.cs" Link="Options\CollectionRules\Triggers\TriggerOptionsConstants.cs" />
<Compile Include="..\..\Tools\dotnet-monitor\CommonOptionsExtensions.cs" Link="Options\CommonOptionsExtensions.cs" />
<Compile Include="..\..\Tools\dotnet-monitor\CommonOptionsMapper.cs" Link="Options\CommonOptionsMapper.cs" />
<Compile Include="..\..\Tools\dotnet-monitor\LoggingEventIds.cs" Link="LoggingEventIds.cs" />
<Compile Include="..\..\Tools\dotnet-monitor\OutputFormat.cs" Link="Options\OutputFormat.cs" />
<Compile Include="..\..\Tools\dotnet-monitor\RootOptions.cs" Link="Options\RootOptions.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@ public virtual async Task StartAsync(string command, string[] args, Cancellation
_adapter.Environment.Add("ASPNETCORE_HOSTINGSTARTUPASSEMBLIES", TestHostingStartupAssemblyName);

// Set configuration via environment variables
var configurationViaEnvironment = ConfigurationFromEnvironment.ToEnvironmentConfiguration(useDotnetMonitorPrefix: true);
var optionsMapper = new CommonOptionsMapper();
var configurationViaEnvironment = optionsMapper.ToEnvironmentConfiguration(ConfigurationFromEnvironment, useDotnetMonitorPrefix: true);
if (configurationViaEnvironment.Count > 0)
{
// Set additional environment variables from configuration
Expand Down Expand Up @@ -197,7 +198,8 @@ protected virtual void StandardOutputCallback(string line)

public void WriteKeyPerValueConfiguration(RootOptions options)
{
foreach (KeyValuePair<string, string> entry in options.ToKeyPerFileConfiguration())
CommonOptionsMapper optionsMapper = new();
foreach (KeyValuePair<string, string> entry in optionsMapper.ToKeyPerFileConfiguration(options))
{
File.WriteAllText(
Path.Combine(SharedConfigDirectoryPath, entry.Key),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Xunit.Abstractions;

Expand Down Expand Up @@ -73,7 +74,9 @@ public static IHost CreateHost(
RootOptions options = new();
setup(options);

IDictionary<string, string> configurationValues = options.ToConfigurationValues();
CommonOptionsMapper optionsMapper = new();
optionsMapper.AddActionSettings<PassThroughOptions>(nameof(PassThroughAction), MapPassThroughOptions);
IDictionary<string, string> configurationValues = optionsMapper.ToConfigurationValues(options);
outputHelper.WriteLine("Begin Configuration:");
foreach ((string key, string value) in configurationValues)
{
Expand Down Expand Up @@ -128,5 +131,21 @@ public static IHost CreateHost(
})
.Build();
}

private static void MapPassThroughOptions(PassThroughOptions obj, string valueName, string separator, IDictionary<string, string> map)
{
if (null != obj)
{
string prefix = FormattableString.Invariant($"{valueName}{separator}");
MapString(obj.Input1, FormattableString.Invariant($"{prefix}{nameof(obj.Input1)}"));
MapString(obj.Input2, FormattableString.Invariant($"{prefix}{nameof(obj.Input2)}"));
MapString(obj.Input3, FormattableString.Invariant($"{prefix}{nameof(obj.Input3)}"));
}

void MapString(string value, string valueName)
{
map.Add(valueName, ConvertUtils.ToString(value, CultureInfo.InvariantCulture));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,21 +142,22 @@ await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions =>
[Fact]
public async Task ProcessInfoTest()
{
PassThroughOptions settings = null;
CollectionRuleActionOptions actionOptions = null;
await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions =>
{
CollectionRuleOptions options = rootOptions.CreateCollectionRule(DefaultRuleName)
.AddPassThroughAction("a1", ConfigurationTokenParser.ProcessNameReference, ConfigurationTokenParser.ProcessIdReference, ConfigurationTokenParser.CommandLineReference)
.SetStartupTrigger();

settings = (PassThroughOptions)options.Actions.Last().Settings;
actionOptions = options.Actions.Last();
}, host =>
{
using CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TimeoutMs);

CollectionRuleOptions ruleOptions = host.Services.GetRequiredService<IOptionsMonitor<CollectionRuleOptions>>().Get(DefaultRuleName);
ILogger<CollectionRuleService> logger = host.Services.GetRequiredService<ILogger<CollectionRuleService>>();
TimeProvider timeProvider = host.Services.GetRequiredService<TimeProvider>();
ICollectionRuleActionOperations actionOperations = host.Services.GetRequiredService<ICollectionRuleActionOperations>();

const string processName = "actionProcess";
const int processId = 123;
Expand All @@ -165,8 +166,8 @@ await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions =>
Guid instanceId = Guid.NewGuid();
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<string, CollectionRuleActionResult>(), 1, settings);
ActionOptionsDependencyAnalyzer analyzer = ActionOptionsDependencyAnalyzer.Create(context, actionOperations);
PassThroughOptions newSettings = (PassThroughOptions)analyzer.SubstituteOptionValues(new Dictionary<string, CollectionRuleActionResult>(), 1, actionOptions);

Assert.Equal(processName, newSettings.Input1);
Assert.Equal(processId.ToString(CultureInfo.InvariantCulture), newSettings.Input2);
Expand All @@ -181,29 +182,30 @@ await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions =>
[Fact]
public async Task HostInfoTest()
{
PassThroughOptions settings = null;
CollectionRuleActionOptions actionOptions = 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;
actionOptions = options.Actions.Last();
}, host =>
{
using CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TimeoutMs);

CollectionRuleOptions ruleOptions = host.Services.GetRequiredService<IOptionsMonitor<CollectionRuleOptions>>().Get(DefaultRuleName);
ILogger<CollectionRuleService> logger = host.Services.GetRequiredService<ILogger<CollectionRuleService>>();
MockTimeProvider timeProvider = host.Services.GetRequiredService<TimeProvider>() as MockTimeProvider;
ICollectionRuleActionOperations actionOperations = host.Services.GetRequiredService<ICollectionRuleActionOperations>();

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<string, CollectionRuleActionResult>(), 1, settings);
ActionOptionsDependencyAnalyzer analyzer = ActionOptionsDependencyAnalyzer.Create(context, actionOperations);
PassThroughOptions newSettings = (PassThroughOptions)analyzer.SubstituteOptionValues(new Dictionary<string, CollectionRuleActionResult>(), 1, actionOptions);

Assert.Equal(hostName, newSettings.Input1);
Assert.Equal(hostInfo.TimeProvider.GetUtcNow().ToUnixTimeSeconds().ToString(CultureInfo.InvariantCulture), newSettings.Input2);
Expand All @@ -223,29 +225,30 @@ public async Task InvalidTokenReferenceTest()
string a2input3 = "$(Actions.a1.MissingResult)";

LogRecord record = new LogRecord();
PassThroughOptions settings = null;
CollectionRuleActionOptions actionOptions = null;
await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions =>
{
CollectionRuleOptions options = rootOptions.CreateCollectionRule(DefaultRuleName)
.AddPassThroughAction("a1", "a1input1", "a1input2", "a1input3")
.AddPassThroughAction("a2", a2input1, a2input2, a2input3)
.SetStartupTrigger();

settings = (PassThroughOptions)options.Actions.Last().Settings;
actionOptions = options.Actions.Last();
}, host =>
{
using CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TimeoutMs);

CollectionRuleOptions ruleOptions = host.Services.GetRequiredService<IOptionsMonitor<CollectionRuleOptions>>().Get(DefaultRuleName);
ILogger<CollectionRuleService> logger = host.Services.GetRequiredService<ILogger<CollectionRuleService>>();
TimeProvider timeProvider = host.Services.GetRequiredService<TimeProvider>();
ICollectionRuleActionOperations actionOperations = host.Services.GetRequiredService<ICollectionRuleActionOperations>();

Guid instanceId = Guid.NewGuid();
CollectionRuleContext context = new(DefaultRuleName, ruleOptions, new TestProcessInfo(instanceId), HostInfo.GetCurrent(timeProvider), logger);

ActionOptionsDependencyAnalyzer analyzer = ActionOptionsDependencyAnalyzer.Create(context);
ActionOptionsDependencyAnalyzer analyzer = ActionOptionsDependencyAnalyzer.Create(context, actionOperations);
analyzer.GetActionDependencies(1);
analyzer.SubstituteOptionValues(new Dictionary<string, CollectionRuleActionResult>(), 1, settings);
analyzer.SubstituteOptionValues(new Dictionary<string, CollectionRuleActionResult>(), 1, actionOptions);

Assert.Equal(3, record.Events.Count);
Assert.Equal(LoggingEventIds.InvalidActionReferenceToken.Id(), record.Events[0].EventId.Id);
Expand All @@ -264,27 +267,28 @@ await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions =>
[Fact]
public async Task RuntimeIdReferenceTest()
{
PassThroughOptions settings = null;
CollectionRuleActionOptions actionOptions = null;
await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions =>
{
CollectionRuleOptions options = rootOptions.CreateCollectionRule(DefaultRuleName)
.AddPassThroughAction("a1", ConfigurationTokenParser.RuntimeIdReference, "test", "test")
.SetStartupTrigger();

settings = (PassThroughOptions)options.Actions.Last().Settings;
}, host =>
actionOptions = options.Actions.Last();
}, host =>
{
using CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TimeoutMs);

CollectionRuleOptions ruleOptions = host.Services.GetRequiredService<IOptionsMonitor<CollectionRuleOptions>>().Get(DefaultRuleName);
ILogger<CollectionRuleService> logger = host.Services.GetRequiredService<ILogger<CollectionRuleService>>();
TimeProvider timeProvider = host.Services.GetRequiredService<TimeProvider>();
ICollectionRuleActionOperations actionOperations = host.Services.GetRequiredService<ICollectionRuleActionOperations>();

Guid instanceId = Guid.NewGuid();
CollectionRuleContext context = new(DefaultRuleName, ruleOptions, new TestProcessInfo(instanceId), HostInfo.GetCurrent(timeProvider), logger);

ActionOptionsDependencyAnalyzer analyzer = ActionOptionsDependencyAnalyzer.Create(context);
PassThroughOptions newSettings = (PassThroughOptions)analyzer.SubstituteOptionValues(new Dictionary<string, CollectionRuleActionResult>(), 1, settings);
ActionOptionsDependencyAnalyzer analyzer = ActionOptionsDependencyAnalyzer.Create(context, actionOperations);
PassThroughOptions newSettings = (PassThroughOptions)analyzer.SubstituteOptionValues(new Dictionary<string, CollectionRuleActionResult>(), 1, actionOptions);

Assert.Equal(instanceId.ToString("D"), newSettings.Input1);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable

#if UNITTEST
using Microsoft.Diagnostics.Monitoring.TestCommon;
#endif
using Microsoft.Diagnostics.Tools.Monitor;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections;
Expand All @@ -15,7 +16,7 @@
using System.Reflection;
using System.Text;

namespace Microsoft.Diagnostics.Tools.Monitor
namespace Microsoft.Diagnostics.Monitoring.Tool.UnitTests
{
internal static class CommonOptionsExtensions
{
Expand Down Expand Up @@ -118,7 +119,8 @@ private static void MapValue(object? value, string valueName, string separator,
valueType.IsEnum ||
typeof(Guid) == valueType ||
typeof(string) == valueType ||
typeof(TimeSpan) == valueType)
typeof(TimeSpan) == valueType ||
typeof(Uri) == valueType)
{
map.Add(
valueName,
Expand Down
Loading
Loading