Add logging scope (#670)
This commit is contained in:
parent
8ba29b578d
commit
3010eaaee2
|
|
@ -30,33 +30,40 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
|
|
||||||
public async Task ExecuteAsync(HttpContext context, HttpSocketOptions options, SocketDelegate socketDelegate)
|
public async Task ExecuteAsync(HttpContext context, HttpSocketOptions options, SocketDelegate socketDelegate)
|
||||||
{
|
{
|
||||||
if (!await AuthorizeHelper.AuthorizeAsync(context, options.AuthorizationData))
|
// Create the log scope and attempt to pass the Connection ID to it so as many logs as possible contain
|
||||||
|
// the Connection ID metadata. If this is the negotiate request then the Connection ID for the scope will
|
||||||
|
// be set a little later.
|
||||||
|
var logScope = new ConnectionLogScope(GetConnectionId(context));
|
||||||
|
using (_logger.BeginScope(logScope))
|
||||||
{
|
{
|
||||||
return;
|
if (!await AuthorizeHelper.AuthorizeAsync(context, options.AuthorizationData))
|
||||||
}
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (HttpMethods.IsOptions(context.Request.Method))
|
if (HttpMethods.IsOptions(context.Request.Method))
|
||||||
{
|
{
|
||||||
// OPTIONS /{path}
|
// OPTIONS /{path}
|
||||||
await ProcessNegotiate(context, options);
|
await ProcessNegotiate(context, options, logScope);
|
||||||
}
|
}
|
||||||
else if (HttpMethods.IsPost(context.Request.Method))
|
else if (HttpMethods.IsPost(context.Request.Method))
|
||||||
{
|
{
|
||||||
// POST /{path}
|
// POST /{path}
|
||||||
await ProcessSend(context);
|
await ProcessSend(context);
|
||||||
}
|
}
|
||||||
else if (HttpMethods.IsGet(context.Request.Method))
|
else if (HttpMethods.IsGet(context.Request.Method))
|
||||||
{
|
{
|
||||||
// GET /{path}
|
// GET /{path}
|
||||||
await ExecuteEndpointAsync(context, socketDelegate, options);
|
await ExecuteEndpointAsync(context, socketDelegate, options, logScope);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
context.Response.StatusCode = StatusCodes.Status405MethodNotAllowed;
|
context.Response.StatusCode = StatusCodes.Status405MethodNotAllowed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ExecuteEndpointAsync(HttpContext context, SocketDelegate socketDelegate, HttpSocketOptions options)
|
private async Task ExecuteEndpointAsync(HttpContext context, SocketDelegate socketDelegate, HttpSocketOptions options, ConnectionLogScope logScope)
|
||||||
{
|
{
|
||||||
var supportedTransports = options.Transports;
|
var supportedTransports = options.Transports;
|
||||||
|
|
||||||
|
|
@ -74,14 +81,14 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.EstablishedConnection(connection.ConnectionId, context.TraceIdentifier);
|
if (!await EnsureConnectionStateAsync(connection, context, TransportType.ServerSentEvents, supportedTransports, logScope))
|
||||||
|
|
||||||
if (!await EnsureConnectionStateAsync(connection, context, TransportType.ServerSentEvents, supportedTransports))
|
|
||||||
{
|
{
|
||||||
// Bad connection state. It's already set the response status code.
|
// Bad connection state. It's already set the response status code.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_logger.EstablishedConnection(connection.ConnectionId, context.TraceIdentifier);
|
||||||
|
|
||||||
// We only need to provide the Input channel since writing to the application is handled through /send.
|
// We only need to provide the Input channel since writing to the application is handled through /send.
|
||||||
var sse = new ServerSentEventsTransport(connection.Application.In, connection.ConnectionId, _loggerFactory);
|
var sse = new ServerSentEventsTransport(connection.Application.In, connection.ConnectionId, _loggerFactory);
|
||||||
|
|
||||||
|
|
@ -97,14 +104,14 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.EstablishedConnection(connection.ConnectionId, context.TraceIdentifier);
|
if (!await EnsureConnectionStateAsync(connection, context, TransportType.WebSockets, supportedTransports, logScope))
|
||||||
|
|
||||||
if (!await EnsureConnectionStateAsync(connection, context, TransportType.WebSockets, supportedTransports))
|
|
||||||
{
|
{
|
||||||
// Bad connection state. It's already set the response status code.
|
// Bad connection state. It's already set the response status code.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_logger.EstablishedConnection(connection.ConnectionId, context.TraceIdentifier);
|
||||||
|
|
||||||
var ws = new WebSocketsTransport(options.WebSockets, connection.Application, connection.ConnectionId, _loggerFactory);
|
var ws = new WebSocketsTransport(options.WebSockets, connection.Application, connection.ConnectionId, _loggerFactory);
|
||||||
|
|
||||||
await DoPersistentConnection(socketDelegate, ws, context, connection);
|
await DoPersistentConnection(socketDelegate, ws, context, connection);
|
||||||
|
|
@ -121,7 +128,7 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await EnsureConnectionStateAsync(connection, context, TransportType.LongPolling, supportedTransports))
|
if (!await EnsureConnectionStateAsync(connection, context, TransportType.LongPolling, supportedTransports, logScope))
|
||||||
{
|
{
|
||||||
// Bad connection state. It's already set the response status code.
|
// Bad connection state. It's already set the response status code.
|
||||||
return;
|
return;
|
||||||
|
|
@ -201,8 +208,7 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
|
|
||||||
var pollAgain = true;
|
var pollAgain = true;
|
||||||
|
|
||||||
// If the application ended before the transport task then we need to potentially need to end the
|
// If the application ended before the transport task then we potentially need to end the connection
|
||||||
// connection
|
|
||||||
if (resultTask == connection.ApplicationTask)
|
if (resultTask == connection.ApplicationTask)
|
||||||
{
|
{
|
||||||
// Complete the transport (notifying it of the application error if there is one)
|
// Complete the transport (notifying it of the application error if there is one)
|
||||||
|
|
@ -315,7 +321,7 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
await socketDelegate(connection);
|
await socketDelegate(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task ProcessNegotiate(HttpContext context, HttpSocketOptions options)
|
private Task ProcessNegotiate(HttpContext context, HttpSocketOptions options, ConnectionLogScope logScope)
|
||||||
{
|
{
|
||||||
// Set the allowed headers for this resource
|
// Set the allowed headers for this resource
|
||||||
context.Response.Headers.AppendCommaSeparatedValues("Allow", "GET", "POST", "OPTIONS");
|
context.Response.Headers.AppendCommaSeparatedValues("Allow", "GET", "POST", "OPTIONS");
|
||||||
|
|
@ -324,6 +330,10 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
|
|
||||||
// Establish the connection
|
// Establish the connection
|
||||||
var connection = _manager.CreateConnection();
|
var connection = _manager.CreateConnection();
|
||||||
|
|
||||||
|
// Set the Connection ID on the logging scope so that logs from now on will have the
|
||||||
|
// Connection ID metadata set.
|
||||||
|
logScope.ConnectionId = connection.ConnectionId;
|
||||||
|
|
||||||
// Get the bytes for the connection id
|
// Get the bytes for the connection id
|
||||||
var negotiateResponseBuffer = Encoding.UTF8.GetBytes(GetNegotiatePayload(connection.ConnectionId, options));
|
var negotiateResponseBuffer = Encoding.UTF8.GetBytes(GetNegotiatePayload(connection.ConnectionId, options));
|
||||||
|
|
@ -364,6 +374,8 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string GetConnectionId(HttpContext context) => context.Request.Query["id"];
|
||||||
|
|
||||||
private async Task ProcessSend(HttpContext context)
|
private async Task ProcessSend(HttpContext context)
|
||||||
{
|
{
|
||||||
var connection = await GetConnectionAsync(context);
|
var connection = await GetConnectionAsync(context);
|
||||||
|
|
@ -393,7 +405,7 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> EnsureConnectionStateAsync(DefaultConnectionContext connection, HttpContext context, TransportType transportType, TransportType supportedTransports)
|
private async Task<bool> EnsureConnectionStateAsync(DefaultConnectionContext connection, HttpContext context, TransportType transportType, TransportType supportedTransports, ConnectionLogScope logScope)
|
||||||
{
|
{
|
||||||
if ((supportedTransports & transportType) == 0)
|
if ((supportedTransports & transportType) == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -421,12 +433,16 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
connection.User = context.User;
|
connection.User = context.User;
|
||||||
connection.SetHttpContext(context);
|
connection.SetHttpContext(context);
|
||||||
|
|
||||||
|
// Set the Connection ID on the logging scope so that logs from now on will have the
|
||||||
|
// Connection ID metadata set.
|
||||||
|
logScope.ConnectionId = connection.ConnectionId;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<DefaultConnectionContext> GetConnectionAsync(HttpContext context)
|
private async Task<DefaultConnectionContext> GetConnectionAsync(HttpContext context)
|
||||||
{
|
{
|
||||||
var connectionId = context.Request.Query["id"];
|
var connectionId = GetConnectionId(context);
|
||||||
|
|
||||||
if (StringValues.IsNullOrEmpty(connectionId))
|
if (StringValues.IsNullOrEmpty(connectionId))
|
||||||
{
|
{
|
||||||
|
|
@ -449,7 +465,7 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
|
|
||||||
private async Task<DefaultConnectionContext> GetOrCreateConnectionAsync(HttpContext context)
|
private async Task<DefaultConnectionContext> GetOrCreateConnectionAsync(HttpContext context)
|
||||||
{
|
{
|
||||||
var connectionId = context.Request.Query["id"];
|
var connectionId = GetConnectionId(context);
|
||||||
DefaultConnectionContext connection;
|
DefaultConnectionContext connection;
|
||||||
|
|
||||||
// There's no connection id so this is a brand new connection
|
// There's no connection id so this is a brand new connection
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
// 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.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Sockets.Internal
|
||||||
|
{
|
||||||
|
public class ConnectionLogScope : IReadOnlyList<KeyValuePair<string, object>>
|
||||||
|
{
|
||||||
|
private string _cachedToString;
|
||||||
|
|
||||||
|
public string ConnectionId { get; set; }
|
||||||
|
|
||||||
|
public ConnectionLogScope(string connectionId)
|
||||||
|
{
|
||||||
|
ConnectionId = connectionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyValuePair<string, object> this[int index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Count == 1 && index == 0)
|
||||||
|
{
|
||||||
|
return new KeyValuePair<string, object>("SocketsConnectionId", ConnectionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count => string.IsNullOrEmpty(ConnectionId) ? 0 : 1;
|
||||||
|
|
||||||
|
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
|
||||||
|
{
|
||||||
|
for (var i = 0; i < Count; ++i)
|
||||||
|
{
|
||||||
|
yield return this[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
if (_cachedToString == null)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(ConnectionId))
|
||||||
|
{
|
||||||
|
_cachedToString = string.Format(
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
"SocketsConnectionId:{0}",
|
||||||
|
ConnectionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _cachedToString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue