摘要:使用記錄系統(tǒng)日志在構(gòu)建一個系統(tǒng)時,我們常常需要記錄當(dāng)前發(fā)生的事情,以及記錄特定消息出現(xiàn)的頻率,根據(jù)出現(xiàn)頻率的高低來決定消息的排列信息,幫助我們找到重要的信息。常見記錄日志的方法有兩種將日志記錄在文件中。
使用Redis記錄系統(tǒng)日志
在構(gòu)建一個系統(tǒng)時,我們常常需要記錄當(dāng)前發(fā)生的事情,以及記錄特定消息出現(xiàn)的頻率,根據(jù)出現(xiàn)頻率的高低來決定消息的排列信息,幫助我們找到重要的信息。
常見記錄日志的方法有兩種:
將日志記錄在文件中。隨時時間流逝將日志行不斷添加到文件里面,并在一段時間后創(chuàng)建新的日志文件。這種方式為每個不同的服務(wù)創(chuàng)建不同的日志,由于服務(wù)輪換日志的機(jī)制不同,也缺少一種能夠方便地聚合所有日志并對其進(jìn)行處理的常見方法。
syslog服務(wù)。這種服務(wù)幾乎運(yùn)行在Linux服務(wù)器和Unix服務(wù)器的514號TCP端口和UDP端口上。syslog接受其他程序發(fā)來的日志消息,并將這個消息路由至存儲在硬盤上的各個日志文件,并且負(fù)責(zé)舊日志的輪換和刪除工作。甚至還可以將日志消息轉(zhuǎn)發(fā)給其他服務(wù)來做進(jìn)一步的處理。
syslog的轉(zhuǎn)發(fā)功能可以將不同的日志分別存儲在同一臺服務(wù)器的多個文件里面,對于長時間地記錄日志非常有幫助。我們可以使用redis來存儲與時間緊密相關(guān)的日志,從而在功能上替代那些需要在短期內(nèi)被存儲的syslog消息。
1. 最新日志我們需要使用 “列表” 來存儲最新日志文件,使用LPUSH命令將日志消息推入到列表中。如果我們之后想要查看已有日志消息的話,可以使用LRANGE命令來拉取列表中的消息。
我們還要命名不同的日志消息隊列,根據(jù)問題的嚴(yán)重性對日志進(jìn)行分級。
import time import logging import unittest import redis from datetime import datetime # 設(shè)置一個字典,將大部分日志的安全級別映射為字符串 SEVERITY = { logging.DEBUG: "debug", logging.INFO: "info", logging.WARNING: "warning", logging.ERROR: "error", logging.CRITICAL: "critical", } SEVERITY.update((name, name) for name in SEVERITY.values()) """ 存儲最新日志文件,命名不同的日志消息隊列,根據(jù)問題的嚴(yán)重性對日志進(jìn)行分級 @param {object} @param {string} name 消息隊列名稱 @param {string} message 消息 @param {string} severity安全級別 @param {object} pip pipline """ def logRecent(conn, name, message, severity=logging.INFO, pip=None): # 將日志的安全級別轉(zhuǎn)換為簡單的字符串 severity = str(SEVERITY.get(severity, severity)).lower() # 創(chuàng)建要保存的redis列表key destination = "recent:%s:%s"%(name, severity) # 將當(dāng)前時間加到消息里面,用于記錄消息的發(fā)送時間 message = time.asctime() + " " + message # 使用流水線來將通信往返次數(shù)降低為一次 pipe = pip or conn.pipeline() # 將消息添加到列表的最前面 pipe.lpush(destination, message) # 修剪日志列表,讓它只包含最新的100條消息 pipe.ltrim(destination, 0, 99) pipe.execute()2. 常見日志
我們需要記錄較高頻率出現(xiàn)的日志,使用“有序集合”,將消息作為成員,消息出現(xiàn)的頻率為成員的分值。
為了確保我們看到的常見消息都是最新的,需要以每小時一次的頻率對消息進(jìn)行輪換,并在輪換日志的時候保留上一個小時記錄的常見消息,從而防止沒有任何消息存儲的情況出現(xiàn)。
""" 記錄較高頻率出現(xiàn)的日志,每小時一次的頻率對消息進(jìn)行輪換,并在輪換日志的時候保留上一個小時記錄的常見消息 @param {object} @param {string} name 消息隊列名稱 @param {string} message 消息 @param {string} severity安全級別 @param {int} timeout 執(zhí)行超時時間 """ def logCommon(conn, name, message, severity=logging.INFO, timeout=5): # 設(shè)置日志安全級別 severity = str(SEVERITY.get(severity, severity)).lower() # 負(fù)責(zé)存儲近期的常見日志消息的鍵 destination = "common:%s:%s"%(name, severity) # 每小時需要輪換一次日志,需要記錄當(dāng)前的小時數(shù) start_key = destination + ":start" pipe = conn.pipeline() end = time.time() + timeout while time.time() < end: try: # 對記錄當(dāng)前小時數(shù)的鍵進(jìn)行監(jiān)聽,確保輪換操作可以正常進(jìn)行 pipe.watch(start_key) # 當(dāng)前時間 now = datetime.utcnow().timetuple() # 取得當(dāng)前所處的小時數(shù) hour_start = datetime(*now[:4]).isoformat() existing = pipe.get(start_key) # 開始事務(wù) pipe.multi() # 如果這個常見日志消息記錄的是上個小時的日志 if existing and existing < hour_start: # 將這些舊的常見日志歸檔 pipe.rename(destination, destination + ":last") pipe.rename(start_key, destination + ":pstart") # 更新當(dāng)前所處的小時數(shù) pipe.set(start_key, hour_start) elif not existing: pipe.set(start_key, hour_start) # 記錄日志出現(xiàn)次數(shù) pipe.zincrby(destination, message) # 將日志記錄到日志列表中,調(diào)用excute logRecent(pipe, name, message, severity, pipe) return except redis.exceptions.WatchError: continue
測試
測試代碼如下:
class TestLog(unittest.TestCase): def setUp(self): import redis self.conn = redis.Redis(db=15) self.conn.flushdb def tearDown(self): self.conn.flushdb() del self.conn print print def testLogRecent(self): import pprint conn = self.conn print "Let"s write a few logs to the recent log" for msg in xrange(5): logRecent(conn, "test", "this is message %s"%msg) recent = conn.lrange("recent:test:info", 0, -1) print "The current recent message log has this many message:", len(recent) print "Those message include:" pprint.pprint(recent[:10]) self.assertTrue(len(recent) >= 5) def testLogCommon(self): import pprint conn = self.conn print "Let"s writ a few logs to the common log" for count in xrange(1, 6): for i in xrange(count): logCommon(conn, "test", "message-%s"%count) common = conn.zrevrange("common:test:info", 0, -1, withscores=True) print "The current common message log has this many message:", len(common) print "Those common message include:" pprint.pprint(common) self.assertTrue(len(common) >= 5) if __name__ == "__main__": unittest.main()
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/40910.html
摘要:包括在內(nèi)的很多軟件都使用這種方法來記錄日志。在這一節(jié)中,我們將介紹如何使用來存儲于時間緊密相關(guān)的日志,從而在功能上替代那些需要在短期內(nèi)被存儲的消息。 上一篇文章:Python--Redis實戰(zhàn):第四章:數(shù)據(jù)安全與性能保障:第8節(jié):關(guān)于性能方面的注意事項下一篇文章:Python--Redis實戰(zhàn):第五章:使用Redis構(gòu)建支持程序:第2節(jié):計數(shù)器和統(tǒng)計數(shù)據(jù) 在構(gòu)建應(yīng)用程序和服務(wù)的過程中...
摘要:持久化到中反向代理的負(fù)載均衡基于的集群搭建如何實現(xiàn)從中訂閱消息轉(zhuǎn)發(fā)到客戶端的擴(kuò)展是阻塞式,使用訂閱發(fā)布模式時,會導(dǎo)致整個進(jìn)程進(jìn)入阻塞。緩存是用于解決高并發(fā)場景下系統(tǒng)的性能及穩(wěn)定性問題的銀彈。 showImg(https://segmentfault.com/img/bVYE6k?w=900&h=385); Redis 是由意大利程序員 Salvatore Sanfilippo(昵稱:a...
閱讀 2002·2019-08-30 15:54
閱讀 3637·2019-08-29 13:07
閱讀 3158·2019-08-29 12:39
閱讀 1845·2019-08-26 12:13
閱讀 1578·2019-08-23 18:31
閱讀 2204·2019-08-23 18:05
閱讀 1881·2019-08-23 18:00
閱讀 1071·2019-08-23 17:15