Skip to content

Adding a static factory for the TerminalLogger #11318

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
a8e7cc0
Adds a static factory that can create a TerminalLogger or ConsoleLogg…
MichalPavlik Nov 21, 2024
f0cbec2
Disable TL progress ctrl codes when verbosity is 'quiet'
MichalPavlik Jan 20, 2025
1685284
Added --tl:<val> arg support
MichalPavlik Jan 21, 2025
db35e8a
Removing debugging code
MichalPavlik Jan 21, 2025
d38dd70
Merge branch 'main' into dev/mipavlik/tl-static-factory
MichalPavlik Jan 21, 2025
8dbfeb8
Merge branch 'main' into dev/mipavlik/tl-static-factory
MichalPavlik Feb 5, 2025
d040fd9
Fixed failing tests
MichalPavlik Feb 5, 2025
94a1378
The factory method is now public
MichalPavlik Feb 5, 2025
01607a8
Unnecessary usings were removed
MichalPavlik Feb 5, 2025
9eb5464
Moving the TerminalLogger back due to issues with shared sources. Add…
MichalPavlik Feb 7, 2025
d2dbfce
Updated parameter doc
MichalPavlik Feb 7, 2025
2defbfe
Addded support for more args prefixes and returned --tl:on option
MichalPavlik Feb 7, 2025
eb0ead3
Moving Terminal Logger once again, but making it public now. All ctor…
MichalPavlik Feb 19, 2025
8dbee80
Fixed tests
MichalPavlik Feb 20, 2025
6d014a6
Merge branch 'main' into dev/mipavlik/tl-static-factory
MichalPavlik Feb 20, 2025
74a4d23
Updated XLFs
MichalPavlik Feb 20, 2025
018d490
Fixed TL activation
MichalPavlik Feb 20, 2025
ab08182
Changing namespace to align with other public loggers. Some types wer…
MichalPavlik Feb 20, 2025
3ac2ee0
Added verbosity parsing capability
MichalPavlik Feb 20, 2025
74cf0c3
Merge branch 'main' into dev/mipavlik/tl-static-factory
MichalPavlik Feb 21, 2025
f7cbe77
Factory now recognizes the "false" value
MichalPavlik Feb 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/Build.UnitTests/ConsoleLogger_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,31 @@ public ConsoleLoggerTest(ITestOutputHelper output)
_output = output;
}

[Theory]
[InlineData(null, false, false, "", nameof(ConsoleLogger))]
[InlineData(null, true, false, "", nameof(ConsoleLogger))]
[InlineData(null, false, true, "", nameof(ConsoleLogger))]
[InlineData(null, true, true, "off", nameof(ConsoleLogger))]
[InlineData("--tl:off", true, true, "", nameof(ConsoleLogger))]
[InlineData(null, true, true, "", "TerminalLogger")]
public void CreateTerminalOrConsoleLogger_CreatesCorrectLoggerInstance(string argsString, bool supportsAnsi, bool outputIsScreen, string evnVariableValue, string expectedLoggerName)
{
string originalValue = Environment.GetEnvironmentVariable("MSBUILDTERMINALLOGGER");
Environment.SetEnvironmentVariable("MSBUILDTERMINALLOGGER", evnVariableValue);

try
{
string[] args = argsString?.Split(' ');
ILogger logger = ConsoleLogger.CreateTerminalOrConsoleLogger(default, args, supportsAnsi, outputIsScreen, default);

logger.ShouldNotBeNull();
logger.GetType().Name.ShouldBe(expectedLoggerName);
}
finally
{
Environment.SetEnvironmentVariable("MSBUILDTERMINALLOGGER", originalValue);
}
}

/// <summary>
/// Verify when the project has not been named that we correctly get the same placeholder
Expand Down
38 changes: 38 additions & 0 deletions src/Build/Logging/ConsoleLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Microsoft.Build.BackEnd.Logging;
using Microsoft.Build.Framework;
using Microsoft.Build.Framework.Logging;
Expand Down Expand Up @@ -328,6 +332,40 @@ protected WriteHandler WriteHandler

#region Methods

