当前位置: 首页 > article >正文

c# 实现一个简单的异常日志记录(异常迭代+分片+定时清理)+AOP Rougamo全局注入

1. 日志目录和文件管理

  • 日志目录:日志文件存储在 ./Exceptions 目录下。
  • 日志文件命名:日志文件的命名格式为 yyyy_MM_dd.log,表示当天的日期。如果当天的日志文件大小超过 maxFileSizeBytes(3KB),则会创建新的日志文件,文件名格式为 yyyy_MM_dd_P{cnt}.log,其中 cnt 是日志文件的编号。
  • 日志文件编码:日志文件使用 UTF-8 编码。

2. 异常日志记录

  • WriteExceptionLog(Exception ex) 方法:
    该方法用于记录异常信息。首先检查日志目录是否存在,如果不存在则创建。
    获取当前日期的日志文件列表,并选择最新的日志文件(按文件名顺序)。
    如果日志文件存在且大小超过 maxFileSizeBytes,则创建一个新的日志文件,文件名中包含 _P{cnt},其中 cnt 是文件的编号。
    将异常信息追加到日志文件中,使用 GetLogEntry(ex) 方法生成异常信息的日志条目。
    最后调用 CleanupOldLogFiles() 方法清理超过 maxLogFileAgeDays(1天)的旧日志文件。

3. 日志条目生成

  • GetLogEntry(Exception ex, int depth = 0) 方法:
    – 该方法递归地生成异常信息的日志条目。
    – 每层异常信息使用 depth 参数控制缩进,便于阅读。
    – 日志条目包括异常时间、异常信息、异常对象和调用堆栈。
    – 如果异常有嵌套的内部异常(InnerException),则递归调用 GetLogEntry 方法生成内部异常的日志条目。

4. 旧日志文件清理

  • CleanupOldLogFiles() 方法:
    – 该方法用于清理超过 maxLogFileAgeDays(1天)的旧日志文件。
    –获取日志目录中所有 .log 文件,检查文件的最后修改时间,如果超过 maxLogFileAgeDays,则删除该文件。

5. 异常处理

  • 异常处理:在 WriteExceptionLog(Exception ex) 方法中,所有的操作都在 lock 块中进行,确保线程安全。如果发生异常,内部异常会被捕获但不会记录,避免日志记录本身抛出的异常导致程序崩溃。
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Shapes;

namespace DataParser.Helpers;

public class LogHelper
{
    private static readonly object objException = new object();
    private static readonly string logDirectory = "./Exceptions";
    private static string curfileName = $"{DateTime.Now:yyyy_MM_dd}.log";
    private static readonly int maxLogFileAgeDays = 1;
    private static readonly long maxFileSizeBytes = 3*1024;
    private static readonly Encoding encoding =Encoding.UTF8;

    static int cnt= 0;

    public static void WriteExceptionLog(Exception ex)
    {
        try
        {
            lock (objException)
            {
                if (!Directory.Exists(logDirectory))
                {
                    Directory.CreateDirectory(logDirectory);
                }

                var files = Directory.GetFiles(logDirectory, "*.log")
                    .Select(x=>System.IO.Path.GetFileName(x))
                    .Where(x => x.Contains($"{DateTime.Now:yyyy_MM_dd}"));

                if(files.Count()>0)
                {
                    var tmp = files.OrderBy(x => x.Length);

                    curfileName = tmp.Last();

                    if (curfileName.Contains("_P"))
                    {
                        Match match = Regex.Match(curfileName, @"_P(\d+)");

                        if (match.Success) 
                        {
                            string str = match.Groups[1].Value;

                            int.TryParse(str, out cnt);
                        }
                    }
                }
                else
                {
                    curfileName = $"{DateTime.Now:yyyy_MM_dd}.log";
                }

                string fileName = System.IO.Path.Combine(logDirectory, curfileName);

                string logEntry = GetLogEntry(ex);


                if (File.Exists(fileName) && (new FileInfo(fileName).Length > maxFileSizeBytes))
                {
                    cnt++;
                    fileName = System.IO.Path.Combine(logDirectory, $"{DateTime.Now:yyyy_MM_dd}_P{cnt}.log");
                }
                else if(!fileName.Contains("_P"))
                {
                    cnt = 0;
                }

                File.AppendAllText(fileName, logEntry, encoding);

                CleanupOldLogFiles();
            }
        }
        catch (Exception innerEx)
        {

        }
    }

