Skip to content

Commit

Permalink
Add substitution tokens for other process info (#3759)
Browse files Browse the repository at this point in the history
  • Loading branch information
wiktork authored and github-actions committed Feb 24, 2023
1 parent 45cbddb commit 76e78b1
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 9 deletions.
19 changes: 13 additions & 6 deletions src/Microsoft.Diagnostics.Monitoring.WebApi/ProcessInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,22 @@ public static async Task<IProcessInfo> FromEndpointInfoAsync(IEndpointInfo endpo
}
}

string processName = GetProcessName(commandLine, endpointInfo.OperatingSystem);

return new ProcessInfoImpl(
endpointInfo,
commandLine,
processName);
}

internal static string GetProcessName(string commandLine, string operatingSystem)
{
string processName = null;
if (!string.IsNullOrEmpty(commandLine))
{
// Get the process name from the command line
bool isWindowsProcess = false;
if (string.IsNullOrEmpty(endpointInfo.OperatingSystem))
if (string.IsNullOrEmpty(operatingSystem))
{
// If operating system is null, the process is likely .NET Core 3.1 (which doesn't have the GetProcessInfo command).
// Since the underlying diagnostic communication channel used by the .NET runtime requires that the diagnostic process
Expand All @@ -116,7 +126,7 @@ public static async Task<IProcessInfo> FromEndpointInfoAsync(IEndpointInfo endpo
}
else
{
isWindowsProcess = ProcessOperatingSystemWindowsValue.Equals(endpointInfo.OperatingSystem, StringComparison.OrdinalIgnoreCase);
isWindowsProcess = ProcessOperatingSystemWindowsValue.Equals(operatingSystem, StringComparison.OrdinalIgnoreCase);
}

string processPath = CommandLineHelper.ExtractExecutablePath(commandLine, isWindowsProcess);
Expand All @@ -131,10 +141,7 @@ public static async Task<IProcessInfo> FromEndpointInfoAsync(IEndpointInfo endpo
}
}

return new ProcessInfoImpl(
endpointInfo,
commandLine,
processName);
return processName;
}

public IEndpointInfo EndpointInfo { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -115,6 +116,45 @@ await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions =>
});
}

[Fact]
public async Task ProcessInfoTest()
{
PassThroughOptions settings = 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;
}, host =>
{
using CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TimeoutMs);

CollectionRuleOptions ruleOptions = host.Services.GetRequiredService<IOptionsMonitor<CollectionRuleOptions>>().Get(DefaultRuleName);
ILogger<CollectionRuleService> logger = host.Services.GetRequiredService<ILogger<CollectionRuleService>>();
ISystemClock clock = host.Services.GetRequiredService<ISystemClock>();

const string processName = "actionProcess";
const int processId = 123;
string commandLine = FormattableString.Invariant($"{processName} arg1");

Guid instanceId = Guid.NewGuid();
CollectionRuleContext context = new(DefaultRuleName, ruleOptions, new TestEndpointInfo(instanceId, processId: processId, commandLine: commandLine), logger, clock);

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

Assert.Equal(processName, newSettings.Input1);
Assert.Equal(processId.ToString(CultureInfo.InvariantCulture), newSettings.Input2);
Assert.Equal(commandLine, newSettings.Input3);

}, serviceCollection =>
{
serviceCollection.RegisterCollectionRuleAction<PassThroughActionFactory, PassThroughOptions>(nameof(PassThroughAction));
});
}

[Fact]
public async Task InvalidTokenReferenceTest()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,15 @@ public object SubstituteOptionValues(IDictionary<string, CollectionRuleActionRes
}
}
}
string commandLine = _ruleContext.EndpointInfo?.CommandLine ?? string.Empty;

settings = _tokenParser.SubstituteOptionValues(settings, new TokenContext
{
CloneOnSubstitution = ReferenceEquals(originalSettings, settings),
RuntimeId = _ruleContext.EndpointInfo?.RuntimeInstanceCookie ?? Guid.Empty
RuntimeId = _ruleContext.EndpointInfo?.RuntimeInstanceCookie ?? Guid.Empty,
ProcessId = _ruleContext.EndpointInfo?.ProcessId ?? 0,
CommandLine = commandLine,
ProcessName = Monitoring.WebApi.ProcessInfoImpl.GetProcessName(commandLine, _ruleContext.EndpointInfo?.OperatingSystem)
});

return settings;
Expand Down
22 changes: 20 additions & 2 deletions src/Tools/dotnet-monitor/ConfigurationTokenParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;

Expand All @@ -16,7 +17,11 @@ internal sealed class TokenContext

public Guid RuntimeId { get; set; } = Guid.Empty;

public int ProcessId { get; set; } = -1;
public int ProcessId { get; set; }

public string ProcessName { get; set; } = string.Empty;

public string CommandLine { get; set; } = string.Empty;

public IDictionary<string, string> EnvironmentBlock { get; set; } = new Dictionary<string, string>();
}
Expand All @@ -31,7 +36,14 @@ internal sealed class ConfigurationTokenParser

private const string ProcessInfoReference = "Process";
private const string RuntimeId = "RuntimeId";
public static readonly string RuntimeIdReference = FormattableString.Invariant($"{SubstitutionPrefix}{ProcessInfoReference}{Separator}{RuntimeId}{SubstitutionSuffix}");
private const string ProcessId = "ProcessId";
private const string ProcessName = "Name";
private const string CommandLine = "CommandLine";

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 ConfigurationTokenParser(ILogger logger)
{
Expand All @@ -51,6 +63,9 @@ public object SubstituteOptionValues(object originalSettings, TokenContext conte
}

string replacement = originalPropertyValue.Replace(RuntimeIdReference, context.RuntimeId.ToString("D"), StringComparison.Ordinal);
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);

if (!ReferenceEquals(replacement, originalPropertyValue))
{
Expand Down Expand Up @@ -95,5 +110,8 @@ public static IEnumerable<PropertyInfo> GetPropertiesFromSettings(object setting
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.PropertyType == typeof(string) && (predicate?.Invoke(p) ?? true)) ??
Enumerable.Empty<PropertyInfo>();

private static string CreateTokenReference(string category, string token) =>
FormattableString.Invariant($"{SubstitutionPrefix}{category}{Separator}{token}{SubstitutionSuffix}");
}
}

0 comments on commit 76e78b1

Please sign in to comment.