Skip to content
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

Implement collection rule pipeline, event counter trigger, and tests. #825

Merged
Show file tree
Hide file tree
Changes from 4 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 @@ -20,6 +20,11 @@
<ProjectReference Include="..\..\Microsoft.Diagnostics.Monitoring.Options\Microsoft.Diagnostics.Monitoring.Options.csproj" />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests"/>
<InternalsVisibleTo Include="Microsoft.Diagnostics.Monitoring.Tool.UnitTests"/>
</ItemGroup>

<Import Project="GenerateDotNetHost.targets" />

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,17 @@ public static class Commands
public const string StartLogging = nameof(StartLogging);
}
}

public static class SpinWait
{
public const string Name = nameof(SpinWait);

public static class Commands
{
public const string StartSpin = nameof(StartSpin);

public const string StopSpin = nameof(StopSpin);
}
}
}
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.Diagnostics.Tools.Monitor;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.Diagnostics.Monitoring.Tool.UnitTests.CollectionRules.Actions
{
internal static class ActionsServiceCollectionExtensions
{
public static IServiceCollection RegisterTestAction(this IServiceCollection services, CallbackActionService callback)
{
services.AddSingleton(callback);
services.RegisterCollectionRuleAction<CallbackAction, object>(CallbackAction.ActionName);
return services;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.Diagnostics.Monitoring.TestCommon;
using Microsoft.Diagnostics.Monitoring.WebApi;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Actions;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Xunit.Abstractions;

namespace Microsoft.Diagnostics.Monitoring.Tool.UnitTests.CollectionRules.Actions
{
internal sealed class CallbackAction : ICollectionRuleAction<object>
{
public static readonly string ActionName = nameof(CallbackAction);

private readonly CallbackActionService _service;

public CallbackAction(CallbackActionService service)
{
_service = service;
}

public async Task<CollectionRuleActionResult> ExecuteAsync(object options, IEndpointInfo endpointInfo, CancellationToken token)
{
await _service.NotifyListeners(token);

return new CollectionRuleActionResult();
}
}

internal sealed class CallbackActionService
{
private readonly List<CompletionEntry> _entries = new();
private readonly SemaphoreSlim _entriesSemaphore = new(1);
private readonly List<DateTime> _executionTimestamps = new();
private readonly ITestOutputHelper _outputHelper;

private int _nextId = 1;

public CallbackActionService(ITestOutputHelper outputHelper)
{
_outputHelper = outputHelper ?? throw new ArgumentNullException(nameof(outputHelper));
}

public async Task NotifyListeners(CancellationToken token)
{
await _entriesSemaphore.WaitAsync(token);
try
{
lock (_executionTimestamps)
{
_executionTimestamps.Add(DateTime.Now);
}

_outputHelper.WriteLine("[Callback] Completing {0} source(s).", _entries.Count);

foreach (var entry in _entries)
{
entry.Complete();
}
_entries.Clear();
}
finally
{
_entriesSemaphore.Release();
}
}

public async Task WaitWithCancellationAsync(CancellationToken token)
{
int id = _nextId++;
string name = $"Callback{id}";

CompletionEntry entry = new(_outputHelper, name);

await _entriesSemaphore.WaitAsync(token);
try
{
_outputHelper.WriteLine("[Test] Registering {0}.", name);

_entries.Add(entry);
}
finally
{
_entriesSemaphore.Release();
}

await entry.WithCancellation(token);
}

public IReadOnlyCollection<DateTime> ExecutionTimestamps
{
get
{
lock (_executionTimestamps)
{
return _executionTimestamps.AsReadOnly();
}
}
}

private sealed class CompletionEntry
{
private readonly TaskCompletionSource<object> _completionSource =
new(TaskCreationOptions.RunContinuationsAsynchronously);
private readonly string _name;
private readonly ITestOutputHelper _outputHelper;

public CompletionEntry(ITestOutputHelper outputHelper, string name)
{
_name = name;
_outputHelper = outputHelper;
}

public void Complete()
{
_outputHelper.WriteLine("[Callback] Begin completing {0}.", _name);
if (!_completionSource.TrySetResult(null))
{
_outputHelper.WriteLine("[Callback] Unable to complete {0}.", _name);
}
_outputHelper.WriteLine("[Callback] End completing {0}.", _name);
}

public async Task WithCancellation(CancellationToken token)
{
_outputHelper.WriteLine("[Test] Begin waiting for {0} completion.", _name);
await _completionSource.WithCancellation(token);
_outputHelper.WriteLine("[Test] End waiting for {0} completion.", _name);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.Diagnostics.Monitoring.WebApi;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Triggers;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.Diagnostics.Monitoring.Tool.UnitTests.CollectionRules.Triggers
{
internal sealed class ManualTriggerFactory : ICollectionRuleTriggerFactory
{
private readonly ManualTriggerService _service;

public ManualTriggerFactory(ManualTriggerService service)
{
_service = service ?? throw new ArgumentNullException(nameof(service));
}

public ICollectionRuleTrigger Create(IEndpointInfo endpointInfo, Action callback)
{
return new ManualTrigger(_service, callback);
}
}

internal sealed class ManualTrigger : ICollectionRuleTrigger
{
public const string TriggerName = nameof(ManualTrigger);

private readonly Action _callback;
private readonly ManualTriggerService _service;

public ManualTrigger(ManualTriggerService service, Action callback)
{
_callback = callback ?? throw new ArgumentNullException(nameof(callback));
_service = service ?? throw new ArgumentNullException(nameof(service));
}

public Task StartAsync(CancellationToken cancellationToken)
{
_service.Notify += NotifyHandler;

return Task.CompletedTask;
}

public Task StopAsync(CancellationToken cancellationToken)
{
_service.Notify -= NotifyHandler;

return Task.CompletedTask;
}

private void NotifyHandler(object sender, EventArgs args)
{
_callback();
}
}

internal sealed class ManualTriggerService
{
public event EventHandler Notify;

public void NotifySubscribers()
{
Notify?.Invoke(this, EventArgs.Empty);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.Diagnostics.Tools.Monitor;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.Diagnostics.Monitoring.Tool.UnitTests.CollectionRules.Triggers
{
internal static class TriggersServiceCollectionExtensions
{
public static IServiceCollection RegisterManualTrigger(this IServiceCollection services, ManualTriggerService service)
{
services.AddSingleton(service);
return services.RegisterCollectionRuleTrigger<ManualTriggerFactory>(ManualTrigger.TriggerName);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
// See the LICENSE file in the project root for more information.

using Microsoft.Diagnostics.Monitoring.Tool.UnitTests;
using Microsoft.Diagnostics.Monitoring.Tool.UnitTests.CollectionRules.Triggers;
using Microsoft.Diagnostics.Monitoring.WebApi.Models;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.Actions;
using Microsoft.Diagnostics.Tools.Monitor.CollectionRules.Options.Triggers;
using System;
using System.Collections.Generic;
using System.Linq;
using Xunit;
Expand Down Expand Up @@ -129,6 +131,31 @@ public static CollectionRuleOptions AddExecuteActionAppAction(this CollectionRul
return options;
}

public static CollectionRuleOptions SetActionLimits(this CollectionRuleOptions options, int? count = null, TimeSpan? slidingWindowDuration = null)
{
if (null == options.Limits)
{
options.Limits = new CollectionRuleLimitsOptions();
}

options.Limits.ActionCount = count;
options.Limits.ActionCountSlidingWindowDuration = slidingWindowDuration;

return options;
}

public static CollectionRuleOptions SetDurationLimit(this CollectionRuleOptions options, TimeSpan duration)
{
if (null == options.Limits)
{
options.Limits = new CollectionRuleLimitsOptions();
}

options.Limits.RuleDuration = duration;

return options;
}

public static CollectionRuleOptions SetEventCounterTrigger(this CollectionRuleOptions options, out EventCounterOptions settings)
{
SetTrigger(options, KnownCollectionRuleTriggers.EventCounter, out CollectionRuleTriggerOptions triggerOptions);
Expand All @@ -140,6 +167,13 @@ public static CollectionRuleOptions SetEventCounterTrigger(this CollectionRuleOp
return options;
}

public static CollectionRuleOptions SetManualTrigger(this CollectionRuleOptions options)
{
SetTrigger(options, ManualTrigger.TriggerName, out _);

return options;
}

public static CollectionRuleOptions SetStartupTrigger(this CollectionRuleOptions options)
{
return SetTrigger(options, KnownCollectionRuleTriggers.Startup, out _);
Expand Down
Loading