Net Core集成Exceptionless分布式日志功能以及全局异常过滤

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:

# Net Core集成Exceptionless分布式日志功能以及全局异常过滤

相信很多朋友都看过我的上篇关于Exceptionless的简单入门教程asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 上篇文章只是简单的介绍了Exceptionless是什么?能做什么呢?以及怎么进行本地部署和异常提交的简单用法,而这篇文章将带你探讨一下Exceptionless的异常收集高级用法以及你熟悉的类似NLog的日志用法。

这篇文章有一部分内容翻译自官方文档,点我阅读 英语好的可以自行阅读 。当然中间很多代码我都进行了重构,还有参考周旭龙的代码,进行了简单地封装,同时加入了为webapi加入异常全局过滤器进行异常日志的记录。希望对大家有所帮助。

本文地址:https://www.cnblogs.com/yilezhu/p/9339017.html
作者:依乐祝

手动发送错误

上篇文章介绍了,导入命名空间后,并使用如下代码就可以简单地提交异常日志:

try {
    throw new ApplicationException(Guid.NewGuid().ToString());
} catch (Exception ex) {
    ex.ToExceptionless().Submit();
}

发送附加信息

当然你还可以为发送的事件添加额外的标记信息,比如坐标,标签,以及其他的用户相关的信息等等

try {
    throw new ApplicationException("Unable to create order from quote.");
} catch (Exception ex) {
    ex.ToExceptionless()
        // 设置一个ReferenceId方便查找
        .SetReferenceId(Guid.NewGuid().ToString("N"))
        // 添加一个不包含CreditCardNumber属性的对象信息
        .AddObject(order, "Order", excludedPropertyNames: new [] { "CreditCardNumber" }, maxDepth: 2)
        // 设置一个名为"Quote"的编号
        .SetProperty("Quote", 123)
        // 添加一个名为“Order”的标签
        .AddTags("Order")
        //  标记为关键异常
        .MarkAsCritical()
        // 设置一个位置坐标
        .SetGeo(43.595089, -88.444602)
        // 在你的系统中设置userid并提供一个有好的名字,俗称昵称
        .SetUserIdentity(user.Id, user.FullName)
        // 为异常信息添加一些用户描述信息.
        .SetUserDescription(user.EmailAddress, "I tried creating an order from my saved quote.")
        // 提交.
        .Submit();
}

统一修改未处理的异常报告

你可以在通过SubmittingEvent 事件设置全局的忽略异常信息添加一些自定义信息等等


 #region Exceptionless配置
            ExceptionlessClient.Default.Configuration.ApiKey = exceptionlessOptions.Value.ApiKey;
            ExceptionlessClient.Default.Configuration.ServerUrl = exceptionlessOptions.Value.ServerUrl;
            ExceptionlessClient.Default.SubmittingEvent += OnSubmittingEvent;
            app.UseExceptionless();
#endregion

 /// <summary>
        /// 全局配置Exceptionless
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnSubmittingEvent(object sender, EventSubmittingEventArgs e)
        {
            // 只处理未处理的异常
            if (!e.IsUnhandledError)
                return;

            // 忽略404错误
            if (e.Event.IsNotFound())
            {
                e.Cancel = true;
                return;
            }

            // 忽略没有错误体的错误
            var error = e.Event.GetError();
            if (error == null)
                return;
            // 忽略 401 (Unauthorized) 和 请求验证的错误.
            if (error.Code == "401" || error.Type == "System.Web.HttpRequestValidationException")
            {
                e.Cancel = true;
                return;
            }
            // Ignore any exceptions that were not thrown by our code.
            var handledNamespaces = new List<string> { "Exceptionless" };
            if (!error.StackTrace.Select(s => s.DeclaringNamespace).Distinct().Any(ns => handledNamespaces.Any(ns.Contains)))
            {
                e.Cancel = true;
                return;
            }
            // 添加附加信息.
            //e.Event.AddObject(order, "Order", excludedPropertyNames: new[] { "CreditCardNumber" }, maxDepth: 2);
            e.Event.Tags.Add("MunicipalPublicCenter.BusinessApi");
            e.Event.MarkAsCritical();
            //e.Event.SetUserIdentity();
        }

配合使用 NLog 或 Log4Net

有时候,程序中需要对日志信息做非常详细的记录,比如在开发阶段。这个时候可以配合 log4net 或者 nlog 来联合使用 exceptionless,详细可以查看这个官方的 示例。

如果你的程序中有在短时间内生成大量日志的情况,比如一分钟产生上千的日志。这个时候你需要使用内存存储(in-memory store)事件,这样客户端就不会将事件系列化的磁盘,所以会快很多。这样就可以使用Log4net 或者 Nlog来将一些事件存储到磁盘,另外 Exceptionless 事件存储到内存当中。

Exceptionless 日志记录的封装

  1. 首先简单地封装一个ILoggerHelper接口

    /// <summary>
    /// lzhu
    /// 2018.7.19
    /// 日志接口
    /// </summary>
    public interface ILoggerHelper
    {
     /// <summary>
     /// 记录trace日志
     /// </summary>
     /// <param name="source">信息来源</param>
     /// <param name="message">日志内容</param>
     /// <param name="args">标记</param>
     void Trace(string source, string message, params string[] args);
     /// <summary>
     /// 记录debug信息
     /// </summary>
     /// <param name="source">信息来源</param>
     /// <param name="message">日志内容</param>
     /// <param name="args">标记</param>
     void Debug(string source, string message, params string[] args);
     /// <summary>
     /// 记录信息
     /// </summary>
     /// <param name="source">信息来源</param>
     /// <param name="message">日志内容</param>
     /// <param name="args">标记</param>
     void Info(string source, string message, params string[] args);
     /// <summary>
     /// 记录警告日志
     /// </summary>
     /// <param name="source">信息来源</param>
     /// <param name="message">日志内容</param>
     /// <param name="args">标记</param>
     void Warn(string source, string message, params string[] args);
     /// <summary>
     /// 记录错误日志
     /// </summary>
     /// <param name="source">信息来源</param>
     /// <param name="message">日志内容</param>
     /// <param name="args">标记</param>
     void Error(string source, string message, params string[] args);
    }
  1. 既然有了接口,那么当然得实现它了

    /// <summary>
    /// lzhu
    /// 2018.7.19
    /// Exceptionless日志实现
    /// </summary>
    public class ExceptionlessLogger : ILoggerHelper
    {
     /// <summary>
     /// 记录trace日志
     /// </summary>
     /// <param name="source">信息来源</param>
     /// <param name="message">日志内容</param>
     /// <param name="args">添加标记</param>
     public void Trace(string source,string message, params string[] args)
     {
         if (args != null && args.Length > 0)
         {
             ExceptionlessClient.Default.CreateLog(source, message, LogLevel.Trace).AddTags(args).Submit();
    
         }
         else
         {
             ExceptionlessClient.Default.SubmitLog(source, message, LogLevel.Trace);
         }
     }
     /// <summary>
     /// 记录debug信息
     /// </summary>
     /// <param name="source">信息来源</param>
     /// <param name="message">日志内容</param>
     /// <param name="args">标记</param>
     public void Debug(string source, string message, params string[] args)
     {
         if (args != null && args.Length > 0)
         {
             ExceptionlessClient.Default.CreateLog(source, message, LogLevel.Debug).AddTags(args).Submit();
         }
         else
         {
             ExceptionlessClient.Default.SubmitLog(source, message, LogLevel.Debug);
         }
     }
     /// <summary>
     /// 记录信息
     /// </summary>
     /// <param name="source">信息来源</param>
     /// <param name="message">日志内容</param>
     /// <param name="args">标记</param>
     public void Info(string source, string message, params string[] args)
     {
         if (args != null && args.Length > 0)
         {
             ExceptionlessClient.Default.CreateLog(source, message, LogLevel.Info).AddTags(args).Submit();
         }
         else
         {
             ExceptionlessClient.Default.SubmitLog(source, message, LogLevel.Info);
         }
     }
     /// <summary>
     /// 记录警告日志
     /// </summary>
     /// <param name="source">信息来源</param>
     /// <param name="message">日志内容</param>
     /// <param name="args">标记</param>
     public void Warn(string source, string message, params string[] args)
     {
         if (args != null && args.Length > 0)
         {
             ExceptionlessClient.Default.CreateLog(source, message, LogLevel.Warn).AddTags(args).Submit();
         }
         else
         {
             ExceptionlessClient.Default.SubmitLog(source, message, LogLevel.Warn);
         }
     }
     /// <summary>
     /// 记录错误日志
     /// </summary>
     /// <param name="source">信息来源</param>
     /// <param name="message">日志内容</param>
     /// <param name="args">标记</param>
     public void Error(string source, string message, params string[] args)
     {
         if (args != null && args.Length > 0)
         {
             ExceptionlessClient.Default.CreateLog(source, message, LogLevel.Error).AddTags(args).Submit();
         }
         else
         {
             ExceptionlessClient.Default.SubmitLog(source, message, LogLevel.Error);
         }
     }
    }
  2. 当然实现好了,可别忘了依赖注入哦

     //注入ExceptionlessLogger服务
         services.AddSingleton<ILoggerHelper, ExceptionlessLogger>();
  3. 这时候该写一个全局异常过滤器了

    /// <summary>
    /// lzhu
    /// 2018.7.19
    /// 定义全局过滤器
    /// </summary>
    public class GlobalExceptionFilter : IExceptionFilter
    {
     
     private readonly ILoggerHelper _loggerHelper;
     //构造函数注入ILoggerHelper
     public GlobalExceptionFilter(ILoggerHelper loggerHelper)
     {
         _loggerHelper = loggerHelper;
     }
    
     public void OnException(ExceptionContext filterContext)
     {
         _loggerHelper.Error(filterContext.Exception.TargetSite.GetType().FullName, filterContext.Exception.ToString(), MpcKeys.GlobalExceptionCommonTags, filterContext.Exception.GetType().FullName);
         var result = new BaseResult()
         {
             errcode = ResultCodeAddMsgKeys.CommonExceptionCode,//系统异常代码
             errmsg= ResultCodeAddMsgKeys.CommonExceptionMsg,//系统异常信息
         };
         filterContext.Result = new ObjectResult(result);
         filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
         filterContext.ExceptionHandled = true;
     }
    }
  4. 全局过滤器写好了,怎么让它生效呢,客观别急啊,上正菜

      //添加验证
         services.AddMvc(options=> {
             options.Filters.Add<GlobalExceptionFilter>();
         }).AddFluentValidation();
  5. 哈哈,没什么说的了,代码都已经写好了,剩下的就是上代码测试结果了。我这里只是简单地api测试下,万能的ValuesController登场:

     // GET api/values/5
     [HttpGet("{id}")]
     public ActionResult<string> Get(int id)
     {
         //try
         //{
         throw new Exception($"测试抛出的异常{id}");
         //}
         //catch (Exception ex)
         //{
         //    ex.ToExceptionless().Submit();
         //}
         //return "Unknown Error!";
    
     }

