@@ -18,11 +18,16 @@ namespace MQTTnet | |||
private static IServiceProvider BuildServiceProvider() | |||
{ | |||
return new ServiceCollection() | |||
var serviceProvider = new ServiceCollection() | |||
.AddMqttClient() | |||
.AddMqttServer() | |||
.AddLogging() | |||
.BuildServiceProvider(); | |||
serviceProvider.GetRequiredService<ILoggerFactory>() | |||
.AddMqttTrace(); | |||
return serviceProvider; | |||
} | |||
public MqttFactory() | |||
@@ -6,6 +6,8 @@ using MQTTnet.Core.Serializer; | |||
using MQTTnet.Core.Server; | |||
using MQTTnet.Implementations; | |||
using System; | |||
using Microsoft.Extensions.Logging; | |||
using MQTTnet.Core.Diagnostics; | |||
namespace MQTTnet | |||
{ | |||
@@ -27,7 +29,6 @@ namespace MQTTnet | |||
services.AddTransient<MqttClientSessionsManager>(); | |||
services.AddTransient<MqttClientRetainedMessagesManager>(); | |||
return services; | |||
} | |||
@@ -51,5 +52,11 @@ namespace MQTTnet | |||
return services; | |||
} | |||
public static ILoggerFactory AddMqttTrace(this ILoggerFactory factory) | |||
{ | |||
factory.AddProvider(new MqttNetTrace()); | |||
return factory; | |||
} | |||
} | |||
} |
@@ -0,0 +1,49 @@ | |||
using System; | |||
using Microsoft.Extensions.Logging; | |||
namespace MQTTnet.Core.Diagnostics | |||
{ | |||
public class MqttNetLogger : ILogger | |||
{ | |||
private readonly string _categoryName; | |||
private readonly MqttNetTrace _mqttNetTrace; | |||
public MqttNetLogger(string categoryName, MqttNetTrace mqttNetTrace) | |||
{ | |||
_categoryName = categoryName; | |||
_mqttNetTrace = mqttNetTrace; | |||
} | |||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) | |||
{ | |||
if (formatter == null) | |||
{ | |||
throw new ArgumentNullException(nameof(formatter)); | |||
} | |||
var message = formatter(state, exception); | |||
var traceMessage = new MqttNetTraceMessage(DateTime.Now, Environment.CurrentManagedThreadId, _categoryName, logLevel, message, exception); | |||
_mqttNetTrace.Publish(traceMessage); | |||
} | |||
public bool IsEnabled(LogLevel logLevel) | |||
{ | |||
return true; | |||
} | |||
//not supported: async local requires netstandard1.3 | |||
//for implementation see https://github.com/aspnet/Logging/blob/dev/src/Microsoft.Extensions.Logging.Console/ConsoleLogScope.cs | |||
public IDisposable BeginScope<TState>(TState state) | |||
{ | |||
return new DisposableScope(); | |||
} | |||
private class DisposableScope : IDisposable | |||
{ | |||
public void Dispose() | |||
{ | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,33 @@ | |||
using System; | |||
using System.Collections.Concurrent; | |||
using Microsoft.Extensions.Logging; | |||
namespace MQTTnet.Core.Diagnostics | |||
{ | |||
public class MqttNetTrace : ILoggerProvider | |||
{ | |||
private readonly ConcurrentDictionary<string, MqttNetLogger> _loggers = new ConcurrentDictionary<string, MqttNetLogger>(); | |||
public static event EventHandler<MqttNetTraceMessagePublishedEventArgs> TraceMessagePublished; | |||
public void Publish(MqttNetTraceMessage msg) | |||
{ | |||
TraceMessagePublished?.Invoke(this, new MqttNetTraceMessagePublishedEventArgs(msg)); | |||
} | |||
public void Dispose() | |||
{ | |||
TraceMessagePublished = null; | |||
} | |||
public ILogger CreateLogger(string categoryName) | |||
{ | |||
return _loggers.GetOrAdd(categoryName, CreateLoggerImplementation); | |||
} | |||
private MqttNetLogger CreateLoggerImplementation(string categoryName) | |||
{ | |||
return new MqttNetLogger(categoryName, this); | |||
} | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
using System; | |||
namespace MQTTnet.Core.Diagnostics | |||
{ | |||
public sealed class MqttNetTraceMessagePublishedEventArgs : EventArgs | |||
{ | |||
public MqttNetTraceMessagePublishedEventArgs(MqttNetTraceMessage traceMessage) | |||
{ | |||
TraceMessage = traceMessage ?? throw new ArgumentNullException(nameof(traceMessage)); | |||
} | |||
public MqttNetTraceMessage TraceMessage { get; } | |||
} | |||
} |
@@ -0,0 +1,30 @@ | |||
using System; | |||
using Microsoft.Extensions.Logging; | |||
namespace MQTTnet.Core.Diagnostics | |||
{ | |||
public sealed class MqttNetTraceMessage | |||
{ | |||
public MqttNetTraceMessage(DateTime timestamp, int threadId, string source, LogLevel level, string message, Exception exception) | |||
{ | |||
Timestamp = timestamp; | |||
ThreadId = threadId; | |||
Source = source; | |||
Level = level; | |||
Message = message; | |||
Exception = exception; | |||
} | |||
public DateTime Timestamp { get; } | |||
public int ThreadId { get; } | |||
public string Source { get; } | |||
public LogLevel Level { get; } | |||
public string Message { get; } | |||
public Exception Exception { get; } | |||
} | |||
} |
@@ -25,6 +25,7 @@ | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="1.1.2" /> | |||
<PackageReference Include="Microsoft.Extensions.Options" Version="1.1.2" /> | |||
<PackageReference Include="System.Threading" Version="4.3.0" /> | |||
</ItemGroup> | |||
</Project> |
@@ -0,0 +1,51 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using Microsoft.Extensions.Logging; | |||
using Microsoft.VisualStudio.TestTools.UnitTesting; | |||
using MQTTnet.Core.Diagnostics; | |||
namespace MQTTnet.Core.Tests | |||
{ | |||
[TestClass] | |||
public class MqttLoggerProviderTest | |||
{ | |||
[TestMethod] | |||
public void TestLoggerCallback() | |||
{ | |||
var serviceCollection = new ServiceCollection(); | |||
serviceCollection.AddLogging(); | |||
var serviceProvider = serviceCollection.BuildServiceProvider(); | |||
using ((IDisposable)serviceProvider) | |||
{ | |||
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>(); | |||
loggerFactory.AddMqttTrace(); | |||
var expectedMsg = "Hello World!"; | |||
MqttNetTraceMessage msg = null; | |||
MqttNetTrace.TraceMessagePublished += (sender, args) => | |||
{ | |||
msg = args.TraceMessage; | |||
}; | |||
var logger = loggerFactory.CreateLogger<MqttLoggerProviderTest>(); | |||
logger.LogInformation(expectedMsg); | |||
Assert.AreEqual(expectedMsg, msg.Message); | |||
var expectedException = new Exception("bad stuff"); | |||
logger.LogError(new EventId(), expectedException, expectedException.Message); | |||
Assert.AreEqual(expectedException, msg.Exception); | |||
Assert.AreEqual(expectedException.Message, msg.Message); | |||
} | |||
} | |||
} | |||
} |