折腾:
【已解决】把Flask中的app的logger改造成单例以避免循环引用和多次初始化Flask的实例
期间,之前是Flask的app.logger的初始化:
def create_log(app):
print("create_log: before init log: app.logger=%s" % app.logger)
logFormatter = logging.Formatter(settings.LOG_FORMAT)
fileHandler = RotatingFileHandler(
settings.LOG_FILE_FILENAME,
maxBytes=settings.LOG_FILE_MAX_BYTES,
backupCount=settings.LOG_FILE_BACKUP_COUNT,
encoding="UTF-8")
fileHandler.setLevel(settings.LOG_LEVEL)
fileHandler.setFormatter(logFormatter)
app.logger.addHandler(fileHandler)
# Note: should NOT set StreamHandler here, otherwise will duplicate debug log
app.logger.setLevel(settings.LOG_LEVEL) # set root log level
log = app.logger
log.info("app=%s", app)
# log.debug("app.config=%s", app.config)
print("create_log: after init log: app.logger=%s" % app.logger)
return log
是没问题的:
log尽可以输出到file文件,也可以输出到console中
现在去改为:
common/FlaskLogSingleton.py
import logging
from logging.handlers import RotatingFileHandler
from conf.app import settings
from common.ThreadSafeSingleton import ThreadSafeSingleton
def init_logger(flask_settings, enableConsole=True):
print("init_logger")
flaskAppLogger = logging.getLogger(flask_settings.FLASK_APP_NAME) # <Logger RobotQA (WARNING)>
print("flaskAppLogger=%s" % flaskAppLogger)
logFormatter = logging.Formatter(flask_settings.LOG_FORMAT)
fileHandler = RotatingFileHandler(
flask_settings.LOG_FILE_FILENAME,
maxBytes=flask_settings.LOG_FILE_MAX_BYTES,
backupCount=flask_settings.LOG_FILE_BACKUP_COUNT,
encoding="UTF-8")
fileHandler.setLevel(flask_settings.LOG_LEVEL_FILE)
fileHandler.setFormatter(logFormatter)
flaskAppLogger.addHandler(fileHandler)
# Note: should NOT set StreamHandler here, otherwise will duplicate debug log
flaskAppLogger.setLevel(flask_settings.LOG_LEVEL_FILE) # set root log level
if enableConsole :
# define a Handler which writes INFO messages or higher to the sys.stderr
console = logging.StreamHandler()
console.setLevel(flask_settings.LOG_LEVEL_CONSOLE)
# set a format which is simpler for console use
formatter = logging.Formatter(
# fmt=logFormatter)
fmt=logFormatter,
datefmt=flask_settings.LOG_CONSOLE_DATA_FORMAT)
# tell the handler to use this format
console.setFormatter(formatter)
flaskAppLogger.addHandler(console)
print("init_logger: after init flaskAppLogger%s" % flaskAppLogger)
return flaskAppLogger
class LoggerSingleton(metaclass=ThreadSafeSingleton):
curLog = ""
def __init__(self):
self.curLog = init_logger(settings)
# Note: during __init__, AVOID use log, otherwise will deadlock
# log.info("LoggerSingleton __init__: curLog=%s", self.curLog)
print("LoggerSingleton __init__: curLog=%s" % self.curLog)
logSingleton = LoggerSingleton()
log = logSingleton.curLog
log.info("LoggerSingleton inited, logSingleton=%s", logSingleton) # <factory.LoggerSingleton object at 0x10cbcafd0>
log.info("log=%s", log) # <Logger RobotQA (DEBUG)>
# # debug for singleton log
# log2 = LoggerSingleton()
# print("log2=%s" % log2)
其中配置是:
conf/app/settings.py
# Flask app name
FLASK_APP_NAME = "RobotQA"
# Log File
LOG_LEVEL_FILE = logging.DEBUG
LOG_FILE_FILENAME = "logs/" + FLASK_APP_NAME + ".log"
LOG_FORMAT = "[%(asctime)s %(levelname)s %(filename)s:%(lineno)d %(funcName)s] %(message)s"
LOG_FILE_MAX_BYTES = 2 * 1024 * 1024
LOG_FILE_BACKUP_COUNT = 10
# Log Console
LOG_LEVEL_CONSOLE = logging.INFO
LOG_CONSOLE_DATA_FORMAT = ‘%Y%m%d %I:%M:%S’
结果是可以输出到文件的,但是输出到console时出错:
<span style="font-family: Monaco; font-size: 12px; color: rgb(51, 51, 51);"–<- Logging error —
Traceback (most recent call last):
File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/logging/__init__.py", line 992, in emit
msg = self.format(record)
File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/logging/__init__.py", line 838, in format
return fmt.format(record)
File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/logging/__init__.py", line 576, in format
if self.usesTime():
File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/logging/__init__.py", line 544, in usesTime
return self._style.usesTime()
File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/logging/__init__.py", line 388, in usesTime
return self._fmt.find(self.asctime_search) >= 0
AttributeError: ‘Formatter’ object has no attribute ‘find’
Call stack:
File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1664, in <module>
main()
File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1658, in main
globals = debugger.run(setup[‘file’], None, None, is_module)
File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1068, in run
pydev_imports.execfile(file, globals, locals) # execute the script
File "/Applications/PyCharm.app/Contents/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, ‘exec’), glob, loc)
File "/Users/crifan/dev/dev_root/xxx/app.py", line 2, in <module>
from factory import create_app
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "/Users/crifan/dev/dev_root/xxx/factory.py", line 14, in <module>
from common.FlaskLogSingleton import log
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "/Users/crifan/dev/xxx/common/FlaskLogSingleton.py", line 55, in <module>
log.info("log=%s", log) # <Logger RobotQA (DEBUG)>
Message: ‘log=%s’
Arguments: (<Logger RobotQA (DEBUG)>,)
去搜:
python logging console AttributeError: ‘Formatter’ object has no attribute ‘find’
Python logging module having a formatter causes AttributeError – Stack Overflow
去加上:
from sys import stdout
试试:
from sys import stdout
console = logging.StreamHandler(stdout)
调试:
结果:问题依旧。
另外,上面代码是参考自己之前的代码:
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
Filename: crifanLogging.py
Function: crifanLib’s logging related functions.
Version: v1.3 20180609
Note:
1. latest version and more can found here:
https://github.com/crifan/crifanLib/blob/master/python/crifanLib
"""
__author__ = "Crifan Li (admin@crifan.org)"
__version__ = "v1.3"
__copyright__ = "Copyright (c) 2018, Crifan Li"
__license__ = "GPL"
import logging
################################################################################
# Config
################################################################################
LOG_FORMAT_FILE = "%(asctime)s %(filename)s:%(lineno)-4d %(levelname)-7s %(message)s"
LOG_LEVEL_FILE = logging.DEBUG
LOG_FORMAT_CONSOLE = "%(asctime)s %(filename)s:%(lineno)-4d %(levelname)-7s %(message)s"
LOG_LEVEL_CONSOLE = logging.INFO
################################################################################
# Constant
################################################################################
CURRENT_LIB_FILENAME = "crifanLogging"
################################################################################
# Logging
################################################################################
def loggingInit(filename = None,
fileLogLevel = LOG_LEVEL_FILE,
fileLogFormat = LOG_FORMAT_FILE,
fileLogDateFormat = ‘%Y/%m/%d %I:%M:%S’,
enableConsole = True,
consoleLogLevel = LOG_LEVEL_CONSOLE,
consoleLogFormat = LOG_FORMAT_CONSOLE,
consoleLogDateFormat = ‘%Y%m%d %I:%M:%S’,
):
"""
init logging for both log to file and console
:param logFilename: input log file name
if not passed, use current lib filename
:return: none
"""
logFilename = ""
if filename:
logFilename = filename
else:
# logFilename = __file__ + ".log"
# ‘/Users/crifan/dev/dev_root/company/xxx/crifanLogging.py.log’
logFilename = CURRENT_LIB_FILENAME + ".log"
# logging.basicConfig(
# level = fileLogLevel,
# format = fileLogFormat,
# datefmt = fileLogDateFormat,
# filename = logFilename,
# encoding = "utf-8",
# filemode = ‘w’)
# rootLogger = logging.getLogger()
rootLogger = logging.getLogger("")
rootLogger.setLevel(fileLogLevel)
fileHandler = logging.FileHandler(
filename=logFilename,
mode=’w’,
encoding="utf-8")
fileHandler.setLevel(fileLogLevel)
fileFormatter = logging.Formatter(
fmt=fileLogFormat,
datefmt=fileLogDateFormat
)
fileHandler.setFormatter(fileFormatter)
rootLogger.addHandler(fileHandler)
if enableConsole :
# define a Handler which writes INFO messages or higher to the sys.stderr
console = logging.StreamHandler()
console.setLevel(consoleLogLevel)
# set a format which is simpler for console use
formatter = logging.Formatter(
fmt=consoleLogFormat,
datefmt=consoleLogDateFormat)
# tell the handler to use this format
console.setFormatter(formatter)
rootLogger.addHandler(console)
################################################################################
# Test
################################################################################
def testLogging():
loggingInit("testLogging.log")
# loggingInit()
logging.debug("log debug")
logging.info("log info")
logging.warning("log waring")
logging.error("log error")
logging.critical("log critical")
logging.exception("log exception") # NoneType: None
if __name__ == ‘__main__’:
# print("[crifanLib-%s] %s" % (__file__, __version__))
print("[crifanLib-%s] %s" % (CURRENT_LIB_FILENAME, __version__))
testLogging()
改造的,之前是正常运行的。
不知道为何此处不对。
Error using python logging from Pydev – Stack Overflow
Python logging error – potentially related to variable scope – Stack Overflow
python – Suspend the formatting of the logger, then go back to it – Stack Overflow
把初始化顺序变一下:
def init_logger(flask_settings, enableConsole=True):
print("init_logger")
flaskAppLogger = logging.getLogger(flask_settings.FLASK_APP_NAME) # <Logger RobotQA (WARNING)>
print("flaskAppLogger=%s" % flaskAppLogger)
flaskAppLogger.setLevel(flask_settings.LOG_LEVEL_FILE)
logFormatter = logging.Formatter(flask_settings.LOG_FORMAT)
fileHandler = RotatingFileHandler(
flask_settings.LOG_FILE_FILENAME,
maxBytes=flask_settings.LOG_FILE_MAX_BYTES,
backupCount=flask_settings.LOG_FILE_BACKUP_COUNT,
encoding="UTF-8")
fileHandler.setLevel(flask_settings.LOG_LEVEL_FILE)
fileHandler.setFormatter(logFormatter)
flaskAppLogger.addHandler(fileHandler)
if enableConsole :
# define a Handler which writes INFO messages or higher to the sys.stderr
console = logging.StreamHandler()
# console = logging.StreamHandler(stdout)
console.setLevel(flask_settings.LOG_LEVEL_CONSOLE)
# set a format which is simpler for console use
formatter = logging.Formatter(
# fmt=logFormatter)
fmt=logFormatter,
datefmt=flask_settings.LOG_CONSOLE_DATA_FORMAT)
# tell the handler to use this format
console.setFormatter(formatter)
flaskAppLogger.addHandler(console)
print("init_logger: after init flaskAppLogger%s" % flaskAppLogger)
return flaskAppLogger
结果:
问题依旧。
python logging AttributeError: ‘Formatter’ object has no attribute ‘find’
logging AttributeError: ‘Formatter’ object has no attribute ‘find’
Issue 16368: error when logging message – Python tracker
AttributeError: Logger instance has no attribute ‘setFormatter’
去掉console的初始化:
def init_logger(flask_settings, enableConsole=True):
print("init_logger")
flaskAppLogger = logging.getLogger(flask_settings.FLASK_APP_NAME) # <Logger RobotQA (WARNING)>
print("flaskAppLogger=%s" % flaskAppLogger)
flaskAppLogger.setLevel(flask_settings.LOG_LEVEL_FILE)
logFormatter = logging.Formatter(flask_settings.LOG_FORMAT)
fileHandler = RotatingFileHandler(
flask_settings.LOG_FILE_FILENAME,
maxBytes=flask_settings.LOG_FILE_MAX_BYTES,
backupCount=flask_settings.LOG_FILE_BACKUP_COUNT,
encoding="UTF-8")
fileHandler.setLevel(flask_settings.LOG_LEVEL_FILE)
fileHandler.setFormatter(logFormatter)
flaskAppLogger.addHandler(fileHandler)
# if enableConsole :
# # define a Handler which writes INFO messages or higher to the sys.stderr
# console = logging.StreamHandler()
# # console = logging.StreamHandler(stdout)
# console.setLevel(flask_settings.LOG_LEVEL_CONSOLE)
# # set a format which is simpler for console use
# formatter = logging.Formatter(
# # fmt=logFormatter)
# fmt=logFormatter,
# datefmt=flask_settings.LOG_CONSOLE_DATA_FORMAT)
# # tell the handler to use this format
# console.setFormatter(formatter)
# flaskAppLogger.addHandler(console)
print("init_logger: after init flaskAppLogger%s" % flaskAppLogger)
return flaskAppLogger
就是正常的:
感觉是Console的log有问题。
pycharm StreamHandler AttributeError: ‘Formatter’ object has no attribute ‘find’
pycharm StreamHandler AttributeError: ‘Formatter’
python StreamHandler AttributeError: ‘Formatter’
python – AttributeError: ‘Logger’ object has no attribute ‚WARNING’ – Stack Overflow
Usage example doesn’t work · Issue #31 · borntyping/python-colorlog
logging.getLogger Python Example
难道此处PyCharm的console很特殊?
重启PyCharm试试
问题依旧。
仔细看log输出:
<span style="font-family: Monaco; font-size: 12px; color: rgb(51, 51, 51);"–<- Logging error —
Traceback (most recent call last):
File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/logging/__init__.py", line 992, in emit
msg = self.format(record)
File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/logging/__init__.py", line 838, in format
return fmt.format(record)
File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/logging/__init__.py", line 576, in format
if self.usesTime():
File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/logging/__init__.py", line 544, in usesTime
return self._style.usesTime()
File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/logging/__init__.py", line 388, in usesTime
return self._fmt.find(self.asctime_search) >= 0
AttributeError: ‘Formatter’ object has no attribute ‘find’
Call stack:
File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1664, in <module>
main()
File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1658, in main
globals = debugger.run(setup[‘file’], None, None, is_module)
File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1068, in run
pydev_imports.execfile(file, globals, locals) # execute the script
File "/Applications/PyCharm.app/Contents/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, ‘exec’), glob, loc)
File "/Users/crifan/dev/dev_root/xxx/app.py", line 2, in <module>
from factory import create_app
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "/Users/crifan/dev/dev_root/xxx/factory.py", line 14, in <module>
from common.FlaskLogSingleton import log
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "/Users/crifan/dev/dev_root/xxx/FlaskLogSingleton.py", line 53, in <module>
log.info("LoggerSingleton inited, logSingleton=%s", logSingleton) # <factory.LoggerSingleton object at 0x10cbcafd0>
Message: ‘LoggerSingleton inited, logSingleton=%s’
Arguments: (<common.FlaskLogSingleton.LoggerSingleton object at 0x104bd4048>,)
难道只是此处的LoggerSingleton是特殊的单例,而导致无法正常在console中输出?
看看后续普通的变量能否正常输出
问题依旧:
pycharm console StreamHandler AttributeError: ‘Formatter’
python self._fmt.find(self.asctime_search)
再去看代码,感觉是代码搞错了,改为:
# fmt=logFormatter,
fmt=flask_settings.LOG_FORMAT,
变成:
LOG_FORMAT = "[%(asctime)s %(levelname)s %(filename)s:%(lineno)d %(funcName)s] %(message)s"
def init_logger(flask_settings, enableConsole=True):
print("init_logger")
flaskAppLogger = logging.getLogger(flask_settings.FLASK_APP_NAME) # <Logger RobotQA (WARNING)>
print("flaskAppLogger=%s" % flaskAppLogger)
flaskAppLogger.setLevel(flask_settings.LOG_LEVEL_FILE)
logFormatter = logging.Formatter(flask_settings.LOG_FORMAT)
fileHandler = RotatingFileHandler(
flask_settings.LOG_FILE_FILENAME,
maxBytes=flask_settings.LOG_FILE_MAX_BYTES,
backupCount=flask_settings.LOG_FILE_BACKUP_COUNT,
encoding="UTF-8")
fileHandler.setLevel(flask_settings.LOG_LEVEL_FILE)
fileHandler.setFormatter(logFormatter)
flaskAppLogger.addHandler(fileHandler)
if enableConsole :
# define a Handler which writes INFO messages or higher to the sys.stderr
console = logging.StreamHandler()
# console = logging.StreamHandler(stdout)
console.setLevel(flask_settings.LOG_LEVEL_CONSOLE)
# set a format which is simpler for console use
formatter = logging.Formatter(
# fmt=logFormatter)
# fmt=logFormatter,
fmt=flask_settings.LOG_FORMAT,
datefmt=flask_settings.LOG_CONSOLE_DATA_FORMAT)
# tell the handler to use this format
console.setFormatter(formatter)
flaskAppLogger.addHandler(console)
print("init_logger: after init flaskAppLogger%s" % flaskAppLogger)
return flaskAppLogger
结果:
终于可以正常输出了:
【总结】
此处是不小心把:
logging.Formatter返回的变量logFormatter,类型是class Formatter(object)
传递给了console初始化时候的:
formatter = logging.Formatter(
fmt=logFormatter,
datefmt=flask_settings.LOG_CONSOLE_DATA_FORMAT)
导致后续console的log打印日志出错。
改为正常的,传递普通的字符串:
LOG_FORMAT = "[%(asctime)s %(levelname)s %(filename)s:%(lineno)d %(funcName)s] %(message)s"
formatter = logging.Formatter(
# fmt=logFormatter,
fmt=LOG_FORMAT,
datefmt=flask_settings.LOG_CONSOLE_DATA_FORMAT)
就可以正常初始化file和console的log了。
正常在console和file中都能看到自己想要的日志了。
附上相关代码:
common/FlaskLogSingleton.py
import logging
from logging.handlers import RotatingFileHandler
from conf.app import settings
from common.ThreadSafeSingleton import ThreadSafeSingleton
# from sys import stdout
def init_logger(flask_settings, enableConsole=True):
print("init_logger")
flaskAppLogger = logging.getLogger(flask_settings.FLASK_APP_NAME) # <Logger RobotQA (WARNING)>
print("flaskAppLogger=%s" % flaskAppLogger)
flaskAppLogger.setLevel(flask_settings.LOG_LEVEL_FILE)
logFormatter = logging.Formatter(flask_settings.LOG_FORMAT)
fileHandler = RotatingFileHandler(
flask_settings.LOG_FILE_FILENAME,
maxBytes=flask_settings.LOG_FILE_MAX_BYTES,
backupCount=flask_settings.LOG_FILE_BACKUP_COUNT,
encoding="UTF-8")
fileHandler.setLevel(flask_settings.LOG_LEVEL_FILE)
fileHandler.setFormatter(logFormatter)
flaskAppLogger.addHandler(fileHandler)
if enableConsole :
# define a Handler which writes INFO messages or higher to the sys.stderr
console = logging.StreamHandler()
# console = logging.StreamHandler(stdout)
console.setLevel(flask_settings.LOG_LEVEL_CONSOLE)
# set a format which is simpler for console use
formatter = logging.Formatter(
# fmt=logFormatter)
# fmt=logFormatter,
fmt=flask_settings.LOG_FORMAT,
datefmt=flask_settings.LOG_CONSOLE_DATA_FORMAT)
# tell the handler to use this format
console.setFormatter(formatter)
flaskAppLogger.addHandler(console)
print("init_logger: after init flaskAppLogger%s" % flaskAppLogger)
return flaskAppLogger
class LoggerSingleton(metaclass=ThreadSafeSingleton):
curLog = ""
def __init__(self):
self.curLog = init_logger(settings)
# Note: during __init__, AVOID use log, otherwise will deadlock
# log.info("LoggerSingleton __init__: curLog=%s", self.curLog)
print("LoggerSingleton __init__: curLog=%s" % self.curLog)
logSingleton = LoggerSingleton()
log = logSingleton.curLog
log.info("LoggerSingleton inited, logSingleton=%s", logSingleton) # <factory.LoggerSingleton object at 0x10cbcafd0>
log.info("log=%s", log) # <Logger RobotQA (DEBUG)>
# # debug for singleton log
# log2 = LoggerSingleton()
# print("log2=%s" % log2)
相关配置:
conf/app/settings.py
# Flask app name
FLASK_APP_NAME = "RobotQA"
# Log File
LOG_LEVEL_FILE = logging.DEBUG
LOG_FILE_FILENAME = "logs/" + FLASK_APP_NAME + ".log"
LOG_FORMAT = "[%(asctime)s %(levelname)s %(filename)s:%(lineno)d %(funcName)s] %(message)s"
LOG_FILE_MAX_BYTES = 2 * 1024 * 1024
LOG_FILE_BACKUP_COUNT = 10
# Log Console
LOG_LEVEL_CONSOLE = logging.INFO
LOG_CONSOLE_DATA_FORMAT = ‘%Y%m%d %I:%M:%S’
转载请注明:在路上 » 【已解决】Flask中logging的单例初始化出错:AttributeError: ‘Formatter’ object has no attribute find