Danro/Assets/Scripts/Utilities/CompositeLogger.cs
Danil Iarosh 1cecbf2c11
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
fix: avoid logger dispose errors in free-play flow
- Исправлен баг `GameManager.NewGame()`: в режиме бесплатной игры (`payment.freeGame = true`) больше не выполняется проверка/списание баланса.
- Добавлен явный guard для free-play перед `Balance.CanStartGame()` и `Balance.SpendBalance(...)`, чтобы не было нежелательных списаний.
- Устранена ошибка закрытия лог-файла `Cannot access a disposed object`:
  - `FileLogger` переведен на безопасный паттерн `Dispose(bool)` с идемпотентностью;
  - добавлена защита от повторного закрытия/записи в уже закрытый поток;
  - финализатор `FileLogger` больше не выполняет managed-cleanup.
- Устранен риск повторной очистки в `CompositeLogger`:
  - добавлен флаг `_disposed`;
  - `Dispose(bool)` теперь выполняется только один раз;
  - финализатор вызывает `Dispose(false)` вместо полного `Dispose()`.
2026-02-24 14:02:38 +03:00

363 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;
namespace RR.Common.Utility
{
public class CompositeLogger : ILogger, IDisposable
{
public ILogHandler logHandler { get; set; }
public bool logEnabled { get; set; }
public LogType filterLogType { get; set; }
private ILogger _oldLogger;
private readonly List<ILogger> _additiveLoggers = new();
private bool _disposed;
public void Initialize(ILogger additiveLogger = null)
{
_oldLogger = Debug.unityLogger;
logHandler = _oldLogger.logHandler;
logEnabled = true;
filterLogType = _oldLogger.filterLogType;
if (additiveLogger != null) _additiveLoggers.Add(additiveLogger);
}
public void AddLogger(ILogger additiveLogger)
{
if (additiveLogger == null)
throw new ArgumentNullException(nameof(additiveLogger));
_additiveLoggers.Add(additiveLogger);
}
public void AddLoggers(params ILogger[] additiveLoggers)
{
if (additiveLoggers == null)
throw new ArgumentNullException(nameof(additiveLoggers));
foreach (var logger in additiveLoggers)
{
if (logger != null)
_additiveLoggers.Add(logger);
}
}
public void RemoveLogger(ILogger additiveLogger)
{
_additiveLoggers.Remove(additiveLogger);
}
public void RemoveLoggers(params ILogger[] additiveLoggers)
{
_additiveLoggers.RemoveAll(additiveLoggers.Contains);
}
public bool IsLogTypeAllowed(LogType logType)
{
return _oldLogger.IsLogTypeAllowed(logType) || _additiveLoggers.Any(logger => logger.IsLogTypeAllowed(logType));
}
#region Ilogger Implementation
public void LogFormat(LogType logType, string format, params object[] args)
{
_oldLogger.LogFormat(logType, format, args);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.LogFormat(logType, format, args);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void LogFormat(LogType logType, Object context, string format, params object[] args)
{
_oldLogger.LogFormat(logType, context, format, args);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.LogFormat(logType, context, format, args);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void LogException(Exception exception)
{
_oldLogger.LogException(exception);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.LogException(exception);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void LogException(Exception exception, Object context)
{
_oldLogger.LogException(exception, context);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.LogException(exception, context);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void Log(object message)
{
_oldLogger.Log(message);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.Log(message);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void Log(LogType logType, object message)
{
_oldLogger.Log(logType, message);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.Log(logType, message);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void Log(LogType logType, object message, GameObject gameObject)
{
_oldLogger.Log(logType, message, gameObject);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.Log(logType, message, gameObject);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void Log(LogType logType, object message, Object context)
{
_oldLogger.Log(logType, message, context);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.Log(logType, message, context);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void Log(LogType logType, string tag, object message, Object context)
{
_oldLogger.Log(logType, tag, message, context);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.Log(logType, tag, message, context);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void Log(LogType logType, string tag, object message)
{
_oldLogger.Log(tag, message);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.Log(tag, message);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void Log(string tag, object message)
{
_oldLogger.Log(tag, message);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.Log(tag, message);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void Log(string tag, object message, Object context)
{
_oldLogger.Log(tag, message, context);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.Log(tag, message, context);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void LogWarning(string tag, object message)
{
_oldLogger.LogWarning(tag, message);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.LogWarning(tag, message);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void LogWarning(string tag, object message, Object context)
{
_oldLogger.LogWarning(tag, message, context);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.LogWarning(tag, message, context);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void LogError(string tag, object message)
{
_oldLogger.LogError(tag, message);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.LogError(tag, message);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
public void LogError(string tag, object message, Object context)
{
_oldLogger.LogError(tag, message, context);
foreach (ILogger additiveLogger in _additiveLoggers)
{
try
{
additiveLogger.LogError(tag, message, context);
}
catch (Exception ex)
{
_oldLogger.LogException(ex);
}
}
}
#endregion
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (_disposed || !disposing) return;
lock (_additiveLoggers)
{
if (_disposed)
return;
foreach (ILogger additiveLogger in _additiveLoggers.ToArray())
{
try
{
if (additiveLogger is IDisposable disposableLogger)
disposableLogger.Dispose();
}
catch (Exception ex)
{
try { _oldLogger?.LogException(ex); } catch { /* ignore */ }
}
}
_additiveLoggers.Clear();
_disposed = true;
}
}
~CompositeLogger()
{
Dispose(false);
}
}
}