Python3 日志入門


python的logging模塊提供了記錄程序運行情況的日志功能,類似於Apache的log4j,很好很強大,這里我們就來看一下Python中內置的日志模塊logging用法詳解

logging模塊簡介

Python的logging模塊提供了通用的日志系統,可以方便第三方模塊或者是應用使用。這個模塊提供不同的日志級別,並可以采用不同的方式記錄日志,比如文件,HTTP GET/POST,SMTP,Socket等,甚至可以自己實現具體的日志記錄方式。
logging模塊與log4j的機制是一樣的,只是具體的實現細節不同。模塊提供logger,handler,filter,formatter。

  • logger:提供日志接口,供應用代碼使用。logger最長用的操作有兩類:配置和發送日志消息。可以通過logging.getLogger(name)獲取logger對象,如果不指定name則返回root對象,多次使用相同的name調用getLogger方法返回同一個logger對象。
  • handler:將日志記錄(log record)發送到合適的目的地(destination),比如文件,socket等。一個logger對象可以通過addHandler方法添加0到多個handler,每個handler又可以定義不同日志級別,以實現日志分級過濾顯示。
  • filter:提供一種優雅的方式決定一個日志記錄是否發送到handler。
  • formatter:指定日志記錄輸出的具體格式。formatter的構造方法需要兩個參數:消息的格式字符串和日期字符串,這兩個參數都是可選的。

與log4j類似,logger,handler和日志消息的調用可以有具體的日志級別(Level),只有在日志消息的級別大於logger和handler的級別。

logging用法解析

1. 初始化 logger = logging.getLogger("endlesscode"),getLogger()方法后面最好加上所要日志記錄的模塊名字,后面的日志格式中的%(name)s 對應的是這里的模塊名字
2. 設置級別 logger.setLevel(logging.DEBUG),Logging中有NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL這幾種級別,日志會記錄設置級別以上的日志
3. Handler,常用的是StreamHandler和FileHandler,windows下你可以簡單理解為一個是console和文件日志,一個打印在CMD窗口上,一個記錄在一個文件上
4. formatter,定義了最終log信息的順序,結構和內容,我喜歡用這樣的格式 '[%(asctime)s] [%(levelname)s] %(message)s', '%Y-%m-%d %H:%M:%S',
  %(name)s Logger的名字
  %(levelname)s 文本形式的日志級別
  %(message)s 用戶輸出的消息
  %(asctime)s 字符串形式的當前時間。默認格式是 “2003-07-08 16:49:45,896”。逗號后面的是毫秒
  %(levelno)s 數字形式的日志級別
  %(pathname)s 調用日志輸出函數的模塊的完整路徑名,可能沒有
  %(filename)s 調用日志輸出函數的模塊的文件名
  %(module)s  調用日志輸出函數的模塊名
  %(funcName)s 調用日志輸出函數的函數名
  %(lineno)d 調用日志輸出函數的語句所在的代碼行
  %(created)f 當前時間,用UNIX標准的表示時間的浮 點數表示
  %(relativeCreated)d 輸出日志信息時的,自Logger創建以 來的毫秒數
  %(thread)d 線程ID。可能沒有
  %(threadName)s 線程名。可能沒有
  %(process)d 進程ID。可能沒有
5. 記錄 使用object.debug(message)來記錄日志

6. logging是線程安全的

簡單示例如下:

import logging
logger = logging.getLogger("simple_example")
logger.setLevel(logging.DEBUG)
# 建立一個filehandler來把日志記錄在文件里,級別為debug以上
fh = logging.FileHandler("spam.log")
fh.setLevel(logging.DEBUG)
# 建立一個streamhandler來把日志打在CMD窗口上,級別為error以上
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)
# 設置日志格式
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
ch.setFormatter(formatter)
fh.setFormatter(formatter)
#將相應的handler添加在logger對象中
logger.addHandler(ch)
logger.addHandler(fh)
# 開始打日志
logger.debug("debug message")
logger.info("info message")
logger.warn("warn message")
logger.error("error message")
logger.critical("critical message")

當你的項目較大時,你就需要一個比較方便規范的使用的方式,建議將你的kog系統設計為class方式,這樣就可以方便使用

#! /usr/bin/env python
#coding=gbk
import logging,os
 
