日誌按照下面四個層次來完成日誌的功能:api
1 Logger暴露出來給應用使用的接口app
2 Handlers是發送日誌記錄(由logger建立的)到合適的目的地,包括文件,屏幕,email...scrapy
3 Filters 是提供一個過濾的機制,決定哪些日誌能夠留下this
4 Formatters是輸出日誌的格式url
建立格式:spa
logger = logging.getLogger('apps') # 建立一個新的apps 的logger logger.setLevel(logging.DEBUG) # 由於它是會默認傳播到祖先logger logger.propagate = False # 默認StreamHandler那裏, 也就是不會打印在頁面上 apps_handler = logging.FileHandler(filename="apps.log") # 添加handler, 決定日誌落地到哪裏,能夠多個 apps_formatter=logging.Formatter('%(asctime)s:%(name)s:%(levelname)s:%(message)s' # 輸出日誌的格式 apps_handler.setFormatter(apps_formatter) # 裝載上 logger.addHandler(apps_handler) # 裝載實例化logger上 # 日誌會打印到apps.log, 而且不會輸出到屏幕(若是logger.propagate=True就會)
繼承;日誌
# 定義一個新的logger child_logger = logging.getLogger('apps.owan') # 由於這個child_logger 是apps.owan, 它是繼承了apps這個logger # 這個child_logger.propagate 默認是True # 因此仍是會傳到它的祖先logger 也就是apps child_logger.info('haha') # 因此這個info 是會傳播到apps 因此apps.log會出現這個日誌。 # 這裏充分說明logger的繼承關係
第一種:就是實例化logger = logging.logger 而後手動給logger添加addHandler, addFilter, handler.setFormatter 添加格式,這樣的形式來獲取logger。。。。。code
import logging # 沒有建立logger, 默認是root logger, 直接打印在屏幕 logging.basicConfig(level=logging.DEBUG,format='%(asctime)s:%(name)s:%(levelname)s:%(message)s') logger = logging.getLogger('apps') apps_handler = logging.FileHandler(filename="apps.log") logger.addHandler(apps_handler) logger.setLevel(logging.DEBUG) logger.info('shis')
第二種:就是使用 logging.config.dictConfig 來從配置文件生成loggerorm
logging.config.dictConfig(log_config.PATTERN) # 沒有定義的logger tool_logger = logging.getLogger('ToolApi') # 因爲沒有定義,會冒泡到root logger tool_logger.info('----------------') # 定義過的api logger api_logger = logging.getLogger('API') # 定義過了,因此api本身的handler會處理,同時會冒泡到root logger處理 api_logger.error('fuck !')
配置文件:blog
PATTERN = { 'version': 1, 'formatters': { 'normal': { 'format': '%(name)s %(asctime)s %(levelname)s %(message)s', 'datefmt': '%Y-%m-%d %H:%M:%S' }, 'raw': { 'format': '%(message)s', }, }, 'handlers': { 'console': { 'class': 'logging.StreamHandler', 'stream': 'ext://sys.stdout', 'formatter': 'normal', }, 'root': { 'class': 'logging.handlers.WatchedFileHandler', 'formatter': 'normal', 'filename': RUNTIME_HOME + '/var/log/root.log', 'mode': 'a', 'level': 'INFO', }, 'extapi': { 'class': 'logging.handlers.WatchedFileHandler', 'formatter': 'normal', 'filename': RUNTIME_HOME + '/var/log/ext_api.log', 'mode': 'a', 'level': 'DEBUG', }, 'api': { 'class': 'logging.handlers.WatchedFileHandler', 'formatter': 'normal', 'filename': RUNTIME_HOME + '/var/log/api.log', 'mode': 'a', 'level': 'DEBUG', }, }, 'loggers': { 'API': {'level': 'DEBUG', 'handlers': ['api'], }, 'EXTAPI': {'level': 'DEBUG', 'handlers': ['extapi'], }, 'requests.packages.urllib3.connectionpool': {'level': 'ERROR'}, }, 'root': { 'handlers': ['root',], 'level': 'INFO', } }
例子:
import logging import os from douban_scrapy import settings from logging.handlers import RotatingFileHandler from logging import StreamHandler # 直接繼承logging.Logger 那麼就是說這個類就是一個Logger, 有了Logger全部方法 # 只是在類裏面添加一些內部方法,讓logger 封裝addhandler, setformatter等方法 class LogHandler(logging.Logger): # 單例模式 _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: # 一開始竟然用了 cls()來實例化 致使無限次調用 # cls._instance = cls(*args, **kwargs) cls._instance = object.__new__(cls, *args, **kwargs) return cls._instance def __init__(self, name, level=logging.DEBUG, to_stream=True, to_file=True): self.name = name self.level = level self.formatter = logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s') # 錯誤的, 繼承了logger 自己就是logger 不用再self.logger=xxx 這樣變成了一個新的變量 #self.logger = logging.Logger(name=name, level=level) super(LogHandler, self).__init__(name=name, level=level) # 寫文件 if to_file: self.__setFileHandler__() # 寫標準輸出 if to_stream: self.__setSteamHandler__() def __setSteamHandler__(self): stream_handler = StreamHandler() stream_handler.setFormatter(self.formatter) self.addHandler(stream_handler) def __setFileHandler__(self): log_path = os.path.join(settings.LOG_DIR, self.name +'.log') handler = RotatingFileHandler(log_path, maxBytes=1024, backupCount=5) handler.setFormatter(self.formatter) self.addHandler(handler) if __name__ == '__main__': logger = LogHandler('scrapy') logger2 = LogHandler('scrapy') print logger, logger2 logger.info('haha')
2:-----------------------------------------------------------------------------------------------------------------------2
import os import logging from logging.handlers import TimedRotatingFileHandler # 日誌級別 CRITICAL = 50 FATAL = CRITICAL ERROR = 40 WARNING = 30 WARN = WARNING INFO = 20 DEBUG = 10 NOTSET = 0 CURRENT_PATH = os.path.dirname(os.path.abspath(__file__)) ROOT_PATH = os.path.join(CURRENT_PATH, os.pardir) LOG_PATH = os.path.join(ROOT_PATH, 'log') class LogHandler(logging.Logger): """ LogHandler """ def __init__(self, name, level=DEBUG, stream=True, file=True): self.name = name self.level = level logging.Logger.__init__(self, self.name, level=level) if stream: self.__setStreamHandler__() if file: self.__setFileHandler__() def __setFileHandler__(self, level=None): """ set file handler :param level: :return: """ file_name = os.path.join(LOG_PATH, '{name}.log'.format(name=self.name)) # 設置日誌回滾, 保存在log目錄, 一天保存一個文件, 保留15天 file_handler = TimedRotatingFileHandler(filename=file_name, when='D', interval=1, backupCount=15) file_handler.suffix = '%Y%m%d.log' if not level: file_handler.setLevel(self.level) else: file_handler.setLevel(level) formatter = logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s') file_handler.setFormatter(formatter) self.file_handler = file_handler self.addHandler(file_handler) def __setStreamHandler__(self, level=None): """ set stream handler :param level: :return: """ stream_handler = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s') stream_handler.setFormatter(formatter) if not level: stream_handler.setLevel(self.level) else: stream_handler.setLevel(level) self.addHandler(stream_handler) def resetName(self, name): """ reset name :param name: :return: """ self.name = name self.removeHandler(self.file_handler) self.__setFileHandler__() if __name__ == '__main__': log = LogHandler('test') log.info('this is a test msg')