從架構(gòu)角度來看 Java 分布式日志如何收集

首先区岗,當我們?nèi)绻鳛榧軜?gòu)師的角度去處理一件事情的時候,必須要有一些大局觀。

也就是要求我們對個 Logging 的生態(tài)有完整的認識酸茴,從而來考慮分布式日志如何處理。

我們先來理解一些概念:

劃分清楚 Logging 兢交、Metrics薪捍、 Tracing

身邊有很多同事會把這三件可能認識不太徹底,其實這是三件分別側(cè)重點不同的事情,每件事都有各自的深度酪穿、邊界和重疊部分凳干。

Logging:

Logging 更加偏重的是一條一條的記錄,而記錄本身是離散事件被济,沒有任何直接關(guān)系救赐。Logging 可以在 Console、ElasticSearch只磷、Kafka净响、File 等各種媒介中顯示。而 Logging 的格式又可以通過各種 Logging 的實現(xiàn)去定義 Logging 的格式喳瓣。

Tracing:

Tracing 的重心是整個處理鏈條的間接關(guān)系馋贤,而是需要把各種離散的 Logging 產(chǎn)生聯(lián)系,讓各種處理事件產(chǎn)生一定范圍畏陕。

Metrics:

Metrics 度量的定義特征是它們是可聚合的配乓。它們是在一段時間內(nèi)構(gòu)成單個邏輯度量,計數(shù)或直方圖的原子數(shù)據(jù)惠毁,偏重于度量犹芹。

而三者的邊界和重疊部分需要我們在整個分布式系統(tǒng)中要非常清楚,而本 chat 就圍繞 Logging 和 Tracing 這兩件事情展開一下。

技術(shù) Tracing 鏈路跟蹤鞠绰、生態(tài)圈現(xiàn)狀

Google Dapper:Dapper——Google 生產(chǎn)環(huán)境下的分布式跟蹤系統(tǒng)腰埂,而緊接著就發(fā)表了論文 Google Dapper paper 。

然后就變成了所有分布式日志 Tracing 的鼻祖了蜈膨,后來發(fā)展起來的 Zipkin屿笼、OpenTracing、sleuth 都是在 Google 的這篇論文作為理論基礎(chǔ)上翁巍,不斷優(yōu)化發(fā)展出來的驴一。

Zipkin:Zipkin 是分布式日志鏈路跟蹤系統(tǒng),最早由 Twitter 創(chuàng)造和開源灶壶,現(xiàn)在交由 OpenZipkin 社區(qū)管理肝断。它可以幫助收集時間數(shù)據(jù)在 Microservice 架構(gòu)需要解決延遲問題。

它管理這些數(shù)據(jù)的收集和查找驰凛。Zipkin 的設計是基于 Dapper胸懈。它也是一個完整的生態(tài)解決方案包括:collector(收集)、 storage(儲存)恰响、 search(搜索)趣钱、 Zipkin Web UI(頁面控制臺)。

而其也對個各種語言做了支持渔隶,我們重點關(guān)注了一下 java 的 client羔挡,看后面的表格洁奈。github : https://github.com/openzipkin;官方地址:http://zipkin.io/

OpenTracing:OpenTracinghttp://opentracing.io/通過提供平臺無關(guān)绞灼、廠商無關(guān)的 API利术,使得開發(fā)人員能夠方便的添加(或更換)追蹤系統(tǒng)的實現(xiàn)。

