python logging模塊-寫日誌、log回滾

python包/模塊,專欄總目錄

1.python自定義模塊

2.python模塊調用順序

3.python logging模塊

4.python定義跨模塊的全局變量

 

1、logging模塊簡介

logging模塊是Python內置的標準模塊,主要用於輸出運行日誌,可以設置輸出日誌的等級、日誌保存路徑、日誌文件回滾等;相比print,具備如下優點:

1、可以通過設置不同的日誌等級,在release版本中只輸出重要信息,而不必顯示大量的調試信息;

2、print將所有信息都輸出到標準輸出中,嚴重影響開發者從標準輸出中查看其它數據;logging則可以由開發者決定將信息輸出到什麼地方,以及怎麼輸出;

3、其它特點:basicConfig整個工程只在第一次配置時生效。

2、logging模塊使用

2.1、基本使用

配置logging基本的設置,然後在控制檯輸出日誌:

import logging
logging.basicConfig(level = logging.INFO,format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")

運行時,控制檯輸出:

logging.basicConfig函數各參數:

filename:指定日誌文件名;

filemode:和file函數意義相同,指定日誌文件的打開模式,'w'或者'a';

format:指定輸出的格式和內容,format可以輸出很多有用的信息,

參數:作用

%(levelno)s:打印日誌級別的數值;

%(levelname)s:打印日誌級別的名稱;

%(pathname)s:打印當前執行程序的路徑,其實就是sys.argv[0];

%(filename)s;:打印當前執行程序名;

%(funcName)s:打印日誌的當前函數;

%(lineno)d:打印日誌的當前行號;

%(asctime)s:打印日誌的時間;

%(thread)d:打印線程ID;

%(threadName)s:打印線程名稱;

%(process)d:打印進程ID;

%(message)s:打印日誌信息。

datefmt:指定時間格式,同time.strftime();

level:設置日誌級別,默認爲logging.WARNNING;

stream:指定將日誌的輸出流,可以指定輸出到sys.stderr,sys.stdout或者文件,默認輸出到sys.stderr,當stream和filename同時指定時,stream被忽略;

注:此方法控制檯測試時,basicConfig整個工程只在第一次配置時生效,整個輸出均爲控制檯。

一條日誌信息對應的是一個事件的發生,而一個事件通常需要包括以下幾個內容:

1、事件的嚴重程度--日誌級別(%(levelname)s:打印日誌級別的名稱);

2、事件發生時間(%(asctime)s:打印日誌的時間);

3、事件發生位置(%(process)d:打印進程ID,%(funcName)s:打印日誌的當前函數,%(lineno)d:打印日誌的當前行號);

4、事件內容(%(message)s:打印日誌信息)。

2.2、將日誌寫入到文件

2.2.1、將日誌寫入到文件

設置logging,創建一個FileHandler,並對輸出消息的格式進行設置,將其添加到logger,然後將日誌寫入到指定的文件中:

import logging
logger = logging.getLogger(__name__)
logger.setLevel(level = logging.INFO)
handler = logging.FileHandler("log.txt")
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)

logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")

log.txt中日誌數據爲:

注:此方法在不同文件時,需傳參,或者重新配置日誌輸出文件,以保證日誌均寫到同一文件;寫日誌默認爲追加模式。

2.2.2、將日誌同時輸出到屏幕和日誌文件

logger中添加StreamHandler,可以將日誌輸出到屏幕上:

import logging

logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)

file_handler = logging.FileHandler("log.txt")  # 文件句柄
file_handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)

console_handler = logging.StreamHandler()  # 流句柄
console_handler.setLevel(logging.INFO)
logger.addHandler(console_handler)

def main():
    logger.info("Start print log")
    logger.debug("Do something")
    logger.warning("Something maybe fail.")
    logger.info("Finish")
    
mian()   

結果如下:

可以發現,logging有一個日誌處理的主對象,其他處理方式都是通過addHandler添加進去,各個處理方式之間相互獨立。logging中包含的handler主要有如下幾種:

handler名稱:位置;作用

StreamHandler:logging.StreamHandler;日誌輸出到流,可以是sys.stderr,sys.stdout或者文件;

FileHandler:logging.FileHandler;日誌輸出到文件;

BaseRotatingHandler:logging.handlers.BaseRotatingHandler;基本的日誌回滾方式;

