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

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.lryc.cn/news/509038.html

相关文章:

  • webrtc学习----前端推流拉流,局域网socket版,一对多
  • 美国加州房价数据分析01
  • 用Python开启人工智能之旅(四)深度学习的框架和使用方法
  • 两分钟解决:vscode卡在设置SSH主机,VS Code-正在本地初始化VSCode服务器
  • 信号仿真高级工程师面试题
  • 循环和迭代
  • 一个简单封装的的nodejs缓存对象
  • 【Rust自学】5.3. struct的方法(Method)
  • ChatGPT之父:奥尔特曼
  • 如何在谷歌浏览器中设置桌面快捷方式
  • systemverilog中的priority if
  • 图像处理-Ch2-空间域的图像增强
  • css 编写注意-1-命名约定
  • 虚幻引擎反射机制
  • Knife4j Swagger
  • Xcode 16 编译弹窗问题、编译通过无法,编译通过打包等问题汇总
  • 卷积神经网络入门指南:从原理到实践
  • eNSP安装教程(内含安装包)
  • VBA技术资料MF244:利用VBA在图表工作表中创建堆积条形图
  • 【计算机网络安全】网络攻击
  • 20241230 基础数学-线性代数-(1)求解特征值(numpy, scipy)
  • 基于图注意力网络的两阶段图匹配点云配准方法
  • 【半导体光电子器件】课后习题答案和知识点汇总
  • Unity命令行传递自定义参数 命令行打包
  • web-worker应用在大文件切片上传
  • Django 模板分割及多语言支持案例【需求文档】-->【实现方案】
  • C中设计不允许继承的类的实现方法是什么?
  • 面对小白的C语言学习方法
  • 使用libgif库解码全过程(C语言)-包括扩展块的处理
  • blazor实现ASP.NET网站用户批量注册方法