ASP.NET Core 中的日誌記錄

內置日誌的使用

Logger 是 asp .net core 的內置 service,因此咱們就不須要在ConfigureService裏面註冊了。同時在asp.net core 2.0版本及之後,系統已經在CreateDefaultBuilder方法裏默認配置了輸出到Console和Debug窗口的Logger。linux

.ConfigureLogging(delegate(WebHostBuilderContext hostingContext, ILoggingBuilder logging)
{
    logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
    logging.AddConsole();
    logging.AddDebug();
})

因此咱們能夠Controller裏面直接注入ILoggerFactory而後再建立具體的Logger。git

private readonly ILogger _logger;
public HomeController(ILoggerFactory logger)
{
    _logger = logger.CreateLogger<HomeController>();
}

可是還有更好的方式,Container能夠直接提供一個ILogger 的實例,這時候呢Logger就會使用T的名字做爲日誌的類別: github

private readonly ILogger _logger;
public HomeController(ILogger<HomeController> logger)
{
    _logger = logger;
}

而後在【Output】窗口中能夠看到輸出的日誌:web

LogDemo> info: LogDemo.Controllers.HomeController[0]
LogDemo>       Return Index view

Log到Debug窗口或者Console窗口仍是比較方便的,可是正式生產環境中這確定不夠用。正式環境應該Log到文件或者數據庫等。接下來試下Nlog。docker

使用Nlog

  1. NuGet添加 NLog.Web.AspNetCore數據庫

    <PackageReference Include="Microsoft.AspNetCore.App" />
  2. 添加配置文件json

    新建一個文件nlog.config(建議所有小寫,linux系統中要注意), 並右鍵點擊其屬性,將其「複製到輸出目錄」設置爲「始終複製」。文件內容以下api

    <?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="c:\temp\internal-nlog.txt">
    
      <!-- enable asp.net core layout renderers -->
      <extensions>
        <add assembly="NLog.Web.AspNetCore"/>
      </extensions>
    
      <!-- the targets to write to -->
      <targets>
        <!-- write logs to file  -->
        <target xsi:type="File" name="allfile" fileName="c:\temp\nlog-all-${shortdate}.log"
                layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}" />
    
        <!-- another file log, only own logs. Uses some ASP.NET core renderers -->
        <target xsi:type="File" name="ownFile-web" fileName="c:\temp\nlog-own-${shortdate}.log"
                layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />
      </targets>
    
      <!-- rules to map from logger name to target -->
      <rules>
        <!--All logs, including from Microsoft-->
        <logger name="*" minlevel="Trace" writeTo="allfile" />
    
        <!--Skip non-critical Microsoft logs and so log only own logs-->
        <logger name="Microsoft.*" maxLevel="Info" final="true" />
        <!-- BlackHole without writeTo -->
        <logger name="*" minlevel="Trace" writeTo="ownFile-web" />
      </rules>
    </nlog>
    <Content Update="nlog.config">
          <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </Content>
  3. 修改Program.cs文件瀏覽器

    添加引用 NLog.Web 和 Microsoft.Extensions.Logging。
    在 .UseStartup () 後添加一句 .UseNLog()

    若是要禁用默認的輸出框日誌,能夠調用logging.ClearProviders()

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .ConfigureLogging(logging =>
            {
                logging.ClearProviders();
                logging.SetMinimumLevel(LogLevel.Trace);
            })
            .UseNLog();
  4. 輸出到數據庫

    除了把日誌輸出到文件以外,也能夠保存到SQL Server, PostgreSQL, MySQL, Elasticsearch等。下面是保存到SQL Server的一個示例配置:

    <?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\logs\internal-nlog.txt">
    
      <!-- enable asp.net core layout renderers -->
      <extensions>
        <add assembly="NLog.Web.AspNetCore"/>
      </extensions>
    
      <!-- the targets to write to -->
      <targets>
        <!-- write logs to file  -->
        <target xsi:type="File" name="allfile" fileName="d:\temp\logs\all-${shortdate}.log"
                layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}" />
    
        <target name="blackhole" xsi:type="Null" />
    
        <target name="database" xsi:type="Database" dbProvider="System.Data.SqlClient">
          <connectionString>
            Server=.;Database=Log;Trusted_Connection=True
          </connectionString>
          <commandText>
                  insert into dbo.Log (
                  Application, Logged, Level, Message,
                  Logger, CallSite, Exception
                  ) values (
                  @Application, @Logged, @Level, @Message,
                  @Logger, @Callsite, @Exception
                  );
              </commandText>
    
              <parameter name="@application" layout="Application" />
              <parameter name="@logged" layout="${date}" />
              <parameter name="@level" layout="${level}" />
              <parameter name="@message" layout="url: ${aspnet-request-url} | action: ${aspnet-mvc-action} | ${message}" />
    
              <parameter name="@logger" layout="${logger}" />
              <parameter name="@callSite" layout="${callsite:filename=true}" />
              <parameter name="@exception" layout="${exception:tostring}" />
        </target>
      </targets>
    
      <!-- rules to map from logger name to target -->
      <rules>
        <!--All logs, including from Microsoft-->
        <logger name="*" minlevel="Info" writeTo="allfile" />
    
        <!--Skip non-critical Microsoft logs and so log only own logs-->
        <logger name="Microsoft.*" maxLevel="Info" final="true" />
        <!-- BlackHole without writeTo -->
        <logger name="*" minlevel="Info" writeTo="database" />
      </rules>
    </nlog>
  5. 配置簡要說明

    targets:用於配置輸出相關內容,好比 type 屬性可選項爲File、Mail、Console等,用於設置輸出目標,layout屬性用於設置輸出信息的組成元素及格式。
    rules: 實際上是一個「路由表」,日誌是從上到下匹配的。 logger name="Microsoft." maxlevel="Info" final="true" 一句話的 final="true" 過濾掉了"Microsoft." Info級別如下的日誌。

  6. 全局異常中間件

    除了輸出日誌外,能夠寫一箇中間件來處理全局的異常。

    public class GlobalErrorHandlingMiddleware
    {
        private readonly RequestDelegate next;
        private readonly ILogger<GlobalErrorHandlingMiddleware> _logger;
    
        public GlobalErrorHandlingMiddleware(RequestDelegate next, ILogger<GlobalErrorHandlingMiddleware> logger)
        {
            this.next = next;
            this._logger = logger;
        }
    
        public async Task Invoke(HttpContext context)
        {
            try
            {
                await next(context);
            }
            catch (Exception ex)
            {
    
                var Request = context.Request;
                ///訪問路徑
                string visit_url = Request.Path;
                ///URL 請求方法
                string method = Request.Method.ToUpper();
                ///URL 請求的參數
                string url_paramters = string.Empty;
    
                if (method == "GET") url_paramters = Request.QueryString.Value;
    
    
                if (method == "POST")
                {
                    foreach (var item in Request.Form)
                        url_paramters = url_paramters + item.Key + "=" + item.Value + "&";
                }
    
                ///錯識信息
                string err_msg = ex.Message;//ex.StackTrace;
    
                ///日誌格式內容
                var logs_msg = $"{visit_url}#{method}#{url_paramters}#{err_msg}";
    
                _logger.LogError(logs_msg);
    
                var statusCode = context.Response.StatusCode;
    
                var msg = $"Status Code: {statusCode}, Message: {ex.Message}";
    
                await HandleExceptionAsync(context, msg);
            }
        }
    
        private static Task HandleExceptionAsync(HttpContext context, string msg)
        {
            //var data = new Result { Title = "異常中間件返回", Msg = msg };
            //var result = JsonConvert.SerializeObject(data);
            //context.Response.ContentType = "application/json;charset=utf-8";
            return context.Response.WriteAsync(msg);
        }
    }
    
    public static class GlobalErrorHandlingMiddlewareExtensions
    {
        public static IApplicationBuilder UseGlobalErrorHandlingMiddleware(
            this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<GlobalErrorHandlingMiddleware>();
        }
    }

