自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

寫給開發(fā)者:記錄日志的10個(gè)建議

開發(fā) 項(xiàng)目管理
歡迎在新的一年來到我的博客。在一個(gè)巴黎devops maillist上回復(fù)了一個(gè)關(guān)于監(jiān)控和日志監(jiān)控之后,我想起了很久以前我的一個(gè)博客計(jì)劃。

歡迎在新的一年來到我的博客。在一個(gè)巴黎devops maillist上回復(fù)了一個(gè)關(guān)于監(jiān)控和日志監(jiān)控之后,我想起了很久以前我的一個(gè)博客計(jì)劃。

盡管在寫這篇博文的時(shí)候,我是在負(fù)責(zé)運(yùn)維工作,不過本文主要是寫給開發(fā)者的。

對(duì)我來說,明白如何記錄日志和記錄什么,是軟件工程師必須明了的最艱巨的任務(wù)之一。之所以這么說,是因?yàn)檫@項(xiàng)任務(wù)與預(yù)測(cè)(divination)類 似,你不知道當(dāng)你要調(diào)試的時(shí)候需要些什么信息……我希望這10個(gè)建議能幫助你更好地在應(yīng)用程序中記錄日志,讓運(yùn)維工程師們受益。:)

1. 你不應(yīng)自己寫log

絕對(duì)不要,即便是用printf或者是自己寫入到log文件,又或自己處理logrotate。請(qǐng)給你的運(yùn)維同志們省省心,調(diào)用標(biāo)準(zhǔn)庫或者系統(tǒng)API來完成它。

這樣,你可以保證程序的運(yùn)行與其他系統(tǒng)組件好好相處,把log寫到正確的位置或者網(wǎng)絡(luò)服務(wù)上,而不需要專門的系統(tǒng)配置。

假如你要使用系統(tǒng)API,也就是syslog(3),學(xué)習(xí)好怎么用它。

如果你更喜歡用logging庫,在Java里面你有很多選擇,例如Log4j,JCL,slf4j和logback。我最喜歡用slf4j和logback的組合,因?yàn)樗鼈兲貏e給力,而且相對(duì)地容易配置(還允許使用JMX進(jìn)行配置或者重載配置文件)。

slf4j最好的是你可以修改logging控制臺(tái)的位置。如果你在編寫一個(gè)庫,這會(huì)變得非常重要,因?yàn)檫@可以讓庫的使用者使用自己的logging控制臺(tái)而不需要修改你的庫。

其他語言當(dāng)然也有多種logging庫,例如ruby的Log4r,stdlib logger,和幾近完美的Jordan Sissel’s Ruby-cabin。

如果你想糾結(jié)CPU占用問題,那么你不用看這篇文章了。還有,不要把log語句放在緊內(nèi)部循環(huán)體內(nèi),否則你永遠(yuǎn)看不出區(qū)別來。

2. 你應(yīng)在適當(dāng)級(jí)別上進(jìn)行l(wèi)og

如果你遵循了上述第一點(diǎn)的做法,接下來你要對(duì)你程序中每一個(gè)log語句使用不同的log級(jí)別。其中最困難的一個(gè)任務(wù)是找出這個(gè)log應(yīng)該是什么級(jí)別

以下是我的一些建議:

  • TRACE level: 如果使用在生產(chǎn)環(huán)境中,這是一個(gè)代碼異味(code smell)。它可以用于開發(fā)過程中追蹤bug,但不要提交到你的版本控制系統(tǒng)
  • DEBUG level: 把一切東西都記錄在這里。這在debug過程中最常用到。我主張?jiān)谶M(jìn)入生產(chǎn)階段前減少debug語句的數(shù)量,只留下最有意義的部分,在調(diào)試(troubleshooting)的時(shí)候激活。
  • INFO level: 把用戶行為(user-driven)和系統(tǒng)的特定行為(例如計(jì)劃任務(wù)…)
  • NOTICE level: 這是生產(chǎn)環(huán)境中使用的級(jí)別。把一切不認(rèn)為是錯(cuò)誤的,可以記錄的事件都log起來
  • WARN level: 記錄在這個(gè)級(jí)別的事件都有可能成為一個(gè)error。例如,一次調(diào)用數(shù)據(jù)庫使用的時(shí)間超過了預(yù)設(shè)時(shí)間,或者內(nèi)存緩存即將到達(dá)容量上限。這可以讓你適當(dāng)?shù)匕l(fā)出警報(bào),或者在調(diào)試時(shí)更好地理解系統(tǒng)在failure之前做了些什么
  • ERROR level: 把每一個(gè)錯(cuò)誤條件都記錄在這。例如API調(diào)用返回了錯(cuò)誤,或是內(nèi)部錯(cuò)誤條件
  • FATAL level: 末日來了。它極少被用到,在實(shí)際程序中也不應(yīng)該出現(xiàn)多少。在這個(gè)級(jí)別上進(jìn)行l(wèi)og意味著程序要結(jié)束了。例如一個(gè)網(wǎng)絡(luò)守護(hù)進(jìn)程無法bind到socket上,那么它唯一能做的就只有l(wèi)og到這里,然后退出運(yùn)行。

