背景:
- 項目使用Python自帶的logging庫來打印日志
- 項目部署在一台Centos7的機器上
- 項目采用
gunicorn
多進程部署
過程:
1、LOG日志代碼封裝:
采用logging庫,並設置when='MIDNIGHT',以天為單位,進行日志分割,前一天的日志會自動加上前一天的日期,最新日志始終會打印到mock-service.log文件中,以下為log打印的封裝
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2019/8/8 22:10
# @Author :
# @Site :
# @File : Logger.py
# @Software: PyCharm
"""
日志類。通過讀取配置文件,定義日志級別、日志文件名、日志格式等。
一般直接把logger import進去
from utils.log import logger
logger.info('test log')
"""
import os
import platform
import logging
from logging.handlers import TimedRotatingFileHandler
class Logger:
def __init__(self, logger_name='framework'):
self.logger = logging.getLogger(logger_name)
logging.root.setLevel(logging.NOTSET)
if platform.system() == 'Windows':
# win機器路徑
self.log_path=os.path.join(os.path.dirname(os.path.dirname(__file__)),'log')
else:
# 服務器路徑
self.log_path='/log'
self.log_file_name = 'mock-service.log' # 日志文件
self.backup_count = 30 # 保留的日志數量
# 日志輸出級別
self.console_output_level = 'INFO'
self.file_output_level = 'INFO'
# 日志輸出格式
pattern = '%(asctime)s - %(filename)s [Line:%(lineno)d] - %(levelname)s - %(message)s'
self.formatter = logging.Formatter(pattern)
def get_logger(self):
"""在logger中添加日志句柄並返回,如果logger已有句柄,則直接返回
我們這里添加兩個句柄,一個輸出日志到控制台,另一個輸出到日志文件。
兩個句柄的日志級別不同,在配置文件中可設置。
"""
if not self.logger.handlers: # 避免重復日志
console_handler = logging.StreamHandler()
console_handler.setFormatter(self.formatter)
console_handler.setLevel(self.console_output_level)
self.logger.addHandler(console_handler)
# 每天重新創建一個日志文件,最多保留backup_count份
# 每天生成的日志文件沒有后綴,需要修改源碼:TimedRotatingFileHandler類下的doRollover方法-->
# dfn = self.rotation_filename(self.baseFilename + "." + time.strftime(self.suffix, timeTuple)后面拼接后綴名
# dfn = self.rotation_filename(self.baseFilename + "." + time.strftime(self.suffix, timeTuple) + ".log")
file_handler = TimedRotatingFileHandler(filename=os.path.join(self.log_path, self.log_file_name),
when='MIDNIGHT',
interval=1,
backupCount=self.backup_count,
delay=True,
encoding='utf-8'
)
file_handler.setFormatter(self.formatter)
file_handler.setLevel(self.file_output_level)
self.logger.addHandler(file_handler)
return self.logger
logger = Logger().get_logger()
2、調用LOG封裝
實際調用時,采用導包並設置別名的方式進行log打印,以下為實際打印log的demo
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2019/12/2 10:32
# @Author :
# @Site :
# @File : 1.py
# @Software: PyCharm
# @Description:
from util.Logger import logger as log
class test1:
def log_test(self):
log.info('log測試')
if __name__ == '__main__':
t = test1()
t.log_test()
3、項目部署環境
該項目目前部署在centos7的一台服務器上面,不過我覺得這個應該和部署在哪里沒關系
4、出現的問題(坑,大坑)
項目持續在服務器上面運行着,但有時候會發現,日志根本沒有打印到mock-service.log
文件中,比如下面這個,12月29的調用日志,打到了27號的log文件中了
解決思路:
1、第一次嘗試:
為了找到原因,之前有些調用log打印是寫在init方法里面,然后下面的方法用self.log進行調用,后面為了解決這個問題,把所有init里面的初始化都去掉了,現在我覺得問題可能還是出在調用上,日志封裝應該沒有問題。現在這個項目有些是類方法在打印日志,有些是封裝的函數在打印日志。整個項目是用flask框架寫的,然后啟動run.py文件,我覺得可能是出現在了某一個地方的調用一直占用了日志打印的進程,導致后面再調用日志打印的時候打印到了之前的文件中,只是一個猜測,也不知道怎么去驗證這個問題
2、第二次嘗試
經過再一次的百度、谷歌查找,發現Python的這個自帶的logging庫是不支持多進程
的,后面查到可通過如下方式進行解決這個問題
關於logging庫
不支持多進程
的文章:
https://juejin.im/post/5bc2bd3a5188255c94465d31
https://zhuanlan.zhihu.com/p/29557920
- 自行對logging庫重寫,以解決該問題
- 通過別人已封裝好的第三方庫
我選擇第二種方式,不重復造輪子,經過一番對比,最終選擇了這個第三方庫concurrent_log
,詳細的可去github倉庫查看,並且作者對重寫后的代碼進行了測試,證明已解決多線程、多進程的問題
安裝方式:
pip3 install concurrent_log
github地址:
https://github.com/huanghyw/concurrent_log