/// <summary>
/// Creates a Terminal logger if possible, or a Console logger.
/// </summary>
/// <param name="verbosity">Level of detail to show in the log.</param>
/// <param name="args">Command line arguments for the logger configuration. Currently, only '--tl:off' is supported to disable TerminalLogger.</param>
public static ILogger CreateTerminalOrConsoleLogger(LoggerVerbosity verbosity, string[] args)
{
(bool supportsAnsi, bool outputIsScreen, uint? originalConsoleMode) = NativeMethodsShared.QueryIsScreenAndTryEnableAnsiColorCodes();

return CreateTerminalOrConsoleLogger(verbosity, args, supportsAnsi, outputIsScreen, originalConsoleMode);
}

internal static ILogger CreateTerminalOrConsoleLogger(LoggerVerbosity verbosity, string[] args, bool supportsAnsi, bool outputIsScreen, uint? originalConsoleMode)
{
string tlArg = args?.LastOrDefault(a => a.StartsWith("--tl:", StringComparison.InvariantCultureIgnoreCase)) ?? string.Empty;

bool isDisabled =
tlArg.Equals("--tl:off", StringComparison.InvariantCultureIgnoreCase) ||
(Environment.GetEnvironmentVariable("MSBUILDTERMINALLOGGER") ?? string.Empty).Equals("off", StringComparison.InvariantCultureIgnoreCase);

if (isDisabled || !supportsAnsi || !outputIsScreen)
{
NativeMethodsShared.RestoreConsoleMode(originalConsoleMode);
return new ConsoleLogger(verbosity);
}

// TODO: Move TerminalLogger to this project, use InternalsVisibleTo attribute and resolve type conflicts errors caused by shared files.
// This logic is tested to ensure that the TerminalLogger is available in the MSBuild assembly and we can create an instance of it.
Type tlType = Assembly.Load("MSBuild").GetType("Microsoft.Build.Logging.TerminalLogger.TerminalLogger");
ILogger terminalLogger = Activator.CreateInstance(tlType, BindingFlags.Instance | BindingFlags.NonPublic, null, [verbosity, originalConsoleMode], null) as ILogger;

return terminalLogger;
}

/// <summary>
/// Apply a parameter.
/// NOTE: This method was public by accident in Whidbey, so it cannot be made internal now. It has
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
]9;4;3;\directory/file(1,2,3,4): warning AA0000: Warning!
directory/file(1,2,3,4): warning AA0000: Warning!
directory/file(1,2,3,4): warning AA0000:
A
Multi
Line
Warning!
directory/file(1,2,3,4): error AA0000: Error!
]9;4;0;\
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
]9;4;3;\directory/file(1,2,3,4): warning AA0000: Warning!
directory/file(1,2,3,4): warning AA0000: Warning!
directory/file(1,2,3,4): warning AA0000:
A
Multi
Line
Warning!
directory/file(1,2,3,4): error AA0000: Error!
]9;4;0;\
Original file line number Diff line number Diff line change
@@ -1 +1 @@
]9;4;3;\]9;4;0;\
emptyString
Original file line number Diff line number Diff line change
@@ -1 +1 @@
]9;4;3;\]9;4;0;\
emptyString
12 changes: 11 additions & 1 deletion src/MSBuild/MSBuild.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@
<Compile Include="CommandLineSwitchException.cs" />
<Compile Include="..\Shared\CoreCLRAssemblyLoader.cs" Condition="'$(TargetFrameworkIdentifier)' != '.NETFramework'" />
<Compile Include="DistributedLoggerRecord.cs" />
<Compile Include="TerminalLogger\*.cs" />
<Compile Include="InitializationException.cs" />
<Compile Include="MSBuildClientApp.cs" />
<Compile Include="NodeEndpointOutOfProcTaskHost.cs" />
Expand All @@ -147,6 +146,17 @@
<Compile Include="OutOfProcTaskAppDomainWrapper.cs" />
<Compile Include="PerformanceLogEventListener.cs" />
<Compile Include="JsonOutputFormatter.cs" />
<Compile Include="TerminalLogger\BuildMessage.cs" />
<Compile Include="TerminalLogger\ITerminal.cs" />
<Compile Include="TerminalLogger\MessageSeverity.cs" />
<Compile Include="TerminalLogger\NodesFrame.cs" />
<Compile Include="TerminalLogger\NodeStatus.cs" />
<Compile Include="TerminalLogger\Project.cs" />
<Compile Include="TerminalLogger\StopwatchAbstraction.cs" />
<Compile Include="TerminalLogger\SystemStopwatch.cs" />
<Compile Include="TerminalLogger\Terminal.cs" />
<Compile Include="TerminalLogger\TerminalLogger.cs" />
<Compile Include="TerminalLogger\TestSummary.cs" />
<Compile Include="XMake.cs" />
<!-- This is to enable CodeMarkers in MSBuild.exe -->
<!-- Win32 RC Files -->
Expand Down
4 changes: 3 additions & 1 deletion src/MSBuild/TerminalLogger/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ internal sealed class Project
/// <summary>
/// Initialized a new <see cref="Project"/> with the given <paramref name="targetFramework"/>.
/// </summary>
/// <param name="projectFile">The full path to the project file.</param>
/// <param name="targetFramework">The target framework of the project or null if not multi-targeting.</param>
/// <param name="stopwatch">A stopwatch to time the build of the project.</param>
public Project(string projectFile, string? targetFramework, StopwatchAbstraction? stopwatch)
{
File = projectFile;
Expand Down Expand Up @@ -58,7 +60,7 @@ public Project(string projectFile, string? targetFramework, StopwatchAbstraction
public bool IsTestProject { get; set; }

/// <summary>
/// True when the project has run target with name "_CachePluginRunStart" defined in <see cref="TerminalLogger._cachePluginStartTarget"/>.
/// True when the project has run target with name "_CachePluginRunStart".
/// </summary>
public bool IsCachePluginProject { get; set; }

Expand Down
20 changes: 14 additions & 6 deletions src/MSBuild/TerminalLogger/TerminalLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,15 @@ internal TerminalLogger(ITerminal terminal)
_manualRefresh = true;
}

/// <summary>
/// Private constructor invoked by static factory.
/// </summary>
internal TerminalLogger(LoggerVerbosity verbosity, uint? originalConsoleMode) : this()
{
Verbosity = verbosity;
_originalConsoleMode = originalConsoleMode;
}

#region INodeLogger implementation

/// <inheritdoc/>
Expand All @@ -270,8 +279,6 @@ public void Initialize(IEventSource eventSource, int nodeCount)
/// <inheritdoc/>
public void Initialize(IEventSource eventSource)
{
(_, _, _originalConsoleMode) = NativeMethodsShared.QueryIsScreenAndTryEnableAnsiColorCodes();

ParseParameters();

eventSource.BuildStarted += BuildStarted;
Expand Down Expand Up @@ -396,7 +403,7 @@ private void BuildStarted(object sender, BuildStartedEventArgs e)

_buildStartTime = e.Timestamp;

if (Terminal.SupportsProgressReporting)
if (Terminal.SupportsProgressReporting && Verbosity != LoggerVerbosity.Quiet)
{
Terminal.Write(AnsiCodes.SetProgressIndeterminate);
}
Expand Down Expand Up @@ -465,7 +472,7 @@ private void BuildFinished(object sender, BuildFinishedEventArgs e)
}
finally
{
if (Terminal.SupportsProgressReporting)
if (Terminal.SupportsProgressReporting && Verbosity != LoggerVerbosity.Quiet)
{
Terminal.Write(AnsiCodes.RemoveProgress);
}
Expand Down Expand Up @@ -1077,8 +1084,9 @@ private void EraseNodes()
/// Construct a build result summary string.
/// </summary>
/// <param name="succeeded">True if the build completed with success.</param>
/// <param name="hasError">True if the build has logged at least one error.</param>
/// <param name="hasWarning">True if the build has logged at least one warning.</param>
/// <param name="countErrors">The number of errors encountered during the build.</param>
/// <param name="countWarnings">The number of warnings encountered during the build.</param>
/// <returns>A string representing the build result summary.</returns>
private static string GetBuildResultString(bool succeeded, int countErrors, int countWarnings)
{
if (!succeeded)
Expand Down
1 change: 0 additions & 1 deletion src/MSBuild/XMake.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4019,7 +4019,6 @@ private static void ProcessTerminalLogger(bool noConsoleLogger,
{
if (!noConsoleLogger)
{
// A central logger will be created for both single proc and multiproc.
TerminalLogger logger = new TerminalLogger(verbosity)
{
Parameters = aggregatedLoggerParameters
Expand Down