BusMessageLogger.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Diagnostics.CodeAnalysis;
  5. using System.Reflection;
  6. using Abc.Zebus.Scan;
  7. using Abc.Zebus.Transport;
  8. using Abc.Zebus.Util.Extensions;
  9. using Microsoft.Extensions.Logging;
  10. namespace Abc.Zebus.Core;
  11. internal class BusMessageLogger
  12. {
  13. private static readonly ConcurrentDictionary<Type, MessageTypeLogHelper> _logHelpers = new();
  14. private readonly ILogger _logger;
  15. private bool _logDebugEnabled;
  16. private bool _logInfoEnabled;
  17. public BusMessageLogger(Type loggerType)
  18. : this(loggerType.FullName!)
  19. {
  20. }
  21. public BusMessageLogger(string loggerFullName)
  22. {
  23. _logger = ZebusLogManager.GetLogger(loggerFullName);
  24. // Instances of BusMessageLogger are static, no need to unsubscribe from this event
  25. ZebusLogManager.ConfigurationUpdated += UpdateLogConfig;
  26. UpdateLogConfig();
  27. void UpdateLogConfig()
  28. {
  29. _logDebugEnabled = _logger.IsEnabled(LogLevel.Debug);
  30. _logInfoEnabled = _logger.IsEnabled(LogLevel.Information);
  31. }
  32. }
  33. public bool IsInfoEnabled(IMessage message)
  34. => _logInfoEnabled && GetLogHelper(message).Logger.IsEnabled(LogLevel.Information);
  35. public void LogHandleMessage(IList<IMessage> messages, string? dispatchQueueName, MessageId? messageId)
  36. {
  37. var message = messages[0];
  38. if (!TryGetLogHelperForInfo(message, out var logHelper))
  39. return;
  40. var messageText = logHelper.GetMessageText(message);
  41. var dispatchQueueNameText = HasCustomDispatchQueue() ? $" [{dispatchQueueName}]" : "";
  42. var batchText = messages.Count > 1 ? $" Count: {messages.Count}" : "";
  43. _logger.LogInformation($"HANDLE{dispatchQueueNameText}: {messageText}{batchText} [{messageId}]");
  44. bool HasCustomDispatchQueue() => !string.IsNullOrEmpty(dispatchQueueName) && dispatchQueueName != DispatchQueueNameScanner.DefaultQueueName;
  45. }
  46. public void LogReceiveMessageAck(MessageExecutionCompleted messageAck)
  47. {
  48. if (!TryGetLogHelperForDebug(messageAck, out _))
  49. return;
  50. _logger.LogDebug($"RECV ACK {{{messageAck}}}");
  51. }
  52. public void LogReceiveMessageLocal(IMessage message)
  53. {
  54. if (!TryGetLogHelperForDebug(message, out var logHelper))
  55. return;
  56. var messageText = logHelper.GetMessageText(message);
  57. _logger.LogDebug($"RECV local: {messageText}");
  58. }
  59. public void LogReceiveMessageRemote(IMessage message, TransportMessage transportMessage)
  60. {
  61. if (!TryGetLogHelperForDebug(message, out var logHelper))
  62. return;
  63. var messageText = logHelper.GetMessageText(message);
  64. _logger.LogDebug($"RECV remote: {messageText} from {transportMessage.SenderId} ({transportMessage.Content.Length} bytes). [{transportMessage.Id}]");
  65. }
  66. public void LogSendMessage(IMessage message, IList<Peer> peers)
  67. {
  68. if (!TryGetLogHelperForInfo(message, out var logHelper))
  69. return;
  70. var messageText = logHelper.GetMessageText(message);
  71. var targetPeersText = GetTargetPeersText(peers);
  72. _logger.LogInformation($"SEND: {messageText} to {targetPeersText}");
  73. }
  74. public void LogSendMessage(IMessage message, IList<Peer> peers, TransportMessage transportMessage)
  75. {
  76. if (!TryGetLogHelperForInfo(message, out var logHelper))
  77. return;
  78. var messageText = logHelper.GetMessageText(message);
  79. var targetPeersText = GetTargetPeersText(peers);
  80. _logger.LogInformation($"SEND: {messageText} to {targetPeersText} ({transportMessage.Content.Length} bytes) [{transportMessage.Id}]");
  81. }
  82. private static string GetTargetPeersText(IList<Peer> peers)
  83. {
  84. switch (peers.Count)
  85. {
  86. case 0:
  87. return "no target peer";
  88. case 1:
  89. return peers[0].Id.ToString();
  90. default:
  91. var otherPeersCount = peers.Count - 1;
  92. return otherPeersCount > 1
  93. ? peers[0].Id + " and " + otherPeersCount + " other peers"
  94. : peers[0].Id + " and " + otherPeersCount + " other peer";
  95. }
  96. }
  97. public static string ToString(IMessage message)
  98. => GetLogHelper(message).GetMessageText(message);
  99. [SuppressMessage("ReSharper", "ConvertClosureToMethodGroup")]
  100. private static MessageTypeLogHelper GetLogHelper(IMessage message)
  101. => _logHelpers.GetOrAdd(message.GetType(), type => CreateLogger(type));
  102. private static MessageTypeLogHelper CreateLogger(Type messageType)
  103. {
  104. var logger = ZebusLogManager.GetLogger(messageType);
  105. var hasToStringOverride = HasToStringOverride(messageType);
  106. return new MessageTypeLogHelper(logger, hasToStringOverride, messageType.GetPrettyName());
  107. }
  108. private bool TryGetLogHelperForInfo(IMessage message, [NotNullWhen(true)] out MessageTypeLogHelper? logHelper)
  109. {
  110. if (!_logInfoEnabled)
  111. {
  112. logHelper = null;
  113. return false;
  114. }
  115. logHelper = GetLogHelper(message);
  116. return logHelper.Logger.IsEnabled(LogLevel.Information);
  117. }
  118. private bool TryGetLogHelperForDebug(IMessage message, [NotNullWhen(true)] out MessageTypeLogHelper? logHelper)
  119. {
  120. if (!_logDebugEnabled)
  121. {
  122. logHelper = null;
  123. return false;
  124. }
  125. logHelper = GetLogHelper(message);
  126. return logHelper.Logger.IsEnabled(LogLevel.Debug);
  127. }
  128. private static bool HasToStringOverride(Type messageType)
  129. {
  130. var methodInfo = messageType.GetMethod("ToString", BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
  131. return methodInfo != null;
  132. }
  133. private class MessageTypeLogHelper
  134. {
  135. public readonly ILogger Logger;
  136. private readonly bool _hasToStringOverride;
  137. private readonly string _messageTypeName;
  138. public MessageTypeLogHelper(ILogger logger, bool hasToStringOverride, string messageTypeName)
  139. {
  140. Logger = logger;
  141. _hasToStringOverride = hasToStringOverride;
  142. _messageTypeName = messageTypeName;
  143. }
  144. public string GetMessageText(IMessage message)
  145. {
  146. return _hasToStringOverride ? $"{_messageTypeName} {{{message}}}" : $"{_messageTypeName}";
  147. }
  148. }
  149. }