< Summary - ReFlex - Library

Information
Class: ReFlex.Core.Common.Util.LogUtilities
Assembly: ReFlex.Core.Common
File(s): D:\a\reflex\reflex\library\src\Core\Common\Util\LogUtilities.cs
Line coverage
92%
Covered lines: 46
Uncovered lines: 4
Coverable lines: 50
Total lines: 113
Line coverage: 92%
Branch coverage
81%
Covered branches: 13
Total branches: 16
Branch coverage: 81.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
LogErrorOnce(...)70%111077.77%
ClearLoggedErrors(...)100%44100%
BuildErrorKey(...)100%11100%
BuildExceptionSignature(...)100%22100%

File(s)

D:\a\reflex\reflex\library\src\Core\Common\Util\LogUtilities.cs

#LineLine coverage
 1using System;
 2using System.Collections.Concurrent;
 3using System.Linq;
 4using System.Text;
 5using NLog;
 6
 7namespace ReFlex.Core.Common.Util
 8{
 9    public static class LogUtilities
 10    {
 111        private static readonly Logger Log = LogManager.GetCurrentClassLogger();
 112        private static readonly ConcurrentDictionary<string, byte> LoggedErrors = new ConcurrentDictionary<string, byte>
 13
 14        /// <summary>
 15        /// Logs an exception only once for the same source, method and exception signature.
 16        /// </summary>
 17        /// <param name="exception">Exception to log.</param>
 18        /// <param name="sourceName">Logical source (for example class name) used for scoping deduplication.</param>
 19        /// <param name="methodName">Method name used for scoping deduplication.</param>
 20        /// <param name="message">Optional custom message for the log entry.</param>
 21        /// <exception cref="ArgumentNullException">Thrown when required arguments are null.</exception>
 22        public static void LogErrorOnce(
 23            Exception exception,
 24            string sourceName,
 25            string methodName,
 26            string message = null)
 1927        {
 1928            if (sourceName == null)
 029                throw new ArgumentNullException(nameof(sourceName));
 1930            if (methodName == null)
 031                throw new ArgumentNullException(nameof(methodName));
 32
 1933            if (exception == null)
 034            {
 035                return;
 36            }
 37
 1938            var errorKey = BuildErrorKey(sourceName, methodName, exception);
 1939            if (!LoggedErrors.TryAdd(errorKey, 0))
 740            {
 741                return;
 42            }
 43
 1244            if (string.IsNullOrWhiteSpace(message))
 945            {
 946                Log.Error(exception);
 947                return;
 48            }
 49
 350            Log.Error(exception, message);
 1951        }
 52
 53        /// <summary>
 54        /// Clears deduplication entries.
 55        /// If <paramref name="sourceName"/> is provided, only entries for this source are removed.
 56        /// If it is null or whitespace, all entries are removed.
 57        /// </summary>
 58        /// <param name="sourceName">Optional source name filter.</param>
 59        public static void ClearLoggedErrors(string sourceName = null)
 2260        {
 2261            if (string.IsNullOrWhiteSpace(sourceName))
 462            {
 463                LoggedErrors.Clear();
 464                return;
 65            }
 66
 1867            var sourcePrefix = $"{sourceName}:";
 1868            var keys = LoggedErrors.Keys
 469                .Where(key => key.StartsWith(sourcePrefix, StringComparison.Ordinal))
 1870                .ToList();
 71
 6272            foreach (var key in keys)
 473            {
 474                LoggedErrors.TryRemove(key, out _);
 475            }
 2276        }
 77
 78        /// <summary>
 79        /// Creates a key that uniquely identifies an error context for deduplicated logging.
 80        /// </summary>
 81        /// <param name="sourceName">Logical source (for example class name).</param>
 82        /// <param name="methodName">Method name where the exception occurred.</param>
 83        /// <param name="exception">Exception to create the key from.</param>
 84        /// <returns>Deterministic key based on source, method and exception signature.</returns>
 85        private static string BuildErrorKey(string sourceName, string methodName, Exception exception)
 1986        {
 1987            return $"{sourceName}:{methodName}:{BuildExceptionSignature(exception)}";
 1988        }
 89
 90        /// <summary>
 91        /// Builds a deterministic signature from the exception chain.
 92        /// This includes each exception type and message, including inner exceptions.
 93        /// </summary>
 94        /// <param name="exception">Exception to convert to a signature.</param>
 95        /// <returns>Signature string that can be used for deduplication.</returns>
 96        private static string BuildExceptionSignature(Exception exception)
 1997        {
 1998            var builder = new StringBuilder();
 1999            var current = exception;
 100
 40101            while (current != null)
 21102            {
 21103                builder.Append(current.GetType().FullName);
 21104                builder.Append(':');
 21105                builder.Append(current.Message);
 21106                builder.Append('|');
 21107                current = current.InnerException;
 21108            }
 109
 19110            return builder.ToString();
 19111        }
 112    }
 113}