feat: внедрение RazorConsole TUI с runtime-управлением MCP-инструментами
- Добавлен RazorConsole.Core для интерактивного TUI-дашборда - ToolRegistryService: живое включение/отключение модулей и отдельных методов - InMemoryLogSink: кольцевой буфер логов с фильтрацией по модулю - TUI: 3 таба (Overview, Logs, Settings) - IToolModule: generic-интерфейс для легкого добавления новых MCP-модулей - Guard-проверка TryCheckEnabled() во всех существующих инструментах
This commit is contained in:
47
LazyBear.MCP/Services/Logging/InMemoryLogSink.cs
Normal file
47
LazyBear.MCP/Services/Logging/InMemoryLogSink.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace LazyBear.MCP.Services.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// Singleton, хранящий последние <see cref="Capacity"/> записей лога в памяти.
|
||||
/// Безопасен для многопоточной записи.
|
||||
/// </summary>
|
||||
public sealed class InMemoryLogSink
|
||||
{
|
||||
public const int Capacity = 500;
|
||||
|
||||
private readonly ConcurrentQueue<LogEntry> _entries = new();
|
||||
|
||||
/// <summary>Событие вызывается при добавлении новой записи.</summary>
|
||||
public event Action<LogEntry>? OnLog;
|
||||
|
||||
public void Add(LogEntry entry)
|
||||
{
|
||||
_entries.Enqueue(entry);
|
||||
|
||||
// Обрезаем старые записи
|
||||
while (_entries.Count > Capacity)
|
||||
{
|
||||
_entries.TryDequeue(out _);
|
||||
}
|
||||
|
||||
OnLog?.Invoke(entry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Возвращает снимок всех записей. Опциональный фильтр по категории-префиксу.
|
||||
/// </summary>
|
||||
public IReadOnlyList<LogEntry> GetEntries(string? categoryPrefix = null)
|
||||
{
|
||||
var all = _entries.ToArray();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(categoryPrefix))
|
||||
{
|
||||
return all;
|
||||
}
|
||||
|
||||
return Array.FindAll(all, e => e.Category.StartsWith(categoryPrefix, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public void Clear() => _entries.Clear();
|
||||
}
|
||||
42
LazyBear.MCP/Services/Logging/InMemoryLoggerProvider.cs
Normal file
42
LazyBear.MCP/Services/Logging/InMemoryLoggerProvider.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace LazyBear.MCP.Services.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// ILoggerProvider, направляющий все логи в <see cref="InMemoryLogSink"/>.
|
||||
/// </summary>
|
||||
[ProviderAlias("InMemory")]
|
||||
public sealed class InMemoryLoggerProvider(InMemoryLogSink sink) : ILoggerProvider
|
||||
{
|
||||
public ILogger CreateLogger(string categoryName) =>
|
||||
new InMemoryLogger(sink, categoryName);
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
|
||||
internal sealed class InMemoryLogger(InMemoryLogSink sink, string category) : ILogger
|
||||
{
|
||||
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None;
|
||||
|
||||
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);
|
||||
var entry = new LogEntry(
|
||||
DateTimeOffset.Now,
|
||||
logLevel,
|
||||
category,
|
||||
message,
|
||||
exception?.ToString());
|
||||
|
||||
sink.Add(entry);
|
||||
}
|
||||
}
|
||||
14
LazyBear.MCP/Services/Logging/LogEntry.cs
Normal file
14
LazyBear.MCP/Services/Logging/LogEntry.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace LazyBear.MCP.Services.Logging;
|
||||
|
||||
public sealed record LogEntry(
|
||||
DateTimeOffset Timestamp,
|
||||
LogLevel Level,
|
||||
string Category,
|
||||
string Message,
|
||||
string? Exception = null)
|
||||
{
|
||||
/// <summary>Короткое имя категории (последний сегмент namespace)</summary>
|
||||
public string ShortCategory => Category.Contains('.')
|
||||
? Category[(Category.LastIndexOf('.') + 1)..]
|
||||
: Category;
|
||||
}
|
||||
Reference in New Issue
Block a user