Skip to content

Commit dd54670

Browse files
committed
High performance logging
Signed-off-by: Tomasz Maruszak <maruszaktomasz@gmail.com>
1 parent b3ce724 commit dd54670

File tree

34 files changed

+963
-164
lines changed

34 files changed

+963
-164
lines changed

src/Host.Plugin.Properties.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<Import Project="Common.NuGet.Properties.xml" />
55

66
<PropertyGroup>
7-
<Version>3.0.0-rc903</Version>
7+
<Version>3.0.0-rc904</Version>
88
</PropertyGroup>
99

1010
</Project>

src/SlimMessageBus.Host.AspNetCore/HttpContextAccessorCurrentMessageBusProvider.cs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,46 @@
33
/// <summary>
44
/// Resolves the <see cref="IMessageBus"/> from the current ASP.NET Core web request (if present, otherwise falls back to the application root container).
55
/// </summary>
6-
public class HttpContextAccessorCurrentMessageBusProvider(
6+
public partial class HttpContextAccessorCurrentMessageBusProvider(
77
ILogger<HttpContextAccessorCurrentMessageBusProvider> logger,
88
IHttpContextAccessor httpContextAccessor,
99
IServiceProvider serviceProvider)
1010
: CurrentMessageBusProvider(serviceProvider)
1111
{
12+
private readonly ILogger<HttpContextAccessorCurrentMessageBusProvider> _logger = logger;
13+
1214
public override IMessageBus GetCurrent()
1315
{
1416
// When the call to resolve the given type is made within an HTTP Request, use the request scope service provider
1517
var httpContext = httpContextAccessor?.HttpContext;
1618
if (httpContext != null)
1719
{
18-
logger.LogTrace("The type IMessageBus will be requested from the per-request scope");
20+
LogCurrentFrom("request");
1921
return httpContext.RequestServices.GetService<IMessageBus>();
2022
}
2123

2224
// otherwise use the app wide scope provider
23-
logger.LogTrace("The type IMessageBus will be requested from the app scope");
25+
LogCurrentFrom("root");
2426
return base.GetCurrent();
2527
}
28+
29+
#region Logging
30+
31+
[LoggerMessage(
32+
EventId = 0,
33+
Level = LogLevel.Trace,
34+
Message = "The type IMessageBus will be requested from the {ScopeName} scope")]
35+
private partial void LogCurrentFrom(string scopeName);
36+
37+
#endregion
2638
}
39+
40+
#if NETSTANDARD2_0
41+
42+
public partial class HttpContextAccessorCurrentMessageBusProvider
43+
{
44+
private partial void LogCurrentFrom(string scopeName)
45+
=> _logger.LogTrace("The type IMessageBus will be requested from the {ScopeName} scope", scopeName);
46+
}
47+
48+
#endif

src/SlimMessageBus.Host.CircuitBreaker/Implementation/CircuitBreakerAbstractConsumerInterceptor.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
/// <summary>
44
/// Circuit breaker to toggle consumer status on an external events.
55
/// </summary>
6-
internal sealed class CircuitBreakerAbstractConsumerInterceptor : IAbstractConsumerInterceptor
6+
internal sealed class CircuitBreakerAbstractConsumerInterceptor(ILogger logger) : IAbstractConsumerInterceptor
77
{
88
public int Order => 100;
99

@@ -33,12 +33,12 @@ async Task BreakerChanged(Circuit state)
3333
var bus = consumer.Settings[0].MessageBusSettings.Name ?? "default";
3434
if (shouldPause)
3535
{
36-
consumer.Logger.LogWarning("Circuit breaker tripped for '{Path}' on '{Bus}' bus. Consumer paused.", path, bus);
36+
logger.LogWarning("Circuit breaker tripped for '{Path}' on '{Bus}' bus. Consumer paused.", path, bus);
3737
await consumer.DoStop().ConfigureAwait(false);
3838
}
3939
else
4040
{
41-
consumer.Logger.LogInformation("Circuit breaker restored for '{Path}' on '{Bus}' bus. Consumer resumed.", path, bus);
41+
logger.LogInformation("Circuit breaker restored for '{Path}' on '{Bus}' bus. Consumer resumed.", path, bus);
4242
await consumer.DoStart().ConfigureAwait(false);
4343
}
4444
consumer.SetIsPaused(shouldPause);

src/SlimMessageBus.Host.Configuration/SlimMessageBus.Host.Configuration.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<Description>Core configuration interfaces of SlimMessageBus</Description>
77
<PackageTags>SlimMessageBus</PackageTags>
88
<RootNamespace>SlimMessageBus.Host</RootNamespace>
9-
<Version>3.0.0-rc903</Version>
9+
<Version>3.0.0-rc904</Version>
1010
</PropertyGroup>
1111

1212
<ItemGroup>

src/SlimMessageBus.Host.Interceptor/SlimMessageBus.Host.Interceptor.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<Import Project="../Common.NuGet.Properties.xml" />
44

55
<PropertyGroup>
6-
<Version>3.0.0-rc903</Version>
6+
<Version>3.0.0-rc904</Version>
77
<Description>Core interceptor interfaces of SlimMessageBus</Description>
88
<PackageTags>SlimMessageBus</PackageTags>
99
</PropertyGroup>

src/SlimMessageBus.Host.Memory/Consumers/AbstractMessageProcessorQueue.cs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
namespace SlimMessageBus.Host.Memory;
22

3-
public abstract class AbstractMessageProcessorQueue(IMessageProcessor<object> messageProcessor, ILogger logger) : IMessageProcessorQueue
3+
public abstract partial class AbstractMessageProcessorQueue(IMessageProcessor<object> messageProcessor, ILogger logger) : IMessageProcessorQueue
44
{
5+
private readonly ILogger _logger = logger;
6+
57
public abstract void Enqueue(object transportMessage, IReadOnlyDictionary<string, object> messageHeaders);
68

79
protected async Task ProcessMessage(object transportMessage, IReadOnlyDictionary<string, object> messageHeaders, CancellationToken cancellationToken)
@@ -23,7 +25,27 @@ protected async Task ProcessMessage(object transportMessage, IReadOnlyDictionary
2325
if (r.Exception != null)
2426
{
2527
// We rely on the IMessageProcessor to execute the ConsumerErrorHandler<T>, but if it's not registered in the DI, it fails, or there is another fatal error then the message will be lost.
26-
logger.LogError(r.Exception, "Error processing message {Message} of type {MessageType}", transportMessage, transportMessage.GetType());
28+
LogMessageError(transportMessage, transportMessage.GetType(), r.Exception);
2729
}
2830
}
31+
32+
#region Logging
33+
34+
[LoggerMessage(
35+
EventId = 0,
36+
Level = LogLevel.Error,
37+
Message = "Error processing message {TransportMessage} of type {TransportMessageType}")]
38+
private partial void LogMessageError(object transportMessage, Type transportMessageType, Exception e);
39+
40+
#endregion
2941
}
42+
43+
#if NETSTANDARD2_0
44+
45+
public abstract partial class AbstractMessageProcessorQueue
46+
{
47+
private partial void LogMessageError(object transportMessage, Type transportMessageType, Exception e)
48+
=> _logger.LogError(e, "Error processing message {TransportMessage} of type {TransportMessageType}", transportMessage, transportMessageType);
49+
}
50+
51+
#endif

src/SlimMessageBus.Host.Memory/MemoryMessageBus.cs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
/// <summary>
66
/// In-memory message bus <see cref="IMessageBus"/> implementation to use for in process message passing.
77
/// </summary>
8-
public class MemoryMessageBus : MessageBusBase<MemoryMessageBusSettings>
8+
public partial class MemoryMessageBus : MessageBusBase<MemoryMessageBusSettings>
99
{
1010
private readonly ILogger _logger;
1111
private IDictionary<string, IMessageProcessor<object>> _messageProcessorByPath;
@@ -63,11 +63,8 @@ public override IDictionary<string, object> CreateHeaders()
6363

6464
public override bool IsMessageScopeEnabled(ConsumerSettings consumerSettings, IDictionary<string, object> consumerContextProperties)
6565
{
66-
#if NETSTANDARD2_0
6766
if (consumerSettings is null) throw new ArgumentNullException(nameof(consumerSettings));
68-
#else
69-
ArgumentNullException.ThrowIfNull(consumerSettings);
70-
#endif
67+
7168
if (consumerContextProperties != null && consumerContextProperties.ContainsKey(MemoryMessageBusProperties.CreateScope))
7269
{
7370
return true;
@@ -133,7 +130,7 @@ private async Task<TResponseMessage> ProduceInternal<TResponseMessage>(object me
133130
path ??= GetDefaultPath(producerSettings.MessageType, producerSettings);
134131
if (!_messageProcessorByPath.TryGetValue(path, out var messageProcessor))
135132
{
136-
_logger.LogDebug("No consumers interested in message type {MessageType} on path {Path}", messageType, path);
133+
LogNoConsumerInterestedInMessageType(path, messageType);
137134
return default;
138135
}
139136

@@ -165,4 +162,24 @@ private async Task<TResponseMessage> ProduceInternal<TResponseMessage>(object me
165162
}
166163
return (TResponseMessage)r.Response;
167164
}
165+
166+
#region Logging
167+
168+
[LoggerMessage(
169+
EventId = 0,
170+
Level = LogLevel.Debug,
171+
Message = "No consumers interested in message type {MessageType} on path {Path}")]
172+
private partial void LogNoConsumerInterestedInMessageType(string path, Type messageType);
173+
174+
#endregion
168175
}
176+
177+
#if NETSTANDARD2_0
178+
179+
public partial class MemoryMessageBus
180+
{
181+
private partial void LogNoConsumerInterestedInMessageType(string path, Type messageType)
182+
=> _logger.LogDebug("No consumers interested in message type {MessageType} on path {Path}", messageType, path);
183+
}
184+
185+
#endif

src/SlimMessageBus.Host.Serialization/SlimMessageBus.Host.Serialization.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<Import Project="../Common.NuGet.Properties.xml" />
44

55
<PropertyGroup>
6-
<Version>3.0.0-rc903</Version>
6+
<Version>3.0.0-rc904</Version>
77
<Description>Core serialization interfaces of SlimMessageBus</Description>
88
<PackageTags>SlimMessageBus</PackageTags>
99
</PropertyGroup>

src/SlimMessageBus.Host/Collections/KindMapping.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
public class KindMapping
44
{
5-
private readonly Dictionary<string, PathKind> _kindByTopic = new();
6-
private readonly Dictionary<Type, PathKind> _kindByMessageType = new();
5+
private readonly Dictionary<string, PathKind> _kindByTopic = [];
6+
private readonly Dictionary<Type, PathKind> _kindByMessageType = [];
77

88
public void Configure(MessageBusSettings settings)
99
{

src/SlimMessageBus.Host/Collections/ProducerByMessageTypeCache.cs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
/// The message type hierarchy is discovered at runtime and cached for faster access.
66
/// </summary>
77
/// <typeparam name="TProducer">The producer type</typeparam>
8-
public class ProducerByMessageTypeCache<TProducer> : IReadOnlyCache<Type, TProducer>
8+
public partial class ProducerByMessageTypeCache<TProducer> : IReadOnlyCache<Type, TProducer>
99
where TProducer : class
1010
{
1111
private readonly ILogger _logger;
@@ -37,7 +37,7 @@ private TProducer CalculateProducer(Type messageType)
3737
var assignableProducer = assignableProducers.FirstOrDefault();
3838
if (assignableProducer.Key != null)
3939
{
40-
_logger.LogDebug("Matched producer for message type {ProducerMessageType} for dispatched message type {MessageType}", assignableProducer.Key, messageType);
40+
LogMatchedProducerForMessageType(messageType, assignableProducer.Key);
4141
return assignableProducer.Value;
4242
}
4343

@@ -52,7 +52,7 @@ private TProducer CalculateProducer(Type messageType)
5252
}
5353
}
5454

55-
_logger.LogDebug("Unable to match any declared producer for dispatched message type {MessageType}", messageType);
55+
LogUnmatchedProducerForMessageType(messageType);
5656

5757
// Note: Nulls are also added to dictionary, so that we don't look them up using reflection next time (cached).
5858
return null;
@@ -74,4 +74,33 @@ private static int CalculateBaseClassDistance(Type type, Type baseType)
7474

7575
return distance;
7676
}
77-
}
77+
78+
#region Logging
79+
80+
[LoggerMessage(
81+
EventId = 0,
82+
Level = LogLevel.Debug,
83+
Message = "Matched producer for message type {ProducerMessageType} for dispatched message type {MessageType}")]
84+
private partial void LogMatchedProducerForMessageType(Type messageType, Type producerMessageType);
85+
86+
[LoggerMessage(
87+
EventId = 1,
88+
Level = LogLevel.Debug,
89+
Message = "Unable to match any declared producer for dispatched message type {MessageType}")]
90+
private partial void LogUnmatchedProducerForMessageType(Type messageType);
91+
92+
#endregion
93+
}
94+
95+
#if NETSTANDARD2_0
96+
97+
public partial class ProducerByMessageTypeCache<TProducer>
98+
{
99+
private partial void LogMatchedProducerForMessageType(Type messageType, Type producerMessageType)
100+
=> _logger.LogDebug("Matched producer for message type {ProducerMessageType} for dispatched message type {MessageType}", producerMessageType, messageType);
101+
102+
private partial void LogUnmatchedProducerForMessageType(Type messageType)
103+
=> _logger.LogDebug("Unable to match any declared producer for dispatched message type {MessageType}", messageType);
104+
}
105+
106+
#endif

src/SlimMessageBus.Host/Consumer/AbstractConsumer.cs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
namespace SlimMessageBus.Host;
22

3-
public abstract class AbstractConsumer : HasProviderExtensions, IAsyncDisposable, IConsumerControl
3+
public abstract partial class AbstractConsumer : HasProviderExtensions, IAsyncDisposable, IConsumerControl
44
{
5+
protected readonly ILogger Logger;
56
private readonly SemaphoreSlim _semaphore;
67
private readonly IReadOnlyList<IAbstractConsumerInterceptor> _interceptors;
78
private CancellationTokenSource _cancellationTokenSource;
@@ -10,7 +11,6 @@ public abstract class AbstractConsumer : HasProviderExtensions, IAsyncDisposable
1011

1112
public bool IsStarted { get; private set; }
1213
public string Path { get; }
13-
public ILogger Logger { get; }
1414
public IReadOnlyList<AbstractConsumerSettings> Settings { get; }
1515
protected CancellationToken CancellationToken => _cancellationTokenSource.Token;
1616

@@ -39,7 +39,7 @@ private async Task<bool> CallInterceptor(Func<IAbstractConsumerInterceptor, Task
3939
}
4040
catch (Exception e)
4141
{
42-
Logger.LogError(e, "Interceptor {Interceptor} failed with error: {Error}", interceptor.GetType().Name, e.Message);
42+
LogInterceptorFailed(interceptor.GetType(), e.Message, e);
4343
}
4444
}
4545
return true;
@@ -178,4 +178,25 @@ protected async virtual ValueTask DisposeAsyncCore()
178178
}
179179

180180
#endregion
181+
182+
#region Logging
183+
184+
[LoggerMessage(
185+
EventId = 0,
186+
Level = LogLevel.Error,
187+
Message = "Interceptor {InterceptorType} failed with error: {Error}")]
188+
private partial void LogInterceptorFailed(Type interceptorType, string error, Exception ex);
189+
190+
#endregion
191+
}
192+
193+
194+
#if NETSTANDARD2_0
195+
196+
public partial class AbstractConsumer
197+
{
198+
private partial void LogInterceptorFailed(Type interceptorType, string error, Exception ex)
199+
=> Logger.LogError(ex, "Interceptor {InterceptorType} failed with error: {Error}", interceptorType, error);
181200
}
201+
202+
#endif

