Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 国产免费91视频,九九精品成人免费国产片,国产成人视屏

          整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          系統(tǒng)日志收集之初探rsyslog

          系統(tǒng)日志收集之初探rsyslog

          統(tǒng)內(nèi)核和許多程序會(huì)產(chǎn)生各種錯(cuò)誤信息、警告信息和其他的提示信息,這些信息對(duì)用戶了解系統(tǒng)的運(yùn)行狀態(tài)是非常有用的,所以需要把它們保存到對(duì)應(yīng)的日志文件中,以便后續(xù)進(jìn)行分析和監(jiān)控系統(tǒng)或軟件的狀態(tài)。 Linux 系統(tǒng)擁有非常靈活和強(qiáng)大的日志功能,幾乎可以保存所有的操作記錄,并可以從中檢索出我們需要的信息。完成這個(gè)工作的守護(hù)進(jìn)程就是 rsyslog。

          介紹

          談及 rsyslog[1],就不可避免的涉及另外兩個(gè)軟件 syslog[2]syslog-ng[3]。這三款軟件設(shè)計(jì)的目標(biāo)是一樣的,就是解決系統(tǒng)和程序的日志收集問題。每一個(gè)項(xiàng)目都在試圖提高前者的穩(wěn)定性和功能性。

          syslog

          syslog 由 Eric Allman 在 1980 年代開發(fā),是 Sendmail 項(xiàng)目的一部分。因?yàn)樗囊子眯员缓芏囝?Unix 系統(tǒng)使用,成為其標(biāo)準(zhǔn)的日志記錄解決方案。在這個(gè)過程中,它演變成一種協(xié)議,成為在互聯(lián)網(wǎng)協(xié)議(TCP/IP)的網(wǎng)絡(luò)中傳遞記錄檔消息的標(biāo)準(zhǔn)。

          syslog 協(xié)議屬于一種主從式協(xié)議:syslog 發(fā)送端會(huì)發(fā)送出一個(gè)小的文字消息(小于 1024 位組)到 syslog 接收端。接收端通常名為 syslogd、syslog daemon 或 syslog 服務(wù)器。系統(tǒng)日志消息可以被以 UDP 協(xié)議或 TCP 協(xié)議來(lái)發(fā)送。這些資料是以明碼類型被發(fā)送。不過由于 SSL 加密外套(例如 Stunnel、sslio 或 sslwrap 等)并非 syslog 協(xié)議本身的一部分,因此可以被用來(lái)透過SSL/TLS 方式提供一層加密。

          syslog-ng

          syslog-ng 是 syslog NextGeneration 的簡(jiǎn)寫。該項(xiàng)目發(fā)起于 1998 年并基于 syslog 協(xié)議開發(fā)。作為 syslog 的下一代產(chǎn)品,功能肯定比 syslog 強(qiáng)大的多,如高性能,可靠的傳輸,支持多平臺(tái),高可靠性,眾多的用戶群體,強(qiáng)大的日志過濾及排序,事件標(biāo)簽和關(guān)聯(lián)性,支持最新的IETF標(biāo)準(zhǔn)等。

          rsyslog

          rsyslog 是 the rocket-fast system for log processing 的簡(jiǎn)寫。該項(xiàng)目始于 2004 年,當(dāng)時(shí) rsyslog 的主要作者 Rainer Gerhards 決定編寫一個(gè)新的強(qiáng)大 syslog 守護(hù)程序來(lái)與 syslog-ng 競(jìng)爭(zhēng)。它實(shí)現(xiàn)了基本的 syslog 協(xié)議,并擴(kuò)展了基于內(nèi)容的過濾功能,豐富了過濾能力,處理脫機(jī)輸出的隊(duì)列操作,支持不同模塊的輸出,配置選項(xiàng)更加靈活,并添加了使用 TCP 進(jìn)行傳輸?shù)墓δ堋?/p>

          由于 rsyslog 的高性能,出色的安全性和模塊化設(shè)計(jì),它不僅作為常規(guī)的系統(tǒng)日志收集工具,還能夠接受各種來(lái)源的輸入,將其轉(zhuǎn)換,然后將結(jié)果輸出到不同的目的地。

          它的優(yōu)勢(shì)有如下:

          ? 高性能(使用 C 編寫,運(yùn)用多線程)

          ? 支持 TCP, SSL, TLS, RELP

          ? 支持多種輸出(MySQL, PostgreSQL等)

          ? 支持對(duì)系統(tǒng)日志的過濾

          ? 靈活配置多種輸出

          正因?yàn)槿绱耍潜姸囝?Unix 系統(tǒng)和 GNU/Linux 發(fā)行版系統(tǒng)日志采集的首選。

          syslog 協(xié)議

          既然三款軟件都基于 syslog 協(xié)議,那就先來(lái)介紹一下 syslog 協(xié)議。相關(guān) RFC 文件有 2001 年發(fā)行的RFC 3164[4](The BSD syslog Protocol), 2009 年發(fā)行的 RFC 5424[5](The Syslog Protocol), RFC 5425[6](Transport Layer Security Mapping for Syslog),RFC 5426[7](Transmission of Syslog Messages over UDP)。其中RFC 3164 已經(jīng)被 RFC 5424 廢除,所以下面介紹的以 RFC 5424 為準(zhǔn)。

          三層模型

          Syslog 協(xié)議使用三層結(jié)構(gòu),第一層是消息層,指要傳輸?shù)男畔ⅲ坏诙邮菓?yīng)用層,主要用于消息的生成,解析,路由和存儲(chǔ),代表有發(fā)送者,中繼器和接受者。第三層是傳輸層,主要用于發(fā)送和接收網(wǎng)絡(luò)上的信息,代表有發(fā)送設(shè)備和接收設(shè)備。 具體的層次結(jié)構(gòu)見下圖(這塊的理解可以參考 OSI 7層模型或 TCP/IP 4層模型):

          部署場(chǎng)景

          syslog 協(xié)議遵循以下的原則:

          ? 協(xié)議沒有信息確認(rèn)機(jī)制 消息從發(fā)送者發(fā)送到接受者的 UDP 514 端口,不需要接收方應(yīng)答。

          ? 發(fā)送者和中繼器可以將相同的消息發(fā)送給多個(gè)接收者和中繼器

          ? 發(fā)送者,中繼器和接收者可以部署在同一個(gè)系統(tǒng)上。

          按照上述原則,有如下圖的部署場(chǎng)景。

          消息格式

          syslog 協(xié)議定義了消息格式,由三部分組成:消息頭 HEADER ,結(jié)構(gòu)化數(shù)據(jù) STRUCTURED-DATA 和消息 MSG(可選)。其中消息頭又包含優(yōu)先級(jí)(PRIority),版本號(hào)(VERSION),時(shí)間戳(TIMESTAMP),主機(jī)名(HOSTNAME), 應(yīng)用名(APP-NAME),進(jìn)程標(biāo)識(shí)(PROCID)和消息標(biāo)識(shí)(MSGID)。

          其中優(yōu)先級(jí)由設(shè)備(Facility)和嚴(yán)重性(Severity)共同決定。PRI=Facility * 8 + Severity。 設(shè)備的可選值有以下24個(gè):

          代號(hào)

          設(shè)備(Facility)

          注釋

          0

          kernel messages

          內(nèi)核相關(guān)

          1

          user-level messages

          用戶相關(guān)(默認(rèn))

          2

          mail system

          郵件相關(guān)

          3

          system daemons

          系統(tǒng)守護(hù)進(jìn)程相關(guān)

          4

          security/authorization messages (note 1)

          登陸授權(quán)相關(guān)

          5

          messages generated internally by syslogd

          syslogd相關(guān)

          6

          line printer subsystem

          打印相關(guān)

          7

          network news subsystem

          新聞相關(guān)

          8

          UUCP subsystem

          unix到unix的cp相關(guān)

          9

          clock daemon (note 2)

          任務(wù)計(jì)劃相關(guān)

          10

          security/authorization messages (note 1)

          登陸授權(quán)相關(guān)

          11

          FTP daemon

          FTP相關(guān)

          12

          NTP subsystem


          13

          log audit (note 1)

          登陸授權(quán)相關(guān)

          14

          log alert (note 1)

          登陸授權(quán)相關(guān)

          15

          clock daemon (note 2)

          任務(wù)計(jì)劃相關(guān)

          16

          local use 0 (local0)

          用戶自定義0

          17

          local use 1 (local1)

          用戶自定義1

          18

          local use 2 (local2)

          用戶自定義2

          19

          local use 3 (local3)

          用戶自定義3

          20

          local use 4 (local4)

          用戶自定義4

          21

          local use 5 (local5)

          用戶自定義5

          22

          local use 6 (local6)

          用戶自定義6

          23

          local use 7 (local7)

          用戶自定義7

          嚴(yán)重性的可選值有以下8個(gè),這也是Facility * 8的原因:

          代號(hào)

          嚴(yán)重性(Severity)

          注釋

          0

          Emergency: system is unusable

          崩潰級(jí)別

          1

          Alert: action must be taken immediately

          報(bào)警級(jí)別

          2

          Critical: critical conditions

          危急級(jí)別

          3

          Error: error conditions

          錯(cuò)誤級(jí)別

          4

          Warning: warning conditions

          警告級(jí)別

          5

          Notice: normal but significant condition

          提示級(jí)別

          6

          Informational: informational messages

          消息級(jí)別

          7

          Debug: debug-level messages

          調(diào)試級(jí)別

          其它注意點(diǎn)

          ? 協(xié)議的實(shí)現(xiàn)必須支持基于 TLS 的傳輸,應(yīng)該支持基于 UDP 的傳輸

          ? 所有接收設(shè)備必須能夠結(jié)構(gòu)長(zhǎng)度不超過480個(gè)八位字節(jié)的消息,應(yīng)該接收長(zhǎng)度最大為2048個(gè)八位字節(jié)的消息,可以接收超過2048個(gè)八位字節(jié)的消息(可以截?cái)嗷騺G棄)。

          rsyslog 使用

          鑒于 rsyslog 已經(jīng)是眾多類 Unix 系統(tǒng)和 GNU/Linux 發(fā)行版系統(tǒng)日志采集的首選,所以這里重點(diǎn)介紹一下 rsyslog 使用。

          配置文件

          一般 rsyslog 的配置文件在 /etc/rsyslog.conf, 其由 3 個(gè)部分組成:模塊(MODULES),全局設(shè)置(GLOBAL DRICTIVES)和規(guī)則(RULE)。這里以 Centos 中 rsyslog 配置為例。

          # rsyslog configuration file
          
          # For more information see /usr/share/doc/rsyslog-*/rsyslog_conf.html
          # If you experience problems, see http://www.rsyslog.com/doc/troubleshoot.html
          
          #### 模塊MODULES ####
          
          # The imjournal module bellow is now used as a message source instead of imuxsock.
          $ModLoad imuxsock # provides support for local system logging (e.g. via logger command)
          $ModLoad imjournal # provides access to the systemd journal
          #$ModLoad imklog # reads kernel messages (the same are read from journald)
          #$ModLoad immark  # provides --MARK-- message capability
          
          # Provides UDP syslog reception
          #$ModLoad imudp
          #$UDPServerRun 514
          
          # Provides TCP syslog reception
          #$ModLoad imtcp
          #$InputTCPServerRun 514
          
          
          #### 全局設(shè)置GLOBAL DIRECTIVES ####
          
          # Where to place auxiliary files
          $WorkDirectory /var/lib/rsyslog
          
          # Use default timestamp format
          $ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
          
          # File syncing capability is disabled by default. This feature is usually not required,
          # not useful and an extreme performance hit
          #$ActionFileEnableSync on
          
          # Include all config files in /etc/rsyslog.d/
          $IncludeConfig /etc/rsyslog.d/*.conf
          
          # Turn off message reception via local log socket;
          # local messages are retrieved through imjournal now.
          $OmitLocalLogging on
          
          # File to store the position in the journal
          $IMJournalStateFile imjournal.state
          
          
          #### 規(guī)則RULES ####
          
          # Log all kernel messages to the console.
          # Logging much else clutters up the screen.
          #kern.*                                                 /dev/console
          
          # Log anything (except mail) of level info or higher.
          # Don't log private authentication messages!
          *.info;mail.none;authpriv.none;cron.none                /var/log/messages
          
          # The authpriv file has restricted access.
          authpriv.*                                              /var/log/secure
          
          # Log all the mail messages in one place.
          mail.*                                                  -/var/log/maillog
          
          
          # Log cron stuff
          cron.*                                                  /var/log/cron
          
          # Everybody gets emergency messages
          *.emerg                                                 :omusrmsg:*
          
          # Save news errors of level crit and higher in a special file.
          uucp,news.crit                                          /var/log/spooler
          
          # Save boot messages also to boot.log
          local7.*                                                /var/log/boot.log
          
          
          # ### begin forwarding rule ###
          # The statement between the begin ... end define a SINGLE forwarding
          # rule. They belong together, do NOT split them. If you create multiple
          # forwarding rules, duplicate the whole block!
          # Remote Logging (we use TCP for reliable delivery)
          #
          # An on-disk queue is created for this action. If the remote host is
          # down, messages are spooled to disk and sent when it is up again.
          #$ActionQueueFileName fwdRule1 # unique name prefix for spool files
          #$ActionQueueMaxDiskSpace 1g   # 1gb space limit (use as much as possible)
          #$ActionQueueSaveOnShutdown on # save messages to disk on shutdown
          #$ActionQueueType LinkedList   # run asynchronously
          #$ActionResumeRetryCount -1    # infinite retries if host is down
          # remote host is: name/ip:port, e.g. 192.168.0.1:514, port optional
          #*.* @@remote-host:514
          # ### end of the forwarding rule ###

          每次修改配置文件后可以通過以下操作來(lái)判斷配置文件是否合理并重啟 rsyslogd 服務(wù)。

          # 驗(yàn)證配置文件 /etc/syslog.conf 是否合理
          rsyslogd -f /etc/rsyslog.conf -N1
          # 重啟 rsyslog 服務(wù)
          systemctl restart rsyslog

          基本驗(yàn)證

          一般系統(tǒng)都會(huì)提供 logger 命令行,可以使用其向系統(tǒng)輸入日志。

          # -i 記錄進(jìn)程id
          # -t 標(biāo)識(shí)記錄的tag
          # -p 指定消息的設(shè)備信息和日志等級(jí),默認(rèn)user.info
          logger -i -t 'hjy_test' -p facility.level 'message'

          實(shí)戰(zhàn)

          網(wǎng)上有一篇博文[8]是介紹將 rsyslog 的日志輸出到 mysql 中,當(dāng)然強(qiáng)大的 rsyslog 不僅支持輸出到 mysql 中,基本覆蓋了所有的主流存儲(chǔ)軟件。 不過這里介紹的實(shí)戰(zhàn)是利用 rsyslog 的用戶自定義設(shè)備來(lái)實(shí)現(xiàn)推薦系統(tǒng)的用戶行為收集。大體的思路是推薦請(qǐng)求或數(shù)據(jù)上報(bào)請(qǐng)求通過 nginx 將請(qǐng)求轉(zhuǎn)發(fā)到多臺(tái) online 服務(wù)中的一個(gè)。 該服務(wù)處理完請(qǐng)求后會(huì)將推薦的數(shù)據(jù)或打點(diǎn)上報(bào)的數(shù)據(jù)通過 SyslogHandler 匯總到目標(biāo)服務(wù)器上進(jìn)行推薦系統(tǒng)用戶行為的統(tǒng)一處理。具體架構(gòu)見下圖:

          優(yōu)勢(shì)如下:

          ? 速度快,穩(wěn)定性高,性能好(支持百萬(wàn)QPS,壓測(cè)到十萬(wàn)級(jí)別[9])

          ? rsyslogd 一般服務(wù)器自帶,不需要安裝,只需簡(jiǎn)單配置

          ? 客戶端實(shí)現(xiàn)簡(jiǎn)單,比如 Python 中 logger 就有 SyslogHandler 來(lái)向 rsyslogd 發(fā)送日志。

          參考文獻(xiàn)

          1.rsyslog官網(wǎng)[10]

          2.rsyslog源碼[11]

          3.維基百科syslog[12]

          4.維基百科rsyslog[13]

          5.維基百科syslog-ng[14]

          6.三種syslog比較[15]

          7.rfc3164[16]

          8.rfc5424[17]

          9.rfc5425[18]

          10.rfc5426[19]

          11.rsyslog配置[20]

          12.記錄rsyslog日志到mysql[21]

          13.rsyslog 的 TCP 轉(zhuǎn)發(fā)性能測(cè)試[22]

          References

          [1] rsyslog: https://en.wikipedia.org/wiki/Rsyslog
          [2] syslog:
          https://zh.wikipedia.org/wiki/Syslog
          [3] syslog-ng:
          https://en.wikipedia.org/wiki/Syslog-ng
          [4] RFC 3164:
          https://tools.ietf.org/html/rfc3164
          [5] RFC 5424:
          https://tools.ietf.org/html/rfc5424
          [6] RFC 5425:
          https://tools.ietf.org/html/rfc5425
          [7] RFC 5426:
          https://tools.ietf.org/html/rfc5426
          [8] 博文:
          https://www.codenong.com/cs105581064/
          [9] 壓測(cè)到十萬(wàn)級(jí)別:
          http://chenlinux.com/2015/02/12/rsyslog-forwarder-testing/

          NET 中的日志使用技巧

          Serilog

          Serilog 是 .NET 社區(qū)中使用最廣泛的日志框架,所以筆者使用一個(gè)小節(jié)單獨(dú)講解使用方法。

          示例項(xiàng)目在 Demo2.Console 中。

          創(chuàng)建一個(gè)控制臺(tái)程序,引入兩個(gè)包:

          Serilog.Sinks.Console
          Serilog.Sinks.File

          除此之外,還有 Serilog.Sinks.ElasticsearchSerilog.Sinks.RabbitMQ 等。Serilog 提供了用于將日志事件以各種格式寫入存儲(chǔ)的接收器。下面列出的許多接收器都是由更廣泛的 Serilog 社區(qū)開發(fā)和支持的;https://github.com/serilog/serilog/wiki/Provided-Sinks

          可以直接使用代碼配置 Serilog:

          private static Serilog.ILogger GetLogger()
          {
          const string LogTemplate="{SourceContext} {Scope} {Timestamp:HH:mm} [{Level}] {Message:lj} {Properties:j} {NewLine}{Exception}";
          var logger=new LoggerConfiguration()
          .Enrich.WithMachineName()
          .Enrich.WithThreadId()
          .Enrich.FromLogContext()
          #if DEBUG
          .MinimumLevel.Debug()
          #else
          .MinimumLevel.Information()
          #endif
          .WriteTo.Console(outputTemplate: LogTemplate)
          .WriteTo.File("log.txt", rollingInterval: RollingInterval.Day, outputTemplate: LogTemplate)
          .CreateLogger();
          return logger;
          }

          如果想從配置文件中加載,添加 Serilog.Settings.Configuration:

          private static Serilog.ILogger GetJsonLogger()
          {
          IConfiguration configuration=new ConfigurationBuilder()
          .SetBasePath(AppContext.BaseDirectory)
          .AddJsonFile(path: "serilog.json", optional: true, reloadOnChange: true)
          .Build();
          if (configuration==)
          {
          throw new ArgumentException($"未能找到 serilog.json 日志配置文件");
          }
          var logger=new LoggerConfiguration()
          .ReadFrom.Configuration(configuration)
          .CreateLogger();
          return logger;
          }

          serilog.json 配置文件示例:

          {
          "Serilog": {
          "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
          "MinimumLevel": {
          "Default": "Debug"
          },
          "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
          "WriteTo": [
          {
          "Name": "Console",
          "Args": {
          "outputTemplate": "{SourceContext} {Scope} {Timestamp:HH:mm} [{Level}] {Message:lj} {Properties:j} {NewLine}{Exception}"
          }
          },
          {
          "Name": "File",
          "Args": {
          "path": "logs/log-.txt",
          "rollingInterval": "Day",
          "outputTemplate": "{SourceContext} {Scope} {Timestamp:HH:mm} [{Level}] {Message:lj} {Properties:j} {NewLine}{Exception}"
          }
          }
          ]
          }
          }

          依賴注入 Serilog。

          引入 Serilog.Extensions.Logging 包。

          private static Microsoft.Extensions.Logging.ILogger InjectLogger()
          {
          var logger=GetJsonLogger();
          var ioc=new ServiceCollection();
          ioc.AddLogging(builder=> builder.AddSerilog(logger: logger, dispose: true));
          var loggerProvider=ioc.BuildServiceProvider().GetRequiredService<ILoggerProvider>();
          return loggerProvider.CreateLogger("Program");
          }

          最后,使用不同方式配置 Serilog 日志,然后啟動(dòng)程序打印日志。

          static void Main()
          {
          var log1=GetLogger();
          log1.Debug("溪源More、癡者工良");
          var log2=GetJsonLogger();
          log2.Debug("溪源More、癡者工良");
          var log3=InjectLogger();
          log3.LogDebug("溪源More、癡者工良");
          }
          20:50 [Debug] 溪源More、癡者工良 {"MachineName": "WIN-KQDULADM5LA", "ThreadId": 1}
          20:50 [Debug] 溪源More、癡者工良 {"MachineName": "WIN-KQDULADM5LA", "ThreadId": 1}
          20:50 [Debug] 溪源More、癡者工良 {"MachineName": "WIN-KQDULADM5LA", "ThreadId": 1}

          在 ASP.NET Core 中使用日志

          示例項(xiàng)目在 Demo2.Api 中。

          新建一個(gè) ASP.NET Core API 新項(xiàng)目,引入 Serilog.AspNetCore 包。

          在 Program 中添加代碼注入 Serilog 。

          var builder=WebApplication.CreateBuilder(args);

          Log.Logger=new LoggerConfiguration()
          .ReadFrom.Configuration(builder.Configuration)
          .CreateLogger();
          builder.Host.UseSerilog(Log.Logger);
          //builder.Host.UseSerilog();

          將前面示例中的 serilog.json 文件內(nèi)容復(fù)制到 appsettings.json 中。

          啟動(dòng)程序后,嘗試訪問 API 接口,會(huì)打印示例如下的日志:

          Microsoft.AspNetCore.Hosting.Diagnostics 20:32 [Information] Request finished HTTP/1.1 GET http://localhost:5148/WeatherForecast - - - 200 - application/json;+charset=utf-8 1029.4319ms {"ElapsedMilliseconds": 1029.4319, "StatusCode": 200, "ContentType": "application/json; charset=utf-8", "ContentLength": , "Protocol": "HTTP/1.1", "Method": "GET", "Scheme": "http", "Host": "localhost:5148", "PathBase": "", "Path": "/WeatherForecast", "QueryString": "", "EventId": {"Id": 2}, "RequestId": "0HMOONQO5ONKU:00000003", "RequestPath": "/WeatherForecast", "ConnectionId": "0HMOONQO5ONKU"}

          如果需要為請(qǐng)求上下文添加一些屬性信息,可以添加一個(gè)中間件,示例如下:

          app.UseSerilogRequestLogging(options=>
          {
          options.EnrichDiagnosticContext=(diagnosticContext, httpContext)=>
          {
          diagnosticContext.Set("TraceId", httpContext.TraceIdentifier);
          };
          });
           HTTP GET /WeatherForecast responded 200 in 181.9992 ms {"TraceId": "0HMSD1OUG2DHG:00000003" ... ...

          對(duì)請(qǐng)求上下文添加屬性信息,比如當(dāng)前請(qǐng)求的用戶信息,在本次請(qǐng)求作用域中使用日志打印信息時(shí),日志會(huì)包含這些上下文信息,這對(duì)于分析日志還有幫助,可以很容易分析日志中那些條目是同一個(gè)上下文。在微服務(wù)場(chǎng)景下,會(huì)使用 ElasticSearch 等日志存儲(chǔ)引擎查詢分析日志,如果在日志中添加了相關(guān)的上下文屬性,那么在分析日志時(shí)可以通過對(duì)應(yīng)的屬性查詢出來(lái),分析日志時(shí)可以幫助排除故障。

          如果需要打印 http 的請(qǐng)求和響應(yīng)日志,我們可以使用 ASP.NET Core 自帶的 HttpLoggingMiddleware 中間件。

          首先注入請(qǐng)求日志攔截服務(wù)。

          builder.Services.AddHttpLogging(logging=>
          {
          logging.LoggingFields=HttpLoggingFields.All;
          // 避免打印大量的請(qǐng)求和響應(yīng)內(nèi)容,只打印 4kb
          logging.RequestBodyLogLimit=4096;
          logging.ResponseBodyLogLimit=4096;
          });

          通過組合 HttpLoggingFields 枚舉,可以配置中間件打印 Request、Query、HttpMethod、Header、Response 等信息。

          可以將HttpLogging 中間件放在 Swagger、Static 之后,這樣的話可以避免打印哪些用處不大的請(qǐng)求,只保留 API 請(qǐng)求相關(guān)的日志。

          app.UseHttpLogging();

          HttpLoggingMiddleware 中的日志模式是以 Information 級(jí)別打印的,在項(xiàng)目上線之后,如果每個(gè)請(qǐng)求都被打印信息的話,會(huì)降低系統(tǒng)性能,因此我們可以在配置文件中覆蓋配置,避免打印普通的日志。

          "Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"

          上下文屬性和作用域

          示例項(xiàng)目在 Demo2.ScopeLog 中。

          日志范圍注意事項(xiàng)
          Microsoft.Extensions.Logging.Abstractions 提供 BeginScopeAPI,可用于添加任意屬性以記錄特定代碼區(qū)域內(nèi)的事件。

          解釋其作用

          API 有兩種形式:

          IDisposable BeginScope<TState>(TState state)
          IDisposable BeginScope(this ILogger logger, string messageFormat, params object[] args)

          使用如下的模板:

          {SourceContext} {Timestamp:HH:mm} [{Level}] (ThreadId:{ThreadId}) {Message}{NewLine}{Exception} {Scope}

          使用示例:

           static void Main()
          {
          var logger=GetLogger();
          using (logger.BeginScope("Checking mail"))
          {
          // Scope is "Checking mail"
          logger.LogInformation("Opening SMTP connection");

          using (logger.BeginScope("Downloading messages"))
          {
          // Scope is "Checking mail" -> "Downloading messages"
          logger.LogError("Connection interrupted");
          }
          }
          }

          而在 Serilog 中,除了支持上述接口外,還通過 LogContext 提供了在日志中注入上下文屬性的方法。其作用是添加屬性之后,使得在其作用域之內(nèi)打印日志時(shí),日志會(huì)攜帶這些上下文屬性信息。

           using (LogContext.PushProperty("Test", 1))
          {
          // Process request; all logged events will carry `RequestId`
          Log.Information("{Test} Adding {Item} to cart {CartId}", 1,1);
          }

          嵌套復(fù)雜一些:

          using (LogContext.PushProperty("A", 1))
          {
          log.Information("Carries property A=1");

          using (LogContext.PushProperty("A", 2))
          using (LogContext.PushProperty("B", 1))
          {
          log.Information("Carries A=2 and B=1");
          }

          log.Information("Carries property A=1, again");
          }

          當(dāng)需要設(shè)置大量屬性時(shí),下面的方式會(huì)比較麻煩;

          using (LogContext.PushProperty("Test1", 1))
          using (LogContext.PushProperty("Test2", 2))
          {
          }

          例如在 ASP.NET Core 中間件中,我們可以批量添加:

           public async Task InvokeAsync(HttpContext context, RequestDelegate next)
          {
          var enrichers=new List<ILogEventEnricher>();
          if (!string.IsOrEmpty(correlationId))
          {
          enrichers.Add(new PropertyEnricher(_options.EnricherPropertyNames.CorrelationId, correlationId));
          }

          using (LogContext.Push(enrichers.ToArray()))
          {
          await next(context);
          }
          }

          在業(yè)務(wù)系統(tǒng)中,可以通過在中間件獲取 Token 中的用戶信息,然后注入到日志上下文中,這樣打印出來(lái)的日志,會(huì)攜帶用戶信息。

          非侵入式日志

          非侵入式的日志有多種方法,比如 ASP.NET Core 中間件管道,或者使用 AOP 框架。

          這里可以使用筆者開源的 CZGL.AOP 框架,Nuget 中可以搜索到。

          示例項(xiàng)目在 Demo2.AopLog 中。

          有一個(gè)類型,我們需要在執(zhí)行 SayHello 之前和之后打印日志,將參數(shù)和返回值記錄下來(lái)。

           public class Hello
          {
          public virtual string SayHello(string content)
          {
          var str=$"Hello,{content}";
          return str;
          }
          }

          編寫統(tǒng)一的切入代碼,這些代碼將在函數(shù)被調(diào)用時(shí)執(zhí)行。

          Before 會(huì)在被代理的方法執(zhí)行前或被代理的屬性調(diào)用時(shí)生效,你可以通過 AspectContext 上下文,獲取、修改傳遞的參數(shù)。

          After 在方法執(zhí)行后或?qū)傩哉{(diào)用時(shí)生效,你可以通過上下文獲取、修改返回值。

          public class LogAttribute : ActionAttribute
          {
          public override void Before(AspectContext context)
          {
          Console.WriteLine($"{context.MethodInfo.Name} 函數(shù)被執(zhí)行前");
          foreach (var item in context.MethodValues)
          Console.WriteLine(item.ToString());
          }

          public override object After(AspectContext context)
          {
          Console.WriteLine($"{context.MethodInfo.Name} 函數(shù)被執(zhí)行后");
          Console.WriteLine(context.MethodResult.ToString());
          return context.MethodResult;
          }
          }

          改造 Hello 類,代碼如下:

          [Interceptor]
          public class Hello
          {
          [Log]
          public virtual string SayHello(string content)
          {
          var str=$"Hello,{content}";
          return str;
          }
          }

          然后創(chuàng)建代理類型:

           static void Main(string[] args)
          {
          Hello hello=AopInterceptor.CreateProxyOfClass<Hello>();
          hello.SayHello("any one");
          Console.Read();
          }

          啟動(dòng)程序,會(huì)輸出:

          SayHello 函數(shù)被執(zhí)行前
          any one
          SayHello 函數(shù)被執(zhí)行后
          Hello,any one

          你完全不需要擔(dān)心 AOP 框架會(huì)給你的程序帶來(lái)性能問題,因?yàn)?CZGL.AOP 框架采用 EMIT 編寫,并且自帶緩存,當(dāng)一個(gè)類型被代理過,之后無(wú)需重復(fù)生成。

          CZGL.AOP 可以通過 .NET Core 自帶的依賴注入框架和 Autofac 結(jié)合使用,自動(dòng)代理 CI 容器中的服務(wù)。這樣不需要 AopInterceptor.CreateProxyOfClass 手動(dòng)調(diào)用代理接口。

          CZGL.AOP 代碼是開源的,可以參考筆者另一篇博文:

          https://www.cnblogs.com/whuanle/p/13160139.html

          . Log4Net簡(jiǎn)介

          Log4net是從Java中的Log4j遷移過來(lái)的一個(gè).Net版的開源日志框架,它的功能很強(qiáng)大,可以將日志分為不同的等級(jí),以不同的格式輸出到不同的存儲(chǔ)介質(zhì)中,比如:數(shù)據(jù)庫(kù)、txt文件、內(nèi)存緩沖區(qū)、郵件、控制臺(tái)、ANSI終端、遠(yuǎn)程接收端等等,我們這里主要介紹最常用的兩種:txt文件和數(shù)據(jù)庫(kù)。

          (PS:其它的存儲(chǔ)介質(zhì)詳見 http://logging.apache.org/log4net/release/config-examples.html)

          Log4net將日志分為五個(gè)級(jí)別,分別是: FATAL(致命錯(cuò)誤) > ERROR(一般錯(cuò)誤) > WARN(警告) > INFO(一般信息) > DEBUG(調(diào)試信息),每個(gè)級(jí)別都對(duì)應(yīng)著一組重載方法進(jìn)行調(diào)用。

          官網(wǎng)地址:http://logging.apache.org/log4net/index.html

          Nuget地址:https://www.nuget.org/packages/log4net/

          Nuget安裝:Install-Package log4net

          最新版本:2.0.8 (2018-08-09)

          本節(jié)主要圍繞兩個(gè)主要的存儲(chǔ)介質(zhì):【txt文件】和【SQLServer數(shù)據(jù)庫(kù)】展開,涵蓋的知識(shí)點(diǎn)有:

          ①. 基本的使用步驟。

          ②. 初始化關(guān)聯(lián)配置文件的幾種形式。

          ③. 代碼調(diào)用詳解。

          ④. 配置文件詳解。

          ⑤. 簡(jiǎn)單的封裝和在MVC框架中的調(diào)用。

          二. 基本使用步驟

          我們先以控制臺(tái)程序?yàn)槔?jiǎn)單介紹Log4net存儲(chǔ)日志到txt文本文檔中,后面在做代碼的詳解。

          1. 新建01-SimpleDemo控制臺(tái)程序,通過指令 【Install-Package log4net】安裝相應(yīng)程序集。

          2. 在默認(rèn)配置文件中App.config(B/S程序則為web.config)中進(jìn)行配置,主要分兩塊:

          A. 在<configuration></configuration>節(jié)點(diǎn)下新增節(jié)點(diǎn)下新增(要在其最頂部):

          <configSections>

          <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />

          </configSections>

          B. 在<configuration></configuration>根節(jié)點(diǎn)下,配置log4net的核心配置代碼, 主要節(jié)點(diǎn)如下:

          <log4net> <appender> </appender> <root></root> </log4net>

          詳細(xì)代碼如下:

           1 <?xml version="1.0" encoding="utf-8" ?>
           2 <configuration>
           3 <!-- 1. 添加log4net的節(jié)點(diǎn)聲明配置代碼-->
           4 <configSections>
           5 <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
           6 </configSections>
           7 <!--2. log4net的核心配置代碼-->
           8 <log4net> 
           9 <!--把日志信息輸出到以日期命名的文件里-->
          10 <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
          11 <!--文件夾的位置-->
          12 <file value="D:\MyLog1\" />
          13 <appendToFile value="true" />
          14 <!--動(dòng)態(tài)生成文件名-->
          15 <param name="StaticLogFileName" value="false" />
          16 <!--以日期命名-->
          17 <param name="DatePattern" value="yyyyMMdd".log"" />
          18 <rollingStyle value="Date" />
          19 <!--日志在日志文件中的布局方式-->
          20 <layout type="log4net.Layout.PatternLayout">
          21 <conversionPattern value="%newline %n記錄時(shí)間:%date %n線程ID:[%thread] %n日志級(jí)別: %-5level %n出錯(cuò)類:%logger property: [%property{NDC}] - %n錯(cuò)誤描述:%message%newline %n"/>
          22 </layout>
          23 <!--使用最小鎖定模型(minimal locking model),以允許多個(gè)進(jìn)程可以寫入同一個(gè)文件 -->
          24 <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
          25 </appender> 
          26 <root>
          27 <level value="ALL"></level>
          28 <appender-ref ref="RollingFileAppender"></appender-ref>
          29 </root> 
          30 </log4net>
          31 <startup>
          32 <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
          33 </startup>
          34 </configuration>
          

          3. 代碼調(diào)用

          1 log4net.Config.XmlConfigurator.Configure();
          2 ILog log=LogManager.GetLogger("test");
          3 log.Debug("調(diào)試信息");
          

          4. 運(yùn)行結(jié)果

          截止此處,日志保存成功。

          三. 初始化配置文件

           前面提到在默認(rèn)配置文件中App.config(B/S程序則為web.config)中進(jìn)行配置,可以通過代碼 log4net.Config.XmlConfigurator.Configure(); 來(lái)初始化配置,或者還可以通過 [assembly: log4net.Config.XmlConfigurator()] 反射的形式進(jìn)行初始化配置,二者可以達(dá)到同樣的效果,代表了兩種初始化配置文件的形式。

          PS: [assembly: log4net.Config.XmlConfigurator()] 可以加在 當(dāng)前使用文件的 namespace上作用于當(dāng)前文件,或者加在Properties/AssemblyInfo.cs中,則該項(xiàng)目全局都無(wú)須再初始化了。

          在實(shí)際項(xiàng)目中,默認(rèn)的配置文件里可能包含很多框架的信息,這個(gè)時(shí)候把 log4net的配置代碼再放入進(jìn)去,就會(huì)顯得有點(diǎn)雜亂,或者有些“奇葩”的人把默認(rèn)配置文件改名了,這個(gè)時(shí)候使用上述默認(rèn)的兩種方式就不好用了,那么這種情況怎么處理呢?

          這里重點(diǎn)介紹 通過 log4net.Config.XmlConfigurator.Configure(); 來(lái)關(guān)聯(lián)配置文件。

          情況一: 使用默認(rèn)配置文件的情況

          1. 代碼配置:log4net.Config.XmlConfigurator.Configure();

          2. 反射配置:[assembly: log4net.Config.XmlConfigurator()]

          (這里只是舉例,很少有修改默認(rèn)配置文件名稱的)

          1. 代碼配置: 首先將App1.config文件的屬性中的“生成操作”改為“ 嵌入的資源”,然后通過以下代碼進(jìn)行配置。

          1 Assembly assembly=Assembly.GetExecutingAssembly();
          2 var xml=assembly.GetManifestResourceStream("_01_SimpleDemo.App1.config");
          3 log4net.Config.XmlConfigurator.Configure(xml);
          

          注:代碼中的 _01_SimpleDemo 為命名空間名。

          2. 反射配置:[assembly: log4net.Config.XmlConfigurator(ConfigFile="log4net.xml")]

            注:用這種方式屬性中的:復(fù)制到輸出目錄需要改為:始終復(fù)制,生成操作不需要配置,使用默認(rèn):無(wú) 即可

          情況三:新建單獨(dú)xml文件,進(jìn)行l(wèi)og4net的配置 (推薦采用這種方式,和原配置文件區(qū)分開,單獨(dú)配置方便,處理方式和情況二是一致的)

          1. 代碼配置:首先將log4net.xml文件的屬性中的“生成操作”改為“ 嵌入的資源”,然后通過以下代碼進(jìn)行配置。

          1 Assembly assembly=Assembly.GetExecutingAssembly();
          2 var xml=assembly.GetManifestResourceStream("_01_SimpleDemo.log4net.xml");
          3 log4net.Config.XmlConfigurator.Configure(xml);
          

          注:代碼中的 _01_SimpleDemo 為命名空間名。

          情況四:無(wú)論是修改默認(rèn)配置文件的名稱為 或者 新建單獨(dú)的xml作為配置文件 → 可以通過絕對(duì)路徑的方式進(jìn)行處理 【不推薦這種方式】

          1. 直接寫絕對(duì)路徑 (注意這種方式【不需要】配置文件屬性為 “嵌入的資源”)

          1 log4net.Config.XmlConfigurator.Configure(new FileInfo(@"D:\06-我的開發(fā)之路\DotNet體系\05-DotNet框架篇\03-Log4net詳解\Code\01-SimpleDemo\log4net.xml"));
          

          2 通過代碼獲取絕對(duì)路徑 (注意這種方式【不需要】配置文件屬性的“生成操作”改為 “嵌入的資源”,但需要改為“始終復(fù)制”,確保輸出到bin文件下)

          1 string assemblyFilePath=Assembly.GetExecutingAssembly().Location;
          2 string assemblyDirPath=Path.GetDirectoryName(assemblyFilePath);
          3 string configFilePath=assemblyDirPath + " //log4net.xml";
          4 log4net.Config.XmlConfigurator.Configure(new FileInfo(configFilePath));
          

          PS:B/S程序下通過 log4net.Config.XmlConfigurator.Configure(new FileInfo(Server.MapPath("~") + @"/log4net.xml")); 來(lái)配置。

          四. 代碼調(diào)用詳解

          Log4net允許多個(gè)ILog對(duì)象同時(shí)存在,通過代碼:ILog log=LogManager.GetLogger("xxx"); 來(lái)創(chuàng)建。

          A: 日志級(jí)別由高到低分別為:FATAL(致命錯(cuò)誤) > ERROR(一般錯(cuò)誤) > WARN(警告) > INFO(一般信息) > DEBUG(調(diào)試信息),另外還有 OFF和 ALL 。

          幾點(diǎn)說明:OFF表示所有信息都不寫入,ALL表示所有信息都寫入,我們也可以通過:<root><level value="WARN" ></ level ></root>這樣配置,表示W(wǎng)ARN級(jí)別以及高于WARN以上的級(jí)別才會(huì)被寫入日志

          B: 寫入日志的方法有:Debug、Error、Fatal、Info、Warn五個(gè)方法,每個(gè)方法都有兩個(gè)重載,如下圖:

          分享在使用配置文件為log4net.xml的情況下的調(diào)用代碼:

           1 Assembly assembly=Assembly.GetExecutingAssembly();
           2 var xml=assembly.GetManifestResourceStream("_01_SimpleDemo.log4net.xml");
           3 log4net.Config.XmlConfigurator.Configure(xml);
           4 ILog log=LogManager.GetLogger("test");
           5 log.Debug("調(diào)試信息");
           6 log.Info("一般信息");
           7 log.Warn("警告");
           8 try
           9 {
          10 int.Parse("ddd");
          11 }
          12 catch (Exception ex)
          13 {
          14 log.Error("一般錯(cuò)誤", ex);
          15 }
          16 log.Fatal("致命錯(cuò)誤");
          

          五. 配置文件詳解

          Log4net的配置文件主要分為兩大部分:分別是 【自定義配置節(jié)點(diǎn)】和 【核心代碼配置】,自定義配置節(jié)點(diǎn)代碼固定,如下圖,核心代碼配置主要位于:<log4net></log4net>節(jié)點(diǎn)中,里面包括<appender></appender>節(jié)點(diǎn)配置日日志輸出途徑 和 <root></root>節(jié)點(diǎn),用于設(shè)置記錄日志的級(jí)別和啟用哪些輸出途徑。

          幾點(diǎn)說明:

          1. 自定義配置節(jié)點(diǎn) <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" /> 代碼固定,直接復(fù)制即可。

          2. <root></root> 節(jié)點(diǎn)主要用來(lái): 配置日志的的輸出級(jí)別和加載日志的輸出途徑。

          A: level中的value值表示該值及其以上的日志級(jí)別才會(huì)輸出,日志級(jí)別包括:OFF > FATAL(致命錯(cuò)誤) > ERROR(一般錯(cuò)誤) > WARN(警告) > INFO(一般信息) > DEBUG(調(diào)試信息) > ALL ,比如:

              <level value="INFO"></level> 表示只有INFO及其以上的日志級(jí)別才會(huì)被保存。
          

          PS:OFF表示所有信息都不寫入,ALL表示所有信息都寫入。

          B: <appender-ref></appender-ref>標(biāo)簽用于加載日志的輸出途徑代碼,通過ref和appender標(biāo)簽的中name屬性相關(guān)聯(lián),比如:

              <appender-ref ref="RollingFileAppender"></appender-ref> 表示開啟txt文檔保存日志的方式。
          

          3. <appender></appender>節(jié)點(diǎn),用來(lái)配置日志的輸出途徑的,本節(jié)主要介紹了輸出到 【txt文本文檔】中 和 【數(shù)據(jù)庫(kù)】。

           A:分享一下數(shù)據(jù)庫(kù)的表結(jié)構(gòu),詳細(xì)配置見下面的代碼分享,需要注意字段類型相匹配,并且要顯式指定其長(zhǎng)度。

          B:關(guān)于txt文本文檔的命名,可以存放到一個(gè)文件夾里,也可以按照時(shí)間來(lái)區(qū)分文件夾,并且命名可以 動(dòng)態(tài)+指定命名的方式。

           C:關(guān)于日志文件大小的說明和文件個(gè)數(shù)的說明,主要需要三個(gè)節(jié)點(diǎn)配合使用(實(shí)際開發(fā)中,如果一個(gè)txt特別大,打開的時(shí)候會(huì)非常的慢,卡頓,所以該步驟有必要配置一下)。

          PS:首先要配置 RollingStyle 節(jié)點(diǎn)為Size模式或者Composite模式,然后配置 maximumFileSize 節(jié)點(diǎn)設(shè)置每個(gè)文件的大小,最后配置 MaxSizeRollBackups 節(jié)點(diǎn),設(shè)置日志文件的個(gè)數(shù)。超出大小后在所有文件名后自動(dòng)增加正整數(shù)重新命名,數(shù)字最大的最早寫入。

          用下面的代碼簡(jiǎn)單測(cè)試一下:

          1 <param name="RollingStyle" value="Composite" />
          2 <param name="maximumFileSize" value="10KB" />
          3 <param name="MaxSizeRollBackups" value="5" />
            
          

           
          詳細(xì)代碼如下:
          
           1 <?xml version="1.0" encoding="utf-8" ?>
           2 <configuration>
           3 <!-- 一. 添加log4net的自定義配置節(jié)點(diǎn)-->
           4 <configSections>
           5 <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
           6 </configSections>
           7 <!--二. log4net的核心配置代碼-->
           8 <log4net>
           9 <!--(一) 配置日志的輸出途徑-->
           10 <!--1. 輸出途徑(一) 將日志以回滾文件的形式寫到文件中-->
           11 <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
           12 <!--1.1 文件夾的位置(也可以寫相對(duì)路徑)-->
           13 <param name="File" value="D:\MyLog1\" />
           14 <!--相對(duì)路徑 C/S程序生成在Debug目錄下-->
           15 <!--<param name="File" value="/Logs/" />-->
           16 <!--1.2 是否追加到文件-->
           17 <param name="AppendToFile" value="true" />
           18 <!--1.3 使用最小鎖定模型(minimal locking model),以允許多個(gè)進(jìn)程可以寫入同一個(gè)文件 -->
           19 <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
           20 <!--1.4 配置Unicode編碼-->
           21 <Encoding value="UTF-8" />
           22 <!--1.5 是否只寫到一個(gè)文件里-->
           23 <param name="StaticLogFileName" value="false" />
           24 <!--1.6 配置按照何種方式產(chǎn)生多個(gè)日志文件 (Date:日期、Size:文件大小、Composite:日期和文件大小的混合方式)-->
           25 <param name="RollingStyle" value="Composite" />
           26 <!--1.7 介紹多種日志的的命名和存放在磁盤的形式-->
           27 <!--1.7.1 在根目錄下直接以日期命名txt文件 注意"的位置,去空格 -->
           28 <param name="DatePattern" value="yyyy-MM-dd".log"" />
           29 <!--1.7.2 在根目錄下按日期產(chǎn)生文件夾,文件名固定 test.log -->
           30 <!--<param name="DatePattern" value="yyyy-MM-dd/"test.log"" />-->
           31 <!--1.7.3 在根目錄下按日期產(chǎn)生文件夾,這是按日期產(chǎn)生文件夾,并在文件名前也加上日期 -->
           32 <!--<param name="DatePattern" value="yyyyMMdd/yyyyMMdd"-test.log"" />-->
           33 <!--1.7.4 在根目錄下按日期產(chǎn)生文件夾,這再形成下一級(jí)固定的文件夾 -->
           34 <!--<param name="DatePattern" value="yyyyMMdd/"OrderInfor/test.log"" />-->
           35 <!--1.8 配置每個(gè)日志的大小。【只在1.6 RollingStyle 選擇混合方式與文件大小方式下才起作用!!!】可用的單位:KB|MB|GB。不要使用小數(shù),否則會(huì)一直寫入當(dāng)前日志,
           36 超出大小后在所有文件名后自動(dòng)增加正整數(shù)重新命名,數(shù)字最大的最早寫入。-->
           37 <param name="maximumFileSize" value="10MB" />
           38 <!--1.9 最多產(chǎn)生的日志文件個(gè)數(shù),超過則保留最新的n個(gè) 將value的值設(shè)置-1,則不限文件個(gè)數(shù) 【只在1.6 RollingStyle 選擇混合方式與文件大小方式下才起作用!!!】
           39 與1.8中maximumFileSize文件大小是配合使用的-->
           40 <param name="MaxSizeRollBackups" value="5" />
           41 <!--1.10 配置文件文件的布局格式,使用PatternLayout,自定義布局-->
           42 <layout type="log4net.Layout.PatternLayout">
           43 <conversionPattern value="記錄時(shí)間:%date %n線程ID:[%thread] %n日志級(jí)別:%-5level %n出錯(cuò)類:%logger property: [%property{NDC}] - %n錯(cuò)誤描述:%message%newline %n%newline"/>
           44 </layout>
           45 </appender>
           46 
           47 <!--2. 輸出途徑(二) 記錄日志到數(shù)據(jù)庫(kù)-->
           48 <appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
           49 <!--2.1 設(shè)置緩沖區(qū)大小,只有日志記錄超設(shè)定值才會(huì)一塊寫入到數(shù)據(jù)庫(kù)-->
           50 <param name="BufferSize" value="1" />
           51 <!--2.2 引用-->
           52 <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
           53 <!--2.3 數(shù)據(jù)庫(kù)連接字符串-->
           54 <connectionString value="data source=localhost;initial catalog=LogDB;integrated security=false;persist security info=True;User ID=sa;Password=123456" />
           55 <!--2.4 SQL語(yǔ)句插入到指定表-->
           56 <commandText value="INSERT INTO LogInfor ([threadId],[log_level],[log_name],[log_msg],[log_exception],[log_time]) VALUES (@threadId, @log_level, @log_name, @log_msg, @log_exception,@log_time)" />
           57 <!--2.5 數(shù)據(jù)庫(kù)字段匹配-->
           58 <!-- 線程號(hào)-->
           59 <parameter>
           60 <parameterName value="@threadId" />
           61 <dbType value="String" />
           62 <size value="100" />
           63 <layout type="log4net.Layout.PatternLayout">
           64 <conversionPattern value="%thread" />
           65 </layout>
           66 </parameter>
           67 <!--日志級(jí)別-->
           68 <parameter>
           69 <parameterName value="@log_level" />
           70 <dbType value="String" />
           71 <size value="100" />
           72 <layout type="log4net.Layout.PatternLayout">
           73 <conversionPattern value="%level" />
           74 </layout>
           75 </parameter>
           76 <!--日志記錄類名稱-->
           77 <parameter>
           78 <parameterName value="@log_name" />
           79 <dbType value="String" />
           80 <size value="100" />
           81 <layout type="log4net.Layout.PatternLayout">
           82 <conversionPattern value="%logger" />
           83 </layout>
           84 </parameter>
           85 <!--日志信息-->
           86 <parameter>
           87 <parameterName value="@log_msg" />
           88 <dbType value="String" />
           89 <size value="5000" />
           90 <layout type="log4net.Layout.PatternLayout">
           91 <conversionPattern value="%message" />
           92 </layout>
           93 </parameter>
           94 <!--異常信息 指的是如Infor 方法的第二個(gè)參數(shù)的值-->
           95 <parameter>
           96 <parameterName value="@log_exception" />
           97 <dbType value="String" />
           98 <size value="2000" />
           99 <layout type="log4net.Layout.ExceptionLayout" />
          100 </parameter>
          101 <!-- 日志記錄時(shí)間-->
          102 <parameter>
          103 <parameterName value="@log_time" />
          104 <dbType value="DateTime" />
          105 <layout type="log4net.Layout.RawTimeStampLayout" />
          106 </parameter>
          107 </appender>
          108 <!--(二). 配置日志的的輸出級(jí)別和加載日志的輸出途徑-->
          109 <root>
          110 <!--1. level中的value值表示該值及其以上的日志級(jí)別才會(huì)輸出-->
          111 <!--OFF > FATAL(致命錯(cuò)誤) > ERROR(一般錯(cuò)誤) > WARN(警告) > INFO(一般信息) > DEBUG(調(diào)試信息) > ALL -->
          112 <!--OFF表示所有信息都不寫入,ALL表示所有信息都寫入-->
          113 <level value="ALL"></level>
          114 <!--2. append-ref標(biāo)簽表示要加載前面的日志輸出途徑代碼 通過ref和appender標(biāo)簽的中name屬性相關(guān)聯(lián)-->
          115 <appender-ref ref="RollingFileAppender"></appender-ref>
          116 <appender-ref ref="AdoNetAppender"></appender-ref>
          117 </root>
          118 </log4net>
          119 
          120 </configuration>
          

          六. 簡(jiǎn)單的封裝及完整代碼分享

          這里模擬在系統(tǒng)框架中對(duì)Log4net進(jìn)行簡(jiǎn)單的封裝,然后在MVC框架中調(diào)用,并分享全部代碼。

           步驟一:新建Ypf.Utils類庫(kù),作為工具類庫(kù),引入log4net程序集,并將前面用到的log4net.xml 復(fù)制進(jìn)來(lái),改屬性為嵌入資源,然后新建LogUtils類(不要起名為L(zhǎng)ogHelp),對(duì)Log4net的方法進(jìn)行簡(jiǎn)單的封裝,主要包括:初始化代碼、ILog實(shí)例創(chuàng)建、五類日志級(jí)別的封裝。

          特別注意:這種封裝會(huì)帶來(lái)一個(gè)問題,會(huì)導(dǎo)致輸出的文件中出錯(cuò)類永遠(yuǎn)顯示的為L(zhǎng)ogUtils這個(gè)封裝類,這里采用StackTrace類進(jìn)行迂回處理一下,就可以定位到具體的出錯(cuò)位置了,如下圖:

          log4net.xml文件代碼如下:

           1 <?xml version="1.0" encoding="utf-8" ?>
           2 <configuration>
           3 <!-- 一. 添加log4net的自定義配置節(jié)點(diǎn)-->
           4 <configSections>
           5 <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
           6 </configSections>
           7 <!--二. log4net的核心配置代碼-->
           8 <log4net>
           9 <!--(一) 配置日志的輸出途徑-->
           10 <!--1. 輸出途徑(一) 將日志以回滾文件的形式寫到文件中-->
           11 <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
           12 <!--1.1 文件夾的位置(也可以寫相對(duì)路徑)-->
           13 <param name="File" value="D:\MyLog1\" />
           14 <!--相對(duì)路徑-->
           15 <!--<param name="File" value="Logs/" />-->
           16 <!--1.2 是否追加到文件-->
           17 <param name="AppendToFile" value="true" />
           18 <!--1.3 使用最小鎖定模型(minimal locking model),以允許多個(gè)進(jìn)程可以寫入同一個(gè)文件 -->
           19 <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
           20 <!--1.4 配置Unicode編碼-->
           21 <Encoding value="UTF-8" />
           22 <!--1.5 是否只寫到一個(gè)文件里-->
           23 <param name="StaticLogFileName" value="false" />
           24 <!--1.6 配置按照何種方式產(chǎn)生多個(gè)日志文件 (Date:日期、Size:文件大小、Composite:日期和文件大小的混合方式)-->
           25 <param name="RollingStyle" value="Composite" />
           26 <!--1.7 介紹多種日志的的命名和存放在磁盤的形式-->
           27 <!--1.7.1 在根目錄下直接以日期命名txt文件 注意"的位置,去空格 -->
           28 <param name="DatePattern" value="yyyy-MM-dd".log"" />
           29 <!--1.7.2 在根目錄下按日期產(chǎn)生文件夾,文件名固定 test.log -->
           30 <!--<param name="DatePattern" value="yyyy-MM-dd/"test.log"" />-->
           31 <!--1.7.3 在根目錄下按日期產(chǎn)生文件夾,這是按日期產(chǎn)生文件夾,并在文件名前也加上日期 -->
           32 <!--<param name="DatePattern" value="yyyyMMdd/yyyyMMdd"-test.log"" />-->
           33 <!--1.7.4 在根目錄下按日期產(chǎn)生文件夾,這再形成下一級(jí)固定的文件夾 -->
           34 <!--<param name="DatePattern" value="yyyyMMdd/"OrderInfor/test.log"" />-->
           35 <!--1.8 配置每個(gè)日志的大小。【只在1.6 RollingStyle 選擇混合方式與文件大小方式下才起作用!!!】可用的單位:KB|MB|GB。不要使用小數(shù),否則會(huì)一直寫入當(dāng)前日志,
           36 超出大小后在所有文件名后自動(dòng)增加正整數(shù)重新命名,數(shù)字最大的最早寫入。-->
           37 <param name="maximumFileSize" value="10MB" />
           38 <!--1.9 最多產(chǎn)生的日志文件個(gè)數(shù),超過則保留最新的n個(gè) 將value的值設(shè)置-1,則不限文件個(gè)數(shù) 【只在1.6 RollingStyle 選擇混合方式與文件大小方式下才起作用!!!】
           39 與1.8中maximumFileSize文件大小是配合使用的-->
           40 <param name="MaxSizeRollBackups" value="5" />
           41 <!--1.10 配置文件文件的布局格式,使用PatternLayout,自定義布局-->
           42 <layout type="log4net.Layout.PatternLayout">
           43 <conversionPattern value="記錄時(shí)間:%date %n線程ID:[%thread] %n日志級(jí)別:%-5level %n出錯(cuò)類:%logger property: [%property{NDC}] - %n錯(cuò)誤描述:%message%newline %n%newline"/>
           44 </layout>
           45 </appender>
           46 
           47 <!--2. 輸出途徑(二) 記錄日志到數(shù)據(jù)庫(kù)-->
           48 <appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
           49 <!--2.1 設(shè)置緩沖區(qū)大小,只有日志記錄超設(shè)定值才會(huì)一塊寫入到數(shù)據(jù)庫(kù)-->
           50 <param name="BufferSize" value="1" />
           51 <!--2.2 引用-->
           52 <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
           53 <!--2.3 數(shù)據(jù)庫(kù)連接字符串-->
           54 <connectionString value="data source=localhost;initial catalog=LogDB;integrated security=false;persist security info=True;User ID=sa;Password=123456" />
           55 <!--2.4 SQL語(yǔ)句插入到指定表-->
           56 <commandText value="INSERT INTO LogInfor ([threadId],[log_level],[log_name],[log_msg],[log_exception],[log_time]) VALUES (@threadId, @log_level, @log_name, @log_msg, @log_exception,@log_time)" />
           57 <!--2.5 數(shù)據(jù)庫(kù)字段匹配-->
           58 <!-- 線程號(hào)-->
           59 <parameter>
           60 <parameterName value="@threadId" />
           61 <dbType value="String" />
           62 <size value="100" />
           63 <layout type="log4net.Layout.PatternLayout">
           64 <conversionPattern value="%thread" />
           65 </layout>
           66 </parameter>
           67 <!--日志級(jí)別-->
           68 <parameter>
           69 <parameterName value="@log_level" />
           70 <dbType value="String" />
           71 <size value="100" />
           72 <layout type="log4net.Layout.PatternLayout">
           73 <conversionPattern value="%level" />
           74 </layout>
           75 </parameter>
           76 <!--日志記錄類名稱-->
           77 <parameter>
           78 <parameterName value="@log_name" />
           79 <dbType value="String" />
           80 <size value="100" />
           81 <layout type="log4net.Layout.PatternLayout">
           82 <conversionPattern value="%logger" />
           83 </layout>
           84 </parameter>
           85 <!--日志信息-->
           86 <parameter>
           87 <parameterName value="@log_msg" />
           88 <dbType value="String" />
           89 <size value="5000" />
           90 <layout type="log4net.Layout.PatternLayout">
           91 <conversionPattern value="%message" />
           92 </layout>
           93 </parameter>
           94 <!--異常信息 指的是如Infor 方法的第二個(gè)參數(shù)的值-->
           95 <parameter>
           96 <parameterName value="@log_exception" />
           97 <dbType value="String" />
           98 <size value="2000" />
           99 <layout type="log4net.Layout.ExceptionLayout" />
          100 </parameter>
          101 <!-- 日志記錄時(shí)間-->
          102 <parameter>
          103 <parameterName value="@log_time" />
          104 <dbType value="DateTime" />
          105 <layout type="log4net.Layout.RawTimeStampLayout" />
          106 </parameter>
          107 </appender>
          108 <!--(二). 配置日志的的輸出級(jí)別和加載日志的輸出途徑-->
          109 <root>
          110 <!--1. level中的value值表示該值及其以上的日志級(jí)別才會(huì)輸出-->
          111 <!--OFF > FATAL(致命錯(cuò)誤) > ERROR(一般錯(cuò)誤) > WARN(警告) > INFO(一般信息) > DEBUG(調(diào)試信息) > ALL -->
          112 <!--OFF表示所有信息都不寫入,ALL表示所有信息都寫入-->
          113 <level value="ALL"></level>
          114 <!--2. append-ref標(biāo)簽表示要加載前面的日志輸出途徑代碼 通過ref和appender標(biāo)簽的中name屬性相關(guān)聯(lián)-->
          115 <appender-ref ref="RollingFileAppender"></appender-ref>
          116 <appender-ref ref="AdoNetAppender"></appender-ref>
          117 </root>
          118 </log4net>
          119 
          120 </configuration>
          

          LogUtils類代碼如下

           1 using log4net;
           2 using System;
           3 using System.Collections.Generic;
           4 using System.Diagnostics;
           5 using System.Linq;
           6 using System.Reflection;
           7 using System.Text;
           8 using System.Threading.Tasks;
           9 
           10 namespace Ypf.Utils
           11 {
           12 public class LogUtils
           13 {
           14 //可以聲明多個(gè)日志對(duì)象
           15 public static ILog log=LogManager.GetLogger(typeof(LogUtils));
           16 
           17 #region 01-初始化Log4net的配置
           18 /// <summary>
           19 /// 初始化Log4net的配置
           20 /// xml文件一定要改為嵌入的資源
           21 /// </summary>
           22 public static void InitLog4Net()
           23 {
           24 Assembly assembly=Assembly.GetExecutingAssembly();
           25 var xml=assembly.GetManifestResourceStream("Ypf.Utils.log4net.xml");
           26 log4net.Config.XmlConfigurator.Configure(xml);
           27 }
           28 #endregion
           29 
           30 
           31 
           32 
           33 /************************* 五種不同日志級(jí)別 *******************************/
           34 //FATAL(致命錯(cuò)誤) > ERROR(一般錯(cuò)誤) > WARN(警告) > INFO(一般信息) > DEBUG(調(diào)試信息)
           35 
           36 /// <summary>
           37 /// 將調(diào)試的信息輸出,可以定位到具體的位置(解決高層封裝帶來(lái)的問題)
           38 /// </summary>
           39 /// <returns></returns>
           40 private static string getDebugInfo()
           41 {
           42 StackTrace trace=new StackTrace(true);
           43 return trace.ToString();
           44 }
           45 
           46 #region 01-DEBUG(調(diào)試信息)
           47 /// <summary>
           48 /// Debug
           49 /// </summary>
           50 /// <param name="msg">日志信息</param>
           51 public static void Debug(string msg)
           52 {
           53 log.Debug(getDebugInfo() + msg);
           54 }
           55 /// <summary>
           56 /// Debug
           57 /// </summary>
           58 /// <param name="msg">日志信息</param>
           59 /// <param name="exception">錯(cuò)誤信息</param>
           60 public static void Debug(string msg, Exception exception)
           61 {
           62 log.Debug(getDebugInfo() + msg, exception);
           63 }
           64 
           65 #endregion
           66 
           67 #region 02-INFO(一般信息)
           68 /// <summary>
           69 /// Info
           70 /// </summary>
           71 /// <param name="msg">日志信息</param>
           72 public static void Info(string msg)
           73 {
           74 log.Info(getDebugInfo() + msg);
           75 }
           76 /// <summary>
           77 /// Info
           78 /// </summary>
           79 /// <param name="msg">日志信息</param>
           80 /// <param name="exception">錯(cuò)誤信息</param>
           81 public static void Info(string msg, Exception exception)
           82 {
           83 log.Info(getDebugInfo() + msg, exception);
           84 }
           85 #endregion
           86 
           87 #region 03-WARN(警告)
           88 /// <summary>
           89 /// Warn
           90 /// </summary>
           91 /// <param name="msg">日志信息</param>
           92 public static void Warn(string msg)
           93 {
           94 log.Warn(getDebugInfo() + msg);
           95 }
           96 /// <summary>
           97 /// Warn
           98 /// </summary>
           99 /// <param name="msg">日志信息</param>
          100 /// <param name="exception">錯(cuò)誤信息</param>
          101 public static void Warn(string msg, Exception exception)
          102 {
          103 log.Warn(getDebugInfo() + msg, exception);
          104 }
          105 #endregion
          106 
          107 #region 04-ERROR(一般錯(cuò)誤)
          108 /// <summary>
          109 /// Error
          110 /// </summary>
          111 /// <param name="msg">日志信息</param>
          112 public static void Error(string msg)
          113 {
          114 log.Error(getDebugInfo() + msg);
          115 }
          116 /// <summary>
          117 /// Error
          118 /// </summary>
          119 /// <param name="msg">日志信息</param>
          120 /// <param name="exception">錯(cuò)誤信息</param>
          121 public static void Error(string msg, Exception exception)
          122 {
          123 log.Error(getDebugInfo() + msg, exception);
          124 }
          125 #endregion
          126 
          127 #region 05-FATAL(致命錯(cuò)誤)
          128 /// <summary>
          129 /// Fatal
          130 /// </summary>
          131 /// <param name="msg">日志信息</param>
          132 public static void Fatal(string msg)
          133 {
          134 log.Fatal(getDebugInfo() + msg);
          135 }
          136 /// <summary>
          137 /// Fatal
          138 /// </summary>
          139 /// <param name="msg">日志信息</param>
          140 /// <param name="exception">錯(cuò)誤信息</param>
          141 public static void Fatal(string msg, Exception exception)
          142 {
          143 log.Fatal(getDebugInfo() + msg, exception);
          144 }
          145 
          146 #endregion
          147 
          148 
          149 
          150 }
          151 }
          

          步驟二:新建名Ypf.MVC的MVC5框架,添加對(duì)Ypf.Utils類庫(kù)的引用,在Global.asax全局文件中添加對(duì) 對(duì)Log4net進(jìn)行初始化。

          然后就可以愉快的進(jìn)行調(diào)用測(cè)試了哦。

           1 /// <summary>
           2 /// 測(cè)試log4net
           3 /// 首先需要再Global中初始化log4net
           4 /// </summary>
           5 /// <returns></returns>
           6 public ActionResult Index()
           7 {
           8 //FATAL(致命錯(cuò)誤) > ERROR(一般錯(cuò)誤) > WARN(警告) > INFO(一般信息) > DEBUG(調(diào)試信息)
           9 LogUtils.Debug("出錯(cuò)了");
          10 try
          11 {
          12 int.Parse("ddf");
          13 }
          14 catch (Exception ex)
          15 {
          16 LogUtils.Debug("出錯(cuò)了",ex);
          17 }
          18 
          19 LogUtils.Info("出錯(cuò)了");
          20 try
          21 {
          22 int.Parse("ddf");
          23 }
          24 catch (Exception ex)
          25 {
          26 LogUtils.Info("出錯(cuò)了", ex);
          27 }
          28 
          29 LogUtils.Warn("出錯(cuò)了");
          30 try
          31 {
          32 int.Parse("ddf");
          33 }
          34 catch (Exception ex)
          35 {
          36 LogUtils.Warn("出錯(cuò)了", ex);
          37 }
          38 
          39 LogUtils.Error("出錯(cuò)了");
          40 try
          41 {
          42 int.Parse("ddf");
          43 }
          44 catch (Exception ex)
          45 {
          46 LogUtils.Error("出錯(cuò)了", ex);
          47 }
          48 
          49 LogUtils.Fatal("出錯(cuò)了");
          50 try
          51 {
          52 int.Parse("ddf");
          53 }
          54 catch (Exception ex)
          55 {
          56 LogUtils.Fatal("出錯(cuò)了", ex);
          57 }
          58 
          59 return View();
          60 }
          

          七. 補(bǔ)充:分文件存放

          在前面的介紹中,忽略了一種情況,各種類型的日志(操作日志也好,錯(cuò)誤日志也好)都存放在一個(gè)txt文檔里,在實(shí)際開發(fā)中很不方便,在這里介紹一種利用Log4net過濾器實(shí)現(xiàn)不同日志分文件夾存放的功能。

          幾種過濾器,可以用來(lái)過濾掉Appender中的內(nèi)容:

          DenyAllFilter: 阻止所有的日志事件被記錄

          LevelMatchFilter: 只有指定等級(jí)的日志事件才被記錄

          LevelRangeFilter :日志等級(jí)在指定范圍內(nèi)的事件才被記錄

          LoggerMatchFilter: 與Logger名稱匹配,才記錄

          PropertyFilter: 消息匹配指定的屬性值時(shí)才被記錄

          StringMathFilter: 消息匹配指定的字符串才被記錄

          分文件夾存放的核心所在:

          ①. 配置文件的調(diào)整:利用LoggerMatchFilter和DenyAllFilter過濾器實(shí)現(xiàn)分文件存放。

          ②:聲明ILog對(duì)象的時(shí)候,需要與LoggerMatchFilter過濾器中的Value值相對(duì)應(yīng),才能保證該ILog對(duì)象記錄的日志存放到該Appender節(jié)點(diǎn)對(duì)應(yīng)的路徑下。

          ③. 高層調(diào)用:

          分享一下log4net的配置文件和LogUtils的封裝文件。


          主站蜘蛛池模板: 美女免费视频一区二区三区| 欧洲精品码一区二区三区免费看 | 无码日韩精品一区二区免费| 末成年女AV片一区二区| 国产在线第一区二区三区| 日韩AV无码一区二区三区不卡| 亚洲国产一区二区三区| 在线视频国产一区| tom影院亚洲国产一区二区| 伊人久久精品无码av一区| 亚洲国产成人久久一区二区三区 | 麻豆精品人妻一区二区三区蜜桃| 亚洲AV无码一区二区三区牛牛| 亚洲色大成网站www永久一区| 一区二区三区日本电影| 一区二区三区在线免费| 交换国产精品视频一区| 99久久精品午夜一区二区| 久久人妻内射无码一区三区| 性无码免费一区二区三区在线| 一区视频在线播放| 精品91一区二区三区| 国产在线精品一区在线观看| 国产aⅴ一区二区三区| 不卡一区二区在线| 视频一区二区精品的福利| 亚洲一区无码精品色| 亚洲一区中文字幕久久| 99偷拍视频精品一区二区| 韩国福利视频一区二区| 精品久久国产一区二区三区香蕉| 国产成人久久精品一区二区三区| 97精品一区二区视频在线观看 | 日韩国产精品无码一区二区三区| 亚洲一区爱区精品无码| 麻豆国产一区二区在线观看| 色窝窝无码一区二区三区| 亚无码乱人伦一区二区| 国产精品亚洲高清一区二区 | 麻豆一区二区三区精品视频 | 久久精品免费一区二区三区 |