< Summary

Information
Class: ArturRios.Logging.Loggers.FileLogger
Assembly: ArturRios.Logging
File(s): /home/runner/work/dotnet-logging/dotnet-logging/src/Loggers/FileLogger.cs
Line coverage
93%
Covered lines: 54
Uncovered lines: 4
Coverable lines: 58
Total lines: 150
Line coverage: 93.1%
Branch coverage
84%
Covered branches: 16
Total branches: 19
Branch coverage: 84.2%
Method coverage
100%
Covered methods: 15
Fully covered methods: 13
Total methods: 15
Method coverage: 100%
Full method coverage: 86.6%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
.cctor()100%11100%
Trace(...)100%11100%
Debug(...)100%11100%
Info(...)100%11100%
Warn(...)100%11100%
Error(...)100%11100%
Exception(...)100%11100%
Critical(...)100%11100%
Fatal(...)100%11100%
Write(...)100%11100%
CreateDirectoryIfNotExists(...)75%44100%
BuildFullPath()100%11100%
BuildFolderPath(...)88.88%9987.5%
BuildFileName(...)83.33%6680%

File(s)

/home/runner/work/dotnet-logging/dotnet-logging/src/Loggers/FileLogger.cs

#LineLine coverage
 1using ArturRios.Logging.Configuration;
 2using ArturRios.Logging.Factories;
 3using ArturRios.Logging.Interfaces;
 4
 5namespace ArturRios.Logging.Loggers;
 6
 7/// <summary>
 8/// Appends log entries to files on disk, organised by the folder scheme and split level in the configuration.
 9/// </summary>
 10/// <remarks>
 11/// Writes are serialized on a process-wide lock, so entries from concurrent threads never interleave.
 12/// </remarks>
 13/// <param name="configuration">Controls the application name, base path, folder scheme and split level.</param>
 5514public class FileLogger(FileLoggerConfiguration configuration) : IInternalLogger
 15{
 16    private const string DefaultLogFolder = "log";
 17    private const string FileExtension = ".log";
 118    private static readonly Lock s_fileLock = new();
 19
 20    /// <summary>
 21    /// The folder name used by <see cref="LogFolderScheme.ByRequest"/>, fixed for the lifetime of this
 22    /// logger. Generating it per write put every single entry in a folder of its own.
 23    /// </summary>
 5524    private readonly string _instanceFolderName = Guid.NewGuid().ToString();
 25
 26    /// <inheritdoc />
 27    public void Trace(string message, string filePath, string methodName)
 28    {
 429        Write(CustomLogLevel.Trace, filePath, methodName, message);
 430    }
 31
 32    /// <inheritdoc />
 33    public void Debug(string message, string filePath, string methodName)
 34    {
 435        Write(CustomLogLevel.Debug, filePath, methodName, message);
 436    }
 37
 38    /// <inheritdoc />
 39    public void Info(string message, string filePath, string methodName)
 40    {
 4941        Write(CustomLogLevel.Information, filePath, methodName, message);
 4942    }
 43
 44    /// <inheritdoc />
 45    public void Warn(string message, string filePath, string methodName)
 46    {
 447        Write(CustomLogLevel.Warning, filePath, methodName, message);
 448    }
 49
 50    /// <inheritdoc />
 51    public void Error(string message, string filePath, string methodName)
 52    {
 453        Write(CustomLogLevel.Error, filePath, methodName, message);
 454    }
 55
 56    /// <inheritdoc />
 57    public void Exception(string message, string filePath, string methodName)
 58    {
 359        Write(CustomLogLevel.Exception, filePath, methodName, message);
 360    }
 61
 62    /// <inheritdoc />
 63    public void Critical(string message, string filePath, string methodName)
 64    {
 365        Write(CustomLogLevel.Critical, filePath, methodName, message);
 366    }
 67
 68    /// <inheritdoc />
 69    public void Fatal(string message, string filePath, string methodName)
 70    {
 371        Write(CustomLogLevel.Fatal, filePath, methodName, message);
 372    }
 73
 74    private void Write(CustomLogLevel level, string filePath, string methodName, string message)
 75    {
 7476        var path = BuildFullPath();
 77
 7478        CreateDirectoryIfNotExists(path);
 79
 80        lock (s_fileLock)
 81        {
 7482            File.AppendAllText(path, LogEntryFactory.Create(level, filePath, methodName, message));
 7483        }
 7484    }
 85
 86    private static void CreateDirectoryIfNotExists(string path)
 87    {
 7488        var directory = Path.GetDirectoryName(path);
 89
 7490        if (directory is not null && !Directory.Exists(directory))
 91        {
 5192            Directory.CreateDirectory(directory);
 93        }
 7494    }
 95
 96    private string BuildFullPath()
 97    {
 7498        var timestamp = DateTime.UtcNow;
 7499        var folderPath = BuildFolderPath(timestamp);
 74100        var fileName = BuildFileName(timestamp);
 101
 74102        return Path.Combine(folderPath, $"{fileName}{FileExtension}");
 103    }
 104
 105    private string BuildFolderPath(DateTime timestamp)
 106    {
 74107        var basePath = configuration.FilePath ?? Path.Combine(AppContext.BaseDirectory, DefaultLogFolder);
 74108        var path = basePath;
 109
 74110        switch (configuration.FolderScheme)
 111        {
 112            case LogFolderScheme.AllInOne:
 113                break;
 114            case LogFolderScheme.ByYear:
 2115                path = Path.Combine(path, timestamp.Year.ToString());
 2116                break;
 117            case LogFolderScheme.ByMonth:
 3118                path = Path.Combine(path, timestamp.Year.ToString(), timestamp.Month.ToString("D2"));
 3119                break;
 120            case LogFolderScheme.ByDay:
 2121                path = Path.Combine(path, timestamp.Year.ToString(), timestamp.Month.ToString("D2"), timestamp.Day.ToStr
 2122                break;
 123            case LogFolderScheme.ByHour:
 2124                path = Path.Combine(path, timestamp.Year.ToString(), timestamp.Month.ToString("D2"), timestamp.Day.ToStr
 2125                break;
 126            case LogFolderScheme.ByRequest:
 7127                path = Path.Combine(path, _instanceFolderName);
 7128                break;
 129            default:
 0130                throw new ArgumentOutOfRangeException(
 0131                    nameof(configuration), configuration.FolderScheme, "Unknown log folder scheme.");
 132        }
 133
 74134        return path;
 135    }
 136
 137    private string BuildFileName(DateTime timestamp)
 138    {
 74139        return configuration.FileSplitLevel switch
 74140        {
 2141            LogSplitLevel.Request => configuration.ApplicationName,
 2142            LogSplitLevel.Year => $"{configuration.ApplicationName}_{timestamp:yyyy}",
 2143            LogSplitLevel.Month => $"{configuration.ApplicationName}_{timestamp:yyyy_MM}",
 66144            LogSplitLevel.Day => $"{configuration.ApplicationName}_{timestamp:yyyy_MM_dd}",
 2145            LogSplitLevel.Hour => $"{configuration.ApplicationName}_{timestamp:yyyy_MM_dd_HH}",
 0146            _ => throw new ArgumentOutOfRangeException(
 0147                nameof(configuration), configuration.FileSplitLevel, "Unknown log split level.")
 74148        };
 149    }
 150}