Files
RobustToolbox/Robust.Shared/Log/LogManager.Sawmill.cs
PJB3005 f40dd51648 Add ISawmill.IsLogLevelEnabled
So you can avoid logging things unless somebody's actually trying to debug something.
2025-01-07 20:54:14 +01:00

215 lines
6.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Threading;
using Serilog;
using Serilog.Events;
using SLogger = Serilog.Core.Logger;
namespace Robust.Shared.Log
{
public sealed partial class LogManager
{
private sealed class Sawmill : ISawmill, IDisposable
{
// Need this to act as a proxy for some internal Serilog APIs related to message parsing.
private readonly SLogger _sLogger = new LoggerConfiguration().CreateLogger();
public string Name { get; }
public Sawmill? Parent { get; }
private bool _disposed;
public LogLevel? Level
{
get => _level;
set
{
if (Name == "root" && value == null)
{
throw new ArgumentException("Cannot set root sawmill level to null.");
}
_level = value;
}
}
private LogLevel? _level = null;
public List<ILogHandler> Handlers { get; } = new();
private readonly ReaderWriterLockSlim _handlerLock = new();
public Sawmill(Sawmill? parent, string name)
{
Parent = parent;
Name = name;
}
public void AddHandler(ILogHandler handler)
{
_handlerLock.EnterWriteLock();
try
{
Handlers.Add(handler);
}
finally
{
_handlerLock.ExitWriteLock();
}
}
public void RemoveHandler(ILogHandler handler)
{
_handlerLock.EnterWriteLock();
try
{
Handlers.Remove(handler);
}
finally
{
_handlerLock.ExitWriteLock();
}
}
public bool IsLogLevelEnabled(LogLevel level)
{
return level >= GetPracticalLevel();
}
public void Log(LogLevel level, Exception? exception, string message, params object?[] args)
{
if (!_sLogger.BindMessageTemplate(message, args, out var parsedTemplate, out var properties))
return;
var msg = new LogEvent(DateTimeOffset.Now, level.ToSerilog(), exception, parsedTemplate, properties);
if (!IsLogLevelEnabled(level))
return;
LogInternal(Name, msg);
}
public void Log(LogLevel level, string message, params object?[] args)
{
if (args.Length != 0 && message.Contains("{0"))
{
// Fallback for logs that still use the string.Format approach.
message = string.Format(message, args);
args = Array.Empty<object>();
}
Log(level, null, message, args);
}
public void Log(LogLevel level, string message)
{
Log(level, message, Array.Empty<object>());
}
private void LogInternal(string sourceSawmill, LogEvent message)
{
_handlerLock.EnterReadLock();
try
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(Sawmill));
}
foreach (var handler in Handlers)
{
handler.Log(sourceSawmill, message);
}
}
finally
{
_handlerLock.ExitReadLock();
}
Parent?.LogInternal(sourceSawmill, message);
}
private LogLevel GetPracticalLevel()
{
if (Level.HasValue)
{
return Level.Value;
}
return Parent?.GetPracticalLevel() ?? default;
}
public void Debug(string message, params object?[] args)
{
Log(LogLevel.Debug, message, args);
}
public void Debug(string message)
{
Log(LogLevel.Debug, message);
}
public void Info(string message, params object?[] args)
{
Log(LogLevel.Info, message, args);
}
public void Info(string message)
{
Log(LogLevel.Info, message);
}
public void Warning(string message, params object?[] args)
{
Log(LogLevel.Warning, message, args);
}
public void Warning(string message)
{
Log(LogLevel.Warning, message);
}
public void Error(string message, params object?[] args)
{
Log(LogLevel.Error, message, args);
}
public void Error(string message)
{
Log(LogLevel.Error, message);
}
public void Fatal(string message, params object?[] args)
{
Log(LogLevel.Fatal, message, args);
}
public void Fatal(string message)
{
Log(LogLevel.Fatal, message);
}
public void Dispose()
{
_handlerLock.EnterWriteLock();
try
{
_disposed = true;
foreach (ILogHandler handler in Handlers)
{
if (handler is IDisposable disposable)
{
disposable.Dispose();
}
}
}
finally
{
_handlerLock.ExitWriteLock();
}
}
}
}
}