这里是直接抛出异常,不进行trycatch,这时候异常会被全局过滤器捕获,然后放到Exceptionless的Log里面,别问我为什么会在log里面,因为我全局过滤器代码里面已经写明了,不明白的回去看代码,然后看接口调用的实现方法。下面上结果:

1532012181451

点进去,看看详细信息:

1532012249948

  1. 再测试下使用try catch捕获的异常处理,这时候异常信息会被提交到Exception这个里面。直接上代码吧

    // GET api/values/5
    [HttpGet("{id}")]
    public ActionResult<string> Get(int id)
    {
     try
     {
         throw new Exception($"测试抛出的异常{id}");
     }
     catch (Exception ex)
     {
         ex.ToExceptionless().Submit();
     }
     return "Unknown Error!";
    
    }

到exceptionless里面看看不活的异常吧。打字很累直接上图吧

1532012444443

点进去看看详细信息,有三个tab,下面之粘贴一个图片了:

1532012508157

### 最后,源码就不上了,因为上面代码很清楚了
### 总结
本文没有对Exceptionless进行过多地介绍,因为博主的上篇文章 已经进行了详细的介绍。直接切入正题,先对官方高级用法进行了简单地翻译。然后对Exceptionless Log这个eventtype进行了简单地封装,让你可以像使用NLog一样很爽的使用Exceptionless。最后通过一个asp.net core web api的项目进行了演示,在全局过滤器中利用封装的Log方法进行全局异常的捕获。希望对大家使用Exceptionless有所帮助。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
7月前
|
人工智能 API C#
使用Microsoft.Extensions.AI简化.NET中的AI集成
使用Microsoft.Extensions.AI简化.NET中的AI集成
157 5
使用Microsoft.Extensions.AI简化.NET中的AI集成
|
6月前
|
开发框架 缓存 .NET
GraphQL 与 ASP.NET Core 集成:从入门到精通
本文详细介绍了如何在ASP.NET Core中集成GraphQL,包括安装必要的NuGet包、创建GraphQL Schema、配置GraphQL服务等步骤。同时,文章还探讨了常见问题及其解决方法,如处理复杂查询、错误处理、性能优化和实现认证授权等,旨在帮助开发者构建灵活且高效的API。
149 3
|
6月前
|
存储 运维 数据可视化
如何为微服务实现分布式日志记录
如何为微服务实现分布式日志记录
315 1
|
8月前
|
存储 数据采集 分布式计算
Hadoop-17 Flume 介绍与环境配置 实机云服务器测试 分布式日志信息收集 海量数据 实时采集引擎 Source Channel Sink 串行复制负载均衡
Hadoop-17 Flume 介绍与环境配置 实机云服务器测试 分布式日志信息收集 海量数据 实时采集引擎 Source Channel Sink 串行复制负载均衡
138 1
|
9月前
|
开发框架 NoSQL .NET
利用分布式锁在ASP.NET Core中实现防抖
【9月更文挑战第5天】在 ASP.NET Core 中,可通过分布式锁实现防抖功能,仅处理连续相同请求中的首个请求,其余请求返回 204 No Content,直至锁释放。具体步骤包括:安装分布式锁库如 `StackExchange.Redis`;创建分布式锁服务接口及其实现;构建防抖中间件;并在 `Startup.cs` 中注册相关服务和中间件。这一机制有效避免了短时间内重复操作的问题。
200 4
|
9月前
|
运维 NoSQL Java
SpringBoot接入轻量级分布式日志框架GrayLog技术分享
在当今的软件开发环境中,日志管理扮演着至关重要的角色,尤其是在微服务架构下,分布式日志的统一收集、分析和展示成为了开发者和运维人员必须面对的问题。GrayLog作为一个轻量级的分布式日志框架,以其简洁、高效和易部署的特性,逐渐受到广大开发者的青睐。本文将详细介绍如何在SpringBoot项目中接入GrayLog,以实现日志的集中管理和分析。
566 1
|
10月前
|
jenkins 测试技术 持续交付
解锁.NET项目高效秘籍:从理论迷雾到实践巅峰,持续集成与自动化测试如何悄然改变游戏规则?
【8月更文挑战第28天】在软件开发领域,持续集成(CI)与自动化测试已成为提升效率和质量的关键工具。尤其在.NET项目中,二者的结合能显著提高开发速度并保证软件稳定性。本文将从理论到实践,详细介绍CI与自动化测试的重要性,并以ASP.NET Core Web API项目为例,演示如何使用Jenkins和NUnit实现自动化构建与测试。每次代码提交后,Jenkins自动触发构建流程,通过编译和运行NUnit测试确保代码质量。这种方式不仅节省了时间,还能快速发现并解决问题,推动.NET项目开发迈向更高水平。
94 8
|
10月前
|
消息中间件 JSON 自然语言处理
Python多进程日志以及分布式日志的实现方式
python日志模块logging支持多线程,但是在多进程下写入日志文件容易出现下面的问题: PermissionError: [WinError 32] 另一个程序正在使用此文件,进程无法访问。 也就是日志文件被占用的情况,原因是多个进程的文件handler对日志文件进行操作产生的。
|
10月前
|
开发框架 .NET 数据库连接
闲话 Asp.Net Core 数据校验(三)EF Core 集成 FluentValidation 校验数据例子
闲话 Asp.Net Core 数据校验(三)EF Core 集成 FluentValidation 校验数据例子
154 1
|
9月前
|
存储 开发框架 前端开发
ASP.NET MVC 迅速集成 SignalR
ASP.NET MVC 迅速集成 SignalR
174 0

热门文章

最新文章