using System; using System.Collections.Generic; using System.Globalization; using System.Text; using Dalamud.Plugin.Services; using Microsoft.Extensions.Logging; using Serilog.Events; namespace LLib.Logging; public sealed class DalamudLoggerProvider : ILoggerProvider, IDisposable, ISupportExternalScope { private sealed class DalamudLogger : ILogger { private sealed class NullScope : IDisposable { public static NullScope Instance { get; } = new NullScope(); private NullScope() { } public void Dispose() { } } private readonly string? _name; private readonly DalamudLoggerProvider _provider; private readonly IPluginLog _pluginLog; private IExternalScopeProvider? ScopeProvider => _provider._scopeProvider; public DalamudLogger(string? name, DalamudLoggerProvider provider, IPluginLog pluginLog) { _name = name; _provider = provider; _pluginLog = pluginLog; } public IDisposable BeginScope(TState state) where TState : notnull { return ScopeProvider?.Push(state) ?? NullScope.Instance; } public bool IsEnabled(LogLevel logLevel) { if (logLevel == LogLevel.None) { return false; } return ToSerilogLevel(logLevel) >= _pluginLog.MinimumLogLevel; } public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { if (!IsEnabled(logLevel)) { return; } ArgumentNullException.ThrowIfNull(formatter, "formatter"); LogEventLevel level = ToSerilogLevel(logLevel); StringBuilder stringBuilder = new StringBuilder(); ScopeProvider?.ForEachScope(delegate(object? scope, StringBuilder builder) { if (scope is IEnumerable> enumerable) { { foreach (KeyValuePair item in enumerable) { builder.Append('<').Append(item.Key).Append('=') .Append(item.Value) .Append("> "); } return; } } if (scope != null) { builder.Append('<').Append(scope).Append("> "); } }, stringBuilder); if (!string.IsNullOrEmpty(_name)) { stringBuilder.Append(_name).Append(": "); } stringBuilder.Append(formatter(state, null)); _pluginLog.Write(level, exception, stringBuilder.ToString()); } private static LogEventLevel ToSerilogLevel(LogLevel logLevel) { return logLevel switch { LogLevel.Trace => LogEventLevel.Verbose, LogLevel.Debug => LogEventLevel.Debug, LogLevel.Information => LogEventLevel.Information, LogLevel.Warning => LogEventLevel.Warning, LogLevel.Error => LogEventLevel.Error, LogLevel.Critical => LogEventLevel.Fatal, _ => throw new ArgumentOutOfRangeException("logLevel", logLevel, null), }; } } private readonly IPluginLog _pluginLog; private readonly Func _categoryNameFormatter; private IExternalScopeProvider? _scopeProvider; public DalamudLoggerProvider(IPluginLog pluginLog, Func? categoryNameFormatter = null) { ArgumentNullException.ThrowIfNull(pluginLog, "pluginLog"); _pluginLog = pluginLog; _categoryNameFormatter = categoryNameFormatter ?? ((Func)((string t) => t)); } public ILogger CreateLogger(string categoryName) { string name = _categoryNameFormatter(categoryName); if (LooksObfuscated(name)) { name = null; } return new DalamudLogger(name, this, _pluginLog); } private static bool LooksObfuscated(string? name) { if (string.IsNullOrEmpty(name)) { return false; } foreach (char c in name) { if (char.IsControl(c)) { return true; } UnicodeCategory unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c); if (((uint)(unicodeCategory - 15) <= 2u || unicodeCategory == UnicodeCategory.OtherNotAssigned) ? true : false) { return true; } } return false; } public void SetScopeProvider(IExternalScopeProvider scopeProvider) { _scopeProvider = scopeProvider; } public void Dispose() { } }