集成ELK

  1. ELK簡介

    ELK是三個開源軟件的縮寫,分別表示:Elasticsearch , Logstash, Kibana , 它們都是開源軟件。如今新增了一個FileBeat,它是一個輕量級的日誌收集處理工具。
    • Logstash: 是動態數據收集管道,可以同時從多個來源採集數據、轉換數據、而後將數據存到數據庫中。
    • Elastaicsearch: 分佈式搜索和分析引擎,提供蒐集、分析、存儲數據三大功能。。
    • Kibana:數據可視化Web。
    • Beats: 輕量型採集器的平臺,從邊緣機器向 Logstash 和 Elasticsearch 發送數據。

    ELK

  2. 環境快速搭建

    這裏使用docker-compose一鍵搭建ELK測試環境:
    1. 請確保已經安裝Docker-compose

      docker-compose --version
    2. 下載代碼從 The ELK stack powered by Docker and Compose 從並運行:

      git clone https://github.com/deviantony/docker-elk.git
      cd docker-elk
      docker-compose up -d
    3. 瀏覽器上訪問安裝服務器的ip:5601 能夠打開 kibana 管理後臺
      • 5000: Logstash TCP input.
      • 9200: Elasticsearch HTTP
      • 9300: Elasticsearch TCP transport
      • 5601: Kibana
  3. 修改nlog配置文件

    <target xsi:type="Network"
            name="ownLog-tcp"
            keepConnection="false"
            address="tcp://IP:5000"
            layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />

    啓動項目測試,進入 kibana 後臺配置並添加 index pattern。

  4. 其餘寫入Elasticsearch的方法

    把日誌寫入Elasticsearch的方法能夠有多種:
    • Logstash:如上面的例子。它的優點是靈活性和諸多插件,它的問題是性能以及資源消耗。
    • Filebeat:輕量級的日誌傳輸工具,能夠將將日誌直接傳輸存儲到 Elasticsearch,也可通過Logstash或者Kafka/Redis。
    • .Net Core logger provider:若是僅限於 .Net Core的話,能夠基於Elasticsearch的 .Net Core SDK封裝一個日誌提供程序直接寫入Elasticsearch。

參考

相關文章
相關標籤/搜索