File: Interaction\SpectreConsoleLoggerProvider.cs
Web Access
Project: src\src\Aspire.Cli\Aspire.Cli.csproj (aspire)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Globalization;
using Aspire.Cli.Diagnostics;
using Microsoft.Extensions.Logging;
 
namespace Aspire.Cli.Interaction;
 
internal class SpectreConsoleLoggerProvider : ILoggerProvider
{
    private readonly TextWriter _output;
    private readonly ConsoleLogBufferContext _bufferContext;
 
    /// <summary>
    /// Creates a logger provider that writes to the specified output.
    /// </summary>
    /// <param name="output">The text writer to write log messages to.</param>
    /// <param name="bufferContext">Shared buffer context for pausing logs during interactive prompts.</param>
    public SpectreConsoleLoggerProvider(TextWriter output, ConsoleLogBufferContext bufferContext)
    {
        _output = output;
        _bufferContext = bufferContext;
    }
 
    public ILogger CreateLogger(string categoryName)
    {
        return new SpectreConsoleLogger(_output, categoryName, _bufferContext);
    }
 
    public void Dispose()
    {
    }
}
 
internal class SpectreConsoleLogger(TextWriter output, string categoryName, ConsoleLogBufferContext bufferContext) : ILogger
{
    public bool IsEnabled(LogLevel logLevel) =>
        logLevel >= LogLevel.Debug &&
        (categoryName.StartsWith("Aspire.Cli", StringComparison.Ordinal) || logLevel >= LogLevel.Warning);
 
    public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
 
    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
    {
        if (!IsEnabled(logLevel))
        {
            return;
        }
 
        var message = formatter(state, exception);
        if (string.IsNullOrEmpty(message) && exception is null)
        {
            return;
        }
 
        var formattedMessage = exception is not null ? $"{message} {exception}" : message;
 
        // Extract the last token from the category name to reduce noise
        var shortCategoryName = CliLogFormat.GetShortCategoryName(categoryName);
 
        // Format timestamp to show only time (HH:mm:ss) for debugging purposes
        var timestamp = DateTime.Now.ToString("HH:mm:ss", CultureInfo.InvariantCulture);
 
        var logMessage = $"[{timestamp}] [{GetLogLevelString(logLevel)}] {shortCategoryName}: {formattedMessage}";
 
        // Write to the configured output (stderr by default)
        bufferContext.WriteOrBuffer(output, logMessage);
    }
 
    private static string GetLogLevelString(LogLevel logLevel) => CliLogFormat.GetConsoleLevelToken(logLevel);
}