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;
|
||||
}
|
||||
|
||||
private IKestrelTrace Log => _serviceContext.Log;
|
||||
|
||||
public void OnConnection(IFeatureCollection features)
|
||||
{
|
||||
var connectionContext = new DefaultConnectionContext(features);
|
||||
|
|
@ -51,14 +53,31 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
|
||||
private async Task Execute(ConnectionContext connectionContext)
|
||||
{
|
||||
using (BeginConnectionScope(connectionContext))
|
||||
{
|
||||
Log.ConnectionStart(connectionContext.ConnectionId);
|
||||
|
||||
try
|
||||
{
|
||||
await _connectionDelegate(connectionContext);
|
||||
}
|
||||
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
|
||||
|
|
|
|||
|
|
@ -91,12 +91,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
}
|
||||
|
||||
private async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> httpApplication)
|
||||
{
|
||||
using (BeginConnectionScope())
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.ConnectionStart(ConnectionId);
|
||||
KestrelEventSource.Log.ConnectionStart(this);
|
||||
|
||||
AdaptedPipeline adaptedPipeline = null;
|
||||
|
|
@ -175,11 +172,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
_context.ServiceContext.ConnectionManager.NormalConnectionCount.ReleaseOne();
|
||||
}
|
||||
|
||||
Log.ConnectionStop(ConnectionId);
|
||||
KestrelEventSource.Log.ConnectionStop(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void CreateFrame<TContext>(IHttpApplication<TContext> httpApplication, IPipeConnection transport, IPipeConnection application)
|
||||
{
|
||||
|
|
@ -497,15 +492,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
_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(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