File: PrintLineDebuggerWriters.cs
Web Access
Project: ..\..\..\src\Build\Microsoft.Build.csproj (Microsoft.Build)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using CommonWriterType = System.Action<string, string, System.Collections.Generic.IEnumerable<string>>;
 
#nullable disable
 
namespace Microsoft.Build.Shared.Debugging
{
    /// <summary>
    ///     A collection of useful writers
    /// </summary>
    internal static class PrintLineDebuggerWriters
    {
        public sealed class IdBasedFilesWriter
        {
            private string LogFileRoot { get; }
 
            public CommonWriterType Writer => (id, callsite, args) =>
            {
                try
                {
                    var file = Path.Combine(LogFileRoot, string.IsNullOrEmpty(id) ? "NoId" : id) + ".csv";
                    File.AppendAllText(file, CsvFormat(string.Empty, callsite, args));
                }
                catch (Exception e)
                {
                    var errorFile = Path.Combine(LogFileRoot, $"LoggingException_{Guid.NewGuid()}");
                    File.AppendAllText(errorFile, $"{SimpleFormat(id, callsite, args)}\n{e.Message}\n{e.StackTrace}");
                }
            };
 
            public IdBasedFilesWriter(string logFileRoot)
            {
                this.LogFileRoot = logFileRoot;
                Directory.CreateDirectory(logFileRoot);
            }
 
            public static IdBasedFilesWriter FromArtifactLogDirectory()
            {
                return new IdBasedFilesWriter(ArtifactsLogDirectory);
            }
        }
 
        public sealed class CompositeWriter
        {
            private IEnumerable<CommonWriterType> Writers { get; }
 
            public CommonWriterType Writer => (id, callsite, args) =>
            {
                var argsArray = args as string[] ?? args.ToArray();
 
                foreach (var writer in Writers)
                {
                    writer(id, callsite, argsArray);
                }
            };
 
            public CompositeWriter(IEnumerable<CommonWriterType> writers)
            {
                Writers = writers;
            }
        }
 
        public static CommonWriterType StdOutWriter = (id, callsite, args) => Console.WriteLine(SimpleFormat(id, callsite, args));
 
        private static Lazy<string> _artifactsLogs = new Lazy<string>(
            () =>
            {
                var executingAssembly = FileUtilities.ExecutingAssemblyPath;
 
                var binPart = $"bin";
 
                var logIndex = executingAssembly.IndexOf(binPart, StringComparison.Ordinal);
 
                var artifactsPart = executingAssembly.Substring(0, logIndex);
                return logIndex < 0
                    ? null
                    : Path.Combine(artifactsPart, "log", "Debug");
            });
 
        public static string ArtifactsLogDirectory => _artifactsLogs.Value;
 
        public static string SimpleFormat(string id, string callsite, IEnumerable<string> args)
        {
            return $"\n{(id == null ? string.Empty : id + ": ")}{callsite}:{string.Join(";", args)}";
        }
 
        public static string CsvFormat(string id, string callsite, IEnumerable<string> args)
        {
            var joinedArgs = $"{EscapeCommas(callsite)},{string.Join(",", args.Select(arg => EscapeCommas(arg)))}\n";
 
            return string.IsNullOrEmpty(id)
                ? joinedArgs
                : $"{EscapeCommas(id)},{joinedArgs}";
 
            string EscapeCommas(string s)
            {
                return s.Replace(",", ";");
            }
        }
    }
}