Add LoggingConnectionFilter.

This commit is contained in:
Chris R 2015-11-16 15:12:12 -08:00
parent 307e020703
commit 1c40548928
5 changed files with 210 additions and 3 deletions

View File

@ -8,7 +8,7 @@ using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Server.Kestrel;
using Microsoft.AspNet.Server.Kestrel.Https;
using Microsoft.AspNet.Server.Kestrel.Filter;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.PlatformAbstractions;
@ -39,6 +39,8 @@ namespace SampleApp
Console.WriteLine("Could not find certificate at '{0}'. HTTPS is not enabled.", testCertPath);
}
app.UseKestrelConnectionLogging();
app.Run(async context =>
{
Console.WriteLine("{0} {1}{2}{3}",

View File

@ -4,9 +4,9 @@
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Server.Kestrel.Filter;
using Microsoft.AspNet.Server.Kestrel.Https;
namespace Microsoft.AspNet.Server.Kestrel.Https
namespace Microsoft.AspNet.Server.Kestrel.Filter
{
public static class HttpsApplicationBuilderExtensions
{

View File

@ -0,0 +1,37 @@
// 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.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNet.Server.Kestrel.Filter
{
public class LoggingConnectionFilter : IConnectionFilter
{
private readonly ILogger _logger;
private readonly IConnectionFilter _previous;
public LoggingConnectionFilter(ILogger logger, IConnectionFilter previous)
{
if (logger == null)
{
throw new ArgumentNullException(nameof(logger));
}
if (previous == null)
{
throw new ArgumentNullException(nameof(previous));
}
_logger = logger;
_previous = previous;
}
public async Task OnConnection(ConnectionFilterContext context)
{
await _previous.OnConnection(context);
context.Connection = new LoggingStream(context.Connection, _logger);
}
}
}

View File

@ -0,0 +1,39 @@
// 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 Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http.Features;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNet.Server.Kestrel.Filter
{
public static class LoggingFilterApplicationBuilderExtensions
{
/// <summary>
/// Emits verbose logs for bytes read from and written to the connection.
/// </summary>
/// <returns></returns>
public static IApplicationBuilder UseKestrelConnectionLogging(this IApplicationBuilder app)
{
return app.UseKestrelConnectionLogging(nameof(LoggingConnectionFilter));
}
/// <summary>
/// Emits verbose logs for bytes read from and written to the connection.
/// </summary>
/// <returns></returns>
public static IApplicationBuilder UseKestrelConnectionLogging(this IApplicationBuilder app, string loggerName)
{
var serverInfo = app.ServerFeatures.Get<IKestrelServerInformation>();
if (serverInfo != null)
{
var prevFilter = serverInfo.ConnectionFilter ?? new NoOpConnectionFilter();
var loggerFactory = app.ApplicationServices.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger(loggerName ?? nameof(LoggingConnectionFilter));
serverInfo.ConnectionFilter = new LoggingConnectionFilter(logger, prevFilter);
}
return app;
}
}
}

View File

@ -0,0 +1,129 @@
// 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.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNet.Server.Kestrel.Filter
{
internal class LoggingStream : Stream
{
private readonly Stream _inner;
private readonly ILogger _logger;
public LoggingStream(Stream inner, ILogger logger)
{
_inner = inner;
_logger = logger;
}
public override bool CanRead
{
get
{
return _inner.CanRead;
}
}
public override bool CanSeek
{
get
{
return _inner.CanSeek;
}
}
public override bool CanWrite
{
get
{
return _inner.CanWrite;
}
}
public override long Length
{
get
{
return _inner.Length;
}
}
public override long Position
{
get
{
return _inner.Position;
}
set
{
_inner.Position = value;
}
}
public override void Flush()
{
_inner.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
int read = _inner.Read(buffer, offset, count);
Log("Read", read, buffer, offset);
return read;
}
public async override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
int read = await _inner.ReadAsync(buffer, offset, count, cancellationToken);
Log("ReadAsync", read, buffer, offset);
return read;
}
public override long Seek(long offset, SeekOrigin origin)
{
return _inner.Seek(offset, origin);
}
public override void SetLength(long value)
{
_inner.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
Log("Write", count, buffer, offset);
_inner.Write(buffer, offset, count);
}
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
Log("WriteAsync", count, buffer, offset);
return _inner.WriteAsync(buffer, offset, count, cancellationToken);
}
private void Log(string method, int count, byte[] buffer, int offset)
{
var builder = new StringBuilder($"{method}[{count}] ");
// Write the hex
for (int i = offset; i < offset + count; i++)
{
builder.Append(buffer[i].ToString("X2"));
builder.Append(" ");
}
builder.AppendLine();
// Write the bytes as if they were ASCII
for (int i = offset; i < offset + count; i++)
{
builder.Append((char)buffer[i]);
}
_logger.LogVerbose(builder.ToString());
}
}
}