RotatingHandler:logging.handlers.RotatingHandler;日誌回滾方式,支持日誌文件最大數量和日誌文件回滾;

TimeRotatingHandler:logging.handlers.TimeRotatingHandler;日誌回滾方式,在一定時間區域內回滾日誌文件;

SocketHandler:logging.handlers.SocketHandler;遠程輸出日誌到TCP/IP sockets;

DatagramHandler:logging.handlers.DatagramHandler;遠程輸出日誌到UDP sockets;

SMTPHandler:logging.handlers.SMTPHandler;遠程輸出日誌到郵件地址;

SysLogHandler:logging.handlers.SysLogHandler;日誌輸出到syslog;

NTEventLogHandler:logging.handlers.NTEventLogHandler;遠程輸出日誌到Windows NT/2000/XP的事件日誌;

MemoryHandler:logging.handlers.MemoryHandler;日誌輸出到內存中的指定buffer;

HTTPHandler:logging.handlers.HTTPHandler;通過"GET"或者"POST"遠程輸出到HTTP服務器。

2.2.3、日誌滾動和過期刪除

1、使用RotatingFileHandler,可以實現日誌按大小回滾(設置,最多備份幾個日誌文件,每個日誌文件最大值)

import logging
from logging.handlers import RotatingFileHandler # 按文件大小回滾handler

def main():
    logger = logging.getLogger(__name__)
    logger.setLevel(level=logging.INFO)
    # 定義一個RotatingFileHandler,最多備份3個日誌文件,每個日誌文件最大1K
    rHandler = RotatingFileHandler("log.txt", maxBytes=1 * 1024, backupCount=3)
    rHandler.setLevel(logging.INFO)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    rHandler.setFormatter(formatter)

    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    console.setFormatter(formatter)

    logger.addHandler(rHandler)
    logger.addHandler(console)

    logger.info("Start print log")
    logger.debug("Do something")
    logger.warning("Something maybe fail.")
    logger.warning("Something maybe fail.")
    logger.info("Finish")
    
mian()        

可以在工程目錄中看到,備份的日誌文件:

2016/10/0919:36732log.txt
2016/10/0919:36967log.txt.1
2016/10/0919:36985log.txt.2
2016/10/0919:36976log.txt.3

2、使用TimedRotatingFileHandler,可實現日誌按時間回滾

import time
import logging
from logging.handlers import TimedRotatingFileHandler  # 設置日誌按時間回滾

def backroll():
    # 日誌打印格式
    log_fmt = '%(asctime)s\tFile \"%(filename)s\",line %(lineno)s\t%(levelname)s: %(message)s'
    formatter = logging.Formatter(log_fmt)

    # 創建TimedRotatingFileHandler對象
    log_file_handler = TimedRotatingFileHandler(filename="ds_update", when="M", interval=2, backupCount=2)
    log_file_handler.setFormatter(formatter)
    logging.basicConfig(level=logging.INFO)

    # 添加句柄
    log = logging.getLogger()
    log.addHandler(log_file_handler)

    # 循環打印日誌
    log_content = "test log"
    count = 0
    while count < 30:
        log.error(log_content)
        time.sleep(0.1)
        count = count + 1
    log.removeHandler(log_file_handler)

backroll()

其中:

filename:日誌文件名的prefix;

when:是一個字符串,用於描述滾動週期的基本單位,字符串的值及意義如下:

“S”: Seconds

“M”: Minutes

“H”: Hours

“D”: Days

“W”: Week day (0=Monday)

“midnight”: Roll over at midnight

interval: 滾動週期,單位有when指定,比如:when=’D’,interval=1,表示每天產生一個日誌文件;

backupCount: 表示日誌文件的保留個數。

2.3、設置消息的等級

可以設置不同的日誌等級,用於控制日誌的輸出,只有級別大於或等於日誌記錄器指定級別的日誌記錄纔會被輸出,小於該級別的日誌記錄將會被丟棄:

日誌等級:使用範圍

FATAL:致命錯誤;

CRITICAL:嚴重錯誤,導致應用程序不能繼續運行時記錄的信息,如內存耗盡、磁盤空間爲空,一般很少使用;

