// 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; } }