< Summary

Information
Class: AmbientServices.AmbientFileLogOverflowWriter
Assembly: AmbientServices
File(s): /home/runner/work/AmbientServices/AmbientServices/AmbientServices/DefaultImplementation/AmbientFileLogOverflowWriter.cs
Tag: 332_35464845198
Line coverage
95%
Covered lines: 41
Uncovered lines: 2
Coverable lines: 43
Total lines: 137
Line coverage: 95.3%
Branch coverage
100%
Covered branches: 14
Total branches: 14
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
.ctor()100%11100%
get_DefaultOverflowLogFilePath()100%44100%
WriteOverflowLine(...)100%2275%
Flush()100%11100%
Dispose()100%11100%
GetOrCreateWriter()100%66100%
CloseWriter()100%22100%

File(s)

/home/runner/work/AmbientServices/AmbientServices/AmbientServices/DefaultImplementation/AmbientFileLogOverflowWriter.cs

#LineLine coverage
 1using System;
 2using System.IO;
 3using System.Text;
 4
 5namespace AmbientServices;
 6
 7/// <summary>
 8/// Default <see cref="IAmbientLogOverflowWriter"/> that appends overflow lines to a file under local application data
 9/// (same folder convention as <see cref="AmbientFileLogger"/>).
 10/// Uses a single long-lived <see cref="StreamWriter"/> per instance to avoid recursion through ambient loggers and per-
 11/// </summary>
 12/// <remarks>
 13/// <pitch>The default place overflowed log lines land: an append-only file under local application data, so a logging b
 14/// <pledge><see cref="IAmbientLogOverflowWriter"/></pledge>
 15/// <pledge><see cref="IDisposable"/></pledge>
 16/// <pledge>
 17/// All lines from one instance are appended to a single fixed file — the path given at construction, or the default ove
 18/// Flushing (or disposing) closes the open writer so the file can be read externally; a later write transparently reope
 19/// </pledge>
 20/// <plan>
 21/// A single lazily-created <see cref="StreamWriter"/> (UTF-8, auto-flush) over a <see cref="FileStream"/> opened with <
 22/// Trade-off profile: durable and simple at the cost of a lock and a synchronous write per line; acceptable because ove
 23/// </plan>
 24/// </remarks>
 25[DefaultAmbientService(typeof(IAmbientLogOverflowWriter))]
 26public sealed class AmbientFileLogOverflowWriter : IAmbientLogOverflowWriter, IDisposable
 27{
 228    private readonly object _writeLock = new();
 29    private readonly string _overflowLogFilePath;
 30    private StreamWriter? _writer;
 31
 32    /// <summary>
 33    /// Constructs a writer that uses the standard local application data overflow log path.
 34    /// </summary>
 35    public AmbientFileLogOverflowWriter()
 236        : this(DefaultOverflowLogFilePath)
 37    {
 238    }
 39
 40    /// <summary>
 41    /// Constructs a writer that appends to the specified file path.
 42    /// </summary>
 43    /// <param name="overflowLogFilePath">The full path of the overflow log file.</param>
 244    public AmbientFileLogOverflowWriter(string overflowLogFilePath)
 45    {
 46#if NET5_0_OR_GREATER
 247        ArgumentNullException.ThrowIfNull(overflowLogFilePath);
 48#else
 49        if (overflowLogFilePath is null) throw new ArgumentNullException(nameof(overflowLogFilePath));
 50#endif
 251        _overflowLogFilePath = overflowLogFilePath;
 252    }
 53
 54    /// <summary>
 55    /// Gets the default overflow log file path (same folder convention as <see cref="AmbientFileLogger"/>).
 56    /// </summary>
 57    public static string DefaultOverflowLogFilePath
 58    {
 59        get
 60        {
 261            string prefix = AmbientFileLogger.CombineRelativeFilePrefixWithProgramData(
 262                AmbientFileLogger.GetExecutableName() + "_AmbientLogBufferOverflow",
 263                AmbientFileLogger.GetProgramDataFolderLocationInternal,
 264                AmbientFileLogger.GetExecutableName);
 265            return prefix + ".log";
 66        }
 67    }
 68
 69    /// <inheritdoc />
 70    public void WriteOverflowLine(string line)
 71    {
 272        if (line == null) return;
 73        try
 74        {
 275            lock (_writeLock)
 76            {
 277                GetOrCreateWriter().WriteLine(line);
 278            }
 279        }
 80#pragma warning disable CA1031
 081        catch
 82#pragma warning restore CA1031
 83        {
 084        }
 285    }
 86
 87    /// <inheritdoc />
 88    public void Flush()
 89    {
 290        lock (_writeLock)
 91        {
 292            CloseWriter();
 293        }
 294    }
 95
 96    /// <inheritdoc />
 297    public void Dispose() => Flush();
 98
 99    private StreamWriter GetOrCreateWriter()
 100    {
 2101        if (_writer != null)
 102        {
 2103            return _writer;
 104        }
 105
 2106        string? directory = Path.GetDirectoryName(_overflowLogFilePath);
 2107        if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
 108        {
 2109            Directory.CreateDirectory(directory);
 110        }
 111
 2112        FileStream stream = new(_overflowLogFilePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
 2113        _writer = new StreamWriter(stream, Encoding.UTF8) { AutoFlush = true };
 2114        return _writer;
 115    }
 116
 117    private void CloseWriter()
 118    {
 2119        if (_writer == null)
 120        {
 2121            return;
 122        }
 123
 2124        StreamWriter writer = _writer;
 2125        _writer = null;
 126        try
 127        {
 2128            writer.Flush();
 2129            writer.Dispose();
 2130        }
 131#pragma warning disable CA1031
 2132        catch
 133#pragma warning restore CA1031
 134        {
 2135        }
 2136    }
 137}