記住,在你的程序中,默認(rèn)的運(yùn)行級(jí)別是高度可變的。例如我通常用INFO運(yùn)行我的服務(wù)端代碼,但是我的桌面程序用的是DEBUG。這是因?yàn)槟愫茈y在 一臺(tái)你沒有接入權(quán)限的機(jī)器上進(jìn)行調(diào)試,但你在做用戶服務(wù)時(shí),比起教他們?cè)趺葱薷膌og level再把生成的log發(fā)給你,我的做法可以讓你輕松得多。當(dāng)然你可以有其他的做法:)

3. honor the log category

我在第一點(diǎn)中提到的大部分logging庫允許指定一個(gè)logging類別。它可以分類log信息,并基于logging框架的配置,在最后以某一形式進(jìn)行l(wèi)og或是不進(jìn)行。

通常,Java開發(fā)者在log語句處使用完整,合格的類名作為類別名。如果你的程序遵循單一職責(zé)原則(Single responsibility principle,原文有誤),這種模式還不錯(cuò)。

在Java的logging庫中,Log類別是按等級(jí)劃分的,例如在 com.daysofwonder.ranking.ELORankingComputation會(huì)匹配到頂級(jí)的 com.daysofwonder.ranking。這可以讓運(yùn)營工程師配置一個(gè)對(duì)此類別下指定的所有ranking子系統(tǒng)作用的logging。如果需 要的話,還可以同時(shí)生成子類別的logging配置。

拓展開來,我們講解一下特定情況下的調(diào)試。假設(shè)你在做一個(gè)應(yīng)答用戶請(qǐng)求的服務(wù)端軟件(如REST API)。它正在對(duì)my.service.api.<apitoken>進(jìn)行l(wèi)og(其中apitoken用于識(shí)別用戶)。那么你可以選擇對(duì) my.service.api類別進(jìn)行l(wèi)og,記錄所有的api,或是對(duì)某違規(guī)API用戶的my.service.api.<bad-user- api-token>進(jìn)行l(wèi)og。當(dāng)然這需要系統(tǒng)允許你在運(yùn)行中修改logging配置。

4. 你應(yīng)該寫有意義的log

這可能是最重要的建議了。沒有什么比你深刻理解程序內(nèi)部,卻寫出含糊的log更糟了。

在你寫日志信息之前,總要提醒自己,有突發(fā)事件的時(shí)候,你唯一擁有的只有來自log文件,你必須從中明白發(fā)生了什么。這可能就是被開除和升職之間的微妙的差距。

當(dāng)開發(fā)者寫log的時(shí)候,它(log語句)是直接寫在代碼環(huán)境中的,在各種條件中我們應(yīng)該寫入基于當(dāng)前環(huán)境的信息。不幸的是,在log文件中并沒有這些環(huán)境,這可能導(dǎo)致這些信息無法被理解。

解決這個(gè)情況(在寫warn和error level時(shí)尤為重要)的一個(gè)方法是,添加輔助信息到log信息中,如果做不到,那么改為把這個(gè)操作的作用寫下。

還有,不要讓一個(gè)log信息的內(nèi)容基于上一個(gè)。這是因?yàn)榍懊娴男畔⒖赡苡捎?與當(dāng)前信息)處于不同的類別或者level而沒被寫入。更壞的情況是,它因多線程或異步操作,在另一個(gè)地方(或是以另一方式)出現(xiàn)。

5. 日志信息應(yīng)該用英語

