using SilkroadBot.Plugins.SDK.Attributes; using SilkroadBot.Plugins.SDK.Interfaces; using SilkroadBot.Plugins.SDK.Models; namespace SilkroadBot.Plugin.Logger; /// /// Logger plugin - comprehensive logging of all game events to file. /// Supports multiple log formats, rotation, and filtering. /// [Plugin("logger", "Event Logger", "Logs all game events to file with rotation and filtering")] public class LoggerPlugin : PluginBase { public override string Id => "logger"; public override string Name => "Event Logger"; public override Version Version => new(1, 0, 0); public override string Author => "SilkroadBot Team"; public override string Description => "Comprehensive game event logging with file rotation"; private StreamWriter? _logWriter; private string _logDirectory = "logs"; private LogFormat _format = LogFormat.Text; private readonly object _writeLock = new(); private long _totalEntries; public override async Task InitializeAsync(IPluginContext context) { await base.InitializeAsync(context); _logDirectory = context.Configuration.Get("logDirectory", "logs"); _format = Enum.Parse(context.Configuration.Get("format", "Text")); if (!Directory.Exists(_logDirectory)) Directory.CreateDirectory(_logDirectory); } public override Task StartAsync(CancellationToken ct = default) { var fileName = $"bot_{DateTime.Now:yyyyMMdd_HHmmss}.log"; var filePath = Path.Combine(_logDirectory, fileName); _logWriter = new StreamWriter(filePath, append: true) { AutoFlush = true }; WriteLog("INFO", "Logger", "Logging started"); return base.StartAsync(ct); } public override Task StopAsync() { WriteLog("INFO", "Logger", $"Logging stopped. Total entries: {_totalEntries}"); _logWriter?.Dispose(); _logWriter = null; return base.StopAsync(); } /// /// Log a game event. /// public void LogEvent(string category, string message, LogLevel level = LogLevel.Information) { WriteLog(level.ToString().ToUpper()[..4], category, message); } /// /// Log a packet. /// public void LogPacket(string direction, ushort opcode, int size, byte[]? data = null) { var hex = data != null ? BitConverter.ToString(data[..Math.Min(32, data.Length)]).Replace("-", " ") : ""; WriteLog("PKT", direction, $"0x{opcode:X4} [{size}B] {hex}"); } private void WriteLog(string level, string category, string message) { if (_logWriter == null) return; lock (_writeLock) { var timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff"); var line = _format switch { LogFormat.Text => $"[{timestamp}] [{level}] [{category}] {message}", LogFormat.Csv => $"\"{timestamp}\",\"{level}\",\"{category}\",\"{message.Replace("\"", "\"\"")}\"", LogFormat.Json => System.Text.Json.JsonSerializer.Serialize(new { timestamp, level, category, message }), _ => $"[{timestamp}] [{level}] {message}" }; _logWriter.WriteLine(line); _totalEntries++; } } public override void Dispose() { _logWriter?.Dispose(); base.Dispose(); } } public enum LogFormat { Text, Csv, Json }