class Logger:
 def __init__(self, path,clevel = logging.DEBUG,Flevel = logging.DEBUG):
  self.logger = logging.getLogger(path)
  self.logger.setLevel(logging.DEBUG)
  fmt = logging.Formatter('[%(asctime)s] [%(levelname)s] %(message)s', '%Y-%m-%d %H:%M:%S')
  #設置CMD日志
  sh = logging.StreamHandler()
  sh.setFormatter(fmt)
  sh.setLevel(clevel)
  #設置文件日志
  fh = logging.FileHandler(path)
  fh.setFormatter(fmt)
  fh.setLevel(Flevel)
  self.logger.addHandler(sh)
  self.logger.addHandler(fh)
 
 def debug(self,message):
  self.logger.debug(message)
 
 def info(self,message):
  self.logger.info(message)
 
 def war(self,message):
  self.logger.warn(message)
 
 def error(self,message):
  self.logger.error(message)
 
 def cri(self,message):
  self.logger.critical(message)
 
if __name__ =='__main__':
 logyyx = Logger('yyx.log',logging.ERROR,logging.DEBUG)
 logyyx.debug('一個debug信息')
 logyyx.info('一個info信息')
 logyyx.war('一個warning信息')
 logyyx.error('一個error信息')
 logyyx.cri('一個致命critical信息')

多模塊使用logging
logging模塊保證在同一個python解釋器內,多次調用logging.getLogger('log_name')都會返回同一個logger實例,即使是在多個模塊的情況下。所以典型的多模塊場景下使用logging的方式是在main模塊中配置logging,這個配置會作用於多個的子模塊,然后在其他模塊中直接通過getLogger獲取Logger對象即可。
配置文件:

[loggers]
keys=root,main
  
[handlers]
keys=consoleHandler,fileHandler
  
[formatters]
keys=fmt
  
[logger_root]
level=DEBUG
handlers=consoleHandler
  
[logger_main]
level=DEBUG
qualname=main
handlers=fileHandler
  
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=fmt
args=(sys.stdout,)
  
[handler_fileHandler]
class=logging.handlers.RotatingFileHandler
level=DEBUG
formatter=fmt
args=('tst.log','a',20000,5,)
  
[formatter_fmt]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s 
datefmt=

主模塊main.py:

import logging
import logging.config
  
logging.config.fileConfig('logging.conf')
root_logger = logging.getLogger('root')
root_logger.debug('test root logger...')
  
logger = logging.getLogger('main')
logger.info('test main logger')
logger.info('start import module \'mod\'...') 
import mod
  
logger.debug('let\'s test mod.testLogger()')
mod.testLogger()
  
root_logger.info('finish test...')

子模塊mod.py:

import logging
import submod
  
logger = logging.getLogger('main.mod')
logger.info('logger of mod say something...') 
  
def testLogger():
  logger.debug('this is mod.testLogger...') 
  submod.tst()

子子模塊submod.py:

import logging
  
logger = logging.getLogger('main.mod.submod')
logger.info('logger of submod say something...') 
  
def tst():
  logger.info('this is submod.tst()...')

然后運行python main.py,控制台輸出:

2012-03-09 18:22:22,793 - root - DEBUG - test root logger...
2012-03-09 18:22:22,793 - main - INFO - test main logger
2012-03-09 18:22:22,809 - main - INFO - start import module 'mod'...
2012-03-09 18:22:22,809 - main.mod.submod - INFO - logger of submod say something... 
2012-03-09 18:22:22,809 - main.mod - INFO - logger say something...
2012-03-09 18:22:22,809 - main - DEBUG - let's test mod.testLogger()
2012-03-09 18:22:22,825 - main.mod - DEBUG - this is mod.testLogger...
2012-03-09 18:22:22,825 - main.mod.submod - INFO - this is submod.tst()...
2012-03-09 18:22:22,841 - root - INFO - finish test...

可以看出,和預想的一樣,然后在看一下tst.log,logger配置中的輸出的目的地:

2012-03-09 18:22:22,793 - main - INFO - test main logger
2012-03-09 18:22:22,809 - main - INFO - start import module 'mod'...
2012-03-09 18:22:22,809 - main.mod.submod - INFO - logger of submod say something... 
2012-03-09 18:22:22,809 - main.mod - INFO - logger say something...
2012-03-09 18:22:22,809 - main - DEBUG - let's test mod.testLogger()
2012-03-09 18:22:22,825 - main.mod - DEBUG - this is mod.testLogger...
2012-03-09 18:22:22,825 - main.mod.submod - INFO - this is submod.tst()...

