如何優雅收集和管理應用的多行日誌

2022-01-10 奇妙的Linux世界

多行日誌(例如異常信息)為調試應用問題提供了許多非常有價值的信息,在分布式微服務流行的今天基本上都會統一將日誌進行收集,比如常見的 ELK、EFK 等方案,但是這些方案如果沒有適當的配置,它們是不會將多行日誌看成一個整體的,而是每一行都看成獨立的一行日誌進行處理,這對我們來說是難以接受的。

在本文中,我們將介紹一些常用日誌收集工具處理多行日誌的策略。

1JSON

保證多行日誌作為單個事件進行處理最簡單的方法就是以 JSON 格式記錄日誌,比如下面是常規 Java 日常日誌的示例:

# javaApp.log
2019-08-14 14:51:22,299 ERROR [http-nio-8080-exec-8] classOne: Index out of range
java.lang.StringIndexOutOfBoundsException: String index out of range: 18
 at java.lang.String.charAt(String.java:658)
 at com.example.app.loggingApp.classOne.getResult(classOne.java:15)
 at com.example.app.loggingApp.AppController.tester(AppController.java:27)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:498)
 at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
 at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
[...]

如果直接收集上面的日誌會識別為多行日誌,如果我們用 JSON 格式來記錄這些日誌,然後介紹 JSON 的數據就簡單多了,比如使用 Log4J2 來記錄,變成下面的格式:

{"@timestamp":"2019-08-14T18:46:04.449+00:00","@version":"1","message":"Index out of range","logger_name":"com.example.app.loggingApp.classOne","thread_name":"http-nio-5000-exec-6","level":"ERROR","level_value":40000,"stack_trace":"java.lang.StringIndexOutOfBoundsException: String index out of range: 18\n\tat java.lang.String.charAt(String.java:658)\n\tat com.example.app.loggingApp.classOne.getResult(classOne.java:15)\n\tat com.example.app.loggingApp.AppController.tester(AppController.java:27)\n\tat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n\tat java.lang.reflect.Method.invoke(Method.java:498)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)\n\tat
[...]
}

這樣整個日誌消息都包含在單個 JSON 對象匯總了,其中就包含完整的異常堆棧信息,絕大多數工具都支持直接解析 JSON 日誌數據,這是最簡單的一種方法,對於運維同學來說也是最省心的,但是大部分開發人員是牴觸用 JSON 格式來記錄日誌的~~~

2Logstash

對於使用 Logstash 的用戶來說,要支持多行日誌也不困難,Logstash 可以使用插件解析多行日誌,該插件在日誌管道的 input 部分進行配置。例如,下面的配置表示讓 Logstash 匹配你的日誌文件中 ISO8601 格式的時間戳,當匹配到這個時間戳的時候,它就會將之前所有不以時間戳開頭的內容摺疊到之前的日誌條目中去。

input {
  file {
    path => "/var/app/current/logs/javaApp.log"
    mode => "tail"
    codec => multiline {
      pattern => "^%{TIMESTAMP_ISO8601} "
      negate => true
      what => "previous"
    }
  }
}

3Fluentd

和 Logstash 類似,Fluentd 也允許我們使用一個插件來處理多行日誌,我們可以配置插件接收一個或多個正則表達式,以下面的 Python 多行日誌為例:

2019-08-01 18:58:05,898 ERROR:Exception on main handler
Traceback (most recent call last):
  File "python-logger.py", line 9, in make_log
    return word[13]
IndexError: string index out of range

如果沒有 multiline 多行解析器,Fluentd 會把每行當成一條完整的日誌,我們可以在 <source> 模塊中添加一個 multiline 的解析規則,必須包含一個 format_firstline 的參數來指定一個新的日誌條目是以什麼開頭的,此外還可以使用正則分組和捕獲來解析日誌中的屬性,如下配置所示:

<source>
  @type tail
  path /path/to/pythonApp.log
  tag sample.tag
  <parse>
    @type multiline
    format_firstline /\d{4}-\d{1,2}-\d{1,2}/
    format1 /(?<timestamp>[^ ]* [^ ]*) (?<level>[^\s]+:)(?<message>[\s\S]*)/
  </parse>
</source>


點擊上方圖片,打開小程序,『美團外賣』紅包天天免費領!

在解析部分我們使用 @type multiline 指定了多行解析器,然後使用 format_firstline 來指定我們多行日誌開頭的規則,這裡我們就用一個簡單的正則匹配日期,然後指定了其他部分的匹配模式,並為它們分配了標籤,這裡我們將日誌拆分成了 timestamp、level、message 這幾個欄位。

經過上面的規則解析過後,現在 Fluentd 會將每個 traceback 日誌看成一條單一的日誌了:

