File: Commands\Test\MTP\Logger.cs
Web Access
Project: ..\..\..\src\Cli\dotnet\dotnet.csproj (dotnet)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Runtime.CompilerServices;
 
namespace Microsoft.DotNet.Cli.Commands.Test;
 
internal static class Logger
{
    [InterpolatedStringHandler]
    internal readonly struct LoggerInterpolatedStringHandler
    {
        /// <summary>The handler we use to perform the formatting.</summary>
        private readonly StringBuilder.AppendInterpolatedStringHandler _stringBuilderHandler;
        private readonly StringBuilder? _stringBuilder;
 
        public LoggerInterpolatedStringHandler(int literalLength, int formattedCount, out bool shouldAppend)
        {
            shouldAppend = Logger.TraceEnabled;
            if (shouldAppend)
            {
                _stringBuilder = new StringBuilder();
                _stringBuilderHandler = new(literalLength, formattedCount, _stringBuilder);
            }
        }
 
        public override string ToString()
            => _stringBuilder?.ToString() ?? string.Empty;
 
        public void AppendLiteral(string value) => _stringBuilderHandler!.AppendLiteral(value);
 
        public void AppendFormatted<T>(T value) => _stringBuilderHandler.AppendFormatted<T>(value);
 
        public void AppendFormatted<T>(T value, string? format) => _stringBuilderHandler.AppendFormatted<T>(value, format);
 
        public void AppendFormatted<T>(T value, int alignment) => _stringBuilderHandler.AppendFormatted<T>(value, alignment);
 
        public void AppendFormatted<T>(T value, int alignment, string? format) => _stringBuilderHandler.AppendFormatted<T>(value, alignment, format);
 
        public void AppendFormatted(ReadOnlySpan<char> value) => _stringBuilderHandler.AppendFormatted(value);
 
        public void AppendFormatted(ReadOnlySpan<char> value, int alignment = 0, string? format = null) => _stringBuilderHandler.AppendFormatted(value, alignment, format);
 
        public void AppendFormatted(string? value) => _stringBuilderHandler.AppendFormatted(value);
 
        public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _stringBuilderHandler.AppendFormatted(value, alignment, format);
 
        public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _stringBuilderHandler.AppendFormatted(value, alignment, format);
    }
 
    public static bool TraceEnabled { get; private set; }
    private static readonly string? _traceFilePath;
    private static readonly object _lock = new();
 
    static Logger()
    {
        _traceFilePath = Environment.GetEnvironmentVariable(CliConstants.TestTraceLoggingEnvVar);
        TraceEnabled = !string.IsNullOrEmpty(_traceFilePath);
 
        string? directoryPath = Path.GetDirectoryName(_traceFilePath);
        if (!string.IsNullOrEmpty(directoryPath))
        {
            Directory.CreateDirectory(directoryPath);
        }
    }
 
    /// <summary>
    /// Use this overload carefully for performance reasons.
    /// We don't want the argument to have expensive calculations.
    /// </summary>
    /// <param name="message"></param>
    public static void LogTrace(string message)
    {
        if (TraceEnabled)
        {
            LogTraceCore(message);
        }
    }
 
    public static void LogTrace(ref LoggerInterpolatedStringHandler handler)
    {
        if (TraceEnabled)
        {
            LogTraceCore(handler.ToString());
        }
    }
 
    public static void LogTrace<T>(T arg, Func<T, string> messageGetter)
    {
        if (TraceEnabled)
        {
            LogTraceCore(messageGetter(arg));
        }
    }
 
    private static void LogTraceCore(string message)
    {
        try
        {
            message = $"[dotnet test - {DateTimeOffset.UtcNow:MM/dd/yyyy HH:mm:ss.fff}]{message}";
 
            lock (_lock)
            {
                using StreamWriter logFile = File.AppendText(_traceFilePath!);
                logFile.WriteLine(message);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[dotnet test - {DateTimeOffset.UtcNow:MM/dd/yyyy HH:mm:ss.fff}]{ex}");
        }
    }
}