src/SlimMessageBus.Host/Consumer/Checkpointing/CheckpointTrigger.cs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
using System.Diagnostics;
44

5-
public class CheckpointTrigger : ICheckpointTrigger
5+
public partial class CheckpointTrigger : ICheckpointTrigger
66
{
77
private readonly ILogger<CheckpointTrigger> _logger;
88

@@ -35,7 +35,6 @@ public static CheckpointValue GetCheckpointValue(HasProviderExtensions settings)
3535
=> new(settings.GetOrDefault(CheckpointSettings.CheckpointCount, CheckpointSettings.CheckpointCountDefault),
3636
settings.GetOrDefault(CheckpointSettings.CheckpointDuration, CheckpointSettings.CheckpointDurationDefault));
3737

38-
3938
#region Implementation of ICheckpointTrigger
4039

4140
public bool IsEnabled
@@ -53,17 +52,37 @@ public bool Increment()
5352
var enabled = IsEnabled;
5453
if (enabled && _logger.IsEnabled(LogLevel.Debug))
5554
{
56-
_logger.LogDebug("Checkpoint triggered after Count: {CheckpointCount}, Duration: {CheckpointDuration} (s)", _lastCheckpointCount, _lastCheckpointDuration.Elapsed.Seconds);
55+
LogCheckpointTriggered(_lastCheckpointCount, _lastCheckpointDuration.Elapsed.Seconds);
5756
}
5857

5958
return enabled;
60-
}
61-
59+
}
60+
6261
public void Reset()
6362
{
6463
_lastCheckpointCount = 0;
6564
_lastCheckpointDuration.Restart();
66-
}
67-
65+
}
66+
67+
#endregion
68+
69+
#region Logging
70+
71+
[LoggerMessage(
72+
EventId = 0,
73+
Level = LogLevel.Debug,
74+
Message = "Checkpoint triggered after Count: {CheckpointCount}, Duration: {CheckpointDuration} (s)")]
75+
private partial void LogCheckpointTriggered(int checkpointCount, int checkpointDuration);
76+
6877
#endregion
6978
}
79+
80+
#if NETSTANDARD2_0
81+
82+
public partial class CheckpointTrigger
83+
{
84+
private partial void LogCheckpointTriggered(int checkpointCount, int checkpointDuration)
85+
=> _logger.LogDebug("Checkpoint triggered after Count: {CheckpointCount}, Duration: {CheckpointDuration} (s)", checkpointCount, checkpointDuration);
86+
}
87+
88+
#endif

0 commit comments

Comments
 (0)