Add logging scope (#670)

This commit is contained in:
BrennanConroy 2017-07-18 16:13:06 -07:00 committed by GitHub
parent 8ba29b578d
commit 3010eaaee2
2 changed files with 117 additions and 35 deletions

View File

@ -30,33 +30,40 @@ namespace Microsoft.AspNetCore.Sockets
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))
{
// OPTIONS /{path}
await ProcessNegotiate(context, options);
}
else if (HttpMethods.IsPost(context.Request.Method))
{
// POST /{path}
await ProcessSend(context);
}
else if (HttpMethods.IsGet(context.Request.Method))
{
// GET /{path}
await ExecuteEndpointAsync(context, socketDelegate, options);
}
else
{
context.Response.StatusCode = StatusCodes.Status405MethodNotAllowed;
if (HttpMethods.IsOptions(context.Request.Method))
{
// OPTIONS /{path}
await ProcessNegotiate(context, options, logScope);
}
else if (HttpMethods.IsPost(context.Request.Method))
{
// POST /{path}
await ProcessSend(context);
}
else if (HttpMethods.IsGet(context.Request.Method))
{
// GET /{path}
await ExecuteEndpointAsync(context, socketDelegate, options, logScope);
}
else
{
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;
@ -74,14 +81,14 @@ namespace Microsoft.AspNetCore.Sockets
return;
}
_logger.EstablishedConnection(connection.ConnectionId, context.TraceIdentifier);
if (!await EnsureConnectionStateAsync(connection, context, TransportType.ServerSentEvents, supportedTransports))
if (!await EnsureConnectionStateAsync(connection, context, TransportType.ServerSentEvents, supportedTransports, logScope))
{
// Bad connection state. It's already set the response status code.
return;
}
_logger.EstablishedConnection(connection.ConnectionId, context.TraceIdentifier);
// 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);
@ -97,14 +104,14 @@ namespace Microsoft.AspNetCore.Sockets
return;
}
_logger.EstablishedConnection(connection.ConnectionId, context.TraceIdentifier);
if (!await EnsureConnectionStateAsync(connection, context, TransportType.WebSockets, supportedTransports))
if (!await EnsureConnectionStateAsync(connection, context, TransportType.WebSockets, supportedTransports, logScope))
{
// Bad connection state. It's already set the response status code.
return;
}
_logger.EstablishedConnection(connection.ConnectionId, context.TraceIdentifier);
var ws = new WebSocketsTransport(options.WebSockets, connection.Application, connection.ConnectionId, _loggerFactory);
await DoPersistentConnection(socketDelegate, ws, context, connection);
@ -121,7 +128,7 @@ namespace Microsoft.AspNetCore.Sockets
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.
return;
@ -201,8 +208,7 @@ namespace Microsoft.AspNetCore.Sockets
var pollAgain = true;
// If the application ended before the transport task then we need to potentially need to end the
// connection
// If the application ended before the transport task then we potentially need to end the connection
if (resultTask == connection.ApplicationTask)
{
// Complete the transport (notifying it of the application error if there is one)
@ -315,7 +321,7 @@ namespace Microsoft.AspNetCore.Sockets
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
context.Response.Headers.AppendCommaSeparatedValues("Allow", "GET", "POST", "OPTIONS");
@ -324,6 +330,10 @@ namespace Microsoft.AspNetCore.Sockets
// Establish the connection
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
var negotiateResponseBuffer = Encoding.UTF8.GetBytes(GetNegotiatePayload(connection.ConnectionId, options));
@ -364,6 +374,8 @@ namespace Microsoft.AspNetCore.Sockets
return sb.ToString();
}
private static string GetConnectionId(HttpContext context) => context.Request.Query["id"];
private async Task ProcessSend(HttpContext 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)
{
@ -421,12 +433,16 @@ namespace Microsoft.AspNetCore.Sockets
connection.User = context.User;
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;
}
private async Task<DefaultConnectionContext> GetConnectionAsync(HttpContext context)
{
var connectionId = context.Request.Query["id"];
var connectionId = GetConnectionId(context);
if (StringValues.IsNullOrEmpty(connectionId))
{
@ -449,7 +465,7 @@ namespace Microsoft.AspNetCore.Sockets
private async Task<DefaultConnectionContext> GetOrCreateConnectionAsync(HttpContext context)
{
var connectionId = context.Request.Query["id"];
var connectionId = GetConnectionId(context);
DefaultConnectionContext connection;
// There's no connection id so this is a brand new connection

View File

@ -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;
}
}
}