一、功能概述
本應用通過?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);}
}