|
// 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 Microsoft.Build.BackEnd.Logging;
using Microsoft.Build.Framework;
using Microsoft.Build.Framework.Logging;
using Microsoft.Build.Framework.Telemetry;
using Microsoft.Build.Shared;
using BaseConsoleLogger = Microsoft.Build.BackEnd.Logging.BaseConsoleLogger;
using ParallelConsoleLogger = Microsoft.Build.BackEnd.Logging.ParallelConsoleLogger;
using SerialConsoleLogger = Microsoft.Build.BackEnd.Logging.SerialConsoleLogger;
#nullable disable
namespace Microsoft.Build.Logging
{
#region Delegates
/// <summary>
/// Delegate to use for writing a string to some location like
/// the console window or the IDE build window.
/// </summary>
/// <param name="message"></param>
public delegate void WriteHandler(string message);
/// <summary>
/// Type of delegate used to set console color.
/// </summary>
/// <param name="color">Text color</param>
public delegate void ColorSetter(ConsoleColor color);
/// <summary>
/// Type of delegate used to reset console color.
/// </summary>
public delegate void ColorResetter();
#endregion
/// <summary>
/// This class implements the default logger that outputs event data
/// to the console (stdout).
/// It is a facade: it creates, wraps and delegates to a kind of BaseConsoleLogger,
/// either SerialConsoleLogger or ParallelConsoleLogger.
/// </summary>
/// <remarks>This class is not thread safe.</remarks>
public class ConsoleLogger : INodeLogger
{
private BaseConsoleLogger _consoleLogger;
private int _numberOfProcessors = 1;
private LoggerVerbosity _verbosity;
private WriteHandler _write;
private ColorSetter _colorSet;
private ColorResetter _colorReset;
private string _parameters;
private bool _skipProjectStartedText = false;
private bool? _showSummary;
#region Constructors
/// <summary>
/// Default constructor.
/// </summary>
public ConsoleLogger()
: this(LoggerVerbosity.Normal)
{
// do nothing
}
/// <summary>
/// Create a logger instance with a specific verbosity. This logs to
/// the default console.
/// </summary>
/// <param name="verbosity">Verbosity level.</param>
public ConsoleLogger(LoggerVerbosity verbosity) :
this(verbosity, Console.Out.Write, BaseConsoleLogger.SetColor, BaseConsoleLogger.ResetColor)
{
// do nothing
}
/// <summary>
/// Initializes the logger, with alternate output handlers.
/// </summary>
/// <param name="verbosity"></param>
/// <param name="write"></param>
/// <param name="colorSet"></param>
/// <param name="colorReset"></param>
public ConsoleLogger(
LoggerVerbosity verbosity,
WriteHandler write,
ColorSetter colorSet,
ColorResetter colorReset)
{
_verbosity = verbosity;
_write = write;
_colorSet = colorSet;
_colorReset = colorReset;
}
/// <summary>
/// This is called by every event handler for compat reasons -- see DDB #136924
/// However it will skip after the first call
/// </summary>
private void InitializeBaseConsoleLogger()
{
if (_consoleLogger != null)
{
return;
}
bool useMPLogger = false;
bool disableConsoleColor = false;
bool forceConsoleColor = false;
bool preferConsoleColor = false;
if (!string.IsNullOrEmpty(_parameters))
{
string[] parameterComponents = _parameters.Split(LoggerParametersHelper.s_parameterDelimiters);
foreach (string param in parameterComponents)
{
if (param.Length <= 0)
{
continue;
}
if (string.Equals(param, "ENABLEMPLOGGING", StringComparison.OrdinalIgnoreCase))
{
useMPLogger = true;
}
if (string.Equals(param, "DISABLEMPLOGGING", StringComparison.OrdinalIgnoreCase))
{
useMPLogger = false;
}
if (string.Equals(param, "DISABLECONSOLECOLOR", StringComparison.OrdinalIgnoreCase))
{
disableConsoleColor = true;
}
if (string.Equals(param, "FORCECONSOLECOLOR", StringComparison.OrdinalIgnoreCase))
{
forceConsoleColor = true;
}
if (string.Equals(param, "PREFERCONSOLECOLOR", StringComparison.OrdinalIgnoreCase))
{
// Use ansi color codes if current target console do support it
preferConsoleColor = ConsoleConfiguration.AcceptAnsiColorCodes;
}
}
}
if (forceConsoleColor || (!disableConsoleColor && preferConsoleColor))
{
_colorSet = BaseConsoleLogger.SetColorAnsi;
_colorReset = BaseConsoleLogger.ResetColorAnsi;
}
else if (disableConsoleColor)
{
_colorSet = BaseConsoleLogger.DontSetColor;
_colorReset = BaseConsoleLogger.DontResetColor;
}
if (_numberOfProcessors == 1 && !useMPLogger)
{
_consoleLogger = new SerialConsoleLogger(_verbosity, _write, _colorSet, _colorReset);
if (this is FileLogger)
{
KnownTelemetry.LoggingConfigurationTelemetry.FileLoggerType = "serial";
}
else
{
KnownTelemetry.LoggingConfigurationTelemetry.ConsoleLoggerType = "serial";
}
}
else
{
_consoleLogger = new ParallelConsoleLogger(_verbosity, _write, _colorSet, _colorReset);
if (this is FileLogger)
{
KnownTelemetry.LoggingConfigurationTelemetry.FileLoggerType = "parallel";
}
else
{
KnownTelemetry.LoggingConfigurationTelemetry.ConsoleLoggerType = "parallel";
}
}
if (_showSummary != null)
{
_consoleLogger.ShowSummary = _showSummary;
}
if (!string.IsNullOrEmpty(_parameters))
{
_consoleLogger.Parameters = _parameters;
_parameters = null;
}
_consoleLogger.SkipProjectStartedText = _skipProjectStartedText;
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the level of detail to show in the event log.
/// </summary>
/// <value>Verbosity level.</value>
public LoggerVerbosity Verbosity
{
get
{
return _consoleLogger?.Verbosity ?? _verbosity;
}
set
{
if (_consoleLogger == null)
{
_verbosity = value;
}
else
{
_consoleLogger.Verbosity = value;
}
}
}
/// <summary>
/// A semi-colon delimited list of "key[=value]" parameter pairs.
/// </summary>
/// <value>null</value>
public string Parameters
{
get
{
return _consoleLogger == null ? _parameters : _consoleLogger.Parameters;
}
set
{
if (_consoleLogger == null)
{
_parameters = value;
}
else
{
_consoleLogger.Parameters = value;
}
}
}
/// <summary>
/// Suppresses the display of project headers. Project headers are
/// displayed by default unless this property is set.
/// </summary>
/// <remarks>This is only needed by the IDE logger.</remarks>
public bool SkipProjectStartedText
{
get
{
return _consoleLogger?.SkipProjectStartedText ?? _skipProjectStartedText;
}
set
{
if (_consoleLogger == null)
{
_skipProjectStartedText = value;
}
else
{
_consoleLogger.SkipProjectStartedText = value;
}
}
}
/// <summary>
/// Suppresses the display of error and warnings summary.
/// </summary>
public bool ShowSummary
{
get
{
if (_consoleLogger == null)
{
return _showSummary == true;
}
return _consoleLogger.ShowSummary == true;
}
set
{
if (_consoleLogger == null)
{
_showSummary = value;
}
else
{
_consoleLogger.ShowSummary = value;
}
}
}
/// <summary>
/// Provide access to the write hander delegate so that it can be redirected
/// if necessary (e.g. to a file)
/// </summary>
protected WriteHandler WriteHandler
{
get
{
return _consoleLogger == null ? _write : _consoleLogger.WriteHandler;
}
set
{
if (_consoleLogger == null)
{
_write = value;
}
else
{
_consoleLogger.WriteHandler = value;
}
}
}
#endregion
#region Methods
/// <summary>
/// Apply a parameter.
/// NOTE: This method was public by accident in Whidbey, so it cannot be made internal now. It has
/// no good reason for being public.
/// </summary>
public void ApplyParameter(string parameterName, string parameterValue)
{
ErrorUtilities.VerifyThrowInvalidOperation(_consoleLogger != null, "MustCallInitializeBeforeApplyParameter");
_consoleLogger.ApplyParameter(parameterName, parameterValue);
}
/// <summary>
/// Signs up the console logger for all build events.
/// </summary>
/// <param name="eventSource">Available events.</param>
public virtual void Initialize(IEventSource eventSource)
{
InitializeBaseConsoleLogger();
_consoleLogger.Initialize(eventSource);
}
/// <summary>
/// Initializes the logger.
/// </summary>
public virtual void Initialize(IEventSource eventSource, int nodeCount)
{
_numberOfProcessors = nodeCount;
InitializeBaseConsoleLogger();
_consoleLogger.Initialize(eventSource, nodeCount);
if (this is not FileLogger)
{
KnownTelemetry.LoggingConfigurationTelemetry.ConsoleLogger = true;
KnownTelemetry.LoggingConfigurationTelemetry.ConsoleLoggerVerbosity = Verbosity.ToString();
}
}
/// <summary>
/// The console logger does not need to release any resources.
/// This method does nothing.
/// </summary>
public virtual void Shutdown()
{
_consoleLogger?.Shutdown();
}
/// <summary>
/// Handler for build started events
/// </summary>
/// <param name="sender">sender (should be null)</param>
/// <param name="e">event arguments</param>
public void BuildStartedHandler(object sender, BuildStartedEventArgs e)
{
InitializeBaseConsoleLogger(); // for compat: see DDB#136924
_consoleLogger.BuildStartedHandler(sender, e);
}
/// <summary>
/// Handler for build finished events
/// </summary>
/// <param name="sender">sender (should be null)</param>
/// <param name="e">event arguments</param>
public void BuildFinishedHandler(object sender, BuildFinishedEventArgs e)
{
InitializeBaseConsoleLogger(); // for compat: see DDB#136924
_consoleLogger.BuildFinishedHandler(sender, e);
}
/// <summary>
/// Handler for project started events
/// </summary>
/// <param name="sender">sender (should be null)</param>
/// <param name="e">event arguments</param>
public void ProjectStartedHandler(object sender, ProjectStartedEventArgs e)
{
InitializeBaseConsoleLogger(); // for compat: see DDB#136924
_consoleLogger.ProjectStartedHandler(sender, e);
}
/// <summary>
/// Handler for project finished events
/// </summary>
/// <param name="sender">sender (should be null)</param>
/// <param name="e">event arguments</param>
public void ProjectFinishedHandler(object sender, ProjectFinishedEventArgs e)
{
InitializeBaseConsoleLogger(); // for compat: see DDB#136924
_consoleLogger.ProjectFinishedHandler(sender, e);
}
/// <summary>
/// Handler for target started events
/// </summary>
/// <param name="sender">sender (should be null)</param>
/// <param name="e">event arguments</param>
public void TargetStartedHandler(object sender, TargetStartedEventArgs e)
{
InitializeBaseConsoleLogger(); // for compat: see DDB#136924
_consoleLogger.TargetStartedHandler(sender, e);
}
/// <summary>
/// Handler for target finished events
/// </summary>
/// <param name="sender">sender (should be null)</param>
/// <param name="e">event arguments</param>
public void TargetFinishedHandler(object sender, TargetFinishedEventArgs e)
{
InitializeBaseConsoleLogger(); // for compat: see DDB#136924
_consoleLogger.TargetFinishedHandler(sender, e);
}
/// <summary>
/// Handler for task started events
/// </summary>
/// <param name="sender">sender (should be null)</param>
/// <param name="e">event arguments</param>
public void TaskStartedHandler(object sender, TaskStartedEventArgs e)
{
InitializeBaseConsoleLogger(); // for compat: see DDB#136924
_consoleLogger.TaskStartedHandler(sender, e);
}
/// <summary>
/// Handler for task finished events
/// </summary>
/// <param name="sender">sender (should be null)</param>
/// <param name="e">event arguments</param>
public void TaskFinishedHandler(object sender, TaskFinishedEventArgs e)
{
InitializeBaseConsoleLogger(); // for compat: see DDB#136924
_consoleLogger.TaskFinishedHandler(sender, e);
}
/// <summary>
/// Prints an error event
/// </summary>
public void ErrorHandler(object sender, BuildErrorEventArgs e)
{
InitializeBaseConsoleLogger(); // for compat: see DDB#136924
_consoleLogger.ErrorHandler(sender, e);
}
/// <summary>
/// Prints a warning event
/// </summary>
public void WarningHandler(object sender, BuildWarningEventArgs e)
{
InitializeBaseConsoleLogger(); // for compat: see DDB#136924
_consoleLogger.WarningHandler(sender, e);
}
/// <summary>
/// Prints a message event
/// </summary>
public void MessageHandler(object sender, BuildMessageEventArgs e)
{
InitializeBaseConsoleLogger(); // for compat: see DDB#136924
_consoleLogger.MessageHandler(sender, e);
}
/// <summary>
/// Prints a custom event
/// </summary>
public void CustomEventHandler(object sender, CustomBuildEventArgs e)
{
InitializeBaseConsoleLogger(); // for compat: see DDB#136924
_consoleLogger.CustomEventHandler(sender, e);
}
/// <summary>
/// Returns the minimum importance of messages logged by this logger.
/// </summary>
/// <returns>
/// The minimum message importance corresponding to this logger's verbosity or (MessageImportance.High - 1)
/// if this logger does not log messages of any importance.
/// </returns>
internal MessageImportance GetMinimumMessageImportance()
{
if (Verbosity >= BaseConsoleLogger.ImportanceToMinimumVerbosity(MessageImportance.Low, out _))
{
return MessageImportance.Low;
}
else if (Verbosity >= BaseConsoleLogger.ImportanceToMinimumVerbosity(MessageImportance.Normal, out _))
{
return MessageImportance.Normal;
}
else if (Verbosity >= BaseConsoleLogger.ImportanceToMinimumVerbosity(MessageImportance.High, out _))
{
return MessageImportance.High;
}
// The logger does not log messages of any importance.
return MessageImportance.High - 1;
}
#endregion
}
}
|