记录日志,在任何项目中,都是很重要的。在Flask
项目中,即有Flask
提供的logger
能够用来记录log
,也能够经过直接使用Python
的logging
模块自定义logger
来记录。那么这二者是什么关系,又该怎么使用呢?html
Python
的logging
模块python
先看下对于logging
模块的官方介绍git
Loggers have the following attributes and methods. Note that Loggers are never instantiated directly, but always through the module-level functionlogging.getLogger(name)
. Multiple calls togetLogger()
with the same name will always return a reference to the same Logger object.The
name
is potentially a period-separated hierarchical value, likefoo.bar.baz
(though it could also be just plainfoo
, for example). Loggers that are further down in the hierarchical list are children of loggers higher up in the list. For example, given a logger with a name offoo
, loggers with names offoo.bar
,foo.bar.baz
, andfoo.bam
are all descendants offoo
. The logger name hierarchy is analogous to the Python package hierarchy, and identical to it if you organise your loggers on a per-module basis using the recommended constructionlogging.getLogger(__name__)
. That’s because in a module,__name__
is the module’s name in the Python package namespace.github
上面主要告诉咱们三点,flask
logging.getLogger(name)
来获取一个logger
,相同名字的logger
,实际上是同一个logger
。logger
是经过name
进行继承的,好比foo.bar
就是foo
的子logger
。就能够是实现咱们经过配置一个rootLogger
,而后直接使用rootLogger.sublogger
来记录一下内容,而不须要单独再配置一遍。logging.getLogger(__name__)
时,__name__
就是这个模块所在的python package
的namespace
。flask提供的loggerapi
再看下flask中的logging模块:多线程
Flask uses standard Pythonlogging
. All Flask-related messages are logged under the'flask'
logger namespace.Flask.logger
returns the logger named'flask.app'
, and can be used to log messages for your application.Depending on the situation, an extension may choose to log to
app.logger
or its own named logger. Consult each extension’s documentation for details.app
咱们能够知道flask的logger就是一个标准的Python logging,它的命名是flask
。咱们既可使用app.logger
,也能够本身定义一个logger
。
那么如何使用app.logger
呢?
有两种方式:
直接调用
logger = logging.getLogger('flask.app') logger.info('flask.app')
使用Flask
提供的接口
from flask import current_app current_app.logger.info('logged by current_app from main')
这里推荐仍是使用第二种,current_app
是一个单例,能够直接引用到app.logger
。
经过修改app.logger
的name
,能够实现子logger
的继承么?
答案是否认的。
修改app.logger
的name
:
# app/__init__.py app.logger.name = 'app'
而后在子模块中定义一个app.module
的logger
来记录:
from flask import current_app import logging logger = logging.getLogger('app.module') @module.route('/test', methods=['GET']) def test(): logger.info('logged by app.module') current_app.logger.info('logged by current_app.logger')
输出结果:
2019-02-01 10:56:01,877 - Thread-2 - app - INFO - logged by current_app.logger
只有current_app.logger
的输出。
修改app.logger
的name
是否是无效呢?
咱们把子模块中的logger
的name
修改成flask.app.module
:
from flask import current_app import logging logger = logging.getLogger('flask.app.module') @module.route('/test', methods=['GET']) def test(): logger.info('logged by flask.app.module') current_app.logger.info('logged by current_app.logger')
输出结果:
2019-02-01 11:00:10,944 - Thread-2 - flask.app.module - INFO - logged by flask.app.module 2019-02-01 11:00:10,946 - Thread-2 - app - INFO - logged by current_app.logger
两个logger
均输出了。
可见,经过修改app.logger.name
能够在记录的时候显示为咱们设置的名称,但实际上这个logger
仍是flask.app
。
__name__
的使用
在自定义logger
的状况下,为了方便起见,咱们能够利用__name__
这个参数。
前面说到:当使用logging.getLogger(__name__)
时,__name__
就是这个模块所在的python package
的namespace
。
通常Flask
的工厂模式结构以下:
app ├── __init__.py ├── main │ ├── __init__.py │ ├── functions.py │ └── views.py └── module ├── __init__.py ├── functions.py └── views.py
那么咱们在先在app.__init__
中定义rootLogger
,而后再在app.module.functions.py
中定义子Logger
,均使用logging.getLogger(__name__)
:
# app.__init__.py 初始化rootlogger rootLogger = logging.getLogger(__name__) rootLogger.setLevel(logging.DEBUG) socketHandler = logging.handlers.SocketHandler('localhost',logging.handlers.DEFAULT_TCP_LOGGING_PORT) rootLogger.addHandler(socketHandler) rootLogger.setLevel(logging.DEBUG) # app.module.functions.py import logging logger = logging.getLogger(__name__) def record_from_logging(): logger.info('logged by logging from __name__')
输出:
2019-02-01 12:18:34,743 - MainThread - app - INFO - register root logger by __name__ 2019-02-01 12:19:24,954 - Thread-4 - app.module.functions - INFO - logged by logging from __name__
能够发现输出的logger.name
就是所在的文件目录,logger
之间的继承关系与整个程序包保持一致。
根据上面分析,那么怎么优雅的记录logger
呢?
若是没有对模块进行分logger
记录要求的话。能够直接使用在程序初始化的时候配置app.logger
(能够自行设置logger.name
)。在模块中经过import current_app
来记录:
# app.__init__.py def register_logging(app): app.logger.name = 'app' # logstash_handler stashHandler = logstash.LogstashHandler('app.config.get('ELK_HOST')', 'app.config.get('ELK_PORT')') app.logger.addHandler(stashHandler) # socket_handler socketHandler = logging.handlers.SocketHandler('localhost', logging.handlers.DEFAULT_TCP_LOGGING_PORT) app.logger.addHandler(socketHandler) # app.module.function.py from flask import current_app @module.route('/test', methods=['GET']) def test(): current_app.logger.info('logging someting') return 'logged by current_app.logger'
输出效果:
2019-02-01 13:49:28,998 - Thread-2 - app - INFO - logged by current_app from main 2019-02-01 13:49:38,346 - Thread-3 - app - INFO - logged by current_app of functions
__注意__: 对于current_app.logger
的引用不能经过以下方式,会有RuntimeError
的报错。
from flask import current_app logger = current_app.logger ## 异常 raise RuntimeError(_app_ctx_err_msg) RuntimeError: Working outside of application context. This typically means that you attempted to use functionality that needed to interface with the current application object in some way. To solve this, set up an application context with app.app_context(). See the documentation for more information.
若是但愿按本身的实际需求,对模块进行分logger
记录要求的话。那么建议本身设置logger
。
# app.__init__.py def register_logging(): # set own root logger rootLogger = logging.getLogger(__name__) rootLogger.setLevel(logging.DEBUG) # socketHandler socketHandler = logging.handlers.SocketHandler('localhost',logging.handlers.DEFAULT_TCP_LOGGING_PORT) rootLogger.addHandler(socketHandler) # logstash_handler stashHandler = logstash.LogstashHandler('app.config.get('ELK_HOST')', 'app.config.get('ELK_PORT')') rootLogger.addHandler(stashHandler) rootLogger.setLevel(logging.DEBUG) # app.module.function.py import logging logger = logging.getLogger(__name__) @module.route('/test', methods=['GET']) def test(): logger.info('logging someting') return 'logged by logging module'
输出效果:
2019-02-01 13:49:49,297 - Thread-5 - app.module.views - INFO - logged by flask.app.module 2019-02-01 13:50:01,013 - Thread-7 - app.module.functions - INFO - logged by logging module of functions
完整代码可参考:https://github.com/keejo125/flask_logging_demo
关于python
中logging
的配置可参考官网:
https://docs.python.org/3/lib...
在配置handler
时,常常会但愿日志能够按时间分割(TimedRotatingFileHandler)或者按大小分割(RotatingFileHandler).
可是在flask
项目中,尤为开启多线程以后,在分割日志(doRollover()
)时会有文件读写的异常:
WindowsError: [Error 32]
建议使用SocketHandler,将日志发送给单独的LogServer
来进行二次处理。
简易的接收socketlog
的LogServer
可参考:https://github.com/keejo125/f...
或者如今流行的stashHandler,将日志发送给ELK来进行二次处理。