| File: Logging\LoggerDescription.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.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; using Microsoft.Build.BackEnd; using Microsoft.Build.Framework; using Microsoft.Build.Shared; using InternalLoggerException = Microsoft.Build.Exceptions.InternalLoggerException; #nullable disable namespace Microsoft.Build.Logging { /// <summary> /// This class is used to contain information about a logger as a collection of values that /// can be used to instantiate the logger and can be serialized to be passed between different /// processes. /// </summary> public class LoggerDescription : ITranslatable { #region Constructor internal LoggerDescription() { } /// <summary> /// Creates a logger description from given data /// </summary> public LoggerDescription( string loggerClassName, string loggerAssemblyName, string loggerAssemblyFile, string loggerSwitchParameters, LoggerVerbosity verbosity) : this(loggerClassName, loggerAssemblyName, loggerAssemblyFile, loggerSwitchParameters, verbosity, isOptional: false) { } /// <summary> /// Creates a logger description from given data /// </summary> public LoggerDescription( string loggerClassName, string loggerAssemblyName, string loggerAssemblyFile, string loggerSwitchParameters, LoggerVerbosity verbosity, bool isOptional) { _loggerClassName = loggerClassName; if (loggerAssemblyFile != null && !Path.IsPathRooted(loggerAssemblyFile)) { loggerAssemblyFile = FileUtilities.NormalizePath(loggerAssemblyFile); } _loggerAssembly = AssemblyLoadInfo.Create(loggerAssemblyName, loggerAssemblyFile); _loggerSwitchParameters = loggerSwitchParameters; _verbosity = verbosity; _isOptional = isOptional; } #endregion #region Properties /// <summary> /// This property exposes the logger id which identifies each distributed logger uniquiely /// </summary> internal int LoggerId { get { return _loggerId; } set { _loggerId = value; } } /// <summary> /// This property generates the logger name by appending together the class name and assembly name /// </summary> public string Name { get { if (!string.IsNullOrEmpty(_loggerClassName) && !string.IsNullOrEmpty(_loggerAssembly.AssemblyFile)) { return $"{_loggerClassName}:{_loggerAssembly.AssemblyFile}"; } else if (!string.IsNullOrEmpty(_loggerClassName)) { return _loggerClassName; } else { return _loggerAssembly.AssemblyFile ?? _loggerAssembly.AssemblyName; } } } /// <summary> /// Returns the string of logger parameters, null if there are none /// </summary> public string LoggerSwitchParameters { get { return _loggerSwitchParameters; } internal set { _loggerSwitchParameters = value; } } public bool IsOptional { get { return _isOptional; } } /// <summary> /// Return the verbosity for this logger (from command line all loggers get same verbosity) /// </summary> public LoggerVerbosity Verbosity { get { return _verbosity; } } #endregion #region Methods /// <summary> /// Create an IForwardingLogger out of the data in this description. This method may throw a variety of /// reflection exceptions if the data is invalid. It is the resposibility of the caller to handle these /// exceptions if desired. /// </summary> /// <returns></returns> [RequiresUnreferencedCode("Loads and instantiates a forwarding logger type by reflecting over an assembly discovered at runtime, which is incompatible with trimming.")] internal IForwardingLogger CreateForwardingLogger() { IForwardingLogger forwardingLogger = null; try { forwardingLogger = (IForwardingLogger)CreateLogger(true); // Check if the class was not found in the assembly if (forwardingLogger == null) { InternalLoggerException.Throw(null, null, "LoggerNotFoundError", true, this.Name); } } catch (Exception e) // Wrap other exceptions in a more meaningful exception. LoggerException and InternalLoggerException are already meaningful. when (!(e is LoggerException /* Polite logger Failure*/ || e is InternalLoggerException /* LoggerClass not found*/ || ExceptionHandling.IsCriticalException(e))) { InternalLoggerException.Throw(e, null, "LoggerCreationError", true, Name); } return forwardingLogger; } /// <summary> /// Create an ILogger out of the data in this description. This method may throw a variety of /// reflection exceptions if the data is invalid. It is the resposibility of the caller to handle these /// exceptions if desired. /// </summary> /// <returns></returns> [RequiresUnreferencedCode("Loads and instantiates a logger type by reflecting over an assembly discovered at runtime, which is incompatible with trimming.")] public ILogger CreateLogger() { return CreateLogger(false); } /// <summary> /// Loads a logger from its assembly, instantiates it, and handles errors. /// </summary> /// <returns>Instantiated logger.</returns> [RequiresUnreferencedCode("Loads and instantiates a logger type by reflecting over an assembly discovered at runtime, which is incompatible with trimming.")] private ILogger CreateLogger(bool forwardingLogger) { ILogger logger = null; try { if (forwardingLogger) { // load the logger from its assembly LoadedType loggerClass = TypeLoader.Create<IForwardingLogger>().Load(_loggerClassName, _loggerAssembly, logWarning: (format, args) => { }); if (loggerClass != null) { // instantiate the logger logger = (IForwardingLogger)Activator.CreateInstance(loggerClass.Type); } } else { // load the logger from its assembly LoadedType loggerClass = TypeLoader.Create<ILogger>().Load(_loggerClassName, _loggerAssembly, logWarning: (format, args) => { }); if (loggerClass != null) { // instantiate the logger logger = (ILogger)Activator.CreateInstance(loggerClass.Type); } } } catch (InvalidCastException e) { // The logger when trying to load has hit an invalid case, this is usually due to the framework assembly being a different version string message = ResourceUtilities.FormatResourceStringStripCodeAndKeyword("LoggerInstantiationFailureErrorInvalidCast", _loggerClassName, _loggerAssembly.AssemblyLocation, e.Message); throw new LoggerException(message, e.InnerException); } catch (TargetInvocationException e) when (e.InnerException is LoggerException le) { // At this point, the interesting stack is the internal exception; // the outer exception is System.Reflection stuff that says nothing // about the nature of the logger failure. // Logger failed politely during construction. In order to preserve // the stack trace at which the error occurred we wrap the original // exception instead of throwing. throw new LoggerException(le.Message, le, le.ErrorCode, le.HelpKeyword); } return logger; } /// <summary> /// Converts the path to the logger assembly to a full path /// </summary> internal void ConvertPathsToFullPaths() { if (_loggerAssembly.AssemblyFile != null) { _loggerAssembly = AssemblyLoadInfo.Create(_loggerAssembly.AssemblyName, Path.GetFullPath(_loggerAssembly.AssemblyFile)); } } #endregion #region Data private string _loggerClassName; private string _loggerSwitchParameters; private AssemblyLoadInfo _loggerAssembly; private LoggerVerbosity _verbosity; private int _loggerId; private bool _isOptional; #endregion #region CustomSerializationToStream internal void WriteToStream(BinaryWriter writer) { writer.WriteOptionalString(_loggerClassName); writer.WriteOptionalString(_loggerSwitchParameters); if (_loggerAssembly == null) { writer.Write((byte)0); } else { writer.Write((byte)1); writer.WriteOptionalString(_loggerAssembly.AssemblyFile); writer.WriteOptionalString(_loggerAssembly.AssemblyName); } writer.Write((Int32)_verbosity); writer.Write((Int32)_loggerId); } internal void CreateFromStream(BinaryReader reader) { _loggerClassName = reader.ReadByte() == 0 ? null : reader.ReadString(); _loggerSwitchParameters = reader.ReadByte() == 0 ? null : reader.ReadString(); if (reader.ReadByte() == 0) { _loggerAssembly = null; } else { string assemblyFile = reader.ReadByte() == 0 ? null : reader.ReadString(); string assemblyName = reader.ReadByte() == 0 ? null : reader.ReadString(); _loggerAssembly = AssemblyLoadInfo.Create(assemblyName, assemblyFile); } _verbosity = (LoggerVerbosity)reader.ReadInt32(); _loggerId = reader.ReadInt32(); } #endregion #region INodePacketTranslatable Members void ITranslatable.Translate(ITranslator translator) { translator.Translate(ref _loggerClassName); translator.Translate(ref _loggerSwitchParameters); translator.Translate(ref _loggerAssembly, AssemblyLoadInfo.FactoryForTranslation); translator.TranslateEnum(ref _verbosity, (int)_verbosity); translator.Translate(ref _loggerId); } internal static LoggerDescription FactoryForTranslation(ITranslator translator) { LoggerDescription description = new LoggerDescription(); ((ITranslatable)description).Translate(translator); return description; } #endregion } }