    private static string GetLogEntry(Exception ex, int depth = 0)
    {
        string indent = new string(' ', depth * 4);
        string logEntry =
            $"{indent}【异常时间】{DateTime.Now}{Environment.NewLine}" +
            $"{indent}【异常信息】{ex.Message}{Environment.NewLine}" +
            $"{indent}【异常对象】{ex.Source}{Environment.NewLine}" +
            $"{indent}【调用堆栈】{Environment.NewLine}   {ex.StackTrace?.Trim() ?? "N/A"}{Environment.NewLine}{Environment.NewLine}{Environment.NewLine}";

        if (ex.InnerException != null)
        {
            logEntry += GetLogEntry(ex.InnerException, depth + 1);
        }

        return logEntry;
    }

    private static void CleanupOldLogFiles()
    {
        var files = Directory.GetFiles(logDirectory, "*.log")
                .Select(f => new FileInfo(f))
                .Where(f => (DateTime.Now - f.LastWriteTime).TotalDays > maxLogFileAgeDays);

        foreach (var file in files)
        {
            File.Delete(file.FullName);
        }

    }
}

Rougamo 实现AOP

导包Rougamo.Fody

using DataParser.Helpers;
using Rougamo;
using Rougamo.Context;
namespace DataParser
{

    public class ExceptionLogAttribute : MoAttribute
    {
        public override void OnException(MethodContext context)
        {
            LogHelper.WriteExceptionLog(context.Exception);
            context.HandledException(this, null);
        }
    }
}
    public partial class MainViewModel:IRougamo<ExceptionLogAttribute>
    {

MainViewModel 类实现了接口 IRougamo<ExceptionLogAttribute>。这意味着在这个类中,所有被 ExceptionLogAttribute 特性标记的方法或类,都会在抛出异常时自动调用 ExceptionLogAttribute OnException 方法


http://www.kler.cn/a/451042.html

相关文章:

  • SQL Server数据库多主模式解决方案
  • 【Python】基础语法介绍
  • zabbix监控山石系列Hillstone监控模版(适用于zabbix7及以上)
  • 京东零售数据可视化平台产品实践与思考
  • 亚远景-SO 21434标准下的汽车网络安全:风险评估与管理的关键实践
  • CS!GO
  • 第二节:让电机转起来【51单片机-L298N-步进电机教程】
  • 台球助教平台系统开发APP和小程序信息收藏功能需求解析(第十二章)
  • React:前端开发领域的璀璨之星
  • RabbitMQ 的7种工作模式
  • 内部知识库的未来展望:技术融合与用户体验的双重升级
  • 小程序租赁系统开发指南与实现策略
  • myql explain sql分析详解
  • 千兆网中的gmii与rgmii
  • 【人工智能-初级】基于用户的协同过滤推荐算法
  • 超详细!一文搞定PID!嵌入式STM32-PID位置环和速度环
  • CMake 统一配置方式的优势
  • vue3中多层级路由缓存失效问题
  • 单元测试(UT,C++版)经验总结(gtest+gmock)
  • GitHub 桌面版配置 |可视化界面进行上传到远程仓库 | gitLab 配置【把密码存在本地服务器】
  • <论文>通过解耦注意力来增强Bert
  • Python数据可视化案例——折线图
  • Django 模型字段类型详解
  • 新手SEO指南如何快速入门与提升网站排名
  • HDR视频技术之十:MPEG 及 VCEG 的 HDR 编码优化
  • Pika Labs技术浅析(三):数据分析