Convert SignalR EventSource usage to new pattern (#11130)

This commit is contained in:
Brennan 2019-06-13 18:40:00 -07:00 committed by David Fowler
parent c24c4cac01
commit 902369610a
6 changed files with 207 additions and 50 deletions

View File

@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.Testing;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
@ -241,29 +242,6 @@ namespace Microsoft.AspNetCore.Hosting.Internal
return new HostingEventSource(Guid.NewGuid().ToString());
}
private class TestEventListener : EventListener
{
private readonly int _eventId;
public TestEventListener(int eventId)
{
_eventId = eventId;
}
public EventWrittenEventArgs EventData { get; private set; }
protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
// The tests here run in parallel and since the single publisher instance (HostingEventingSource)
// notifies all listener instances in these tests, capture the EventData that a test is explicitly
// looking for and not give back other tests' data.
if (eventData.EventId == _eventId)
{
EventData = eventData;
}
}
}
private class CounterListener : EventListener
{
private readonly Dictionary<string, Channel<double>> _counters = new Dictionary<string, Channel<double>>();

View File

@ -6,6 +6,7 @@
<ItemGroup>
<Compile Include="$(SharedSourceRoot)test\SkipOnHelixAttribute.cs" />
<Compile Include="$(SharedSourceRoot)EventSource.Testing\TestEventListener.cs" />
<Content Include="testroot\**\*" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
<Content Include="Microsoft.AspNetCore.Hosting.StaticWebAssets.xml" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

View File

@ -0,0 +1,29 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Diagnostics.Tracing;
namespace Microsoft.AspNetCore.Internal
{
internal class TestEventListener : EventListener
{
private readonly int _eventId;
public TestEventListener(int eventId)
{
_eventId = eventId;
}
public EventWrittenEventArgs EventData { get; private set; }
protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
// The tests here run in parallel, capture the EventData that a test is explicitly
// looking for and not give back other tests' data.
if (eventData.EventId == _eventId)
{
EventData = eventData;
}
}
}
}

View File

