@@ -18,11 +18,16 @@ namespace MQTTnet | |||||
private static IServiceProvider BuildServiceProvider() | private static IServiceProvider BuildServiceProvider() | ||||
{ | { | ||||
return new ServiceCollection() | |||||
var serviceProvider = new ServiceCollection() | |||||
.AddMqttClient() | .AddMqttClient() | ||||
.AddMqttServer() | .AddMqttServer() | ||||
.AddLogging() | .AddLogging() | ||||
.BuildServiceProvider(); | .BuildServiceProvider(); | ||||
serviceProvider.GetRequiredService<ILoggerFactory>() | |||||
.AddMqttTrace(); | |||||
return serviceProvider; | |||||
} | } | ||||
public MqttFactory() | public MqttFactory() | ||||
@@ -6,6 +6,8 @@ using MQTTnet.Core.Serializer; | |||||
using MQTTnet.Core.Server; | using MQTTnet.Core.Server; | ||||
using MQTTnet.Implementations; | using MQTTnet.Implementations; | ||||
using System; | using System; | ||||
using Microsoft.Extensions.Logging; | |||||
using MQTTnet.Core.Diagnostics; | |||||
namespace MQTTnet | namespace MQTTnet | ||||
{ | { | ||||
@@ -27,7 +29,6 @@ namespace MQTTnet | |||||
services.AddTransient<MqttClientSessionsManager>(); | services.AddTransient<MqttClientSessionsManager>(); | ||||
services.AddTransient<MqttClientRetainedMessagesManager>(); | services.AddTransient<MqttClientRetainedMessagesManager>(); | ||||
return services; | return services; | ||||
} | } | ||||
@@ -51,5 +52,11 @@ namespace MQTTnet | |||||
return services; | 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> | <ItemGroup> | ||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="1.1.2" /> | <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="1.1.2" /> | ||||
<PackageReference Include="Microsoft.Extensions.Options" Version="1.1.2" /> | <PackageReference Include="Microsoft.Extensions.Options" Version="1.1.2" /> | ||||
<PackageReference Include="System.Threading" Version="4.3.0" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
</Project> | </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); | |||||
} | |||||
} | |||||
} | |||||
} |