客户端日志和异常处理

目录

  • 一. 使用Serilog结构化日志记录日志信息
  • 二. 捕获全局异常
  • 三. 使用AOP统一处理异常

一. 使用Serilog结构化日志记录日志信息

Serilog包的引用和使用语法都可以在网上找到(https://github.com/serilog/serilog/wiki/),不再赘述,这里仅分享一下自己在项目中的简单使用。
FileLogHelper帮助类对日志记录进行封装:

public static class FileLogHelper
{/// <summary>/// 日志模板/// </summary>private static string ErrorTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss} {Message:j} {NewLine} {Exception} {NewLine}";private static string InfoTemplate = "{Message:j} {NewLine}";/// <summary>/// 记录异常信息/// </summary>private static ILogger ErrorLog;/// <summary>/// 记录各类日志信息/// </summary>private static ILogger InfoLog;static FileLogHelper(){InitLog();}private static void InitLog(){ErrorLog = new LoggerConfiguration().MinimumLevel.Warning() // 最小日志级别,小于该级别的日志不会记录.WriteTo.Map(le => "",(d, lc) =>{lc.File($"Log/Error/{d:yyyyMMdd}.txt", // 日志文件路径,文件以日期命名rollingInterval: RollingInterval.Day, // 日志文件按天滚动创建,即每天创建新的文件outputTemplate: ErrorTemplate, // 指定日志输出模板rollOnFileSizeLimit: true, // 滚动文件是否限制大小 fileSizeLimitBytes: 52428800 // 滚动文件限制的大小,单位byte, 52428800为50M);}).CreateLogger();InfoLog = new LoggerConfiguration().MinimumLevel.Information().WriteTo.Map(le => "",(d, lc) =>{lc.File($"Log/Info/{d:yyyyMMdd}.txt",rollingInterval: RollingInterval.Day,outputTemplate: InfoTemplate,rollOnFileSizeLimit: true,fileSizeLimitBytes: 52428800);}).CreateLogger();}/// <summary>/// 记录异常信息/// </summary>/// <param name="exception"></param>/// <param name="message"></param>public static void LogError(Exception exception, string message = "错误"){ErrorLog.Error(exception, message);}/// <summary>/// 记录各类日志信息(结构化记录)/// </summary>/// <param name="title"></param>/// <param name="message"></param>/// <param name="detailMessage"></param>/// <param name="paramter"></param>public static void LogInfo(string title, string message, string detailMessage, object paramter){LogInfoView log = new LogInfoView(title, message, detailMessage, paramter);InfoLog.Information("{@log}", log);}/// <summary>/// 结构化日志对象/// </summary>private class LogInfoView{public LogInfoView(string title, string message, string detailMessage, object paramter){ this.Title = title;this.Message = message;this.DetailMessage = detailMessage;this.Paramter = paramter;}public string DateTime { get; set; } = System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");public string Title { get; set; }public string Message { get; set; }public string DetailMessage { get; set; }public object Paramter { get; set; }}
}

帮助类中创建了两个静态日志对象ErrorLog和InfoLog,方法LogError通过ErrorLog对象记录异常信息,方法LogInfo通过InfoLog对象记录一般日志信息:

  • ErrorLog和InfoLog分别指定了不同的日志文件目录(Log/Error/与Log/Info/),便于区分和查找;
  • InfoLog使用结构化日志,将日志信息包装成LogInfoView对象,在文本中以JSON格式记录。InfoLog使用了与ErrorLog不同的日志模板,仅保留了{Message:j},这样一条记录就是一个JSON数据,整个日志文件中的数据就是JSON Lines格式,方便内容解析。

按上述设置后,产生的日志文件目录如下:
在这里插入图片描述

结构化日志InfoLog产生的日志内容如下,每一行都是json格式:
(用户密码是不应该输出到日志的,这里只是为了演示效果):
在这里插入图片描述

非结构化的ErrorLog产生的日志内容如下:
在这里插入图片描述

二. 捕获全局异常

winform客户端中,如果程序发生异常后没有对其捕获处理,就会造成系统崩溃,影响用户体验。一种保险的方式是添加全局异常捕获事件,所有未处理的异常都会在这里被捕获到,从而避免系统直接闪崩。异常信息的记录使用了上面第一节中的FileLogHelper帮助类。

internal static class Program
{/// <summary>/// 应用程序的主入口点。/// </summary>[STAThread]static void Main(){Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);CatchApplicationException(); // 捕获全局异常Application.Run(new MainForm());}/// <summary>/// 捕获全局异常/// </summary>private static void CatchApplicationException(){//捕获UI异常Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);Application.ThreadException += OnUIThreadExceptionCatched;//捕获非UI异常AppDomain.CurrentDomain.UnhandledException += OnDomainExceptionCatched;}/// <summary>/// 捕获UI异常/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private static void OnUIThreadExceptionCatched(object sender, ThreadExceptionEventArgs e){FileLogHelper.LogError(e.Exception, "未处理的UI异常");MessageBox.Show("软件发生UI错误!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);}/// <summary>/// 捕获非UI异常/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private static void OnDomainExceptionCatched(object sender, UnhandledExceptionEventArgs e){FileLogHelper.LogError((Exception)e.ExceptionObject, "未处理的异常");MessageBox.Show("软件发生错误!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);}
}

三. 使用AOP统一处理异常

使用AOP处理异常的好处就是不用写大量的try-catch,一是减少重复的代码,二是不让try-catch影响核心代码的可读性;缺点就是统一处理异常后,灵活性会降低。要不要使用可以根据自己的具体情况来。

在异常处理方面,使用起来比较方面的AOP组件,这里推荐两个:一个是Postsharp(https://doc.postsharp.net/),另一个是Rougamo(肉夹馍,https://github.com/inversionhourglass/Rougamo)。推荐的原因是,这两个都可以通过特性Attribute标注的方式进行代码织入,都支持应用于方法、类、程序集等不同的粒度,且都支持设置可访问性属性。一些区别在于:Rougamo是完全开源的,可以随意使用;而Postsharp分商业版和社区版,引用ChatGPT上的解释,比较明了:

在这里插入图片描述

社区版虽说功能有限制,但也足够我们使用了。

这里便以PostSharp举例。如何引用Postsharp可以参考官方文档:https://doc.postsharp.net/deploymentconfiguration/deployment,在NuGet中直接搜索安装就可以。

我们创建一个自己的异常处理Attribute,如下:
(参考文档:https://doc.postsharp.net/custompatterns/aspects/tutorials/exception-handling)

/// <summary>
/// 异常处理
/// </summary>
[PSerializable]
public class LogAttribute : OnExceptionAspect
{/// <summary>/// 异常处理结束后的方法返回值/// </summary>public object ReturnValue { get; set; }/// <summary>/// 异常处理方式/// FlowBehavior.Return:忽略异常。如果方法有返回值,必须通过设置ReturnValue来设置方法的返回值。/// FlowBehavior.Continue:忽略异常,在OnException方法中,与FlowBehavior.Return的作用一样。///                       同样的,如果方法有返回值,必须通过设置ReturnValue来设置方法的返回值。/// FlowBehavior.RethrowException:在异常处理程序退出后重新引发原始异常。/// FlowBehavior.ThrowException:一旦异常处理程序退出,就会抛出一个新的异常。///                             当应该向用户隐藏原始异常的详细信息时,或者当要显示更有意义的异常时,这很有用。///                             抛出新异常时,必须将新异常对象分配给MethodExecutionArgs的exception成员。/// </summary>public FlowBehavior FlowBehavior { get; set; } = FlowBehavior.Return;/// <summary>/// 不记录日志/// </summary>public bool IgnoreLogRecord { get; set; } = false;/// <summary>/// 捕获异常/// </summary>/// <param name="args"></param>public override void OnException(MethodExecutionArgs args){if (!IgnoreLogRecord){// 记录异常日志FileLogHelper.LogError(args.Exception);}args.FlowBehavior = FlowBehavior; // 设置异常处理方式args.ReturnValue = ReturnValue; // 设置方法返回值}
}

自定义的LogAttribute 类中,新加了三个属性:ReturnValue、FlowBehavior、IgnoreLogRecord

  • ReturnValue属性:对于有返回值的方法发生异常时,通过ReturnValue属性,我们可以自定义异常处理结束后的方法返回值;
  • FlowBehavior属性:可以用来设置异常处理方式,上面的代码中我们默认设置为FlowBehavior.Return,即在异常处理程序OnException处理完后忽略异常。
  • IgnoreLogRecord属性:可以用来设置是否记录异常信息,配合上面第一节说的日志记录来使用,默认记录日志。

接下来我们举几个例子来演示如何使用它。

  1. 在有返回值的方法中,如果想忽略异常并指定异常发生后的返回值,可以这样使用:
private void OnLoginFormLoad(object sender, EventArgs e)
{m_allRoles = GetLoginRoles();
}/// <summary>
/// 获取角色集合
/// 发生异常后返回null
/// </summary>
/// <returns></returns>
[Log(ReturnValue = null)]
private List<LoginRole> GetLoginRoles()
{using (UDPDbContext dbContext = new UDPDbContext()){dbContext.Dispose(); // 这里故意释放掉数据库上下文,为的是让下面的代码抛出异常return dbContext.LoginRoles.ToList();}
}

窗体Load方法中调用GetLoginRoles()方法获取角色集合,GetLoginRoles()方法上我们用LogAttribute进行标记(特性标记可以省去特性类名后面的“Attribute”),并指定ReturnValue = null,即方法发生异常后,返回null。

我们打断点进行观察,异常发生后,进入异常处理程序OnException,在异常处理程序中记录日志(默认记录),设置异常处理方式(默认FlowBehavior.Return忽略异常),最后设置返回值ReturnValue,这个ReturnValue的值就是我们在方法上标记LogAttribute时指定的。
在这里插入图片描述
异常处理程序执行完后,程序继续执行,在OnLoginFormLoad方法中,发现GetLoginRoles()并没有抛出异常,并且返回了null,达到了预期效果。
在这里插入图片描述

  1. 如果担心基础层的日志帮助类FileLogHelper出现异常,这种情况下,一般要吞噬掉异常,不能让日志记录的异常影响到我们的业务逻辑,并且我们不应该再去让异常处理程序OnException去记录日志了,因为此时记录日志的代码已经不能正常工作了。

使用LogAttribute特性,我们可以这样处理:

[Log(AttributeTargetMemberAttributes = MulticastAttributes.Public, IgnoreLogRecord = true)]
public static class FileLogHelper
{/** 此处省略日志记录代码,与第一节中的代码保持一模一样*/
}

LogAttribute特性直接应用于FileLogHelper类上,并通过给AttributeTargetMemberAttributes 属性赋值MulticastAttributes.Public,使其仅对类中的public方法起作用;IgnoreLogRecord 赋值为true,则在异常处理程序OnException中不记录异常。

  1. 还可能遇到一种情况:对整个类的异常统一设置异常处理方式后,对其中的个别方法需要特殊处理。比如类中的大部分方法都不需要记录日志,但是对于其中的某个方法需要记录异常日志。
    举个代码示例,下面的MainForm窗体类中,除了登录方法OnLoginOutButtonClick外,其他的都不需要记录异常日志,那我们可以这样进行特性标注:
[Log(AspectPriority = 1, IgnoreLogRecord = true)]
public partial class MainForm : Form
{public MainForm(){InitializeComponent();}private void InitializeComponent(){// ...}private void OnMainFormLoad(object sender, EventArgs e){// ...}[Log(AspectPriority = 2, IgnoreLogRecord = false)]private void OnLoginOutButtonClick(object sender, EventArgs e){// ...}
}

由于使用了多层异常处理特性,我们需要使用AspectPriority属性来指定优先级。当符合多个异常处理配置的条件时,按优先级高的来处理(AspectPriority值大的)。参考文档:https://doc.postsharp.net/custompatterns/aspects/applying/attribute-multicasting

  1. 异常处理还可以应用于整个程序集,如下,在程序集文件AssemblyInfo.cs中进行配置,AssemblyInfo.cs文件在项目的Properties路径下:
    在这里插入图片描述

以上就是常见的使用场景。

这里再做一点扩展了解:
Postsharp和Rougamo都是通过静态织入的方式实现了AOP,即在编译时将额外的异常处理代码织入到我们的源代码中。
我们拿上面的日志帮助类FileLogHelper为例,使用[Log(AttributeTargetMemberAttributes = MulticastAttributes.Public, IgnoreLogRecord = true)]进行标记后,进行编译,然后将编译后的dll使用反编译工具查看源码:

在这里插入图片描述
可以看出,FileLogHelper类中的Public方法在编译后自动加上了try-catch块。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/56644.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入浅出Transformer

作者&#xff1a;麦克船长 链接&#xff1a;https://www.zhihu.com/question/471328838/answer/2864224369 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 如果说「从浅入深」理解 Transformer&#xff0c;逐渐要到…

自然语言处理 Transformer 模型

一、Transformer 模型出现之前的NLP 语言模型 1、N 元文法语言模型 1.1、马尔科夫假设&#xff08;Markov Assumption&#xff09;与 N 元文法语言模型&#xff08;N-gram Language Model&#xff09; 下一个词出现的概率只依赖于它前面 n-1 个词&#xff0c;这种假设被称为马尔…

一文速览大语言模型在分子领域中的探索

随着 ChatGPT 的快速崛起&#xff0c;大型语言模型&#xff08;LLM&#xff09;已经在人类语言建模领域展示出了其非凡的能力。无论是证明数学公式、编写代码&#xff0c;还是以不同的风格创作诗歌&#xff0c;LLM 都能胜任。然而&#xff0c;尽管 LLM 在人类语言的掌握上已达到…

“AI+教育”:景联文科技高质量教育GPT题库助力教学创新

去年年底&#xff0c;OpenAI推出ChatGPT&#xff0c;掀起AI热潮&#xff0c;教育作为“AI”应用落地的关键场景&#xff0c;再次受到广泛关注。 “AI教育”的快速发展&#xff0c;是受到技术、需求和政策三重因素共同驱动的结果。 在技术方面&#xff0c;随着人工智能技术的不断…

对话系统文献综述

一、简介 论文&#xff1a;https://arxiv.org/pdf/1711.01731.pdf 该论文对对话系统进行了综述&#xff0c;并讨论了今后可能的研究方向。 目前的对话系统大致被分成两类&#xff1a; 1 任务导向的对话系统 任务为导向的对话系统是帮助用户去完成特定任务&#xff0c;比如找…

如何利用大数据分析技术预测员工离职?

为什么我们要对员工离职进行预测&#xff0c;因为它会帮助公司减少员工离职成本的消耗。 本文为美国肯尼索州立大学刘丽媛&#xff08;Lilian&#xff09;在HR成长部落2019年6月23日的线下活动分享文字实录&#xff0c;希望对大家在数据分析在HR领域的应用有所启发。 大家好&am…

Python员工离职数据分析

Python员工离职数据分析 import pandas as pd import seaborn as sns import matplotlib.pyplot as plt import warnings warnings.filterwarnings(ignore)# 数据全显示 pd.set_option(display.max_columns, None) # 颜色 colors sns.color_palette() # 数据精度 pd.set_opti…

从数据分析,看公司员工流失率分析报告

一、概述&#xff1a; 1、员工离职和员工流失率的概念&#xff1a; 员工离职&#xff1a;员工被辞退及辞职、不续签的情况 员工流失率&#xff08;离职率&#xff09;&#xff1a;公司离职的员工人数/&#xff08;入职人数最初原有人数&#xff09;*100% 例如&#xff1a;月…

数据分析——员工离职预测

员工离职预测 一. 题目描述二. 数据说明三. 步骤实现1.导入并查看2.数据探索性分析3.数据处理4.模型分析5.结果分析(1) 可视化的结果分析(2&#xff09;特征选择结果分析(3)多模型交叉验证结果分析 一. 题目描述 员工是否准备离职是困扰用人单位的问题&#xff0c;其关系到单位…

逾2500名选手竞跑乌鲁木齐冰雪马拉松

中新社乌鲁木齐1月13日电(记者 孙亭文) 2019乌鲁木齐丝绸之路冰雪马拉松(简称“冰马”)13日鸣枪开跑&#xff0c;逾2500名跑友在天山山麓的“冰天雪地”中竞跑。 1月13日&#xff0c;2019乌鲁木齐丝绸之路冰雪马拉松在乌鲁木齐市城南的天山脚下鸣枪开跑&#xff0c;逾2500名跑友…

html 跑步比赛小游戏,疯狂趣味跑步竞赛3D

疯狂趣味跑步竞赛3D是一款跑酷类型的休闲游戏&#xff0c;游戏在整体画面上搭配了最新的技术&#xff0c;使得游戏画面看起来非常华丽&#xff0c;人物的建模也很可爱。玩家在游戏中可以在各种风格的跑道上进行比赛&#xff0c;还可以在线跟别的玩家进行匹配。喜欢这款游戏的快…

oracle计算日期倒计时,时间倒算回去到了哪里?│机械倒数计时码表

Laura Lan蓝思晴 《大寫的蘿菈》主笔 华语圈知名钟表珠宝评论家&#xff0c;专精机械钟表赏析&#xff0c;亦对珠宝、生活、文化与艺术等各领域皆有着独到的见解。创办《引想力工作室》从事专业文案、顾问、讲习等工作。 所有不能实现的&#xff0c;制表大师们都曾经挖空心思研…

茂名天源石化等项目开工 石化业高质量发展看广东

目前来看&#xff0c;广东省已经拥有诸多国外化工巨头、大型民营炼化企业和不少国企的炼化项目&#xff0c;成为很多石化企业首选的项目落地基地。“石化业高质量发展看广东”&#xff0c;已经逐渐明朗。今年以来&#xff0c;已有恒力石化(惠州)PTA项目、东华能源(茂名)烷烃资源…

c++并行编程中的“锁”难题

linux服务器开发相关视频解析&#xff1a; 在并行程序中&#xff0c;锁的使用会主要会引发两类难题&#xff1a;一类是诸如死锁、活锁等引起的多线程Bug&#xff1b;另一类是由锁竞争引起的性能瓶颈。本文将介绍并行编程中因为锁引发的这两类难题及其解决方案。 1、用锁来防止…

恒力石化、茂名天源石化等项目开工 2025年广东石化产业营收力争超2万亿

目前来看&#xff0c;广东省已经拥有诸多国外化工巨头、大型民营炼化企业和不少国企的炼化项目&#xff0c;成为很多石化企业首选的项目落地基地。“石化业高质量发展看广东”&#xff0c;已经逐渐明朗。 今年3月31日&#xff0c;广东省发展改革委官网公布《广东省2021年重点建…

Linux中的线程

1.线程的基本概念 2.线程和进程的区别 线程安全 *线程的同步 线程的调度 线程的通信编程思想之多线程与多进程(1)——以操作系统的角度述说线程与进程_阳光日志-CSDN博客_多线程和多进程编程线程是什么&#xff1f;要理解这个概念&#xff0c;须要先了解一下操作系统的一些…

武汉超级计算机中心,加快打造“五个中心” 武汉率先开建人工智能计算中心...

(来源&#xff1a;武汉市发改委) 原标题&#xff1a;加快打造“五个中心” 武汉率先开建人工智能计算中心 从华为东莞松山湖基地运来的预制化模块箱体&#xff0c;正在光谷科学岛起步区被吊装&#xff0c;未来将被“拼装”成武汉重要的人工智能算力基础设施。3月1日&#xff0c…

产业丨国产数据库行业火热,厂商各有各的算盘

前言&#xff1a; 随着数字经济的不断发展&#xff0c;未来数据库发展有着四个趋势&#xff1a;开源、HTAP、云原生以及和大数据技术融合。 如今&#xff0c;随着众多企业入局&#xff0c;国产数据库正在打着一场激烈的翻身仗。 前言&#xff1a; 随着数字经济的不断发展&a…

不到一年涨粉849万,“神级账号”小鱼海棠背后成功的秘诀是什么?

如何能在短时间内精确吸粉百万&#xff0c;快速度过账号冷启动&#xff0c;跻身头部网红的行列&#xff1f;快手视频博主“小鱼海棠”就交出了一份完美的答卷。 凭借牢牢抓住与999个帅哥拍照这一创意点&#xff0c;“小鱼海棠”一战封神&#xff0c;成为网红界一匹飞速狂奔的黑…

灿谷集团荣获“公益践行奖”

2022年1月13-14日&#xff0c;“第十一届公益节暨企业社会责任嘉年华”在上海举行。在公益节上&#xff0c;灿谷集团凭借在履行企业社会责任方面的杰出表现&#xff0c;获评“2021年度公益践行奖”。 公益节设立于2011年&#xff0c;是国内首个由大众媒体联袂发起的以“公益”…