{
  "timestamp": "2019-08-01 19:22:14,196",
  "level": "ERROR:",
  "message": "Exception on main handler\nTraceback (most recent call last):\n  File \"python-logger.py\", line 9, in make_log\n    return word[13]\nIndexError: string index out of range"
}

該日誌已被格式化為 JSON,我們匹配的標籤也被設置為了 Key。

在 Fluentd 官方文檔中也有幾個示例說明:

比如輸入的 Rails 日誌如下所示:

Started GET "/users/123/" for 127.0.0.1 at 2013-06-14 12:00:11 +0900
Processing by UsersController#show as HTML
  Parameters: {"user_id"=>"123"}
  Rendered users/show.html.erb within layouts/application (0.3ms)
Completed 200 OK in 4ms (Views: 3.2ms | ActiveRecord: 0.0ms)

我們可以使用下面的解析配置進行多行匹配:

<parse>
  @type multiline
  format_firstline /^Started/
  format1 /Started (?<method>[^ ]+) "(?<path>[^"]+)" for (?<host>[^ ]+) at (?<time>[^ ]+ [^ ]+ [^ ]+)\n/
  format2 /Processing by (?<controller>[^\u0023]+)\u0023(?<controller_method>[^ ]+) as (?<format>[^ ]+?)\n/
  format3 /(  Parameters: (?<parameters>[^ ]+)\n)?/
  format4 /  Rendered (?<template>[^ ]+) within (?<layout>.+) \([\d\.]+ms\)\n/
  format5 /Completed (?<code>[^ ]+) [^ ]+ in (?<runtime>[\d\.]+)ms \(Views: (?<view_runtime>[\d\.]+)ms \| ActiveRecord: (?<ar_runtime>[\d\.]+)ms\)/
</parse>

解析過後得到的日誌如下所示:

{
  "method"           :"GET",
  "path"             :"/users/123/",
  "host"             :"127.0.0.1",
  "controller"       :"UsersController",
  "controller_method":"show",
  "format"           :"HTML",
  "parameters"       :"{ \"user_id\":\"123\"}",
  ...
}

比如現在我們要解析的日誌如下所示:

2013-3-03 14:27:33 [main] INFO  Main - Start
2013-3-03 14:27:33 [main] ERROR Main - Exception
javax.management.RuntimeErrorException: null
    at Main.main(Main.java:16) ~[bin/:na]
2013-3-03 14:27:33 [main] INFO  Main - End

則我們可以使用下面的解析規則進行多行匹配:

<parse>
  @type multiline
  format_firstline /\d{4}-\d{1,2}-\d{1,2}/
  format1 /^(?<time>\d{4}-\d{1,2}-\d{1,2} \d{1,2}:\d{1,2}:\d{1,2}) \[(?<thread>.*)\] (?<level>[^\s]+)(?<message>.*)/
</parse>

解析過後的日誌為:

{
  "thread" :"main",
  "level"  :"INFO",
  "message":"  Main - Start"
}
{
  "thread" :"main",
  "level"  :"ERROR",
  "message":" Main - Exception\njavax.management.RuntimeErrorException: null\n    at Main.main(Main.java:16) ~[bin/:na]"
}
{
  "thread" :"main",
  "level"  :"INFO",
  "message":"  Main - End"
}

上面的多行解析配置中除了 format_firstline 指定多行日誌的開始行匹配之外,還用到了 format1、format2…formatN 這樣的配置,其中 N 的範圍是 1...20,是多行日誌的 Regexp 格式列表,為了便於配對,可以將 Regexp 模式分割成多個 regexpN 參數,將這些匹配模式連接起來構造出多行模式的正則匹配。

4Fluent Bit

Fluent Bit 的 tail input 插件也提供了處理多行日誌的配置選項,比如現在我們還是來處理之前的 Python 多行日誌:

2019-08-01 18:58:05,898 ERROR:Exception on main handler
Traceback (most recent call last):
File "python-logger.py", line 9, in make_log
return word[13]
IndexError: string index out of range

如果不用多行解析器 Fluent Bit 同樣會將每一行當成一條日誌進行處理,我們可以配置使用 Fluent Bit 內置的 regex 解析器插件來結構化多行日誌:

  [PARSER]
      Name         log_date
      Format       regex
      Regex        /\d{4}-\d{1,2}-\d{1,2}/

  [PARSER]
      Name        log_attributes
      Format      regex
      Regex       /(?<timestamp>[^ ]* [^ ]*) (?<level>[^\s]+:)(?<message>[\s\S]*)/

   [INPUT]
      Name              tail
      tag               sample.tag
      path              /path/to/pythonApp.log
      Multiline         On
      Parser_Firstline  log_date
      Parser_1          log_attributes