另外在瀏覽網頁時發現一個講的比較好的:http://outofmemory.cn/code-snippet/450/python-rizhi-logging-module-usage-summary

下面這個是目前會用,但是不是甚懂的一個logger系統設計:

import logging
import logging.config

class SpiderFilter(logging.Filter):

    def __init__(self, allow=None, disable=None):
        self.allow_channels = allow
        self.disable_channels = disable

    def filter(self, record):
        if self.allow_channels is not None:
            if record.name in self.allow_channels:
                allow = True
            else:
                allow = False
        elif self.disable_channels is not None:
            if record.name in self.disable_channels:
                allow = False
            else:
                allow = True
        else:
            allow = False
        return allow


LOGGING = {
    'version': 1,
    'disable_existing_loggers': True,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
        },
        'simple': {
            'format': '%(asctime)s -- %(name)s !!!%(levelname)s!!!: %(message)s'
        },
    },
    'filters': {
        'basic': {
            '()': SpiderFilter,
            'allow': ('mongo', 'redis', 'mysql'),
        },
        'warn': {
            '()': SpiderFilter,
            'disable': ()
        }
    },
    'handlers': {
        'file': {
            'level': 'WARN',
            'formatter': 'simple',
            'class': 'logging.FileHandler',
            'filename': 'spider.log',
            'mode': 'a',
            'filters': ['warn']
        },
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'database': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'formatter': 'simple',
            'filename': 'spider.log',
            'mode': 'a',
            'filters': ['basic']
        }
    },
    'loggers': {
        'mongo': {
            'handlers':['file'],
            'propagate': True,
            'level':'ERROR',
        },
        'mysql': {
            'handlers': ['database'],
            'level': 'DEBUG',
            'propagate': False,
        },
        'redis': {
            'handlers': ['console', 'database'],
            'level': 'INFO',
            'filters': ['basic']
        }
    },
    'root': {
        'level': 'DEBUG',
        'handlers': ['console']
    }
}

if __name__ == '__main__':
    logging.config.dictConfig(LOGGING)
    logging.getLogger('mysql').debug('Simple Log Test!')
    logging.getLogger('mongo').critical('Test!')

 

logging簡單使用

看下面的代碼就可以對logging的使用有一個基本的認識

# 亭子
#!/usr/bin/env python3
# -*- coding:utf-8 -*-

'''
記錄了logging的簡單使用方式,主要怎么配置logging
'''
import logging

def simple_example():
    #默認過濾級別為warning,默認輸出到控制台
    logging.warning("warning")
    logging.info("info")

#logging:配置日志級別,輸出位置
def logging_to_file():
    '''
    #參數:filename:用指定的文件名創建filedhandler,這樣日志會被存儲到指定的文件中,如果不指定,則默認輸出到控制台
    #參數:filemode默認filemode的默認值"a",表示append,當然你也可以指定為"w"
    #參數:level,一共5個級別,critical(50)>error(40)>warning(30)>info(20)>debug(10),默認級別為Warning,這里可以用對應的數值表示level
    '''

    #將日志信息只輸入到指定的文件
    logging.basicConfig(filename="example.log", level=logging.DEBUG)
    # logging.basicConfig(filename="example.log", level=logging.DEBUG, filemode="w")
    # logging.basicConfig(filename="example.log", level=10, filemode="w")
    logging.debug("debu1g")
    logging.info("info")
    logging.warning("info")
    logging.error("error")
    logging.critical("critical")

    # logging variable data:打印變量
    logging.error("logging variable date: %s,%s,%s","var01","var02","var03")

#logging:配置日志格式
def displaying_format_massages():
    '''
    format參數中可能用到的格式化串
    %(name)s Logger的名字
    %(levelno)s 數字形式的日志級別
    %(levelname)s 文本形式的日志級別
    %(pathname)s 調用日志輸出函數的模塊的完整路徑名,可能沒有
    %(filename)s 調用日志輸出函數的模塊的文件名
    %(module)s 調用日志輸出函數的模塊名
    %(funcName)s 調用日志輸出函數的函數名
    %(lineno)d 調用日志輸出函數的語句所在的代碼行
    %(created)f 當前時間,用UNIX標准的表示時間的浮 點數表示
    %(relativeCreated)d 輸出日志信息時的,自Logger創建以 來的毫秒數
    %(asctime)s 字符串形式的當前時間。默認格式是 “2003-07-08 16:49:45,896”。逗號后面的是毫秒
    %(thread)d 線程ID。可能沒有
    %(threadName)s 線程名。可能沒有
    %(process)d 進程ID。可能沒有
    %(message)s用戶輸出的消息
    '''

    # format:指定handler使用的日志顯示格式。
    logging.basicConfig(format="%(asctime)s %(filename)s[line:%(lineno)d]%(levelname)s - %(message)s")
    logging.warning("warning")

#logging:配置日志時間格式
def displaying_date_message():
    # datefmt:指定日期時間格式,也就是“%(asctime)s”的格式
    logging.basicConfig(format="%(asctime)s %(filename)s[line:%(lineno)d]%(levelname)s - %(message)s",datefmt="%m/%d/%Y %I:%M:%S %p")
    logging.warning("warning")

if __name__ == "__main__":
    #test
    displaying_date_message()

 

logging高級進階

我們直接上代碼了,先對logging的高級應用做一個簡單的流程梳理,具體仔細的東西,后面慢慢看。代碼中也有很多注釋,所以看起來應該不是很費勁。

##亭子
#!/usr/bin/env python3
# -*- coding:utf-8 -*-

'''
對logging進行更加靈活的控制:需要了解Logger,Handler,Formatter,Filter

步驟
1. 創建logger實例並配置
2. 創建formatter對象
3. 創建你需要的handler對象並配置
4. 將handler加載到logger實例中
5. 寫日志

這個模塊主要包含了幾個內容
1. 輸出日志到控制台中
2. 輸出日志到指定的日志文件中
3. 輸出的日志按照指定的大小來自動管理,當文件達到指定的大小后,自動創建新文件
4. 輸出的文件按照時間來自動管理,當文件達到指定的時候后,自動創建新文件
'''

import logging
from logging import handlers
import time

if __name__ =="__main__":
    #初始化logger
    logger = logging.getLogger()
    # 配置日志級別,如果不顯示配置,默認為Warning,表示所有warning級別已下的其他level直接被省略,
    # 內部綁定的handler對象也只能接收到warning級別以上的level,你可以理解為總開關
    logger.setLevel(logging.INFO)

    formatter = logging.Formatter(fmt="%(asctime)s %(filename)s[line:%(lineno)d]%(levelname)s - %(message)s",
                                  datefmt="%m/%d/%Y %I:%M:%S %p")  # 創建一個格式化對象

    console = logging.StreamHandler() # 配置日志輸出到控制台
    console.setLevel(logging.WARNING) # 設置輸出到控制台的最低日志級別
    console.setFormatter(formatter)  # 設置格式
    logger.addHandler(console)

    # file_logging = logging.FileHandler("example.log") # 配置日志輸出到文件
    # file_logging.setLevel(logging.WARNING)
    # file_logging.setFormatter(formatter)
    # logger.addHandler(file_logging)

    # 和上面的FIleHandler差不多,只是handler對象可以管理文件大小,當文件大於指定的大小后,會自動將當前文件改名,然后重新創建一個新的同名文件繼續輸出
    # file_rotating_file = handlers.RotatingFileHandler("cat.log",maxBytes=1024,backupCount=3)
    # file_rotating_file.setLevel(logging.INFO)
    # file_rotating_file.setFormatter(formatter)
    # logger.addHandler(file_rotating_file)

    # 和上面的handler有點類似,不過,它是通過判斷文件大小來決定何時重新創建日志文件,而是間隔一定的時候自動創建日志文件。
    # 代表每7天備份文件
    file_time_rotating = handlers.TimedRotatingFileHandler("app.log",when="s",interval=10,backupCount=5)
    file_time_rotating.setLevel(logging.INFO)
    file_time_rotating.setFormatter(formatter)
    logger.addHandler(file_time_rotating)

    #use logger
    logger.debug("debug")
    logger.info("info")
    logger.warning("warning")
    logger.error("error")
    logger.critical("critical message")

    time.sleep(12)

    logger.debug("debug")
    logger.info("info")
    logger.warning("warning")
    logger.error("error")
    logger.critical("critical message")

 

