117 lines
3.8 KiB
C#
117 lines
3.8 KiB
C#
// 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.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
|
|
namespace Microsoft.AspNetCore.SignalR.Tests
|
|
{
|
|
/// <summary>
|
|
/// A logger factory that will prepend the current SignalR connection ID to the message.
|
|
/// </summary>
|
|
public class WrappingLoggerFactory : ILoggerFactory
|
|
{
|
|
private readonly ILoggerFactory _innerLoggerFactory;
|
|
private readonly DummyProvider _provider;
|
|
|
|
public WrappingLoggerFactory(ILoggerFactory innerLoggerFactory)
|
|
{
|
|
_innerLoggerFactory = innerLoggerFactory;
|
|
_provider = new DummyProvider();
|
|
AddProvider(_provider);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_innerLoggerFactory.Dispose();
|
|
}
|
|
|
|
public ILogger CreateLogger(string categoryName)
|
|
{
|
|
return new WrappingLogger(_provider, _innerLoggerFactory.CreateLogger(categoryName));
|
|
}
|
|
|
|
public void AddProvider(ILoggerProvider provider)
|
|
{
|
|
_innerLoggerFactory.AddProvider(provider);
|
|
}
|
|
|
|
private class DummyProvider : ILoggerProvider, ISupportExternalScope
|
|
{
|
|
public IExternalScopeProvider ScopeProvider { get; private set; }
|
|
|
|
public void Dispose()
|
|
{
|
|
}
|
|
|
|
public ILogger CreateLogger(string categoryName)
|
|
{
|
|
return NullLogger.Instance;
|
|
}
|
|
|
|
public void SetScopeProvider(IExternalScopeProvider scopeProvider)
|
|
{
|
|
ScopeProvider = scopeProvider;
|
|
}
|
|
}
|
|
|
|
private class WrappingLogger : ILogger
|
|
{
|
|
private readonly DummyProvider _provider;
|
|
private readonly ILogger _logger;
|
|
|
|
public WrappingLogger(DummyProvider provider, ILogger logger)
|
|
{
|
|
_provider = provider;
|
|
_logger = logger;
|
|
}
|
|
|
|
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
|
{
|
|
// Build the message outside of the formatter
|
|
// Serilog doesn't appear to use the formatter and just writes the state
|
|
var connectionId = GetConnectionId();
|
|
|
|
var sb = new StringBuilder();
|
|
if (connectionId != null)
|
|
{
|
|
sb.Append(connectionId + " - ");
|
|
}
|
|
sb.Append(formatter(state, exception));
|
|
var message = sb.ToString();
|
|
|
|
_logger.Log(logLevel, eventId, message, exception, (s, ex) => s);
|
|
}
|
|
|
|
public bool IsEnabled(LogLevel logLevel)
|
|
{
|
|
return _logger.IsEnabled(logLevel);
|
|
}
|
|
|
|
public IDisposable BeginScope<TState>(TState state)
|
|
{
|
|
return _logger.BeginScope(state);
|
|
}
|
|
|
|
private string GetConnectionId()
|
|
{
|
|
string connectionId = null;
|
|
_provider.ScopeProvider?.ForEachScope<object>((scope, s) =>
|
|
{
|
|
if (scope is IReadOnlyList<KeyValuePair<string, object>> logScope)
|
|
{
|
|
if (logScope.FirstOrDefault(kv => kv.Key == "TransportConnectionId" || kv.Key == "ClientConnectionId").Value is string id)
|
|
{
|
|
connectionId = id;
|
|
}
|
|
}
|
|
}, null);
|
|
return connectionId;
|
|
}
|
|
}
|
|
}
|
|
} |