C# 日志管理功能代码

发布于:2025-06-07 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、功能概述

本应用通过 AsyncFileLogger 类提供了灵活的日志控制功能,可在运行时通过 UI 界面启用或禁用日志记录。日志系统具有以下特点:

  1. 可控制开关:通过按钮随时启用或禁用日志,无需重启应用
  2. 异步写入:日志记录采用异步方式,不会阻塞主程序运行
  3. 自动分割:当日志文件大小超过指定限制时,自动创建新文件
  4. 内存优化:内存中最多保留指定数量的最近日志条目
  5. 状态显示:实时显示当前日志状态(启用 / 禁用)

二、使用方法

1. 初始化应用

在应用启动时,MainForm 会自动初始化日志系统:

public MainForm()
{
    InitializeComponent();
    
    // 初始化日志记录器
    _logger = new AsyncFileLogger(this);
    
    // 绑定UI元素
    _logger.BindUIElements(_btnEnableLogging, _btnDisableLogging, _lblLoggingStatus);
}

2. 启用日志

点击 "启用日志" 按钮:

  1. 日志系统将被初始化
  2. 创建日志文件,默认格式为 SerialPortLog_日期_时间.log
  3. 记录 "Logging Enabled" 和 "System Started" 日志条目
  4. 按钮状态更新:
    • 启用日志按钮(不可用)
    • 禁用日志按钮(可用)
    • 日志状态标签(显示 "日志状态:已启用")

3. 禁用日志

点击 "禁用日志" 按钮:

  1. 记录 "Logging Disabled" 日志条目
  2. 停止日志写入线程
  3. 清空日志队列
  4. 按钮状态更新:
    • 启用日志按钮(可用)
    • 禁用日志按钮(不可用)
    • 日志状态标签(显示 "日志状态:已禁用")

4. 记录日志

在应用代码中,通过 _logger.Log() 方法记录日志:

private void SomeMethod()
{
    try
    {
        // 执行操作
        _logger.Log("执行了某些操作");
    }
    catch (Exception ex)
    {
        _logger.Log($"发生错误: {ex.Message}");
    }
}

5. 查看日志文件

日志文件保存在应用程序运行目录下,包含完整的时间戳和详细的操作记录。每条日志格式为:

YYYY-MM-DD HH:MM:SS.FFF - [日志内容]

三、注意事项

  1. 性能影响:虽然日志采用异步写入,但高频率日志记录仍可能影响性能,建议在生产环境中禁用

  2. 文件管理

    • 日志文件自动保存在应用程序目录
    • 单个文件大小超过限制时自动创建新文件
    • 建议定期清理历史日志文件
  3. 状态验证

    • 通过 UI 按钮状态和标签文本确认当前日志状态
    • 检查日志文件时间戳确保记录正常
  4. 异常处理

    • 日志系统内部有完善的异常处理机制
    • 日志写入错误会输出到调试窗口
    • 严重错误会自动创建新日志文件

四、高级配置

如需调整日志系统参数,可在初始化时传入相应参数:

自定义日志文件名

_logger = new AsyncFileLogger(this, "MyCustomLog.log");

调整日志文件大小限制

// 设置为20MB
_logger = new AsyncFileLogger(this, null, 20);

增加内存日志条目数量

// 内存中保留200条日志
_logger = new AsyncFileLogger(this, null, 10, 200);

通过合理配置这些参数,可以优化日志系统的性能和资源占用。

五、详细代码

功能特点

  1. 独立封装:完全独立的日志类,不依赖特定应用程序
  2. 灵活配置
    • 自定义日志文件名和路径
    • 调整单个文件最大大小
    • 控制内存中保留的日志条目数量
  3. UI集成
    • 支持绑定按钮和标签自动更新状态
    • 线程安全的UI更新机制
  4. 资源管理
    • 实现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);
    }
}


网站公告

今日签到

点亮在社区的每一天
去签到