@ -1,28 +1,36 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics.Tracing;
using System.Threading;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Http.Connections.Internal
{
[EventSource(Name = "Microsoft-AspNetCore-Http-Connections")]
internal class HttpConnectionsEventSource : EventSource
{
public static readonly HttpConnectionsEventSource Log = new HttpConnectionsEventSource();
private readonly EventCounter _connectionsStarted;
private readonly EventCounter _connectionsStopped;
private readonly EventCounter _connectionsTimedOut;
private readonly EventCounter _connectionDuration;
private PollingCounter _connectionsStartedCounter;
private PollingCounter _connectionsStoppedCounter;
private PollingCounter _connectionsTimedOutCounter;
private PollingCounter _currentConnectionsCounter;
private EventCounter _connectionDuration;
private HttpConnectionsEventSource()
private long _connectionsStarted;
private long _connectionsStopped;
private long _connectionsTimedOut;
private long _currentConnections;
internal HttpConnectionsEventSource()
: base("Microsoft.AspNetCore.Http.Connections")
{
}
// Used for testing
internal HttpConnectionsEventSource(string eventSourceName)
: base(eventSourceName)
{
_connectionsStarted = new EventCounter("ConnectionsStarted", this);
_connectionsStopped = new EventCounter("ConnectionsStopped", this);
_connectionsTimedOut = new EventCounter("ConnectionsTimedOut", this);
_connectionDuration = new EventCounter("ConnectionDuration", this);
}
// This has to go through NonEvent because only Primitive types are allowed
@ -30,11 +38,13 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
[NonEvent]
public void ConnectionStop(string connectionId, ValueStopwatch timer)
{
Interlocked.Increment(ref _connectionsStopped);
Interlocked.Decrement(ref _currentConnections);
if (IsEnabled())
{
var duration = timer.IsActive ? timer.GetElapsedTime().TotalMilliseconds : 0.0;
_connectionDuration.WriteMetric((float)duration);
_connectionsStopped.WriteMetric(1.0f);
_connectionDuration.WriteMetric(duration);
if (IsEnabled(EventLevel.Informational, EventKeywords.None))
{
@ -46,15 +56,13 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
[Event(eventId: 1, Level = EventLevel.Informational, Message = "Started connection '{0}'.")]
public ValueStopwatch ConnectionStart(string connectionId)
{
if (IsEnabled())
{
_connectionsStarted.WriteMetric(1.0f);
Interlocked.Increment(ref _connectionsStarted);
Interlocked.Increment(ref _currentConnections);
if (IsEnabled(EventLevel.Informational, EventKeywords.None))
{
WriteEvent(1, connectionId);
return ValueStopwatch.StartNew();
}
if (IsEnabled(EventLevel.Informational, EventKeywords.None))
{
WriteEvent(1, connectionId);
return ValueStopwatch.StartNew();
}
return default;
}
@ -65,14 +73,42 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
[Event(eventId: 3, Level = EventLevel.Informational, Message = "Connection '{0}' timed out.")]
public void ConnectionTimedOut(string connectionId)
{
if (IsEnabled())
{
_connectionsTimedOut.WriteMetric(1.0f);
Interlocked.Increment(ref _connectionsTimedOut);
if (IsEnabled(EventLevel.Informational, EventKeywords.None))
if (IsEnabled(EventLevel.Informational, EventKeywords.None))
{
WriteEvent(3, connectionId);
}
}
protected override void OnEventCommand(EventCommandEventArgs command)
{
if (command.Command == EventCommand.Enable)
{
// This is the convention for initializing counters in the RuntimeEventSource (lazily on the first enable command).
// They aren't disabled afterwards...
_connectionsStartedCounter ??= new PollingCounter("connections-started", this, () => _connectionsStarted)
{
WriteEvent(3, connectionId);
}
DisplayName = "Total Connections Started",
};
_connectionsStoppedCounter ??= new PollingCounter("connections-stopped", this, () => _connectionsStopped)
{
DisplayName = "Total Connections Stopped",
};
_connectionsTimedOutCounter ??= new PollingCounter("connections-timed-out", this, () => _connectionsTimedOut)
{
DisplayName = "Total Connections Timed Out",
};
_currentConnectionsCounter ??= new PollingCounter("current-connections", this, () => _currentConnections)
{
DisplayName = "Current Connections",
};
_connectionDuration ??= new EventCounter("connections-duration", this)
{
DisplayName = "Average Connection Duration",
};
}
}

View File

@ -0,0 +1,112 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics.Tracing;
using Microsoft.AspNetCore.Internal;
using Microsoft.Extensions.Internal;
using Xunit;
namespace Microsoft.AspNetCore.Http.Connections.Internal
{
public class HttpConnectionsEventSourceTests
{
[Fact]
public void MatchesNameAndGuid()
{
// Arrange & Act
var eventSource = new HttpConnectionsEventSource();
// Assert
Assert.Equal("Microsoft.AspNetCore.Http.Connections", eventSource.Name);
Assert.Equal(Guid.Parse("c26fe4b6-8790-5d41-5900-0f2b6b74efaa"), eventSource.Guid);
}
[Fact]
public void ConnectionStart()
{
// Arrange
var expectedEventId = 1;
var eventListener = new TestEventListener(expectedEventId);
var httpConnectionsEventSource = GetHttpConnectionEventSource();
eventListener.EnableEvents(httpConnectionsEventSource, EventLevel.Informational);
// Act
httpConnectionsEventSource.ConnectionStart("1");
// Assert
var eventData = eventListener.EventData;
Assert.NotNull(eventData);
Assert.Equal(expectedEventId, eventData.EventId);
Assert.Equal("ConnectionStart", eventData.EventName);
Assert.Equal(EventLevel.Informational, eventData.Level);
Assert.Same(httpConnectionsEventSource, eventData.EventSource);
Assert.Equal("Started connection '{0}'.", eventData.Message);
Assert.Collection(eventData.Payload,
arg =>
{
Assert.Equal("1", arg);
});
}
[Fact]
public void ConnectionStop()
{
// Arrange
var expectedEventId = 2;
var eventListener = new TestEventListener(expectedEventId);
var httpConnectionsEventSource = GetHttpConnectionEventSource();
eventListener.EnableEvents(httpConnectionsEventSource, EventLevel.Informational);
// Act
var stopWatch = ValueStopwatch.StartNew();
httpConnectionsEventSource.ConnectionStop("1", stopWatch);
// Assert
var eventData = eventListener.EventData;
Assert.NotNull(eventData);
Assert.Equal(expectedEventId, eventData.EventId);
Assert.Equal("ConnectionStop", eventData.EventName);
Assert.Equal(EventLevel.Informational, eventData.Level);
Assert.Same(httpConnectionsEventSource, eventData.EventSource);
Assert.Equal("Stopped connection '{0}'.", eventData.Message);
Assert.Collection(eventData.Payload,
arg =>
{
Assert.Equal("1", arg);
});
}
[Fact]
public void ConnectionTimedOut()
{
// Arrange
var expectedEventId = 3;
var eventListener = new TestEventListener(expectedEventId);
var httpConnectionsEventSource = GetHttpConnectionEventSource();
eventListener.EnableEvents(httpConnectionsEventSource, EventLevel.Informational);
// Act
httpConnectionsEventSource.ConnectionTimedOut("1");
// Assert
var eventData = eventListener.EventData;
Assert.NotNull(eventData);
Assert.Equal(expectedEventId, eventData.EventId);
Assert.Equal("ConnectionTimedOut", eventData.EventName);
Assert.Equal(EventLevel.Informational, eventData.Level);
Assert.Same(httpConnectionsEventSource, eventData.EventSource);
Assert.Equal("Connection '{0}' timed out.", eventData.Message);
Assert.Collection(eventData.Payload,
arg =>
{
Assert.Equal("1", arg);
});
}
private static HttpConnectionsEventSource GetHttpConnectionEventSource()
{
return new HttpConnectionsEventSource(Guid.NewGuid().ToString());
}
}
}

View File

@ -7,6 +7,7 @@
<ItemGroup>
<Compile Include="$(SharedSourceRoot)Buffers.Testing\**\*.cs" />
<Compile Include="$(SharedSourceRoot)SyncPoint\SyncPoint.cs" />
<Compile Include="$(SharedSourceRoot)EventSource.Testing\TestEventListener.cs" />
</ItemGroup>
<ItemGroup>