Create the connection logging scope in ConnectionHandler (#2013)
- Instead of doing it on the FrameConnection only. This will make sure all middleware logs get the connection id as part of their scope.
This commit is contained in:
parent
11a9b6498d
commit
dc9ed60465
|
|
@ -24,6 +24,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
||||||
_connectionDelegate = connectionDelegate;
|
_connectionDelegate = connectionDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IKestrelTrace Log => _serviceContext.Log;
|
||||||
|
|
||||||
public void OnConnection(IFeatureCollection features)
|
public void OnConnection(IFeatureCollection features)
|
||||||
{
|
{
|
||||||
var connectionContext = new DefaultConnectionContext(features);
|
var connectionContext = new DefaultConnectionContext(features);
|
||||||
|
|
@ -51,14 +53,31 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
||||||
|
|
||||||
private async Task Execute(ConnectionContext connectionContext)
|
private async Task Execute(ConnectionContext connectionContext)
|
||||||
{
|
{
|
||||||
|
using (BeginConnectionScope(connectionContext))
|
||||||
|
{
|
||||||
|
Log.ConnectionStart(connectionContext.ConnectionId);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _connectionDelegate(connectionContext);
|
await _connectionDelegate(connectionContext);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_serviceContext.Log.LogCritical(0, ex, $"{nameof(ConnectionHandler)}.{nameof(Execute)}() {connectionContext.ConnectionId}");
|
Log.LogCritical(0, ex, $"{nameof(ConnectionHandler)}.{nameof(Execute)}() {connectionContext.ConnectionId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.ConnectionStop(connectionContext.ConnectionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IDisposable BeginConnectionScope(ConnectionContext connectionContext)
|
||||||
|
{
|
||||||
|
if (Log.IsEnabled(LogLevel.Critical))
|
||||||
|
{
|
||||||
|
return Log.BeginScope(new ConnectionLogScope(connectionContext.ConnectionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal for testing
|
// Internal for testing
|
||||||
|
|
|
||||||
|
|
@ -91,12 +91,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> httpApplication)
|
private async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> httpApplication)
|
||||||
{
|
|
||||||
using (BeginConnectionScope())
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Log.ConnectionStart(ConnectionId);
|
|
||||||
KestrelEventSource.Log.ConnectionStart(this);
|
KestrelEventSource.Log.ConnectionStart(this);
|
||||||
|
|
||||||
AdaptedPipeline adaptedPipeline = null;
|
AdaptedPipeline adaptedPipeline = null;
|
||||||
|
|
@ -175,11 +172,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
||||||
_context.ServiceContext.ConnectionManager.NormalConnectionCount.ReleaseOne();
|
_context.ServiceContext.ConnectionManager.NormalConnectionCount.ReleaseOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.ConnectionStop(ConnectionId);
|
|
||||||
KestrelEventSource.Log.ConnectionStop(this);
|
KestrelEventSource.Log.ConnectionStop(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
internal void CreateFrame<TContext>(IHttpApplication<TContext> httpApplication, IPipeConnection transport, IPipeConnection application)
|
internal void CreateFrame<TContext>(IHttpApplication<TContext> httpApplication, IPipeConnection transport, IPipeConnection application)
|
||||||
{
|
{
|
||||||
|
|
@ -497,15 +492,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
||||||
_writeTimingWrites--;
|
_writeTimingWrites--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IDisposable BeginConnectionScope()
|
|
||||||
{
|
|
||||||
if (Log.IsEnabled(LogLevel.Critical))
|
|
||||||
{
|
|
||||||
return Log.BeginScope(new ConnectionLogScope(ConnectionId));
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO.Pipelines;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
using Microsoft.AspNetCore.Protocols.Features;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||||
|
using Microsoft.AspNetCore.Testing;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
|
{
|
||||||
|
public class ConnectionHandlerTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void OnConnectionCreatesLogScopeWithConnectionId()
|
||||||
|
{
|
||||||
|
var serviceContext = new TestServiceContext();
|
||||||
|
var tcs = new TaskCompletionSource<object>();
|
||||||
|
var handler = new ConnectionHandler(serviceContext, _ => tcs.Task);
|
||||||
|
|
||||||
|
var connection = new TestConnection();
|
||||||
|
|
||||||
|
handler.OnConnection(connection);
|
||||||
|
|
||||||
|
// The scope should be created
|
||||||
|
var scopeObjects = ((TestKestrelTrace)serviceContext.Log)
|
||||||
|
.Logger
|
||||||
|
.Scopes
|
||||||
|
.OfType<IReadOnlyList<KeyValuePair<string, object>>>()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
Assert.Single(scopeObjects);
|
||||||
|
var pairs = scopeObjects[0].ToDictionary(p => p.Key, p => p.Value);
|
||||||
|
Assert.True(pairs.ContainsKey("ConnectionId"));
|
||||||
|
Assert.Equal(connection.ConnectionId, pairs["ConnectionId"]);
|
||||||
|
|
||||||
|
tcs.TrySetResult(null);
|
||||||
|
|
||||||
|
// Verify the scope was disposed after request processing completed
|
||||||
|
Assert.True(((TestKestrelTrace)serviceContext.Log).Logger.Scopes.IsEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestConnection : FeatureCollection, IConnectionIdFeature, IConnectionTransportFeature
|
||||||
|
{
|
||||||
|
public TestConnection()
|
||||||
|
{
|
||||||
|
Set<IConnectionIdFeature>(this);
|
||||||
|
Set<IConnectionTransportFeature>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PipeFactory PipeFactory { get; } = new PipeFactory();
|
||||||
|
|
||||||
|
public IPipeConnection Transport { get; set; }
|
||||||
|
public IPipeConnection Application { get; set; }
|
||||||
|
|
||||||
|
public IScheduler InputWriterScheduler => TaskRunScheduler.Default;
|
||||||
|
|
||||||
|
public IScheduler OutputReaderScheduler => TaskRunScheduler.Default;
|
||||||
|
|
||||||
|
public Task ConnectionAborted => Task.CompletedTask;
|
||||||
|
|
||||||
|
public Task ConnectionClosed => Task.CompletedTask;
|
||||||
|
|
||||||
|
public string ConnectionId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -528,29 +528,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
Assert.True(_frameConnection.TimedOut);
|
Assert.True(_frameConnection.TimedOut);
|
||||||
Assert.True(aborted.Wait(TimeSpan.FromSeconds(10)));
|
Assert.True(aborted.Wait(TimeSpan.FromSeconds(10)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task StartRequestProcessingCreatesLogScopeWithConnectionId()
|
|
||||||
{
|
|
||||||
_ = _frameConnection.StartRequestProcessing(new DummyApplication());
|
|
||||||
|
|
||||||
var scopeObjects = ((TestKestrelTrace)_frameConnectionContext.ServiceContext.Log)
|
|
||||||
.Logger
|
|
||||||
.Scopes
|
|
||||||
.OfType<IReadOnlyList<KeyValuePair<string, object>>>()
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
_frameConnection.OnConnectionClosed(ex: null);
|
|
||||||
|
|
||||||
await _frameConnection.StopAsync().TimeoutAfter(TimeSpan.FromSeconds(5));
|
|
||||||
|
|
||||||
Assert.Single(scopeObjects);
|
|
||||||
var pairs = scopeObjects[0].ToDictionary(p => p.Key, p => p.Value);
|
|
||||||
Assert.True(pairs.ContainsKey("ConnectionId"));
|
|
||||||
Assert.Equal(_frameConnection.ConnectionId, pairs["ConnectionId"]);
|
|
||||||
|
|
||||||
// Verify the scope was disposed after request processing completed
|
|
||||||
Assert.True(((TestKestrelTrace)_frameConnectionContext.ServiceContext.Log).Logger.Scopes.IsEmpty);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue