記錄日志有哪些好的技巧仰猖?

一、簡介

在軟件開發(fā)中奈籽,我們出于各種目的饥侵,需要將程序運行中的一些狀態(tài)記錄在日志中。
日志記錄衣屏,并不是越多越好躏升,也不是記錄的越頻繁越好,而是需要我們精心設(shè)計記錄日志的時機(jī)狼忱、內(nèi)容膨疏、格式(以方便后續(xù)解析、查詢?nèi)罩荆┑鹊取?br> 本文簡單介紹了記錄日志的一些基本原則和注意事項钻弄,更具體的記日志的技巧佃却、最佳實踐、日志框架等請參考子文檔 Go如何記錄日志 窘俺。

二饲帅、記錄日志的目的(why)

開發(fā)調(diào)試

目的是開發(fā)期調(diào)試程序使用,這種日志量比較大瘤泪,且沒有什么實質(zhì)性的意義灶泵,只應(yīng)該出現(xiàn)在開發(fā)期,而不應(yīng)該在項目上線之后輸出均芽。

記錄用戶行為

這種類型的日志丘逸,記錄用戶的操作行為单鹿,用于大數(shù)據(jù)分析掀宋,比如監(jiān)控、風(fēng)控仲锄、推薦等等劲妙。這種日志,一般是給其他團(tuán)隊分析使用儒喊,而且可能是多個團(tuán)隊镣奋,因此一般會有一定的格式要求,開發(fā)者應(yīng)該按照這個格式來記錄怀愧,便于其他團(tuán)隊的使用侨颈。當(dāng)然余赢,要記錄哪些行為、操作哈垢,一般也是約定好的妻柒,因此,開發(fā)者主要是執(zhí)行的角色耘分。

程序運行狀況

記錄程序的運行狀況举塔,特別是非預(yù)期的行為、異常情況求泰,這種日志央渣,主要是給開發(fā)、維護(hù)人員使用渴频。什么時候記錄芽丹,記錄什么內(nèi)容,完全取決于開發(fā)人員卜朗,開發(fā)者具有高度自主性志衍。本文討論的主要也是指這種類型的日志,因為作為一個服務(wù)端開發(fā)聊替、運維人員楼肪,程序運行日志往往是解決線上問題的救命稻草。

系統(tǒng)惹悄、機(jī)器狀況

比如網(wǎng)絡(luò)請求春叫、系統(tǒng)CPU、內(nèi)存泣港、IO使用情況等等暂殖,這種日志主要是給運維人員使用,生成各種更直觀的展現(xiàn)形式当纱,在系統(tǒng)出問題的時候報警呛每。

三、日志的要素(what)

每條日志都可以被當(dāng)作一個事件(event)坡氯,紀(jì)錄了該事件發(fā)生時各個信息:

時間

日志的時間可以包含多種含義晨横,不同含義的時間傳遞不同的信息:

  1. 指事件發(fā)生的時間,而不是日志被打印的時間箫柳。該時間附近范圍內(nèi)手形,結(jié)合該事件及服務(wù)器的網(wǎng)絡(luò)、CPU悯恍、IO等狀況库糠,可以了解他們之間的相關(guān)性,有助于分析事件發(fā)生的原因涮毫。
  2. 持續(xù)時間瞬欧。例如網(wǎng)絡(luò)請求的耗時贷屎、處理請求的各個階段的耗時等。
  3. 事件發(fā)生的順序艘虎。通過時間戳的順序豫尽,了解到一系列事件的發(fā)生順序。對多進(jìn)程顷帖、多線程美旧、分布式系統(tǒng)有幫助。

位置

事件發(fā)生在哪個模塊贬墩、哪個文件榴嗅、哪個函數(shù)、哪一行代碼里陶舞。

級別

日志的重要程度嗽测。用于:

  1. 不同的環(huán)境(測試、生產(chǎn))下肿孵,打印不同級別的日志
  2. 不同級別的日志產(chǎn)生不同級別的監(jiān)控報警

內(nèi)容

簡明扼要的描述發(fā)生了什么樣的事情唠粥。目的是通過日志本身,而不是重新閱讀相關(guān)代碼來搞清楚發(fā)生了什么事情停做。
例如:logger.warn('user_login failed due to unvalid_username')

唯一標(biāo)識

不管是面向用戶的服務(wù)晤愧、面向機(jī)器集群的服務(wù),都需要一個唯一標(biāo)識作為日志的主體蛉腌,以方便查找該事件主體的其他信息官份。(很多元數(shù)據(jù)是不會記在日志里的,只會記一個唯一標(biāo)識)
例如:logger.warn('user_login failed due to password, username %s', username)

事件上下文

除了時間烙丛、位置舅巷、級別、內(nèi)容河咽,其他的一些有用的信息钠右。
例如:

  1. logger.warn('user_login failed due to wrong password, username %s', username)
  2. logger.warn('user_login failed due to invalid password, username %s', username)
  3. logger.warn('user_login failed due to empty password, username %s', username)
    為了獲取更豐富的上下文,有些數(shù)據(jù)需要從 Nginx 那里獲取并傳給本服務(wù)忘蟹,然后打印飒房。參考:和日志相關(guān)的Nginx設(shè)置

格式化

將上述的各個信息按照固定的順序打印出來,不僅方便查找(例如使用grep寒瓦,sed情屹,awk等),也方便收集日志的程序(ES)解析日志杂腰。

其他

根據(jù)各自的業(yè)務(wù)特點,還可以在日志中記錄(包括椅文、不限于):

  1. 錯誤次數(shù)
  2. 當(dāng)前正在處理的請求數(shù)
  3. 處理的進(jìn)度(33%喂很,50%惜颇,78%。少辣。凌摄。)
  4. IP
  5. 問題出現(xiàn)時的請求鏈接

四、記錄日志的一些原則和技巧

使用框架或模塊

Java : Log4j漓帅、Log4j2锨亏、Commons LoggingSlf4j忙干、Logback
Python 內(nèi)置的 logging 模塊
beego 框架里的 github.com/astaxie/beego/logs 模塊
其他的一些Go的框架:

  1. Logrus: github.com/Sirupsen/logrus (Docker use this)
  2. github.com/op/go-logging
  3. github.com/golang/glog (from Google, implementation of their C++ glog library in Go)
  4. github.com/cihub/seelog

不能出錯

記錄日志的目的是為了方便發(fā)現(xiàn)問題器予,解決問題,那么就需要保證記錄日志本身不能出錯捐迫。
尤其是對于 Error乾翔、Fatal級別的日志,出現(xiàn)的概率低施戴,所以要做好單元測試反浓,以保證記錄日志本身是沒問題的,是不會影響正常業(yè)務(wù)的赞哗。

避免敏感信息

  1. 避免記錄用戶密碼雷则。
  2. 避免記錄用戶個人信息,如身份證號肪笋、手機(jī)號等巧婶。應(yīng)該只在日志里記錄該用戶的唯一標(biāo)識,然后根據(jù)該唯一標(biāo)識去其他的系統(tǒng)(例如數(shù)據(jù)庫)查看詳細(xì)信息涂乌。

記錄“不可能發(fā)生”的事件

雖然正常邏輯下艺栈,某些情況是永遠(yuǎn)都不可能發(fā)生的,但是還是需要給這些不可能發(fā)生的情況打印一條日志湾盒。
例如 條件語句里的 else湿右,switch 里的 default,都需要進(jìn)行防御式的編程罚勾,同時記錄日志毅人。

Lazy logging

日志本身是一個字符串,可以通過多種方式拼接而成尖殃。如果根據(jù)log level丈莺,該條日志不應(yīng)被打印,那么就應(yīng)該避免拼接這個操作送丰,也應(yīng)該避免字符串拼接里的函數(shù)的調(diào)用缔俄。
例如:

#coding=utf-8
import logging

logger = logging.getLogger('LazyLogging')
logger.setLevel(logging.DEBUG)
hander = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
hander.setFormatter(formatter) 
logger.addHandler(hander)

def getUserCount():
    logger.info('getUserCount is called')
    return 1

logger.debug("There are " + str(getUserCount()) + " users logged in now.")

  1. log level 設(shè)置為 DEBUG(第5行),那么會先打印第12行,再打印第15行
  2. log level 設(shè)置為 INFO俐载,依然會打印第12行蟹略,說明:
    1. 問題1: 即便沒有打印第15行,依然生成了一個字符串
    2. 問題2: 而且調(diào)用了 getUserCount 這個函數(shù)
  3. 解決上述的問題1遏佣,可以把第15行修改為:
logger.debug("There are %s users logged in now.", getUserCount())

  1. 為了解決上述的問題2挖炬,一個辦法是:
class lazyEval:
    def __init__(self, f, *args):
        self.func = f
        self.args = args

    def __str__(self):
        return str(self.func(*self.args))

