preface

System abnormal monitoring can be said to be the most important, the system can not always run well, development and operation and maintenance can not stare at the system 24 hours, we should receive abnormal information in the first time after the system threw exceptions. In Asp.net Core I use both interceptors and middleware to monitor exceptions. It is better to write global exception monitoring data into the database for easy query.

Configuration NLog

NLog configuration file

<?xml version="1.0" encoding="utf-8"? >
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true"
      internalLogLevel="info"
      internalLogFile="d:\temp\internal-nlog.txt">

  <! -- the targets to write to -->
  <targets>
    <! -- write logs to file -->
    <target xsi:type="File" name="allfile" fileName="d:\temp\nlog-all-${shortdate}.log"
            layout="${longdate}|${event-properties:item=EventId.Id}|${uppercase:${level}}|${logger}|${message} ${exception}" />

    <! -- another file log, only own logs. Uses some ASP.NET core renderers -->
    <target xsi:type="File" name="ownFile-web" fileName="d:\temp\nlog-own-${shortdate}.log"
            layout="${longdate}|${event-properties:item=EventId.Id}|${uppercase:${level}}|${logger}|${message} ${exception}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />

    <! -- write to the void aka just remove -->
    <target xsi:type="Null" name="blackhole" />
  </targets>

  <! -- rules to map from logger name to target -->
  <rules>
    <! --All logs, including from Microsoft-->
    <logger name="*" minlevel="Trace" writeTo="allfile" />

    <! --Skip Microsoft logs and so log only own logs-->
    <logger name="Microsoft.*" minlevel="Trace" writeTo="blackhole" final="true" />
    <logger name="*" minlevel="Trace" writeTo="ownFile-web" />
  </rules>
</nlog>
Copy the code

Injection NLog

In the Program. The cs injection NLog dependence, add before relying on Microsoft. You need to import the two namespace Extensions. Logging, NLog. Web.

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
                                  {
                                      webBuilder.UseStartup<Startup>();
                                  })
        .ConfigureLogging(logging=> 
                          {
                              logging.ClearProviders();
                              logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
                          })
        .UseNLog(); 
}
Copy the code

The interceptor

The interceptor most commonly used in Asp.Mvc is also supported in Asp. Net Core. The interceptor is first defined and then injected. The custom interceptor implements the interface IExceptionFilter. The interface requires the OnException method, which is triggered when the system encounters an uncaught exception. Here the global anomaly information can best be put into the database, convenient background query, and then after throwing the anomaly, it is best to send emails and alarm messages to the person in charge, or directly call the phone.

public class GlobalExceptionFilter : IExceptionFilter
{

    private IWebHostEnvironment _env;
    private ILogger<GlobalExceptionFilter> _logger;

    public GlobalExceptionFilter(IWebHostEnvironment _env,ILogger<GlobalExceptionFilter> _logger)
    {
         this._env = _env;
         this._logger = _logger;
    }

    public void OnException(ExceptionContext context)
    {

        if (context.Exception.GetType() == typeof(BusException))
        {
            // If it is a custom exception, no processing is performed
        }
        else{}// Log to the database
         // Send alarm email to the person in charge, asynchronously
         // Send alarm SMS or alarm phone to the person in charge, asynchronously

         Exception ex = context.Exception;
         // There must be more than one system to monitor exceptions.
         int sysId = 1; 
         // If nginx is used to load the server, the IP address must be compatible with the load.
         // Monitor the IP address to locate which server is down
         string ip = context.HttpContext.Connection.RemoteIpAddress.ToString();

         _logger.LogError(System No. :{sysId}The host IP:{ip}, stack information:{ex.StackTrace}, exception description:{ex.Message}");
         context.Result = new JsonResult(ResultBody.error(ex.Message));
         context.ExceptionHandled = true; }}Copy the code

At Startup. ConfigureServices method into global exception handling in the interceptor.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    // Inject global exception handling
    services.AddMvc(option =>
    {
        option.Filters.Add(typeof(GlobalExceptionFilter));
    });
}
Copy the code

OK, with the interceptor defined, let’s try throwing an uncaught exception ourselves. As shown, all return the same JSON return value.

The middleware

When defining the middleware, middleware leading into the log namespace. Microsoft Extensions. Logging.

public class GlobalExceptionMiddleware
{
    private readonly RequestDelegate next;
    private ILogger<GlobalExceptionMiddleware> logger;
    public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> logger)
    {
        this.next = next;
        this.logger = logger;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await next.Invoke(context);
        }
        catch (Exception ex)
        {
            awaitHandleExceptionAsync(context, ex); }}private async Task HandleExceptionAsync(HttpContext context, Exception e)
    {
        if (e.GetType() == typeof(BusException))
        {
            // If it is a custom exception, no processing is performed
        }
        else{}/ / logs

        int sysId = 1;
        string ip = context.Connection.RemoteIpAddress.ToString();
        logger.LogError(System No. :{sysId}The host IP:{ip}, stack information:{e.StackTrace}, exception description:{e.Message}");
        string result = System.Text.Json.JsonSerializer.Serialize(ResultBody.error(e.Message));
        awaitcontext.Response.WriteAsync(result); }}Copy the code

Register middleware in the startup. Configure method.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env,ILoggerFactory loggerFactory)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    // Register exception handling middleware
    app.UseMiddleware<GlobalExceptionMiddleware>();

    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
                     {
                         endpoints.MapControllerRoute(
                             name: "default",
                             pattern: "{controller=Home}/{action=Index}/{id? }");
                     });
}
Copy the code

This is where the middleware handles the exception and finally writes a string to the client response, which is where the interceptor handles it differently. Of course, JSON objects are more intuitive for the client or front-end.

Refer to the link

www.cnblogs.com/suizhikuo/p… www.cnblogs.com/viter/p/100… www.jianshu.com/p/cab597211…