File: test\Libraries\Microsoft.Extensions.AI.Abstractions.Tests\CapturingLogger.cs
Web Access
Project: src\test\Libraries\Microsoft.Extensions.AI.Tests\Microsoft.Extensions.AI.Tests.csproj (Microsoft.Extensions.AI.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
 
#pragma warning disable SA1402 // File may only contain a single type
 
namespace Microsoft.Extensions.AI;
 
internal sealed class CapturingLogger : ILogger
{
    private readonly Stack<LoggerScope> _scopes = new();
    private readonly List<LogEntry> _entries = [];
    private readonly LogLevel _enabledLevel;
 
    public CapturingLogger(LogLevel enabledLevel = LogLevel.Trace)
    {
        _enabledLevel = enabledLevel;
    }
 
    public IReadOnlyList<LogEntry> Entries => _entries;
 
    public IDisposable? BeginScope<TState>(TState state)
        where TState : notnull
    {
        var scope = new LoggerScope(this);
        _scopes.Push(scope);
        return scope;
    }
 
    public bool IsEnabled(LogLevel logLevel) => logLevel >= _enabledLevel;
 
    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);
        lock (_entries)
        {
            _entries.Add(new LogEntry(logLevel, eventId, state, exception, message));
        }
    }
 
    private sealed class LoggerScope(CapturingLogger owner) : IDisposable
    {
        public void Dispose() => owner.EndScope(this);
    }
 
    private void EndScope(LoggerScope loggerScope)
    {
        if (_scopes.Peek() != loggerScope)
        {
            throw new InvalidOperationException("Logger scopes out of order");
        }
 
        _scopes.Pop();
    }
 
    public record LogEntry(LogLevel Level, EventId EventId, object? State, Exception? Exception, string Message);
}
 
internal sealed class CapturingLoggerProvider : ILoggerProvider
{
    public CapturingLogger Logger { get; } = new();
 
    public ILogger CreateLogger(string categoryName) => Logger;
 
    void IDisposable.Dispose()
    {
        // nop
    }
}