logger.debug("There are %s users logged in now.", lazyEval(getUserCount))

Java里的方式:

logger.debug(``"There are {} users logged in now."``, () -> getUserCount());

目前Golang里也存在這樣的問題,但是還沒找到怎么做到 lazyLogging状婶。
但是意敛,可以看出上面的方式也不優(yōu)雅,所以應(yīng)當(dāng)避免在打印日志時調(diào)用函數(shù)膛虫。

異步打印日志

互聯(lián)網(wǎng)應(yīng)用程序中草姻,高并發(fā)下的寫日志會帶來大量的IO操作,從而影響正常服務(wù)的性能走敌。這時候的記日志就需要專門的服務(wù)去做碴倾,例如把日志打到消息隊列里,然后再寫到磁盤上掉丽。

設(shè)置緩存

默認(rèn)的日志是隨時 flush 到console跌榔、文件里的,通過設(shè)置緩存進(jìn)行批量操作捶障,可以一定程度上優(yōu)化服務(wù)性能僧须。

對日志歸檔、分類

打印到一個文件里项炼,會使得該文件越來越大担平,不方便查找。
按日志屬性分類:access log锭部,error log暂论,
按日期歸檔:lathspell-api.2018-01-17.log,lathspell-api.2018-01-18.log
按大小切分:lathspell-api.2018-01-17.log.1拌禾,lathspell-api.2018-01-17.log.2取胎,lathspell-api.2018-01-17.log.3 (避免文件太大)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市湃窍,隨后出現(xiàn)的幾起案子闻蛀,更是在濱河造成了極大的恐慌,老刑警劉巖您市,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件觉痛,死亡現(xiàn)場離奇詭異,居然都是意外死亡茵休,警方通過查閱死者的電腦和手機(jī)薪棒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門手蝎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人盗尸,你說我怎么就攤上這事柑船∶背牛” “怎么了泼各?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長亏拉。 經(jīng)常有香客問我扣蜻,道長,這世上最難降的妖魔是什么及塘? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任莽使,我火速辦了婚禮,結(jié)果婚禮上笙僚,老公的妹妹穿的比我還像新娘芳肌。我一直安慰自己,他們只是感情好肋层,可當(dāng)我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布亿笤。 她就那樣靜靜地躺著,像睡著了一般栋猖。 火紅的嫁衣襯著肌膚如雪净薛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天蒲拉,我揣著相機(jī)與錄音肃拜,去河邊找鬼。 笑死雌团,一個胖子當(dāng)著我的面吹牛燃领,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播锦援,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼猛蔽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了雨涛?” 一聲冷哼從身側(cè)響起枢舶,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎替久,沒想到半個月后凉泄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡蚯根,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年后众,在試婚紗的時候發(fā)現(xiàn)自己被綠了胀糜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蒂誉,死狀恐怖教藻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情右锨,我是刑警寧澤括堤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站绍移,受9級特大地震影響悄窃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蹂窖,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一轧抗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瞬测,春花似錦横媚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至狮斗,卻和暖如春绽乔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背碳褒。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工折砸, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人沙峻。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓睦授,卻偏偏與公主長得像,于是被迫代替她去往敵國和親摔寨。 傳聞我的和親對象是個殘疾皇子去枷,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,914評論 2 355

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

  • 在應(yīng)用程序中添加日志記錄總的來說基于三個目的:監(jiān)視代碼中變量的變化情況,周期性的記錄到文件中供其他應(yīng)用進(jìn)行統(tǒng)計分析...
    時待吾閱讀 5,048評論 1 13
  • 一是复、簡介 二删顶、記錄日志的目的(why)開發(fā)調(diào)試記錄用戶行為程序運行狀況系統(tǒng)、機(jī)器狀況 三淑廊、日志的要素(what)時...
    四火流年閱讀 939評論 0 3
  • 在應(yīng)用程序中添加日志記錄總的來說基于三個目的:監(jiān)視代碼中變量的變化情況逗余,周期性的記錄到文件中供其他應(yīng)用進(jìn)行統(tǒng)計分析...
    時待吾閱讀 4,985評論 0 6
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)季惩,斷路器录粱,智...
    卡卡羅2017閱讀 134,657評論 18 139
  • From:Python之日志處理(logging模塊) - 云游道士 - 博客園 https://www.cnbl...
    vigny的先生閱讀 2,685評論 3 5