ERROR:發生錯誤時,由於一個更嚴重的問題導致某些功能不能正常運行時記錄的信息,如IO操作失敗或者連接問題;

WARNING:當某些不期望的事情發生時記錄的信息,但此時程序可以正常運行,如用戶登錄密碼錯誤;

INFO:處理請求或者狀態變化等日常事務,通常只記錄關鍵節點信息,用於確認一切都是按照我們預期的那樣進行工作;

DEBUG:調試過程中使用DEBUG等級,最詳細的日誌信息,如算法中每個循環的中間狀態。

上面列表中的日誌等級是從上到下依次降低的,即:FATAL < CRITICAL < ERROR < WARNING < INFO < DEBUG,而日誌的信息量是依次增加的,通過設置log等級level=logging.INFO,控制輸出log級別;

開發、部署階段:使用DEBUG或INFO級別的日誌獲取儘可能詳細的日誌信息來進行開發或部署調試。

上線、生產環境階段:使用WARNING或ERROR或CRITICAL級別的日誌來降低機器的I/O壓力和提高獲取錯誤日誌信息的效率。

2.4、捕獲traceback

Python中的traceback模塊被用於跟蹤異常返回信息,可以在logging中記錄下traceback,代碼:

import logging

logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
handler = logging.FileHandler("log.txt")
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)

console = logging.StreamHandler()
console.setLevel(logging.INFO)

logger.addHandler(handler)
logger.addHandler(console)

def main():
    logger.info("Start print log")
    logger.debug("Do something")
    logger.warning("Something maybe fail.")
    try:
        open("sklearn.txt", "rb")
    except Exception:
        logger.error("Faild to open sklearn.txt from logger.error", exc_info=True) #logger.exception(msg,_args)等價於logger.error(msg,exc_info = True,_args)
    logger.info("Finish")

main()

輸出log:

2.5、多模塊使用logging

主模塊mainModule.py:

import logging
import subModule

def main():
    logger = logging.getLogger("mainModule")  # 爲logger起個名字
    logger.setLevel(level=logging.INFO)
    handler = logging.FileHandler("log.txt")
    handler.setLevel(logging.INFO)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    handler.setFormatter(formatter)

    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    console.setFormatter(formatter)

    logger.addHandler(handler)
    logger.addHandler(console)

    logger.info("creating an instance of subModule.subModuleClass")
    a = subModule.SubModuleClass()
    logger.info("calling subModule.subModuleClass.doSomething")
    a.doSomething()
    logger.info("done with  subModule.subModuleClass.doSomething")
    logger.info("calling subModule.some_function")
    subModule.som_function()
    logger.info("done with subModule.some_function")

main()

子模塊subModule.py:

import logging
module_logger = logging.getLogger("mainModule.sub")  # mainModule logger子類

class SubModuleClass(object):
    def __init__(self):
        self.logger = logging.getLogger("mainModule.sub.module")
        self.logger.info("creating an instance in SubModuleClass")

    def doSomething(self):
        self.logger.info("do something in SubModule")
        a = []
        a.append(1)
        self.logger.debug("list a = " + str(a))
        self.logger.info("finish something in SubModuleClass")

def som_function():
    module_logger.info("call function some_function")

執行之後,在控制和日誌文件log.txt中輸出:

首先在主模塊定義了logger'mainModule',並對它進行了配置,就可以在解釋器進程裏面的其他地方通過getLogger('mainModule')得到的對象都是一樣的,不需要重新配置,可以直接使用。定義的該logger的子logger,都可以共享父logger的定義和配置,所謂的父子logger是通過命名來識別,任意以'mainModule'開頭的logger都是它的子logger,例如'mainModule.sub'。

實際開發一個application,首先可以通過logging配置文件編寫好這個application所對應的配置,可以生成一個根logger,如'PythonAPP',然後在主函數中通過fileConfig加載logging配置,接着在application的其他地方、不同的模塊中,可以使用根logger的子logger,如'PythonAPP.Core','PythonAPP.Web'來進行log,而不需要反覆的定義和配置各個模塊的logger。

3、通過JSON或者YAML文件配置logging模塊

儘管可以在Python代碼中配置logging,但是這樣並不夠靈活,最好的方法是使用一個配置文件來配置。在Python 2.7及以後的版本中,可以從字典中加載logging配置,也就意味着可以通過JSON或者YAML文件加載日誌的配置。