OpenTracing 正在為全球的分布式追蹤低矮,提供統(tǒng)一的概念和數(shù)據(jù)標準印叁。而 OpenTracing 來自大名鼎鼎的 CNCF(Cloud Native Computing Foundation, https://www.cncf.io/)。

雖然 OpenTracing 晚于 Zipkin 但是更大大佬军掂、更大抽象轮蜕,這使其很快成為業(yè)內(nèi)首選。而開源團隊:Zipkin蝗锥、TRACER跃洛、JAEGER(Uber 出品)等等逐漸實現(xiàn)了 OpenTracing 的標準。

Sleuth:這個熱鬧的事情终议,怎么能少了 Spring 開源社區(qū)呢汇竭?Spring-Cloud-Sleuth 是 Spring Cloud 的組成部分之一,為 SpringCloud 應用實現(xiàn)了一種分布式追蹤解決方案穴张,其兼容了Zipkin细燎、HTrace、OpenTracing皂甘。

而底層是基于 Zipkin 的 brave 做了實現(xiàn)玻驻。設計思路也是參考 Dapper(他們之間的關(guān)系,作者認為應該是這樣的 sleuth 通過 brave 默認輸出到 Zipkin偿枕,由 Zipkin 決定是否是 OpenTracing 格式的輸出璧瞬,PS:這個歡迎大家一起討論)。

官方地址:http://cloud.spring.io/spring-cloud-static/spring-cloud-sleuth/2.0.0.RC1/single/spring-cloud-sleuth.html

Jaeger 分布式監(jiān)控系統(tǒng)由 Uber 設計并實現(xiàn)益老,現(xiàn)已捐贈給 CNCF 基金會彪蓬,作為分布式環(huán)境下推薦的監(jiān)控系統(tǒng)。其實主要是作為 Tracing 的 server 端捺萌,前期先支持了 Zipkin 后來又實現(xiàn)了 OpenTracing 的標準。有 UI 和儲存(ElasticSearch膘茎、cassandra)桃纯。和 Zipkin 的 ui 服務器端有點像。官方地址:https://www.jaegertracing.io/

重點關(guān)注一下 Zipkin 的開源庫

Tracing 整體負責干的事情有:

生成 trackId, spanId披坏。

負責 MDC 給 log态坦。

發(fā)送給 tracingserver 端,server 負責儲存和搜索棒拂。

Tracing UI 負責展示伞梯。

技術(shù) Logging 本身玫氢,生態(tài)圈現(xiàn)狀

上面我們了解整個 Tracing 的技術(shù)棧,我們再來看下關(guān)于 Logging 的技術(shù)棧谜诫。

Spring Logging:Java Util Logging漾峡、SLF4J、Log4J喻旷、Log4J2和Logback 這些都是老生常談的問題了生逸,默認 Spring Logging 內(nèi)部采用的是 Commons Logging。

但是當我們引用相應的其它 Logging 的實現(xiàn)和相應的 Logging 文件的時候就會自動切換 Logging 的實現(xiàn)且预,并做到兼容槽袄。我們唯一需要注意的是:SpringProfile 的支持,如下:

<springProfile name="staging">

? ?<!-- configuration to be enabled when the "staging" profile is active -->

</springProfile>

<springProfile name="dev, staging">

? ?<!-- configuration to be enabled when the "dev" or "staging" profiles are active -->

</springProfile>

<springProfile name="!production">

? ?<!-- configuration to be enabled when the "production" profile is not active -->

</springProfile>

自定義日志實現(xiàn):

ELK:Spring Logging 緊緊是負責單機日志輸出锋谐,而分布式不得不請出 ELK遍尺。

ElasticSearch 負責作為我們的 logs 的儲存和查詢,其數(shù)據(jù)可以提供給 Jaeger 使用可以給 Kibana 使用涮拗。

而 Kibana 負責做各種基于 logs 的 chat 圖和查看詳細的 Logging 的日志記錄的詳情乾戏。Logstash 不用多說了,負責給我們收集日志多搀,包括網(wǎng)關(guān)層歧蕉,業(yè)務層等。

Sentry:也是一個重量級選手康铭。負責解決我們系統(tǒng)中的 error 日志和 error 日志警告惯退。

Sentry 就是來幫我們解決這個問題的,它是一款精致的 Django 應用从藤,目的在于幫助開發(fā)人員從散落在多個不同服務器上毫無頭緒的日志文件里發(fā)掘活躍的異常催跪,繼而找到潛在的臭蟲。

Sentry 是一個日志平臺, 它分為客戶端和服務端夷野,客戶端(目前客戶端有 Python懊蒸、PHP、C#悯搔、Ruby 等多種語言)就嵌入在你的應用程序中間骑丸,程序出現(xiàn)異常就向服務端發(fā)送消息,服務端將消息記錄到數(shù)據(jù)庫中并提供一個 Web 界面方便查看妒貌。

Sentry 還有有很多亮點通危,比如敏感信息過濾, release 版本跟蹤灌曙,關(guān)鍵字查找菊碟,受影響用戶統(tǒng)計,權(quán)限管理等(部分可能需要我們通過代碼提供內(nèi)容)可以通過 Sentry 進行問題分配與跟蹤在刺。

Sentry 的 plugin 模塊還可以集成大量的第三方工具如: slack 逆害, jira 头镊。

對我們來說最大的便利就是利用日志進行錯誤發(fā)現(xiàn)和排查的效率變高了。

重要的有一下三點:

及時提醒

報警的及時性:不需要自己再去額外集成報警系統(tǒng)魄幕,一旦產(chǎn)生了 issue 便以郵件通知到項目組的每個成員相艇。

問題關(guān)聯(lián)信息的聚合

每個問題不僅有一個整體直觀的描繪,聚合的日志信息省略了人工從海量日志中尋找線索梅垄,免除大量無關(guān)信息的干擾厂捞。

豐富的上下文

Sentry 不僅豐富還規(guī)范了上下文的內(nèi)容,也讓我們意識到更多的有效內(nèi)容队丝,提高日志的質(zhì)量靡馁。

技術(shù)選型 VS

當我們了解了我們需要知道的技術(shù)點之后,接下去就是針對我們公司具體業(yè)務現(xiàn)狀進行選型机久,以我們公司為例臭墨,可能不止一個 Java 團隊,還有 Ruby膘盖,node.js 等其它語言的開發(fā)團隊胧弛。

好多其它技術(shù)選型都是基于 cncf 的,如:k8s侠畔、docker结缚、permissions 等,所以我們就一如既往的還選擇了 CNCF 的技術(shù)體系及 OpenTracing软棺。

其實如果要去真實比較的話红竭,差別也不是特別大,并且都做到了相互的兼容喘落。而 Jaeger VS Zipkin server 選擇了 Jaeger茵宪,因其啟動簡單與 Java 解耦。

Java 語言體系采用 Spring 的 Sleuth瘦棋,這樣我們可以省很多事情稀火,并且也是很成熟的解決方案,而 Spring Cloud 生態(tài)也非常成熟赌朋。

實戰(zhàn)

生產(chǎn)的日志要求

每個請求的參數(shù)是什么凰狞,輸出結(jié)果是什么,debug 可以選擇自由開啟沛慢。

每個請求的鏈路要串起來服球。

error 獨立收集上下文是什么,及時警告颠焦,各個環(huán)境分開。

生產(chǎn)的日志實現(xiàn)

第一個問題:所有請求的日志明細

1. 我們利用

importorg.springframework.web.filter.CommonsRequestLoggingFilter;

來打印我們的所有的請求的日志配置如下:

//我們只需要將此類在配置文件中加載即可往枣。里面可以設置Logging里面是否打印header 伐庭、request payload粉渠、query String 、client信息等圾另。唯一的缺點就是沒有辦法打印responseBody霸株。@Bean@ConditionalOnMissingBeanpublicCommonsRequestLoggingFilterrequestLoggingFilter(){ ? ? ? ?CommonsRequestLoggingFilter loggingFilter =newCommonsRequestLoggingFilter(); ? ? ? ?loggingFilter.setIncludeClientInfo(true); ? ? ? ?loggingFilter.setIncludeQueryString(true); ? ? ? ?loggingFilter.setIncludePayload(true); ? ? ? ?loggingFilter.setIncludeHeaders(true);returnloggingFilter; ? ?}

//源碼和原理其實非常簡單,做個filter做logging debug即可集乔。publicclassCommonsRequestLoggingFilterextendsAbstractRequestLoggingFilter{@OverrideprotectedbooleanshouldLog(HttpServletRequest request){returnlogger.isDebugEnabled(); ? ?}/**

? ? * Writes a log message before the request is processed.

? ? */@OverrideprotectedvoidbeforeRequest(HttpServletRequest request, String message){ ? ? ? ?logger.debug(message); ? ?}/**

? ? * Writes a log message after the request is processed.

? ? */@OverrideprotectedvoidafterRequest(HttpServletRequest request, String message){ ? ? ? ?logger.debug(message); ? ?}}

日志輸出的格式如下:

[36667] 2018-05-19 20:22:06.185 - [notification-api,93bb291ab411e41a,93bb291ab411e41a,false] - DEBUG [http-nio-8080-exec-1] org.springframework.web.filter.CommonsRequestLoggingFilter.log - Before request [uri=/hello;client=127.0.0.1;headers={host=[127.0.0.1:8080], connection=[keep-alive], accept=[*/*], user-agent=[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36], referer=[http://127.0.0.1:8080/swagger-ui.html], accept-encoding=[gzip, deflate, br], accept-language=[en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7], cookie=[OUTFOX_SEARCH_USER_ID_NCOO=1602949848.9012377; gsScrollPos-73=]}]

[36667] 2018-05-19 20:22:06.434 - [notification-api,93bb291ab411e41a,93bb291ab411e41a,false] - DEBUG [http-nio-8080-exec-1] org.springframework.web.filter.CommonsRequestLoggingFilter.log - After request [uri=/hello;client=127.0.0.1;headers={host=[127.0.0.1:8080], connection=[keep-alive], accept=[*/*], user-agent=[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36], referer=[http://127.0.0.1:8080/swagger-ui.html], accept-encoding=[gzip, deflate, br], accept-language=[en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7], cookie=[OUTFOX_SEARCH_USER_ID_NCOO=1602949848.9012377; gsScrollPos-73=]}]

2. 針對沒有 responseBody 的問題去件,我們可以自定義一個攔截器,和 CommonsRequestLoggingFilter 做差不多的事情即可扰路。這里需要注意的是需要用到:

importorg.springframework.web.util.ContentCachingRequestWrapper;

importorg.springframework.web.util.ContentCachingResponseWrapper;

來做參數(shù)的輸出和 response 的 io 的輸出尤溜。但是切記很多東西不需要重復寫給大家看一個關(guān)鍵代碼:

第二個問題: 將 Logging 收集到 ELK

此處我們采用的是 Docker 容器,直接將日志輸出到控制臺汗唱,用 logstash 直接收集 Docker 的日志給 ElasticSearch 在 kibana 顯示宫莱。如下圖所示:

我們只需要 search trackID 即可。

或者以 logback 為例哩罪,添加 logstash appender授霸。關(guān)鍵代碼如下:

? ? ? ? ? ? <!-- Appender to log to file in a JSON format -->

? ? ? ? ? ? <appender name="logstash" class="ch.qos.logback.core.rolling.RollingFileAppender">

? ? ? ? ? ? <file>${LOG_FILE}.json</file>

? ? ? ? ? ? <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

? ? ? ? ? ? <fileNamePattern>${LOG_FILE}.json.%d{yyyy-MM-dd}.gz</fileNamePattern>

? ? ? ? ? ? <maxHistory>7</maxHistory>

? ? ? ? ? ?</rollingPolicy>

? ? ? ? ? <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">

? ? ? ? ? ? <providers>

? ? ? ? ? ?<timestamp>

? ? ? ? ? ? ? ?<timeZone>UTC</timeZone>

? ? ? ? ? ?</timestamp>

? ? ? ? ? ? ? <pattern>

? ? ? ? ? ? ? ?<pattern>

? ? ? ? ? ? ? ? ? ?{

? ? ? ? ? ? ? ? ? ?"severity": "%level",

? ? ? ? ? ? ? ? ? ?"service": "${springAppName:-}",

? ? ? ? ? ? ? ? ? ?"trace": "%X{X-B3-TraceId:-}",

? ? ? ? ? ? ? ? ? ?"span": "%X{X-B3-SpanId:-}",

? ? ? ? ? ? ? ? ? ?"parent": "%X{X-B3-ParentSpanId:-}",

? ? ? ? ? ? ? ? ? ?"exportable": "%X{X-Span-Export:-}",

? ? ? ? ? ? ? ? ? ?"pid": "${PID:-}",

? ? ? ? ? ? ? ? ? ?"thread": "%thread",

? ? ? ? ? ? ? ? ? ?"class": "%logger{40}",

? ? ? ? ? ? ? ? ? ?"rest": "%message"

? ? ? ? ? ? ? ? ? ?}

? ? ? ? ? ? ? ?</pattern>

? ? ? ? ? ?</pattern>

? ? ? ?</providers>

? ?</encoder>

</appender>

第三個問題:我們在我們的每個請求 Header 上加上 traceId

//從上下文中取到traceId,然后丟到返回的header里面@OverridepublicvoiddoFilter(ServletRequest request, ServletResponse response, FilterChain chain)throwsIOException, ServletException{ ? ? ? ?String traceId = ThreadContext.get("traceId"); ? ? ? ? ? ?chain.doFilter(request, response); ? ? ? ?((HttpServletResponse)response).setHeader("TraceId", traceId); ? ?}

第四個問題:Tracing 處理

1. 有了上面的理論基礎(chǔ)际插,就是就看看 spring cloud sleuth 怎么支持 OpenTracing 和生成 tracId 和 span碘耳,及其將 log 吐給 jaeger。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末框弛,一起剝皮案震驚了整個濱河市辛辨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌功咒,老刑警劉巖愉阎,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異力奋,居然都是意外死亡榜旦,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門景殷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來溅呢,“玉大人,你說我怎么就攤上這事猿挚「谰桑” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵绩蜻,是天一觀的道長铣墨。 經(jīng)常有香客問我,道長办绝,這世上最難降的妖魔是什么伊约? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任姚淆,我火速辦了婚禮,結(jié)果婚禮上屡律,老公的妹妹穿的比我還像新娘腌逢。我一直安慰自己,他們只是感情好超埋,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布搏讶。 她就那樣靜靜地躺著,像睡著了一般霍殴。 火紅的嫁衣襯著肌膚如雪媒惕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天繁成,我揣著相機與錄音吓笙,去河邊找鬼。 笑死巾腕,一個胖子當著我的面吹牛面睛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播尊搬,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼叁鉴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了佛寿?” 一聲冷哼從身側(cè)響起幌墓,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎冀泻,沒想到半個月后常侣,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡弹渔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年胳施,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肢专。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡舞肆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出博杖,到底是詐尸還是另有隱情椿胯,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布剃根,位于F島的核電站哩盲,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜种冬,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一镣丑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧娱两,春花似錦、人聲如沸金吗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽摇庙。三九已至旱物,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間卫袒,已是汗流浹背宵呛。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留夕凝,地道東北人宝穗。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像码秉,于是被迫代替她去往敵國和親逮矛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容