aspnetcore配置log4net並添加全局異常處理


第一步:在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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM