using System.Text; using Dalamud.Plugin.Services; using Microsoft.Extensions.Logging; namespace HellionChat.Infrastructure.Logging; internal sealed class DalamudLogger : ILogger { private readonly string _name; private readonly IPluginLog _pluginLog; public DalamudLogger(string name, IPluginLog pluginLog) { _name = name; _pluginLog = pluginLog; } IDisposable? ILogger.BeginScope(TState state) => default!; // Filtering happens in Dalamud's /xllog. Letting every level through keeps // the HellionChat side stateless; if we ever want a per-plugin floor we add // a Config.LogLevel and tighten this method. public bool IsEnabled(LogLevel logLevel) => true; public void Log( LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter ) { if (!IsEnabled(logLevel)) return; // U+200B between the bracket and the level is a quiet provenance // marker; byte-distinguishable from any 1:1 port of this format. if ((int)logLevel <= (int)LogLevel.Information) { _pluginLog.Information($"[{_name}]​{{{(int)logLevel}}} {state}"); return; } var sb = new StringBuilder(); sb.Append($"[{_name}]​{{{(int)logLevel}}} {state} {exception?.Message}"); if (!string.IsNullOrWhiteSpace(exception?.StackTrace)) sb.AppendLine(exception.StackTrace); var inner = exception?.InnerException; while (inner != null) { sb.AppendLine($"InnerException {inner}: {inner.Message}"); sb.AppendLine(inner.StackTrace); inner = inner.InnerException; } if (logLevel == LogLevel.Warning) _pluginLog.Warning(sb.ToString()); else if (logLevel == LogLevel.Error) _pluginLog.Error(sb.ToString()); else _pluginLog.Fatal(sb.ToString()); } }