asp.net core中使用Serilog以及自定義Enricher

Serilog簡介

與其他許多.NET庫一樣,Serilog還提供了對文件,控制臺等的基本診斷日志記錄。它易于設(shè)置,具有簡潔的API,并且可以在最新的.NET平臺之間移植。

使用和配置、自定義Enricher

由于 serilog 配置信息比較多,避免硬編碼代碼中,我們在 appsetting.jgon 同級目錄中新建一個(gè) serilogsetting.json 的日志配置文件,簡單內(nèi)容如下:

{
    "Serilog": {
        "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.Seq" ],
        "MinimumLevel": {
            "Default": "Information",
            "Override": {
                "System": "Error",
                "Microsoft": "Error"
            }
        },
        "WriteTo": [
            {
                "Name": "Console"
            },
            {
                "Name": "Seq",
                "Args": {
                    "serverUrl": "http://localhost:5341/",
                    "apiKey": "L2on8gpgjose5uldhdch"
                }
            }
        ]
    }
}

更多配置內(nèi)容請參考:serilog wiki 文檔

接下來,在 Program.cs 中添加一個(gè)方法,初始化這些配置,然后重寫下構(gòu)建主機(jī)的代碼:

public class Program
{
    public static int Main(string[] args)
    {
        var logConfig = GetLogConfig();
        Log.Logger = new LoggerConfiguration()
                     .ReadFrom.Configuration(logConfig)
                     .Enrich.FormLogContext()
                     .CreateLogger(); 
        try
        {
            // web host 初始化
            var host = BuildWebHost(args);
            host.Run();
            return 0;
        }
        catch(Exception e)
        {
            Log.Fatal(ex, "{ApplicationContext} 出現(xiàn)錯(cuò)誤:{messsage} !", AppName, ex.Message);
            return 1;
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }
    
    /// <summary>
    /// 讀取日志配置
    /// </summary>
    /// <returns></returns>
    private static IConfiguration GetLogConfig()
    {
        var builder = new ConfigurationBuilder()
                      .AddJsonFile("serilogsetting.json", optional: false, reloadOnChange: true);
        return builder.Build();
    
    }
}

然后,添加一個(gè)自定義Enircher,用來記錄HttpContext,的數(shù)據(jù)。_enrichAction則可以自定義,默認(rèn)記錄ip,請求路徑,請求方法,和返回響應(yīng)碼:

public class HttpContextEnricher:ILogEventEnricher
{
    private readonly IServiceProvider _serviceProvider;
    private readonly Action<LogEvent, ILogEventPropertyFactory, HttpContext> _enrichAction;
    
    public HttpContextEnricher(IServiceProvider serviceProvider) : this(serviceProvider, null)
    {}
      
    public HttpContextEnricher(IServiceProvider serviceProvider, Action<LogEvent, ILogEventPropertyFactory, HttpContext> enrichAction)
    {
        _serviceProvider = serviceProvider;
        if (enrichAction == null)
        {
            _enrichAction = (logEvent, propertyFactory, httpContext) =>{
                var x_forwarded_for = new StringValues();
                if (httpContext.Request.Headers.ContainsKey("X-Forwarded-For"))
                {
                   x_forwarded_for = httpContext.Request.Headers["X-Forwarded-For"];
                }
                logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("client_ip", JsonConvert.SerializeObject(x_forwarded_for)));
                logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("request_path", httpContext.Request.Path));
                logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("request_method", httpContext.Request.Method));
                 if (httpContext.Response.HasStarted)
                 {
                     logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("response_status", httpContext.Response.StatusCode));
                 }
            }
        }else{
            _enrichAction = enrichAction;
        }
    }
    
    
    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
    {
        var httpContext = _serviceProvider.GetService<IHttpContextAccessor>()?.HttpContext;
         if (null != httpContext){
             _enrichAction.Invoke(logEvent, propertyFactory, httpContext);
         }
    }
    
}

現(xiàn)在,我們添加了一個(gè)記錄HttpContext的Enricher,但是怎么用起來呢,怎么添加到日志對象中呢。就要靠asp.net core的中間件了。新建一個(gè)HttpContextLogMiddleware中間件:

public class HttpContextLogMiddleware
{
    private readonly RequestDelegate _next;

    public HttpContextLogMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var serviceProvider = context.RequestServices;
        // 將我們自定義的Enricher添加到LogContext中。
        // LogContext功能很強(qiáng)大,可以動(dòng)態(tài)添加屬性,具體使用介紹,參見官方wiki文檔
        using (LogContext.Push(new HttpContextEnricher(serviceProvider)))
        {
            await _next(context);
        }
    }
}

// 使用擴(kuò)展方法形式注入中間件
public static IApplicationBuilder UseHttpContextLog(
    this IApplicationBuilder builder)
{
    return builder.UseMiddleware<HttpContextLogMiddleware>();
}

這樣,我們就建立了一個(gè)中間件,接下來在Startup.cs的Configure方法中app.UseHttpContextLog();即可。

此處需要注意中間件的順序,參見官方ASP.NET CORE的中間件介紹

這樣,我們就添加了一個(gè)自定義的Enricher來記錄請求的上下文信息。啟動(dòng)應(yīng)用程序,看下輸出結(jié)果:


image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容