// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.Extensions.Logging;
using Microsoft.Testing.Extensions.Logging;
using Microsoft.Testing.Platform.Builder;
using MelILoggerFactory = Microsoft.Extensions.Logging.ILoggerFactory;
using MelLoggerFactory = Microsoft.Extensions.Logging.LoggerFactory;
namespace Microsoft.Testing.Extensions;
///
/// Extension methods on for forwarding the
/// Microsoft Testing Platform diagnostic logs to Microsoft.Extensions.Logging providers.
///
///
/// This API is experimental. It may change, break, or be removed at any time without notice.
///
[Experimental("TPEXP", UrlFormat = "https://aka.ms/testingplatform/diagnostics#{0}")]
public static class MicrosoftExtensionsLoggingBuilderExtensions
{
///
/// Forwards every diagnostic log message produced by Microsoft Testing Platform and its
/// extensions to Microsoft.Extensions.Logging providers configured by the supplied delegate.
///
///
///
/// A new is created internally for each call and disposed when the
/// test application terminates. The minimum level is initialized from the platform's effective
/// diagnostic level so log volume is bounded by the platform configuration; per-category filters in
/// the can only narrow this set, never widen it.
///
///
/// When the platform's effective level is
/// (the default when --diagnostic is not enabled), the supplied
/// delegate is not invoked and no is created, so expensive sinks
/// (network, file, gRPC) are not initialized for runs that will never emit a log.
///
///
/// Microsoft Testing Platform's has no notion
/// of ; messages forwarded through this bridge are
/// logged with EventId.None.
///
///
/// Microsoft Testing Platform does not create or translate
/// scopes. Any ambient
/// scopes observed by a configured provider are owned entirely by the caller's logging pipeline.
/// The original state and formatter are forwarded unchanged, so structured state already present
/// in a platform log entry remains available to providers.
///
///
/// The bridge is process-local. A logger factory instance cannot be propagated to a separately
/// launched test host or controller process; each process that needs the bridge must register it.
///
///
/// This extension is additive: it does not replace the platform's built-in --diagnostic
/// file logger. When --diagnostic is enabled, messages are written to both the diagnostic
/// file and the configured Microsoft.Extensions.Logging providers.
///
///
/// The test application builder.
/// A delegate that configures the .
/// The same for chaining.
public static ITestApplicationBuilder AddMicrosoftExtensionsLogging(
this ITestApplicationBuilder builder,
Action configure)
{
_ = builder ?? throw new ArgumentNullException(nameof(builder));
_ = configure ?? throw new ArgumentNullException(nameof(configure));
builder.Logging.AddProvider((mtpLevel, _) =>
{
if (mtpLevel == Microsoft.Testing.Platform.Logging.LogLevel.None)
{
// No log message will ever flow; avoid constructing the user's logging pipeline.
return NopLoggerProvider.Instance;
}
MelILoggerFactory factory = MelLoggerFactory.Create(loggingBuilder =>
{
loggingBuilder.SetMinimumLevel(LogLevelMapper.ToMicrosoftExtensions(mtpLevel));
configure(loggingBuilder);
});
return new MicrosoftExtensionsLoggingProvider(factory, ownsFactory: true);
});
return builder;
}
///
/// Forwards every diagnostic log message produced by Microsoft Testing Platform and its
/// extensions to a caller-owned .
///
///
///
/// The factory's lifetime is owned by the caller; the bridge will not dispose it.
/// This overload is intended for scenarios where the same
/// instance is shared with other components, such as the system under test.
///
///
/// When the platform's effective level is ,
/// the bridge becomes a no-op and no calls are made on .
///
///
/// The bridge is process-local. The supplied factory remains in the process where this method is
/// called and is not propagated to separately launched test host or controller processes.
///
///
/// The test application builder.
/// A that the platform should forward logs to.
/// The same for chaining.
public static ITestApplicationBuilder AddMicrosoftExtensionsLogging(
this ITestApplicationBuilder builder,
MelILoggerFactory loggerFactory)
{
_ = builder ?? throw new ArgumentNullException(nameof(builder));
_ = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
builder.Logging.AddProvider((mtpLevel, _) => mtpLevel == Microsoft.Testing.Platform.Logging.LogLevel.None
? NopLoggerProvider.Instance
: new MicrosoftExtensionsLoggingProvider(loggerFactory, ownsFactory: false));
return builder;
}
}