| File: BackEnd\Components\Logging\NodeLoggingContext.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 Microsoft.Build.Execution; using Microsoft.Build.Framework; using Microsoft.Build.TelemetryInfra; #nullable disable namespace Microsoft.Build.BackEnd.Logging { /// <summary> /// The logging context for an entire node. /// </summary> internal class NodeLoggingContext : BuildLoggingContext { /// <summary> /// Used to create the initial, base logging context for the node. /// </summary> /// <param name="loggingService">The logging service to use.</param> /// <param name="nodeId">The </param> /// <param name="inProcNode"><code>true</code> if this is an in-process node, otherwise <code>false</code>.</param> internal NodeLoggingContext(ILoggingService loggingService, int nodeId, bool inProcNode) : base(loggingService, new BuildEventContext(nodeId, BuildEventContext.InvalidTargetId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTaskId), inProcNode) { Assumed.NotEqual(nodeId, BuildEventContext.InvalidNodeId, "Should not ever be given an invalid NodeId"); // The in-proc node will have its BuildStarted, BuildFinished events sent by the BuildManager itself. if (!IsInProcNode) { LoggingService.LogBuildStarted(); } this.IsValid = true; } /// <summary> /// Per-<see cref="BuildRequestEngine"/> telemetry collector. /// Null when telemetry collection is disabled. /// </summary> internal ITelemetryCollector TelemetryCollector { get; set; } /// <summary> /// Log the completion of a build /// </summary> /// <param name="success">Did the build succeed or not</param> internal void LogBuildFinished(bool success) { Assumed.True(this.IsValid, "Build not started."); // The in-proc node will have its BuildStarted, BuildFinished events sent by the BuildManager itself. if (!IsInProcNode) { LoggingService.LogBuildFinished(success); } this.IsValid = false; } /// <summary> /// Log that a project has started if it has no parent (the first project) /// </summary> /// <param name="requestEntry">The build request entry for this project.</param> /// <returns>The BuildEventContext to use for this project.</returns> internal ProjectLoggingContext LogProjectStarted(BuildRequestEntry requestEntry) { (ProjectStartedEventArgs arg, ProjectLoggingContext ctx) = CreateProjectLoggingContext(requestEntry); LoggingService.LogProjectStarted(arg); return ctx; } internal (ProjectStartedEventArgs, ProjectLoggingContext) CreateProjectLoggingContext(BuildRequestEntry requestEntry) { Assumed.True(this.IsValid, "Build not started."); return ProjectLoggingContext.CreateLoggingContext(this, requestEntry); } /// <summary> /// Log that a project has started if it is serviced from the cache /// </summary> /// <param name="request">The build request.</param> /// <param name="configuration">The configuration used to build the request.</param> /// <returns>The BuildEventContext to use for this project.</returns> internal ProjectLoggingContext LogProjectStarted(BuildRequest request, BuildRequestConfiguration configuration) { Assumed.True(this.IsValid, "Build not started."); // Use the persisted ProjectEvaluationId which remains available even when the project is cached. int evaluationId = configuration?.ProjectEvaluationId ?? BuildEventContext.InvalidEvaluationId; return new ProjectLoggingContext(this, request, configuration.ProjectFullPath, configuration.ToolsVersion, evaluationId); } /// <summary> /// Logs the project started/finished pair for projects which are skipped entirely because all /// of their results are available in the cache. /// </summary> internal void LogRequestHandledFromCache(BuildRequest request, BuildRequestConfiguration configuration, BuildResult result) { ProjectLoggingContext projectLoggingContext = LogProjectStarted(request, configuration); // When pulling a request from the cache, we want to make sure we log a target skipped event for any targets which // were used to build the request including default and initial targets. foreach ((string name, TargetBuiltReason reason) target in configuration.GetTargetsUsedToBuildRequest(request)) { var targetResult = result[target.name]; bool isFailure = targetResult.ResultCode == TargetResultCode.Failure; var skippedTargetEventArgs = new TargetSkippedEventArgs(message: null) { BuildEventContext = projectLoggingContext.BuildEventContext, TargetName = target.name, BuildReason = target.reason, SkipReason = isFailure ? TargetSkipReason.PreviouslyBuiltUnsuccessfully : TargetSkipReason.PreviouslyBuiltSuccessfully, OriginallySucceeded = !isFailure, OriginalBuildEventContext = (targetResult as TargetResult)?.OriginalBuildEventContext }; projectLoggingContext.LogBuildEvent(skippedTargetEventArgs); if (targetResult.ResultCode == TargetResultCode.Failure) { break; } } projectLoggingContext.LogProjectFinished(result.OverallResult == BuildResultCode.Success); } } }