csharp
/// <summary>
/// 简单的线程安全日志记录器
/// 支持多线程同时写入,自动按天分割文件
/// </summary>
public sealed class LogHelper : IDisposable
{
// 单例实例
private static readonly Lazy<LogHelper> _instance = new Lazy<LogHelper>(() => new LogHelper());
public static LogHelper Instance => _instance.Value;
// 线程安全的日志队列
private readonly BlockingCollection<LogEntry> _logQueue = new BlockingCollection<LogEntry>();
// 写入器任务
private readonly Task _writerTask;
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
// 当前日志文件路径
private string _currentLogFilePath = string.Empty;
private StreamWriter _currentWriter;
private readonly object _writerLock = new object();
// 配置
public string LogDirectory { get; set; } = AppDomain.CurrentDomain.BaseDirectory + "Logs";
public bool WriteToConsole { get; set; } = true;
public bool AutoFlush { get; set; } = true;
private LogHelper()
{
// 确保日志目录存在
EnsureLogDirectory();
// 启动后台写入线程
_writerTask = Task.Run(() => ProcessLogQueueAsync(_cancellationTokenSource.Token));
}
/// <summary>
/// 写日志
/// </summary>
/// <param name="message">日志消息</param>
public void Log(string message)
{
if (string.IsNullOrWhiteSpace(message)) return;
var logEntry = new LogEntry
{
Timestamp = DateTime.Now,
Message = message,
ThreadId = Thread.CurrentThread.ManagedThreadId
};
// 将日志加入队列(如果队列已满,会阻塞直到有空间)
_logQueue.Add(logEntry);
}
/// <summary>
/// 格式化日志(可选自定义格式)
/// </summary>
public string FormatLog(LogEntry entry)
{
return $"[{entry.Timestamp:yyyy-MM-dd HH:mm:ss.fff}] [Thread:{entry.ThreadId:D4}] {entry.Message}";
}
/// <summary>
/// 后台处理日志队列
/// </summary>
private async Task ProcessLogQueueAsync(CancellationToken cancellationToken)
{
foreach (var logEntry in _logQueue.GetConsumingEnumerable(cancellationToken))
{
try
{
// 检查是否需要切换日志文件(按天)
CheckAndSwitchLogFile();
// 格式化日志
var formattedLog = FormatLog(logEntry);
// 写入控制台
if (WriteToConsole)
{
Console.WriteLine(formattedLog);
}
// 写入文件
lock (_writerLock)
{
if (_currentWriter != null && !_currentWriter.BaseStream.CanWrite)
{
// 如果流已关闭,重新创建
_currentWriter.Dispose();
_currentWriter = null;
}
if (_currentWriter == null)
{
_currentWriter = new StreamWriter(_currentLogFilePath, true, System.Text.Encoding.UTF8)
{
AutoFlush = AutoFlush
};
}
_currentWriter.WriteLine(formattedLog);
}
}
catch (Exception ex)
{
// 写入失败时输出到控制台
Console.WriteLine($"[Logger Error] Failed to write log: {ex.Message}");
}
}
}
/// <summary>
/// 检查并切换日志文件(按天)
/// </summary>
private void CheckAndSwitchLogFile()
{
string today = DateTime.Today.ToString("yyyy-MM-dd");
string newLogFilePath = Path.Combine(LogDirectory, $"log_{today}.txt");
if (newLogFilePath != _currentLogFilePath)
{
lock (_writerLock)
{
if (newLogFilePath != _currentLogFilePath)
{
// 关闭旧的文件写入器
if (_currentWriter != null)
{
try
{
_currentWriter.Flush();
_currentWriter.Dispose();
}
catch
{
// 忽略关闭异常
}
_currentWriter = null;
}
// 更新当前日志文件路径
_currentLogFilePath = newLogFilePath;
// 创建新的文件写入器
_currentWriter = new StreamWriter(_currentLogFilePath, true, System.Text.Encoding.UTF8)
{
AutoFlush = AutoFlush
};
}
}
}
}
/// <summary>
/// 确保日志目录存在
/// </summary>
private void EnsureLogDirectory()
{
if (!Directory.Exists(LogDirectory))
{
Directory.CreateDirectory(LogDirectory);
}
}
/// <summary>
/// 刷新缓冲区
/// </summary>
public void Flush()
{
lock (_writerLock)
{
_currentWriter?.Flush();
}
}
/// <summary>
/// 立即写入一条日志(同步,用于紧急情况)
/// </summary>
public void LogImmediate(string message)
{
if (string.IsNullOrWhiteSpace(message)) return;
var logEntry = new LogEntry
{
Timestamp = DateTime.Now,
Message = message,
ThreadId = Thread.CurrentThread.ManagedThreadId
};
var formattedLog = FormatLog(logEntry);
// 写入控制台
if (WriteToConsole)
{
Console.WriteLine(formattedLog);
}
// 确保文件准备好
CheckAndSwitchLogFile();
// 直接写入文件
lock (_writerLock)
{
try
{
if (_currentWriter == null)
{
_currentWriter = new StreamWriter(_currentLogFilePath, true, System.Text.Encoding.UTF8)
{
AutoFlush = AutoFlush
};
}
_currentWriter.WriteLine(formattedLog);
_currentWriter.Flush();
}
catch (Exception ex)
{
Console.WriteLine($"[Logger Error] Immediate write failed: {ex.Message}");
}
}
}
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
try
{
// 停止队列
_logQueue.CompleteAdding();
_cancellationTokenSource.Cancel();
// 等待写入线程完成
_writerTask?.Wait(TimeSpan.FromSeconds(5));
// 关闭写入器
lock (_writerLock)
{
if (_currentWriter != null)
{
_currentWriter.Flush();
_currentWriter.Dispose();
_currentWriter = null;
}
}
_cancellationTokenSource.Dispose();
_logQueue.Dispose();
}
catch
{
// 释放时忽略异常
}
}
/// <summary>
/// 日志条目结构
/// </summary>
public class LogEntry
{
public DateTime Timestamp { get; set; }
public string Message { get; set; } = string.Empty;
public int ThreadId { get; set; }
}
}