| File: Logging\DistributedLoggers\DistributedFileLogger.cs | Web Access |
| Project: src\msbuild\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.IO; using Microsoft.Build.Framework; using Microsoft.Build.Shared; #nullable disable namespace Microsoft.Build.Logging { /// <summary> /// This class will create a text file which will contain the build log for that node /// </summary> public class DistributedFileLogger : IForwardingLogger { #region Constructors /// <summary> /// Default constructor. /// </summary> public DistributedFileLogger() : base() { } #endregion #region Methods /// <summary> /// Initializes the logger. /// </summary> public void Initialize(IEventSource eventSource, int nodeCount) { Initialize(eventSource); } /// <summary> /// Parses out the logger parameters from the Parameters string. /// </summary> private void ParseFileLoggerParameters() { if (this.Parameters != null) { string[] parameterComponents = this.Parameters.Split(s_fileLoggerParameterDelimiters); for (int param = 0; param < parameterComponents.Length; param++) { if (parameterComponents[param].Length > 0) { string[] parameterAndValue = parameterComponents[param].Split(s_fileLoggerParameterValueSplitCharacter); if (parameterAndValue.Length > 1) { ApplyFileLoggerParameter(parameterAndValue[0], parameterAndValue[1]); } else { ApplyFileLoggerParameter(parameterAndValue[0], null); } } } } } /// <summary> /// Apply a parameter /// </summary> private void ApplyFileLoggerParameter(string parameterName, string parameterValue) { if (string.Equals("LOGFILE", parameterName, StringComparison.OrdinalIgnoreCase)) { if (string.IsNullOrEmpty(parameterValue)) { string message = ResourceUtilities.FormatResourceStringStripCodeAndKeyword("InvalidFileLoggerFile", string.Empty, ResourceUtilities.GetResourceString("logfilePathNullOrEmpty")); throw new LoggerException(message); } // Set log file to the right half of the parameter string and then remove it as it is going to be replaced in Initialize _logFile = parameterValue; int indexOfParameter = _parameters.IndexOf(parameterName + s_fileLoggerParameterValueSplitCharacter[0] + parameterValue, 0, StringComparison.OrdinalIgnoreCase); int length = ((string)(parameterName + s_fileLoggerParameterValueSplitCharacter[0] + parameterValue)).Length; // Check to see if the next char is a ; if so remove that as well if ((indexOfParameter + length) < _parameters.Length && _parameters[indexOfParameter + length] == ';') { length++; } _parameters = _parameters.Remove(indexOfParameter, length); } } /// <summary> /// Initializes the logger. /// </summary> public void Initialize(IEventSource eventSource) { ArgumentNullException.ThrowIfNull(eventSource); ParseFileLoggerParameters(); string fileName = _logFile; try { // Create a new file logger and pass it some parameters to make the build log very detailed _nodeFileLogger = new FileLogger(); string extension = Path.GetExtension(_logFile); // If there is no extension add a default of .log to it if (string.IsNullOrEmpty(extension)) { _logFile += ".log"; extension = ".log"; } // Log 0-based node id's, where 0 is the parent. This is a little unnatural for the reader, // but avoids confusion by being consistent with the Engine and any error messages it may produce. fileName = _logFile.Replace(extension, _nodeId + extension); _nodeFileLogger.Verbosity = LoggerVerbosity.Detailed; _nodeFileLogger.Parameters = $"ShowEventId;ShowCommandLine;logfile={fileName};{_parameters}"; } catch (ArgumentException e) // Catching Exception, but rethrowing unless it's a well-known exception. { _nodeFileLogger?.Shutdown(); string errorCode; string helpKeyword; string message = ResourceUtilities.FormatResourceStringStripCodeAndKeyword(out errorCode, out helpKeyword, "InvalidFileLoggerFile", fileName, e.Message); throw new LoggerException(message, e, errorCode, helpKeyword); } // Say we are operating on 2 processors so we can get the multiproc output _nodeFileLogger.Initialize(eventSource, 2); } /// <summary> /// Instructs the logger to shut down. /// </summary> public void Shutdown() { _nodeFileLogger?.Shutdown(); } #endregion #region Properties // Need to access this for testing purposes internal FileLogger InternalFilelogger { get { return _nodeFileLogger; } } /// <summary> /// Gets or sets the <see cref="IEventRedirector"/> object used to redirect build events. /// </summary> public IEventRedirector BuildEventRedirector { get { return _buildEventRedirector; } set { _buildEventRedirector = value; } } /// <summary> /// Gets or sets the identifier of the node which the forwarding logger is attached to. /// </summary> public int NodeId { get { return _nodeId; } set { _nodeId = value; } } /// <summary> /// Gets or sets <see cref="LoggerVerbosity"/>. This is currently hard-coded as <see cref="LoggerVerbosity.Detailed"/>. /// </summary> public LoggerVerbosity Verbosity { get => InternalError.Throw<LoggerVerbosity>("Should not be getting verbosity from distributed file logger"); set { // Dont really care about verbosity at this point, but dont want to throw exception as it is set for all distributed loggers } } /// <summary> /// Gets or sets the parameters. /// </summary> public string Parameters { get { return _parameters; } set { _parameters = value; } } #endregion #region Data // The file logger which will do the actual logging of the node's build output private FileLogger _nodeFileLogger; // Reference for the central logger private IEventRedirector _buildEventRedirector; // The Id of the node the forwardingLogger is attached to private int _nodeId; // Directory to place the log files, by default this will be in the current directory when the node is created private string _logFile = "msbuild.log"; // Logger parameters private string _parameters; // File logger parameters delimiters. private static readonly char[] s_fileLoggerParameterDelimiters = { ';' }; // File logger parameter value split character. private static readonly char[] s_fileLoggerParameterValueSplitCharacter = { '=' }; #endregion } }