這個(gè)建議可能有點(diǎn)奇怪,尤其是對(duì)法國佬(French guy)來說。我還是認(rèn)為英語遠(yuǎn)比法語更簡煉,更適應(yīng)技術(shù)語言。如果一個(gè)信息里面包含超過50%的英語單詞,你有什么理由去用法語寫log呢

把英法之爭丟一邊,下面是這個(gè)建議背后的原因:

  • 英語意味著你的log是用ASCII編碼的。這非常重要,因?yàn)槟悴粫?huì)真正知道log信息會(huì)發(fā)生什么,或是它被歸檔前經(jīng)過何種軟件層和介質(zhì)。如果你 的信息里面使用了特殊字符集,乃至UTF-8,它可能并不會(huì)被正確地顯示(render),更糟的是,它可能在傳輸過程中被損壞,變得不可讀。不過這還有 個(gè)問題,log用戶輸入時(shí),可能有各種字符集或者編碼。
  • 如果你的程序被大多數(shù)人使用,而你又沒有足夠的資源做國際化,英語會(huì)成為你的不二之選。如果你有國際化,那么讓界面與終端用戶更親近(closer)(這通常不會(huì)是你的log)
  • 如果你國際化了你的log(例如所有的warning和error level信息),給他們一個(gè)特定的有意義的錯(cuò)誤碼。這樣,用戶做與語言無關(guān)的搜索,找到相關(guān)信息。這種良好的模式已經(jīng)在虛擬內(nèi)存(VMS)操作系統(tǒng)中應(yīng) 用了很久,而我必須承認(rèn)它非常有用。如果你曾經(jīng)設(shè)計(jì)過這種模式,你還可以試試這種模式: APP-S-CODE 或者 APP-S-SUB-CODE,它們分別代表:
    APP: 應(yīng)用程序的3字縮寫
    S: 嚴(yán)重程度的1字縮寫(例如D代表debug,I代表info)
    SUB: 這個(gè)code所從屬的應(yīng)用程序的子部分
    CODE: 一個(gè)數(shù)字代號(hào),指定這個(gè)問題中的錯(cuò)誤

#p#

6. 你應(yīng)該給log帶上上下文

沒有什么比這樣的log信息更糟的了

  1. Transaction failed 

或是

  1. User operation succeeds 

又或是API異常時(shí):

  1. java.lang.IndexOutOfBoundsException

沒有相應(yīng)的上下文,這些信息不過是噪音,它們不會(huì)對(duì)調(diào)試過程中有意義的數(shù)值或是空間起作用(add value and consume space)。

帶上上下文的信息要有價(jià)值得多,例如:

  1. Transaction 234632 failed: cc number checksum incorrect 

或是

  1. User 54543 successfully registered e-mail<a href="mailto:user@domain.com">user@domain.com</a> 

又或是

  1. IndexOutOfBoundsException: index 12 is greater than collection size 10 