和 Fluentd 類似,Parser_Firstline 參數指定了與多行日誌開頭相匹配的解析器的名稱,當然我們也可以包含額外的解析器來進一步結構化你的日誌。這裡我們配置了首先使用 Parser_Firstline 參數來匹配 ISO8601 日期開頭的日誌行,然後使用 Parser_1 參數來指定匹配模式,以匹配日誌消息的其餘部分,並為它們分配了 timestamp、level、message 標籤。

最終轉換過後我們的日誌變成了如下所示的格式:

{
  "timestamp": "2019-08-01 19:22:14,196",
  "level": "ERROR:",
  "message": "Exception on main handler\nTraceback (most recent call last):\n  File \"python-logger.py\", line 9, in make_log\n    return word[13]\nIndexError: string index out of range"
}

本文轉載自:「 陽明的博客 」,原文:https://tinyurl.com/2dnbtaaz ,版權歸原作者所有。歡迎投稿,投稿郵箱: editor@hi-linux.com。

相關焦點

  • 多行日誌收集管理搞不定?
    多行日誌(例如異常信息)為調試應用問題提供了許多非常有價值的信息,在分布式微服務流行的今天基本上都會統一將日誌進行收集,比如常見的 ELK、EFK 等方案,但是這些方案如果沒有適當的配置,它們是不會將多行日誌看成一個整體的,而是每一行都看成獨立的一行日誌進行處理,這對我們來說是難以接受的。
  • Kubernetes ELK 日誌收集
    Kubernetes EFK日誌收集Kubernetes日誌收集架構Kubernetes集群本身不提供收集日誌的解決方案,目前基於ELK日誌收集的方案主要有三種在節點運行一個agent收集日誌在Pod中包含一個sidecar容器來收集日誌直接通過應用程式將日誌信息推送到採集後端 (例kafka,es等)節點級別的日誌記錄節點日誌採集 通過在每個節點上運行一個日誌收集的Agent來採集數據,日誌採集agent是一種專用工具,用於將日誌數據推送到統一的後端
  • 如何使用註解優雅的記錄操作日誌
    寫在開頭 本文討論如何優雅的記錄操作日誌,並且實現了一個SpringBoot Starter(取名log-record-starter),方便的使用註解記錄操作日誌,並將日誌數據推送到指定數據管道(消息隊列等)本文靈感來源於美團技術團隊的文章:如何優雅地記錄操作日誌
  • 應急響應-作業系統日誌收集與分析
    一、Windows日誌收集與分析    在運維工作過程中,如若windows伺服器被入侵,往往需要檢索和分析相應的安全日誌
  • 日誌收集工具 logpipe 更新至 0.9.0 版本
    日誌收集工具 logpipe 0.9.0 已發布。
  • 日誌管理有什麼用?
    合理的分析企業日誌是IT管理員的重要工作技能之一,日誌是用來記錄信息的,如伺服器的日誌主要是用來記錄一些訪問信息,以便出現狀況時,可以根據日誌來判斷原因。 對於IT管理員來說,日誌管理具有重大意義。日誌管理分為很多種類,平時所用的應用程式、網絡、印表機、Windows系統及Linux系統等,都會產生日誌,我們在進行一些操作時,這些日誌文件通常會記錄下我們操作的一些相關內容,這些內容對系統安全工作人員相當有用。
  • 日誌收集工具 logpipe 更新至 0.16.1 版本
    作為一個日誌採集的本地代理,內存佔用應該小而受控,性能應該高效,耗費CPU低對應用影響儘可能小,要能異步實時追蹤日誌文件增長,某些應用會在目標目錄下產生多個日誌文件甚至現在不能確定將來的日誌文件名,架構上要支持多輸入多輸出流式日誌採集傳輸,為了達成以上需求,我研究了所需技術,評估實現難度並不高,就自研了logpipe。
  • 系統日誌管理規範
    系統日誌管理是為了規範系統日誌管理過程,加強系統日誌的管理與保護,提升信息系統安全保障能力。
  • 通過vSphere Client收集ESXi主機日誌的方法
    該文檔是關於通過vSphere Client收集ESXi主機日誌的,如需通過Web Client等方式收集日誌,可以參考VMware
  • K8S集群模式下fluent-bit日誌收集方案設計和實踐
    本篇文章中結合作者使用經驗,分析和設計 K8s 日誌收集實踐過程。」1、回顧隨著集群規模不斷擴大,日誌收集問題將一直縈繞在我們耳邊,前段時間我用七篇文章安利了使用 fluentd 及 fluent-bit 好處,具體可以參考如下連結:下面我就直接介紹fluent-bit整體收集架構和插件,如果對整體有不理解的部分,可以參考如上連結。
  • 分布式日誌收集之Logstash 筆記(二)
    ,北京天氣竟然下起了大雪,不錯,最近幾年已經很少見到雪了,想起小時候冬天的樣子,回憶的影子還是歷歷在目。<qtime>\d+)查詢偏移開始:start=(?<start>\d+)返回數量:rows=(?
  • 網信辦發布APP管理規定 用戶日誌信息保存60天
    原標題:APP記錄用戶日誌信息保存60天   昨天,國家網際網路信息辦公室發布《移動網際網路應用程式信息服務管理規定》(以下簡稱《規定》),要求按照「後臺實名、前臺自願」的原則,對註冊用戶進行基於行動電話號碼等真實身份信息認證,建立健全用戶信息安全保護機制和全信息內容審核管理機制等。
  • 安全日誌分析系統架構
    而平時創建日誌分析系統很多都是同時匯聚很多系統的日誌的, WAF,IDS,網關,很多系統產生日誌都可以用同一系統,匯聚日誌,分析日誌。我們先回顧一下基於Openresty+的WEB防護系統的那張圖。(Exchange)WAF系統日誌。0×12 移動網關上圖右邊的系統是一個移動網關,移動服務網關和郵件網關功能類似,讓外網用戶的請求通過網關轉給內網伺服器。圖上畫是基於單層負載均衡,當代入到內部系統時,內網的被請求系統是不是基於負載均衡就不考慮了。
  • 以「班務日誌」創新班級管理
    班級成立初期,著力於學生班級自主管理、自我管理能力的培育。我們以「班務日誌」為載體,為學生提供實踐活動平臺,健全評價機制,討論制定班級行為規範評定細則,確定日誌主體內容,形成日誌記載表。對照班級行為規範評定細則,認真填寫「班務日誌」記載表和值日的反思體會。放學前總結髮言,重點說明值日過程中發現的優缺點,並提出改進意見和建議。「班務日誌」實施一學期,湧現了一批優秀的值日班長。第二學期改為兩人合作,由優秀的值日班長帶同學,保證班級管理有條不紊的同時讓全體同學參與班級管理,實現每位同學有能力獨立完成值日班長的工作。
  • 如何應用觀察者設計模式重構系統中日誌處理功能實現的程序代碼
    軟體項目實訓及課程設計指導——如何應用觀察者設計模式重構系統中的日誌處理功能實現的程序代碼1、GOF設計模式中的觀察者設計模式(1)什麼是觀察者設計模式GOF設計模式中的觀察者設計模式定義了一種解耦「一對多」的依賴關係的編程模式
  • 網絡管理員必備的10個優秀日誌分析工具
    日誌分析工具從設備的日誌文件中收集數據,並將其轉換為易於閱讀的格式。在日誌分析工具中,以圖形將性能的相關數據顯示到儀錶盤。以這種集中格式,讀取性能數據要比嘗試直接讀取日誌文件作為文本文件容易得多。1.SplunkSplunk使用最廣泛的日誌管理平臺之一。Splunk實時監控日誌和數據。Splunk的多功能性使其能夠從網絡中的幾乎任何設備或應用中獲取日誌數據。使用時,可以使用搜索欄查看實時和歷史數據。還有搜索建議可幫助你更輕鬆地找到所需信息。
  • logpipe日誌採集工具
    logpipe是一個分布式、高可用的用於採集、傳輸、對接落地的日誌工具,採用了插件風格的框架結構設計,支持多輸入多輸出按需配置組件用於流式日誌收集架構,無第三方依賴。logpipe由若干個input、事件總線和若干個output組成。啟動logpipe管理進程(monitor),派生一個工作進程(worker),監控工作進程崩潰則重啟工作進程。工作進程裝載配置加載若干個input插件和若干個output插件,進入事件循環,任一input插件產生消息後輸出給所有output插件。
  • linux系統下各種日誌文件的介紹,查看,及日誌服務配置
    日誌文件的分類1.內核及系統日誌: 內核及系統日誌:這種日誌數據由系統服務rsyslog同-管理,根據其主配置文件/etc/rsyslog.conf中的設置決定將內核消息及各種系統程序消息記錄到什麼位置。系統中大部分的程序會把自己的日誌文件交由rsyslog管理,因而這些應用程式使用的日誌記錄格式都很相似。
  • app測試日誌如何獲取,logcat值得擁有
    Logcat是一個命令行工具,用於轉儲系統消息日誌,包括設備拋出錯誤時的堆棧軌跡,以及從您的應用中使用 Log 類寫入的消息。要通過 adb shell 運行 Logcat,一般用法如下:[adb] logcat [<option>] ...
  • Oracle DBA如何管理DB2(下)
    存儲管理    從邏輯觀點上來看,Oracle有一個包含多個表空間的資料庫,表空間包含多個段(表,索引,回滾等),還包括由多個資料庫塊組成的擴展(Extent)。從物理意義上看,數據文件被分配給表空間,這些數據文件是由O/S塊組成。