|
// 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;
using Microsoft.Build.Exceptions;
using Microsoft.Build.Experimental.BuildCheck;
using Microsoft.Build.Experimental.BuildCheck.Infrastructure;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
namespace Microsoft.Build.BackEnd.Logging
{
/// <summary>
/// This object encapsulates the logging service plus the current BuildEventContext and
/// hides the requirement to pass BuildEventContexts to the logging service or query the
/// host for the logging service all of the time.
/// </summary>
internal class LoggingContext : IBuildEngineDataConsumer
{
/// <summary>
/// The logging service to which this context is attached
/// </summary>
private readonly ILoggingService _loggingService;
/// <summary>
/// The build event context understood by the logging service.
/// </summary>
private BuildEventContext _eventContext;
/// <summary>
/// True if this context is still valid (i.e. hasn't been "finished")
/// </summary>
private bool _isValid;
protected bool _hasLoggedErrors;
/// <summary>
/// Constructs the logging context from a logging service and an event context.
/// </summary>
/// <param name="loggingService">The logging service to use</param>
/// <param name="eventContext">The event context</param>
public LoggingContext(ILoggingService loggingService, BuildEventContext eventContext)
{
ErrorUtilities.VerifyThrowArgumentNull(loggingService);
ErrorUtilities.VerifyThrowArgumentNull(eventContext);
_loggingService = loggingService;
_eventContext = eventContext;
_isValid = false;
_hasLoggedErrors = false;
}
/// <summary>
/// Constructs a logging context from another logging context. This is used primarily in
/// the constructors for other logging contexts to populate the logging service parameter,
/// while the event context will come from a call into the logging service itself.
/// </summary>
/// <param name="baseContext">The context from which this context is being created.</param>
/// <param name="newEventContext">The new logging context to be associated here.</param>
public LoggingContext(LoggingContext baseContext, BuildEventContext newEventContext)
{
_loggingService = baseContext._loggingService;
_eventContext = newEventContext;
_isValid = baseContext._isValid;
}
/// <summary>
/// Consumer of the execution information from the build engine.
/// </summary>
internal IBuildEngineDataConsumer BuildEngineDataConsumer => this;
/// <summary>
/// Retrieves the logging service
/// </summary>
public ILoggingService LoggingService
{
[DebuggerStepThrough]
get
{ return _loggingService; }
}
/// <summary>
/// Retrieves the build event context
/// UNDONE: (Refactor) We eventually want to remove this because all logging should go
/// through a context object. This exists only so we can make certain
/// logging calls in code which has not yet been fully refactored.
/// </summary>
public BuildEventContext BuildEventContext
{
[DebuggerStepThrough]
get
{
return _eventContext;
}
}
/// <summary>
/// Returns true if the context is still valid, false if the
/// appropriate 'Finished' call has been invoked.
/// </summary>
public bool IsValid
{
[DebuggerStepThrough]
get
{
return _isValid;
}
[DebuggerStepThrough]
protected set
{
_isValid = value;
}
}
internal bool HasLoggedErrors { get { return _hasLoggedErrors; } set { _hasLoggedErrors = value; } }
/// <summary>
/// Helper method to create a message build event from a string resource and some parameters
/// </summary>
/// <param name="importance">Importance level of the message</param>
/// <param name="messageResourceName">string within the resource which indicates the format string to use</param>
/// <param name="messageArgs">string resource arguments</param>
internal void LogComment(MessageImportance importance, string messageResourceName, params object?[]? messageArgs)
{
CheckValidity();
_loggingService.LogComment(_eventContext, importance, messageResourceName, messageArgs);
}
/// <summary>
/// Helper method to create a message build event from a string resource and some parameters
/// </summary>
/// <param name="importance">Importance level of the message</param>
/// <param name="file">The file in which the event occurred</param>
/// <param name="messageResourceName">string within the resource which indicates the format string to use</param>
/// <param name="messageArgs">string resource arguments</param>
internal void LogComment(MessageImportance importance, BuildEventFileInfo file, string messageResourceName, params object?[]? messageArgs)
{
CheckValidity();
_loggingService.LogBuildEvent(new BuildMessageEventArgs(
null,
null,
file.File,
file.Line,
file.Column,
file.EndLine,
file.EndColumn,
ResourceUtilities.GetResourceString(messageResourceName),
helpKeyword: null,
senderName: "MSBuild",
importance,
DateTime.UtcNow,
messageArgs)
{
BuildEventContext = _eventContext
});
}
/// <summary>
/// Helper method to create a message build event from a string
/// </summary>
/// <param name="importance">Importance level of the message</param>
/// <param name="message">message to log</param>
internal void LogCommentFromText(MessageImportance importance, string message)
{
CheckValidity();
_loggingService.LogCommentFromText(_eventContext, importance, message);
}
/// <summary>
/// Helper method to create a message build event from a string
/// </summary>
/// <param name="importance">Importance level of the message</param>
/// <param name="message">Message to log</param>
/// <param name="messageArgs">Format string arguments</param>
internal void LogCommentFromText(MessageImportance importance, string message, params object[] messageArgs)
{
CheckValidity();
_loggingService.LogCommentFromText(_eventContext, importance, message, messageArgs);
}
/// <summary>
/// Log an error
/// </summary>
/// <param name="file">The file in which the error occurred</param>
/// <param name="messageResourceName">The resource name for the error</param>
/// <param name="messageArgs">Parameters for the resource string</param>
internal void LogError(BuildEventFileInfo file, string messageResourceName, params object[] messageArgs)
{
CheckValidity();
_loggingService.LogError(_eventContext, file, messageResourceName, messageArgs);
_hasLoggedErrors = true;
}
/// <summary>
/// Log an error
/// </summary>
/// <param name="subcategoryResourceName">The resource name which indicates the subCategory</param>
/// <param name="file">The file in which the error occurred</param>
/// <param name="messageResourceName">The resource name for the error</param>
/// <param name="messageArgs">Parameters for the resource string</param>
internal void LogErrorWithSubcategory(string? subcategoryResourceName, BuildEventFileInfo file, string messageResourceName, params object[] messageArgs)
{
CheckValidity();
_loggingService.LogError(_eventContext, subcategoryResourceName, file, messageResourceName, messageArgs);
_hasLoggedErrors = true;
}
/// <summary>
/// Log an error
/// </summary>
/// <param name="subcategoryResourceName">The resource name which indicates the subCategory</param>
/// <param name="errorCode"> Error code</param>
/// <param name="helpKeyword">Help keyword</param>
/// <param name="file">The file in which the error occurred</param>
/// <param name="message">Error message</param>
internal void LogErrorFromText(string? subcategoryResourceName, string? errorCode, string? helpKeyword, BuildEventFileInfo file, string message)
{
CheckValidity();
_loggingService.LogErrorFromText(_eventContext, subcategoryResourceName, errorCode, helpKeyword, file, message);
_hasLoggedErrors = true;
}
/// <summary>
/// Log an invalid project file exception
/// </summary>
/// <param name="invalidProjectFileException">The invalid Project File Exception which is to be logged</param>
internal void LogInvalidProjectFileError(InvalidProjectFileException invalidProjectFileException)
{
CheckValidity();
_loggingService.LogInvalidProjectFileError(_eventContext, invalidProjectFileException);
_hasLoggedErrors = true;
}
/// <summary>
/// Log an error based on an exception
/// </summary>
/// <param name="exception">The exception wich is to be logged</param>
/// <param name="file">The file in which the error occurred</param>
/// <param name="messageResourceName">The string resource which has the formatting string for the error</param>
/// <param name="messageArgs">The arguments for the error message</param>
internal void LogFatalError(Exception exception, BuildEventFileInfo file, string messageResourceName, params object?[]? messageArgs)
{
CheckValidity();
_loggingService.LogFatalError(_eventContext, exception, file, messageResourceName, messageArgs);
_hasLoggedErrors = true;
}
internal void LogWarning(string messageResourceName, params object[] messageArgs)
{
CheckValidity();
_loggingService.LogWarning(_eventContext, null, BuildEventFileInfo.Empty, messageResourceName, messageArgs);
}
/// <summary>
/// Log a warning
/// </summary>
/// <param name="subcategoryResourceName">The subcategory resource name</param>
/// <param name="file">The file in which the warning occurred</param>
/// <param name="messageResourceName">The string resource which contains the formatted warning string</param>
/// <param name="messageArgs">parameters for the string resource</param>
internal void LogWarning(string? subcategoryResourceName, BuildEventFileInfo file, string messageResourceName, params object?[]? messageArgs)
{
CheckValidity();
_loggingService.LogWarning(_eventContext, subcategoryResourceName, file, messageResourceName, messageArgs);
}
/// <summary>
/// Log a warning based on a text message
/// </summary>
/// <param name="subcategoryResourceName">The subcategory resource name</param>
/// <param name="warningCode"> Warning code</param>
/// <param name="helpKeyword"> Help keyword</param>
/// <param name="file">The file in which the warning occurred</param>
/// <param name="message">The message to be logged as a warning</param>
internal void LogWarningFromText(string? subcategoryResourceName, string warningCode, string helpKeyword, BuildEventFileInfo file, string message)
{
CheckValidity();
_loggingService.LogWarningFromText(_eventContext, subcategoryResourceName, warningCode, helpKeyword, file, message);
}
/// <summary>
/// Will Log a build Event. Will also take into account OnlyLogCriticalEvents when determining if to drop the event or to log it.
/// </summary>
/// <param name="buildEvent">The event to log</param>
internal void LogBuildEvent(BuildEventArgs buildEvent)
{
CheckValidity();
LoggingService.LogBuildEvent(buildEvent);
}
/// <summary>
/// Log an error based on an exception
/// </summary>
/// <param name="exception">The exception to be logged</param>
/// <param name="file">The file in which the error occurred</param>
internal void LogFatalBuildError(Exception exception, BuildEventFileInfo file)
{
CheckValidity();
LoggingService.LogFatalBuildError(BuildEventContext, exception, file);
_hasLoggedErrors = true;
}
/// <summary>
/// Logs a file to be included in the binary logger
/// </summary>
/// <param name="filePath">Path to response file</param>
internal void LogIncludeFile(string filePath)
{
CheckValidity();
_loggingService.LogIncludeFile(BuildEventContext, filePath);
}
public void ProcessPropertyRead(PropertyReadInfo propertyReadInfo)
=> _loggingService.BuildEngineDataRouter.ProcessPropertyRead(
propertyReadInfo,
new CheckLoggingContext(_loggingService, BuildEventContext));
public void ProcessPropertyWrite(PropertyWriteInfo propertyWriteInfo)
=> _loggingService.BuildEngineDataRouter.ProcessPropertyWrite(
propertyWriteInfo,
new CheckLoggingContext(_loggingService, BuildEventContext));
private protected void CheckValidity()
{
if (!_isValid)
{
ErrorUtilities.ThrowInternalError("LoggingContext (type: {0}) was not valid during logging attempt.",
this.GetType());
}
}
}
}
|