一、功能概述
本应用通过 AsyncFileLogger
类提供了灵活的日志控制功能,可在运行时通过 UI 界面启用或禁用日志记录。日志系统具有以下特点:
- 可控制开关:通过按钮随时启用或禁用日志,无需重启应用
- 异步写入:日志记录采用异步方式,不会阻塞主程序运行
- 自动分割:当日志文件大小超过指定限制时,自动创建新文件
- 内存优化:内存中最多保留指定数量的最近日志条目
- 状态显示:实时显示当前日志状态(启用 / 禁用)
二、使用方法
1. 初始化应用
在应用启动时,MainForm
会自动初始化日志系统:
public MainForm()
{
InitializeComponent();
// 初始化日志记录器
_logger = new AsyncFileLogger(this);
// 绑定UI元素
_logger.BindUIElements(_btnEnableLogging, _btnDisableLogging, _lblLoggingStatus);
}
2. 启用日志
点击 "启用日志" 按钮:
- 日志系统将被初始化
- 创建日志文件,默认格式为
SerialPortLog_日期_时间.log
- 记录 "Logging Enabled" 和 "System Started" 日志条目
- 按钮状态更新:
- 启用日志按钮(不可用)
- 禁用日志按钮(可用)
- 日志状态标签(显示 "日志状态:已启用")
3. 禁用日志
点击 "禁用日志" 按钮:
- 记录 "Logging Disabled" 日志条目
- 停止日志写入线程
- 清空日志队列
- 按钮状态更新:
- 启用日志按钮(可用)
- 禁用日志按钮(不可用)
- 日志状态标签(显示 "日志状态:已禁用")
4. 记录日志
在应用代码中,通过 _logger.Log()
方法记录日志:
private void SomeMethod()
{
try
{
// 执行操作
_logger.Log("执行了某些操作");
}
catch (Exception ex)
{
_logger.Log($"发生错误: {ex.Message}");
}
}
5. 查看日志文件
日志文件保存在应用程序运行目录下,包含完整的时间戳和详细的操作记录。每条日志格式为:
YYYY-MM-DD HH:MM:SS.FFF - [日志内容]
三、注意事项
性能影响:虽然日志采用异步写入,但高频率日志记录仍可能影响性能,建议在生产环境中禁用
文件管理:
- 日志文件自动保存在应用程序目录
- 单个文件大小超过限制时自动创建新文件
- 建议定期清理历史日志文件
状态验证:
- 通过 UI 按钮状态和标签文本确认当前日志状态
- 检查日志文件时间戳确保记录正常
异常处理:
- 日志系统内部有完善的异常处理机制
- 日志写入错误会输出到调试窗口
- 严重错误会自动创建新日志文件
四、高级配置
如需调整日志系统参数,可在初始化时传入相应参数:
自定义日志文件名
_logger = new AsyncFileLogger(this, "MyCustomLog.log");
调整日志文件大小限制:
// 设置为20MB
_logger = new AsyncFileLogger(this, null, 20);
增加内存日志条目数量:
// 内存中保留200条日志
_logger = new AsyncFileLogger(this, null, 10, 200);
通过合理配置这些参数,可以优化日志系统的性能和资源占用。
五、详细代码
功能特点
- 独立封装:完全独立的日志类,不依赖特定应用程序
- 灵活配置:
- 自定义日志文件名和路径
- 调整单个文件最大大小
- 控制内存中保留的日志条目数量
- UI集成:
- 支持绑定按钮和标签自动更新状态
- 线程安全的UI更新机制
- 资源管理:
- 实现IDisposable接口,确保资源正确释放
- 自动处理日志文件分割和关闭
这个封装使日志功能可以轻松移植到其他项目中,只需简单初始化并调用Log方法即可记录日志,同时提供直观的UI控制界面。
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Forms;
/// <summary>
/// 高性能日志记录器,支持异步写入和动态开关控制
/// </summary>
public class AsyncFileLogger : IDisposable
{
// 配置参数
private readonly string _logFilePath;
private readonly int _maxFileSizeMB;
private readonly int _maxMemoryEntries;
// 状态变量
private BlockingCollection<string> _logQueue;
private Thread _logWriterThread;
private bool _isEnabled;
private readonly object _syncRoot = new object();
private readonly CancellationTokenSource _cancellationToken = new CancellationTokenSource();
private readonly List<string> _memoryLog = new List<string>();
// UI相关
private readonly Control _ownerControl;
private Button _enableButton;
private Button _disableButton;
private Label _statusLabel;
/// <summary>
/// 获取当前日志状态
/// </summary>
public bool IsEnabled => _isEnabled;
/// <summary>
/// 获取内存中的日志内容
/// </summary>
public string MemoryLog
{
get
{
lock (_memoryLog)
{
return string.Join(Environment.NewLine, _memoryLog);
}
}
}
/// <summary>
/// 初始化日志记录器
/// </summary>
/// <param name="ownerControl">拥有该日志器的控件,用于UI更新</param>
/// <param name="logFileName">日志文件名,默认为当前时间戳</param>
/// <param name="maxFileSizeMB">单个日志文件最大大小(MB)</param>
/// <param name="maxMemoryEntries">内存中保留的最大日志条目数</param>
public AsyncFileLogger(
Control ownerControl,
string logFileName = null,
int maxFileSizeMB = 10,
int maxMemoryEntries = 100)
{
_ownerControl = ownerControl ?? throw new ArgumentNullException(nameof(ownerControl));
_maxFileSizeMB = maxFileSizeMB;
_maxMemoryEntries = maxMemoryEntries;
// 初始化日志文件路径
if (string.IsNullOrEmpty(logFileName))
{
logFileName = $"SerialPortLog_{DateTime.Now:yyyyMMdd_HHmmss}.log";
}
_logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, logFileName);
_logQueue = new BlockingCollection<string>();
// 默认禁用日志
_isEnabled = false;
}
/// <summary>
/// 绑定UI控件
/// </summary>
public void BindUIElements(Button enableButton, Button disableButton, Label statusLabel)
{
_enableButton = enableButton;
_disableButton = disableButton;
_statusLabel = statusLabel;
// 设置初始状态
UpdateUIStatus();
// 绑定事件
if (_enableButton != null)
{
_enableButton.Click -= EnableLogging_Click;
_enableButton.Click += EnableLogging_Click;
}
if (_disableButton != null)
{
_disableButton.Click -= DisableLogging_Click;
_disableButton.Click += DisableLogging_Click;
}
}
/// <summary>
/// 启用日志记录
/// </summary>
public void EnableLogging()
{
lock (_syncRoot)
{
if (_isEnabled)
return;
_isEnabled = true;
StartLogWriter();
Log("===== Logging Enabled =====");
Log($"Log file: {_logFilePath}");
UpdateUIStatus();
}
}
/// <summary>
/// 禁用日志记录
/// </summary>
public void DisableLogging()
{
lock (_syncRoot)
{
if (!_isEnabled)
return;
Log("===== Logging Disabled =====");
_isEnabled = false;
StopLogWriter();
UpdateUIStatus();
}
}
/// <summary>
/// 记录日志
/// </summary>
public void Log(string message)
{
if (!_isEnabled)
return;
string logEntry = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} - {message}";
// 添加到内存日志
lock (_memoryLog)
{
_memoryLog.Add(logEntry);
// 限制日志大小
if (_memoryLog.Count > _maxMemoryEntries)
_memoryLog.RemoveAt(0);
}
// 添加到日志队列
try
{
if (_isEnabled && !_logQueue.IsAddingCompleted)
{
_logQueue.Add(logEntry);
}
}
catch (Exception ex)
{
Debug.WriteLine($"Error adding log to queue: {ex.Message}");
}
}
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
DisableLogging();
_cancellationToken.Cancel();
_logQueue?.Dispose();
}
// 启动日志写入线程
private void StartLogWriter()
{
if (_logWriterThread == null || !_logWriterThread.IsAlive)
{
_logWriterThread = new Thread(LogWriterLoop)
{
IsBackground = true,
Priority = ThreadPriority.BelowNormal
};
_logWriterThread.Start();
}
}
// 停止日志写入线程
private void StopLogWriter()
{
_logQueue.CompleteAdding();
// 清空队列
while (_logQueue.TryTake(out _)) { }
if (_logWriterThread != null && _logWriterThread.IsAlive)
{
_logWriterThread.Join(1000);
}
// 重置队列
_logQueue.Dispose();
_logQueue = new BlockingCollection<string>();
}
// 日志写入线程主循环
private void LogWriterLoop()
{
string currentLogPath = _logFilePath;
try
{
while (!_cancellationToken.IsCancellationRequested && _isEnabled)
{
StreamWriter writer = null;
try
{
writer = new StreamWriter(currentLogPath, true, Encoding.UTF8);
writer.AutoFlush = true;
// 记录日志文件创建
string initLog = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} - Log file created: {currentLogPath}";
writer.WriteLine(initLog);
// 处理队列中的日志条目
while (!_cancellationToken.IsCancellationRequested && _isEnabled)
{
if (_logQueue.TryTake(out string logEntry, 300))
{
writer.WriteLine(logEntry);
// 检查文件大小
if (writer.BaseStream.Length > _maxFileSizeMB * 1024 * 1024)
{
break; // 创建新文件
}
}
}
}
catch (OperationCanceledException)
{
// 正常取消
}
catch (Exception ex)
{
Debug.WriteLine($"Error writing log: {ex.Message}");
Thread.Sleep(1000); // 避免频繁重试
}
finally
{
writer?.Dispose();
}
// 创建新的日志文件
if (!_cancellationToken.IsCancellationRequested && _isEnabled)
{
currentLogPath = Path.Combine(
Path.GetDirectoryName(_logFilePath),
$"{Path.GetFileNameWithoutExtension(_logFilePath)}_{DateTime.Now:yyyyMMdd_HHmmss}.log");
Log($"Log file exceeded {_maxFileSizeMB}MB, created new file: {currentLogPath}");
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"Log writer thread error: {ex.Message}");
}
}
// 更新UI状态
private void UpdateUIStatus()
{
if (_ownerControl.InvokeRequired)
{
_ownerControl.Invoke(new Action(UpdateUIStatus));
return;
}
if (_enableButton != null)
_enableButton.Enabled = !_isEnabled;
if (_disableButton != null)
_disableButton.Enabled = _isEnabled;
if (_statusLabel != null)
_statusLabel.Text = $"日志状态: {(_isEnabled ? "已启用" : "已禁用")}";
}
// UI事件处理
private void EnableLogging_Click(object sender, EventArgs e)
{
EnableLogging();
MessageBox.Show("日志功能已启用", "日志状态",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void DisableLogging_Click(object sender, EventArgs e)
{
DisableLogging();
MessageBox.Show("日志功能已禁用", "日志状态",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
使用方法示例
下面是如何在您的应用程序中使用这个日志类的示例:
public partial class MainForm : Form
{
private Button _btnEnableLogging;
private Button _btnDisableLogging;
private Label _lblLoggingStatus;
private AsyncFileLogger _logger;
public MainForm()
{
InitializeComponent();
// 初始化日志记录器
_logger = new AsyncFileLogger(this);
// 绑定UI元素
_logger.BindUIElements(_btnEnableLogging, _btnDisableLogging, _lblLoggingStatus);
}
private void InitializeComponent()
{
// 窗体初始化代码...
// 创建UI控件
_btnEnableLogging = new Button();
_btnDisableLogging = new Button();
_lblLoggingStatus = new Label();
// 配置控件...
// 添加到窗体
this.Controls.Add(_btnEnableLogging);
this.Controls.Add(_btnDisableLogging);
this.Controls.Add(_lblLoggingStatus);
}
// 在需要记录日志的地方调用
private void SomeMethod()
{
try
{
// 执行操作
_logger.Log("执行了某些操作");
}
catch (Exception ex)
{
_logger.Log($"发生错误: {ex.Message}");
}
}
// 窗体关闭时释放资源
protected override void OnFormClosing(FormClosingEventArgs e)
{
_logger.Dispose();
base.OnFormClosing(e);
}
}