在上面這一例子中的異常,如果你想把它傳播開, 確保在處理的時(shí)候帶上與當(dāng)前l(fā)evel相應(yīng)的上下文,讓調(diào)試更簡單,如下一個(gè)java的例子:

  1. public void storeUserRank(int userId,int rank,String game) { 
  2.      try { 
  3.           ...deal database ... 
  4.      } catch (DatabaseException de) { 
  5.           throw new RankingException("Can't store ranking for user "+userId+" in game "+ game + " because " + de.getMessage() ); 
  6.      } 

這樣,rank API的上層客戶端就可以有足夠的上下文信息log這個(gè)error。更好的做法是讓上下文成為exception的參數(shù),而不是信息,如果需要的話,上層可以對(duì)它進(jìn)行修正(use remediation)。

保留上下文的一個(gè)簡單方法是使用一些java logging庫的MDC實(shí)現(xiàn)。MDC是一個(gè)每線程關(guān)聯(lián)數(shù)組(per thread associative array)??梢孕薷膌ogger設(shè)置,讓每一行l(wèi)og總是輸出MDC內(nèi)容。如果你的程序使用每線程模式,這可以幫助解決保留上下文的問題。這個(gè) java的例子對(duì)給定的請(qǐng)求,使用MDC記錄每用戶的信息:

  1. class UserRequest { 
  2.      ... 
  3.      public void execute(int userid) { 
  4.           MDC.put("user",userid); 
  5.   
  6.           // ... all logged message now will display the user=<userid> for this thread context ... 
  7.           log.info("Successful execution of request"); 
  8.   
  9.           // user request processing is now finished,no need to log our current user anymore 
  10.           MDC.remote("user"); 
  11.      } 

提示,MDC系統(tǒng)在異步logging模式中的表現(xiàn)并不好,例如Akka的logging系統(tǒng)。因?yàn)镸DC是保存在一個(gè)每線程存儲(chǔ)區(qū)域的,而且在異 步系統(tǒng)中你無法保證在寫入log的線程是有MDC的那一個(gè)。在這種情況下,你需要手動(dòng)地使用每一個(gè)log語句來log這些上下文。

7. 你應(yīng)該用機(jī)器可解析的格式來打日志

Log信息對(duì)人很友善,但是對(duì)機(jī)器就慘了。有時(shí)人工地讀這些log文件并不足夠,你需要進(jìn)行一些自動(dòng)化過程(例如通過警報(bào)和審查)。或是你想集中存儲(chǔ)你的log,以進(jìn)行搜索。

如下,如果你把log的上下文嵌在string中會(huì)發(fā)生什么:

  1. log.info("User {} plays {} in game {}",userId,card,gameId); 

這會(huì)生成這樣的文本:

  1. 2013-01-1217:49:37,656[T1]INFOc.d.g.UserRequestUser1334563plays4ofspadesingame23425656 

現(xiàn)在,如果你想使它可解析,你需要下面這個(gè)(未測(cè)試過的)正則表達(dá)式:

  1. /User(\d+)plays(.+)ingame(\d+)$/ 

好了,這并不輕松而且容易出錯(cuò),把它接入到你代碼中已有的string參數(shù)中。

這個(gè)方法怎么樣,我相信Jordan Sissel在他的ruby-cabin庫中第一次介紹的: 在你的log里面加入機(jī)器可解析格式的上下文。我們上述的例子中這樣可以使用JSON:

  1. 2013-01-1217:49:37,656[T1]INFOc.d.g.UserRequestUserplays{'user':1334563,'card':'4ofspade','game':23425656} 

現(xiàn)在你的log分析器可以更容易地寫入,更直接地索引,而且你可以釋放logstash所有的威力。

8. 日志不宜太多或太少

這聽著貌似很愚蠢。log的數(shù)量是有一個(gè)合適的平衡的。

太多的log會(huì)使從中獲得有價(jià)值的東西變得困難。當(dāng)人工地瀏覽這種十分混亂的log,嘗試調(diào)試產(chǎn)品在早上3點(diǎn)的一個(gè)問題可不是一個(gè)好事。

太少的log,你可能無法調(diào)試問題: 調(diào)試就像在拼一個(gè)困難的拼圖,你需要得到足夠的拼塊。

不幸的是,這沒有魔法般的規(guī)則去知道應(yīng)該log些什么。所以需要嚴(yán)格地遵從第一第二點(diǎn),程序可以變得很靈活,輕松地增減log的長度(verbosity)。

解決這個(gè)問題的一個(gè)方法是,在開發(fā)過程中盡可能多地進(jìn)行l(wèi)og(不要被加入用于程序調(diào)試的log所迷惑)。當(dāng)應(yīng)用程序進(jìn)入生產(chǎn)過程時(shí),對(duì)生成的 log進(jìn)行一次分析,根據(jù)所發(fā)現(xiàn)的問題增減log語句。尤其是在調(diào)試時(shí),在你需要的部分,你可以有更多的上下文或logging,確保在下一個(gè)版本中加入 這些語句(可以的話,同時(shí)解決它來讓這個(gè)問題在記憶中保持新鮮)。當(dāng)然,這需要運(yùn)維人員和開發(fā)者之間大量的交流。

這是一個(gè)復(fù)雜的任務(wù),但是我推薦你重構(gòu)logging語句,如你重構(gòu)代碼一樣多。這樣可以在產(chǎn)品的log和它的log語句的修改中有一個(gè)緊密的反饋循環(huán)。如果你的組織有一個(gè)連續(xù)的交付進(jìn)程的話,它會(huì)十分有效,正如持續(xù)的重構(gòu)。

Logging語句是與代碼注釋同級(jí)的代碼元數(shù)據(jù)。保持logging語句與代碼相同步是很重要的。沒什么比調(diào)試時(shí)獲得與所運(yùn)行的代碼毫無關(guān)系的信息更糟了。

 #p#

9. 你應(yīng)該考慮閱讀者

為什么要對(duì)應(yīng)用程序做log

唯一的答案是,在某一天會(huì)有人去讀它(或是它的意義)。更重要的是,猜猜誰會(huì)讀它,這是很有趣的事。對(duì)于不同的”誰”,你將要寫下的log信息的內(nèi)容,上下文,類別和level會(huì)大不同。

這些”誰”包括:

  • 一個(gè)嘗試自己解決問題的終端用戶(想象一個(gè)客戶端或桌面程序)
  • 一個(gè)在調(diào)試產(chǎn)品問題的系統(tǒng)管理員或者運(yùn)維工程師
  • 一個(gè)在開發(fā)中debug,或者在解決產(chǎn)品問題的開發(fā)者

開發(fā)者了解程序內(nèi)部,所以給他的log信息可以比給終端用戶的復(fù)雜得多。為你的目標(biāo)閱讀者調(diào)整你的表達(dá)方式,乃至為此加入額外的類別(dedicate separate catagories)。

10. 你不應(yīng)該只為調(diào)試而log

正如log會(huì)有不同的閱讀者,它也有不同的使用理由。即便調(diào)試是最顯而易見的閱讀log的目的,你同樣可以有效地把log用在:

  • 審查: 有時(shí)商業(yè)上會(huì)有需求。這可以獲取與管理或者合法用戶的有意義的事件。通常會(huì)有一些語句描述這個(gè)系統(tǒng)中的用戶在做些什么(例如誰登錄了,誰在編輯……)
  • 建檔: log是打上了時(shí)間戳的(有時(shí)是微妙級(jí)的),可以成為一個(gè)為程序各部分建檔的好工具。例如記錄一個(gè)操作的開始和結(jié)束,你可以自動(dòng)化(通過解析log)或是在調(diào)試中,進(jìn)行性能度量,而不需要把這些度量加到程序中。
  • 統(tǒng)計(jì): 如果你每次對(duì)一個(gè)特定事件(例如特定的錯(cuò)誤或事件)進(jìn)行l(wèi)og,你可以對(duì)運(yùn)行中的程序(或用戶行為)進(jìn)行有趣的統(tǒng)計(jì)。這可以添加(hook)到一個(gè)警報(bào)系統(tǒng)中去連續(xù)地發(fā)現(xiàn)大量error。

總結(jié)

我希望這可以幫助你生成更多有用的log。如果我忘記了一些必須的(對(duì)你而言)建議,請(qǐng)諒解。對(duì)了,如果你看了這篇博客之后并不能更好地進(jìn)行l(wèi)og,我并不負(fù)責(zé)

如果這10個(gè)建議還不夠的話,盡管在評(píng)論中補(bǔ)充更多有用的建議。

原文鏈接:http://www.masterzen.fr/2013/01/13/the-10-commandments-of-logging/

譯文鏈接:http://blog.jobbole.com/52018/

責(zé)任編輯:陳四芳 來源: 伯樂在線
相關(guān)推薦

2022-07-27 11:46:35

Asciinema日志工具

2011-02-22 14:07:52

2015-08-11 11:01:22

設(shè)計(jì)原則開發(fā)者

2012-04-02 22:49:58

Android

2011-12-05 22:44:53

Android

2011-10-11 10:07:37

2015-03-10 09:23:21

前端開發(fā)Sublime插件Sublime

2014-10-09 09:29:25

AngularJS

2011-03-17 15:25:31

2015-04-21 12:54:21

2012-03-01 15:55:42

2014-07-21 10:00:37

框架HTML5框架模板

2013-10-22 09:54:42

開發(fā)者應(yīng)用

2015-03-17 14:31:53

Web開發(fā)web開發(fā)者云開發(fā)環(huán)境

2014-07-10 10:15:41

代碼代碼庫

2014-05-27 13:48:34

開發(fā)者付費(fèi)

2010-12-03 11:17:12

2012-04-01 15:03:39

大型項(xiàng)目開發(fā)者

2011-07-15 10:02:01

JavaScript

2011-03-30 08:49:34

WebjQuery
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)