GeWuYou f984f4a600 refactor(core): 优化核心组件的线程安全性和错误处理
- 重构 AsyncLogAppender 的 Flush 方法,添加超时控制和 SpinWait 优化
- 为 BindableProperty 添加线程安全锁保护,确保并发访问的安全性
- 在 BindableProperty 中实现回调外部调用以避免死锁问题
- 为 EasyEvents 使用 ConcurrentDictionary 替代 Dictionary 提高并发性能
- 添加协程调度器异常处理回调机制,防止异常传播导致调度器崩溃
- 为 FileAppender 添加初始化异常处理和资源清理逻辑
- 补充完整的单元测试覆盖并发场景下的线程安全性验证
2026-03-04 11:04:59 +08:00

117 lines
3.0 KiB
C#

using System.IO;
using System.Text;
using GFramework.Core.Abstractions.logging;
using GFramework.Core.logging.formatters;
namespace GFramework.Core.logging.appenders;
/// <summary>
/// 文件日志输出器(线程安全)
/// </summary>
public sealed class FileAppender : ILogAppender, IDisposable
{
private readonly string _filePath;
private readonly ILogFilter? _filter;
private readonly ILogFormatter _formatter;
private readonly object _lock = new();
private bool _disposed;
private StreamWriter? _writer;
/// <summary>
/// 创建文件日志输出器
/// </summary>
/// <param name="filePath">日志文件路径</param>
/// <param name="formatter">日志格式化器</param>
/// <param name="filter">日志过滤器(可选)</param>
/// <exception cref="ArgumentException">当文件路径为空或无效时抛出</exception>
/// <exception cref="IOException">当无法创建或打开日志文件时抛出</exception>
public FileAppender(
string filePath,
ILogFormatter? formatter = null,
ILogFilter? filter = null)
{
if (string.IsNullOrWhiteSpace(filePath))
throw new ArgumentException("File path cannot be null or whitespace.", nameof(filePath));
_filePath = filePath;
_formatter = formatter ?? new DefaultLogFormatter();
_filter = filter;
try
{
EnsureDirectoryExists();
InitializeWriter();
}
catch
{
// 确保在初始化失败时清理资源
_writer?.Dispose();
_writer = null;
throw;
}
}
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
if (_disposed) return;
lock (_lock)
{
_writer?.Flush();
_writer?.Dispose();
_writer = null;
_disposed = true;
}
}
/// <summary>
/// 追加日志条目到文件
/// </summary>
/// <param name="entry">日志条目</param>
public void Append(LogEntry entry)
{
if (_disposed)
throw new ObjectDisposedException(nameof(FileAppender));
if (_filter != null && !_filter.ShouldLog(entry))
return;
var message = _formatter.Format(entry);
lock (_lock)
{
_writer?.WriteLine(message);
}
}
/// <summary>
/// 刷新文件缓冲区
/// </summary>
public void Flush()
{
lock (_lock)
{
_writer?.Flush();
}
}
private void EnsureDirectoryExists()
{
var directory = Path.GetDirectoryName(_filePath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
}
private void InitializeWriter()
{
_writer = new StreamWriter(_filePath, append: true, Encoding.UTF8)
{
AutoFlush = true
};
}
}