3.1 通過JSON文件配置

JSON配置文件:

{
    "version": 1,
    "disable_existing_loggers": false,
    "formatters": {
        "simple": {
            "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
        }
    },
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "level": "DEBUG",
            "formatter": "simple",
            "stream": "ext://sys.stdout"
        },
        "info_file_handler": {
            "class": "logging.handlers.RotatingFileHandler",
            "level": "INFO",
            "formatter": "simple",
            "filename": "info.log",
            "maxBytes": "10485760",
            "backupCount": 20,
            "encoding": "utf8"
        },
        "error_file_handler": {
            "class": "logging.handlers.RotatingFileHandler",
            "level": "ERROR",
            "formatter": "simple",
            "filename": "errors.log",
            "maxBytes": 10485760,
            "backupCount": 20,
            "encoding": "utf8"
        }
    },
    "loggers": {
        "my_module": {
            "level": "ERROR",
            "handlers": ["info_file_handler"],
            "propagate": "no"
        }
    },
    "root": {
        "level": "INFO",
        "handlers": ["console", "info_file_handler", "error_file_handler"]
    }
}

通過JSON加載配置文件,然後通過logging.dictConfig配置logging:

import json
import logging.config
import os

def setup_logging(default_path="logging.json", default_level=logging.INFO, env_key="LOG_CFG"):
    path = default_path
    value = os.getenv(env_key, None)
    if value:
        path = value
    if os.path.exists(path):
        with open(path, "r") as f:
            config = json.load(f)
            logging.config.dictConfig(config)
    else:
        logging.basicConfig(level=default_level)

def func():
    logging.info("start func")
    logging.info("exec func")
    logging.info("end func")

if __name__ == "__main__":
    setup_logging(default_path="logging.json")
    func()

3.2、通過YAML文件配置

通過YAML文件進行配置.

 

4、擴展閱讀

1、logging日誌中formatter

# 定義handler的輸出格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)

https://www.cnblogs.com/goodhacker/p/3355660.html

2、logging子模塊

https://blog.csdn.net/zyz511919766/article/details/25136485

5、配置實例

1、常規配置:文本,Print

import os
import logging
import datetime

__all__ = ["log_init"]

# log 配置參數
"""
level配置:
    線上環境配爲ERROR或者WARNING;收集運行信息,則配爲INFO。
    線下測試配爲INFO、DEBUG。
format_str配置:
    分隔符: -
    時間:%(asctime)s
    級別:%(levelname)s
    發生位置:%(name)s - %(funcName)s - %(lineno)d
    事件內容:%(message)s
"""

config = {
    "console": False,  # 是否在控制檯顯示
    "level": logging.ERROR,  # INFO < WARNING < ERROR,
    "format_str": "%(asctime)s - %(levelname)s - %(name)s - %(funcName)s - %(lineno)d - %(message)s",
}


def get_file_name():
    log_dir = "./log"
    if not os.path.exists(log_dir):
        os.makedirs(log_dir)

    pid = os.getpid()
    cur_date = datetime.date.today() - datetime.timedelta(days=0)
    filename = '%s/log_%s_%s.log' % (log_dir, cur_date, str(pid))
    return filename


def log_init():
    logger = logging.getLogger()
    logger.setLevel(level=config["level"])
    formatter = logging.Formatter(config["format_str"])

    filename = get_file_name()
    file_handler = logging.FileHandler(filename)
    file_handler.setLevel(level=config["level"])
    file_handler.setFormatter(formatter)
    logger.addHandler(file_handler)

    if config["console"]:
        console_handler = logging.StreamHandler()
        console_handler.setLevel(level=config["level"])
        console_handler.setFormatter(formatter)
        logger.addHandler(console_handler)

    logger.info("logging config ok.")

2、常規配置,僅在控制檯輸出

import logging

def log_init(level=logging.DEBUG):
    logger = logging.getLogger()
    logger.setLevel(level=level)
    formatter = logging.Formatter("%(levelname)s - %(lineno)d - %(message)s")

    console_handler = logging.StreamHandler()
    console_handler.setLevel(level=level)
    console_handler.setFormatter(formatter)
    logger.addHandler(console_handler)

    logger.info("logging config ok.")

log_init(level=logging.DEBUG)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章