首先,這里說幾個概念,是我們代碼里面涉及到的,也是我們這個章節里面比較重要的。那就是“Logger”,“Handler”,“Formatter”,“Filter”,下面做簡單的解釋

  1. logger提供了應用程序可以直接使用的接口;
  2. handler將(logger創建的)日志記錄發送到合適的目的輸出;
  3. filter提供了細度設備來決定輸出哪條日志記錄;(這里我就先不涉及了,為什么,因為我也不懂,后面如果涉及到,再慢慢補充)
  4. formatter決定日志記錄的最終輸出格式。

Logger

每個程序在輸出信息之前都要獲得一個Logger。Logger通常對應了程序的模塊名,比如聊天工具的圖形界面模塊可以這樣獲得它的Logger:
LOG=logging.getLogger(”chat.gui”)
而核心模塊可以這樣:
LOG=logging.getLogger(”chat.kernel”)

Logger.setLevel(lel):指定最低的日志級別,低於lel的級別將被忽略。debug是最低的內置級別,critical為最高
Logger.addFilter(filt)、Logger.removeFilter(filt):添加或刪除指定的filter
Logger.addHandler(hdlr)、Logger.removeHandler(hdlr):增加或刪除指定的handler
Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical():可以設置的日志級別

Handler

handler對象負責發送相關的信息到指定目的地。Python的日志系統有多種Handler可以使用。有些Handler可以把信息輸出到控制台,有些Logger可以把信息輸出到文件,還有些Handler可以把信息發送到網絡上。如果覺得不夠用,還可以編寫自己的Handler。可以通過addHandler()方法添加多個多handler
Handler.setLevel(lel):指定被處理的信息級別,低於lel級別的信息將被忽略
Handler.setFormatter():給這個handler選擇一個格式
Handler.addFilter(filt)、Handler.removeFilter(filt):新增或刪除一個filter對象

每個Logger可以附加多個Handler。接下來我們就來介紹一些常用的Handler:

1) logging.StreamHandler

使用這個Handler可以向類似與sys.stdout或者sys.stderr的任何文件對象(file object)輸出信息。它的構造函數是:StreamHandler([strm])
其中strm參數是一個文件對象。默認是sys.stderr

2) logging.FileHandler

和StreamHandler類似,用於向一個文件輸出日志信息。不過FileHandler會幫你打開這個文件。它的構造函數是:FileHandler(filename[,mode])
filename是文件名,必須指定一個文件名。
mode是文件的打開方式。參見Python內置函數open()的用法。默認是’a',即添加到文件末尾。

3) logging.handlers.RotatingFileHandler

這個Handler類似於上面的FileHandler,但是它可以管理文件大小。當文件達到一定大小之后,它會自動將當前日志文件改名,然后創建 一個新的同名日志文件繼續輸出。比如日志文件是chat.log。當chat.log達到指定的大小之后,RotatingFileHandler自動把文件改名為chat.log.1。不過,如果chat.log.1已經存在,會先把chat.log.1重命名為chat.log.2。。。最后重新創建 chat.log,繼續輸出日志信息。它的構造函數是:
RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])
其中filename和mode兩個參數和FileHandler一樣。
maxBytes用於指定日志文件的最大文件大小。如果maxBytes為0,意味着日志文件可以無限大,這時上面描述的重命名過程就不會發生。
backupCount用於指定保留的備份文件的個數。比如,如果指定為2,當上面描述的重命名過程發生時,原有的chat.log.2並不會被更名,而是被刪除。

4) logging.handlers.TimedRotatingFileHandler

這個Handler和RotatingFileHandler類似,不過,它沒有通過判斷文件大小來決定何時重新創建日志文件,而是間隔一定時間就 自動創建新的日志文件。重命名的過程與RotatingFileHandler類似,不過新的文件不是附加數字,而是當前時間。它的構造函數是:
TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
其中filename參數和backupCount參數和RotatingFileHandler具有相同的意義。
interval是時間間隔。
when參數是一個字符串。表示時間間隔的單位,不區分大小寫。它有以下取值:
S 秒
M 分
H 小時
D 天
W 每星期(interval==0時代表星期一)
midnight 每天凌晨

 

 

參考地址

python 構建自己的log系統: https://www.cnblogs.com/crawer-1/p/8258146.html

文章部分文字內容摘取自:http://www.cnblogs.com/wenwei-blog/p/7196658.html,感謝偉大的互聯網。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM