aspnetcore配置log4net并添加全局异常处理
文章转自https://www.cnblogs.com/diwu0510/p/10873142.html
第一步:在NuGet中引用log4net
第二步:创建log4net.config
<?xml version="1.0" encoding="utf-8"?> <configuration> <log4net> <!-- 错误日志类--> <logger name="Error"> <level value="ALL" /> <appender-ref ref="ErrorAppender" /> </logger> <!-- 错误日志附加介质--> <appender name="ErrorAppender" type="log4net.Appender.RollingFileAppender"> <!--日志文件路径--> <param name="File" value="Logs\\Error\\" /> <!--是否是向文件中追加日志--> <param name="AppendToFile" value="true" /> <!--log保留天数--> <param name="MaxSizeRollBackups" value="1000" /> <!--最大文件大小--> <param name="MaxFileSize" value="10240" /> <!--日志文件名是否是固定不变的--> <param name="StaticLogFileName" value="false" /> <!--日志文件名格式为:2008-08-31.log--> <param name="DatePattern" value="yyyy-MM-dd.'log'" /> <!--日志根据日期滚动--> <param name="RollingStyle" value="Date" /> <!--信息日志布局--> <layout type="log4net.Layout.PatternLayout"> <param name="ConversionPattern" value="%n==========%n【日志级别】:%-5level%n【记录时间】:%date %n【执行时间】:[%r]毫秒%n%message%n" /> </layout> </appender> <!-- 信息日志类 --> <logger name="Info"> <level value="ALL" /> <appender-ref ref="InfoAppender" /> </logger> <!-- 信息日志附加介质--> <appender name="InfoAppender" type="log4net.Appender.RollingFileAppender"> <!--日志文件路径--> <param name="File" value="Logs\\Info\\" /> <!--是否是向文件中追加日志--> <param name="AppendToFile" value="true" /> <!--log保留天数--> <param name="MaxSizeRollBackups" value="100" /> <param name="MaxFileSize" value="1" /> <!--日志文件名是否是固定不变的--> <param name="StaticLogFileName" value="false" /> <!--日志文件名格式为:2008-08-31.log--> <param name="DatePattern" value="yyyy-MM-dd.'log'" /> <!--日志根据日期滚动--> <param name="RollingStyle" value="Date" /> <!--信息日志布局--> <layout type="log4net.Layout.PatternLayout"> <param name="ConversionPattern" value="%n==========%n【日志级别】:%-5p%n【记录时间】:%d [%t]%n【信息详情】:%m%n" /> </layout> </appender> </log4net> </configuration>
第三步:新建Log4NetConfig.cs类,这里我是把工厂名放在配置文件中获取,一般情况下不需要这样操作,直接在代码内写死即可。
using log4net; using log4net.Config; using Microsoft.Extensions.Configuration; using System; using System.IO; namespace Taoxue.SchoolRoll.Website { public class Log4NetConfig { public static string RepositoryName { get; set; } public static void Init(IConfiguration configuration) { var repositoryName = configuration.GetSection("Log4Net:RepositoryName").Value; if (string.IsNullOrWhiteSpace(repositoryName)) { throw new Exception("必须在配置文件中添加 Log4Net > RepositoryName 节点"); } RepositoryName = repositoryName; var configFilePath = configuration.GetSection("Log4Net:ConfigFilePath").Value; if (string.IsNullOrWhiteSpace(configFilePath)) { configFilePath = "log4net.config"; } var file = new FileInfo(configFilePath); var repository = LogManager.CreateRepository(repositoryName); XmlConfigurator.Configure(repository, file); } } }
第四步:在startup.cs中初始化配置
public Startup(IConfiguration configuration) { Configuration = configuration; Log4NetConfig.Init(Configuration); }
第五步:在appsettings.json中创建Log4net节点,注意,这里我是把log4net.config放在 站点根目录/Log4Net 目录下
"Log4Net": { "RepositoryName": "NETCoreRepository", "ConfigFilePath": "Log4Net/log4net.config" }
第六步:创建Log4NetUtil.cs文件
using log4net; using System; namespace Taoxue.SchoolRoll.Website { public class Log4NetUtil { private static readonly ILog ErrorLog = LogManager.GetLogger(Log4NetConfig.RepositoryName, "Error"); private static readonly ILog InfoLog = LogManager.GetLogger(Log4NetConfig.RepositoryName, "Info"); /// <summary> /// 全局异常错误记录持久化 /// </summary> /// <param name="throwMsg"></param> /// <param name="ex"></param> public static void LogError(string throwMsg, Exception ex) { var errorMsg = $"【抛出信息】:{throwMsg} \r\n【异常类型】:{ex.GetType().Name} \r\n【异常信息】:{ex.Message} \r\n【堆栈调用】:\r\n{ex.StackTrace}"; ErrorLog.Error(errorMsg); } public static void LogInfo(string msg) { InfoLog.Info(msg); } } }
至此,Log4Net配置完成,下面添加一个全局异常处理类测试下
using HZC.Core; using HZC.Utils.Mvc; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using System; namespace Taoxue.SchoolRoll.Website.Extensions { public class MvcGlobalExceptionFilter : IExceptionFilter { public void OnException(ExceptionContext context) { try { Log4NetUtil.LogError("Mvc全局异常", context.Exception); if (context.HttpContext.Request.IsAjax()) { context.Result = new JsonResult(ResultUtil.Fail(context.Exception.Message)); } else { context.Result = new RedirectToActionResult("Error", "Home", new { }); } } catch (Exception e) { Console.WriteLine(e); throw; }
context.ExceptionHandled = true; // 注意:如果不添加这句代码,程序不会自动断路,会继续向下进行。 } } }
在 startup.cs 的 ConfigureServices 中使用如下代码
services.AddMvc(option => { option.Filters.Add<MvcGlobalExceptionFilter>(); }) .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
修改 HomeController 的 Index ,手动抛出异常
public IActionResult Index() { throw new Exception("异常测试"); return View(); }
执行程序,在 根目录/Logs/Error 文件夹下查看生成的日志文件,发现已经写入如下内容
========== 【日志级别】:ERROR 【记录时间】:2019-05-16 00:40:49,071 【执行时间】:[1750]毫秒 【抛出信息】:Mvc全局异常 【异常类型】:Exception 【异常信息】:异常测试 【堆栈调用】: at Taoxue.SchoolRoll.Website.Controllers.HomeController.Index() in E:\项目\CSharp\xx\xxxx\Taoxue.SchoolRoll.Website\Controllers\HomeController.cs:line 19 at lambda_method(Closure , Object , Object[] ) at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters) at Microsoft.AspNetCore.Mvc.Internal.ActionMethodExecutor.SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeActionMethodAsync() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextExceptionFilterAsync()
到这里,log4net配置和全局异常捕捉就完成了。
需要注意的是,这里所谓的全局是指进入mvc中间件后产生的异常,如果在其他中间件中异常了,上面的方法是捕捉不到的,下面来模拟一下:
在 startup.cs 的 Configure 方法中 app.UserMvc(...) 之前添加如下代码
app.Use(async (context, next) => { if (context.Request.Path.Value.Contains("test")) { throw new Exception("中间件异常测试"); } await next(); });
上面代码的作用是,当访问地址中带有 test 则抛出异常。
执行程序,访问 http://localhost:5000/test 会发现,程序报错,这时查看日志会发现,异常并没有被记录,也就是说,异常没有被我们的MvcGlobalExceptionFilter捕捉到,这显然不是我们想要的。
如果要捕捉到这种异常需要怎么处理呢?其实程序中已经给出了提示:
if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); }
这是 startup.cs Configure 中自动帮我们添加的代码,就是用于异常的捕获和处理,一种方式是对 /Home/Error 进行处理,在该action内记录异常,这种方法比较简单,这里就不详细说了。
接下来,我们用自定义中间件的方式来实现一下。
新建 GlobalExceptionMiddleware.cs 文件
using HZC.Core; using HZC.Utils.Mvc; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using System; using System.Threading.Tasks; namespace Taoxue.SchoolRoll.Website.Extensions { public class GlobalExceptionMiddleWare { public readonly RequestDelegate Next; public GlobalExceptionMiddleWare(RequestDelegate next) { Next = next; } public async Task Invoke(HttpContext context) { try { await Next(context); } catch (Exception e) { context.Response.Clear(); context.Response.StatusCode = StatusCodes.Status200OK; Log4NetUtil.LogError("全局异常", e); if (context.Request.IsAjax()) { context.Response.ContentType = ResponseContentTypes.Json; await context.Response.WriteAsync(JsonConvert.SerializeObject(new { Code = 500, Message = e.Message })); } else { context.Response.Redirect("/Home/Error"); } } } } }
建议在 Invoke 方法中 catch 代码块中再包一层 try..catch ,以防止处理代码发生错误时出现异常。
接下来,修改 startup.cs 的 Configure 方法,注释掉系统自带的异常处理代码,添加我们自定义的中间件
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { //if (env.IsDevelopment()) //{ // app.UseDeveloperExceptionPage(); //} //else //{ // app.UseExceptionHandler("/Home/Error"); // app.UseHsts(); //} app.UseMiddleware<GlobalExceptionMiddleWare>(); app.Use(async (context, next) => { if (context.Request.Path.Value.Contains("test")) { throw new Exception("中间件异常测试"); } await next(); }); app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); }
再次执行程序,访问 http://localhost:5000/test 会发现我们定义的逻辑被执行,并且该异常也被记录到了日志文件中
========== 【日志级别】:ERROR 【记录时间】:2019-05-16 01:20:56,051 【执行时间】:[7830]毫秒 【抛出信息】:全局异常 【异常类型】:Exception 【异常信息】:中间件异常测试 【堆栈调用】: at Taoxue.SchoolRoll.Website.Startup.<>c.<<Configure>b__5_0>d.MoveNext() in E:\项目\CSharp\xx\xxxx\Taoxue.SchoolRoll.Website\Startup.cs:line 77 --- End of stack trace from previous location where exception was thrown --- at Taoxue.SchoolRoll.Website.Extensions.GlobalExceptionMiddleWare.Invoke(HttpContext context) in E:\项目\CSharp\xx\xxxx\Taoxue.SchoolRoll.Website\Extensions\GlobalExceptionMiddleWare.cs:line 23
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构