Flask日志记录 - 无法将其写入文件

时间:2013-07-19 09:46:46

标签: python logging python-2.7 flask

好的,这是我设置所有内容的代码:

if __name__ == '__main__':
    app.debug = False

    applogger = app.logger

    file_handler = FileHandler("error.log")
    file_handler.setLevel(logging.DEBUG)

    applogger.setLevel(logging.DEBUG)
    applogger.addHandler(file_handler)

    app.run(host='0.0.0.0')

会发生什么

  1. 创建了error.log
  2. 没有写任何东西
  3. 尽管没有添加StreamHandler并将debug设置为false,我仍然可以将所有内容都添加到STDOUT(这可能是正确的,但仍然看起来很奇怪)
  4. 我完全离开这里或者发生了什么事?

7 个答案:

答案 0 :(得分:75)

为什么不这样做:

if __name__ == '__main__':
    init_db()  # or whatever you need to do

    import logging
    logging.basicConfig(filename='error.log',level=logging.DEBUG)

    app.run(host="0.0.0.0")

如果您现在启动应用程序,您将看到error.log包含:

INFO:werkzeug: * Running on http://0.0.0.0:5000/

有关详细信息,请访问http://docs.python.org/2/howto/logging.html

好的,因为你坚持认为你不能用我向你展示的方法使用两个处理程序,我将添加一个使这一点非常清楚的例子。首先,将此日志记录代码添加到main:

import logging, logging.config, yaml
logging.config.dictConfig(yaml.load(open('logging.conf')))

现在还添加一些调试代码,以便我们看到我们的设置有效:

logfile    = logging.getLogger('file')
logconsole = logging.getLogger('console')
logfile.debug("Debug FILE")
logconsole.debug("Debug CONSOLE")

剩下的就是“logging.conf”程序。让我们用它:

version: 1
formatters:
  hiformat:
    format: 'HI %(asctime)s - %(name)s - %(levelname)s - %(message)s'
  simple:
    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: hiformat
    stream: ext://sys.stdout
  file:
    class: logging.FileHandler
    level: DEBUG
    formatter: simple
    filename: errors.log
loggers:
  console:
    level: DEBUG
    handlers: [console]
    propagate: no
  file:
    level: DEBUG
    handlers: [file]
    propagate: no
root:
  level: DEBUG
  handlers: [console,file]

此配置比需要的更复杂,但它还显示了日志记录模块的一些功能。

现在,当我们运行我们的应用程序时,我们会看到此输出(werkzeug-和console-logger):

HI 2013-07-22 16:36:13,475 - console - DEBUG - Debug CONSOLE
HI 2013-07-22 16:36:13,477 - werkzeug - INFO -  * Running on http://0.0.0.0:5000/

另请注意,使用了带有“HI”的自定义格式化程序。

现在查看“errors.log”文件。它包含:

2013-07-22 16:36:13,475 - file - DEBUG - Debug FILE
2013-07-22 16:36:13,477 - werkzeug - INFO -  * Running on http://0.0.0.0:5000/

答案 1 :(得分:13)

好吧,我的失败源于两个误解:

1)Flask显然会忽略所有自定义日志记录,除非它在生产模式下运行

2)debug = False不足以让它在生产模式下运行。您必须将应用程序包装在任何类型的WSGI服务器中才能执行此操作

从gevent的WSGI服务器启动应用程序(并将日志记录初始化移动到更合适的位置)后,一切似乎都正常工作

答案 2 :(得分:9)

您在应用控制台中看到的输出来自底层的Werkzeug记录器,可以通过logging.getLogger('werkzeug')访问。

您的日志记录也可以在开发和发布中运行,还可以为该记录器和Flask添加处理程序。

更多信息和示例代码:Write Flask Requests to an Access Log

答案 3 :(得分:3)

我不喜欢其他的答案,所以我坚持下去,看起来我必须在Flask完成它自己的设置之后制作我的日志配置。

@app.before_first_request
def initialize():

    logger = logging.getLogger("your_package_name")
    logger.setLevel(logging.DEBUG)
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    formatter = logging.Formatter(
    """%(levelname)s in %(module)s [%(pathname)s:%(lineno)d]:\n%(message)s"""
    )
    ch.setFormatter(formatter)
    logger.addHandler(ch)

我的应用程序的结构类似于

/package_name
    __main__.py <- where I put my logging configuration
    __init__.py <- conveniance for myself, not necessary
    /tests
    /package_name <- Actual flask app
    __init__.py
    /views
    /static
    /templates
    /lib

遵循这些指示http://flask.pocoo.org/docs/0.10/patterns/packages/

答案 4 :(得分:3)

为什么不深入研究代码,看看......

我们登陆的模块是flask.logging.py,它定义了一个名为create_logger(app)的函数。在解决Flask的日志记录问题时,检查该功能将为潜在的罪魁祸首提供一些线索。

编辑:这个答案适用于版本1之前的Flask。flask.logging.py模块从那时起发生了很大的变化。答案仍然有助于对python日志记录提出一些一般性的警告和建议,但请注意,Flask在这方面的一些特性已在版本1中得到解决,可能不再适用。

该函数中第一个可能的冲突原因是这一行:

logger = getLogger(app.logger_name)

让我们看看原因:

变量app.logger_nameFlask.__init__()方法中设置为import_name的值,Flask(__name__)本身就是app.logger_name的接收参数。这是__name__被赋予my_logger = logging.getLogger('awesomeapp') # doesn't seem like a bad idea fh = logging.FileHandler('/tmp/my_own_log.log') my_logger.setLevel(logging.DEBUG) my_logger.addHandler(fh) 的值,这可能是您的主要包裹的名称,在这个例子中,我们称之为“awesomeapp&#39;。” p>

现在,假设您决定手动配置和创建自己的记录器。您认为如果您的项目被命名为&#34; awesomeapp&#34;您也可以使用该名称来配置您的记录器,我认为这很可能。

Flask.logger

这样做是有意义的......除了一些问题。

第一次调用flask.logging.create_logger()属性时,它将依次调用函数logger = getLogger(app.logger_name) ,然后将执行以下操作:

app.logger_name

还记得你在项目后如何命名你的记录器以及logging.getLogger()如何分享这个名字?在上面的代码行中发生的是,函数del logger.handlers[:] 现在已经检索到您之前创建的记录器,并且以下说明将以一种让您稍后搔痒的方式将其弄乱。例如

logging.StreamHandler

噗,您刚丢失了之前在记录器中注册过的所有处理程序。

在函数中发生的其他事情,没有太多细节。它创建并注册两个可以向sys.stderr和/或Response对象吐出的class DebugLogger(Logger): def getEffectiveLevel(self): if self.level == 0 and app.debug: return DEBUG return Logger.getEffectiveLevel(self) class DebugHandler(StreamHandler): def emit(self, record): if app.debug and _should_log_for(app, 'debug'): StreamHandler.emit(self, record) class ProductionHandler(StreamHandler): def emit(self, record): if not app.debug and _should_log_for(app, 'production'): StreamHandler.emit(self, record) debug_handler = DebugHandler() debug_handler.setLevel(DEBUG) debug_handler.setFormatter(Formatter(DEBUG_LOG_FORMAT)) prod_handler = ProductionHandler(_proxy_stream) prod_handler.setLevel(ERROR) prod_handler.setFormatter(Formatter(PROD_LOG_FORMAT)) logger.__class__ = DebugLogger logger.addHandler(debug_handler) logger.addHandler(prod_handler) 个对象。一个用于日志级别&#39; debug&#39;另一个用于生产&#39;。

my_logger = getLogger('awesomeapp_logger')

有了上述细节,当Flask参与其中时,为什么我们的手动配置的记录器和处理程序行为异常会更清楚。新信息为我们提供了新的选择。如果您仍想保留单独的处理程序,最简单的方法是将记录器命名为与项目不同的名称(例如logging.FileHandler)。如果您希望与Flask中的日志记录协议保持一致,另一种方法是使用与Flask类似的方法在Flask.logger上注册import logging def set_file_logging_handler(app): logging_path = app.config['LOGGING_PATH'] class DebugFileHandler(logging.FileHandler): def emit(self, record): # if your app is configured for debugging # and the logger has been set to DEBUG level (the lowest) # push the message to the file if app.debug and app.logger.level==logging.DEBUG: super(DebugFileHandler, self).emit(record) debug_file_handler = DebugFileHandler('/tmp/my_own_log.log') app.logger.addHandler(debug_file_handler) app = Flask(__name__) # the config presumably has the debug settings for your app app.config.from_object(config) set_file_logging_handler(app) app.logger.info('show me something') 对象。

/*
 *  this is used to perturb given vector 'direction' by changing it by angle not more than 'angle' vector from 
 *  base direction. Used to provide errors for player playing algorithms 
 * 
 */ 
Vector3 perturbDirection( Vector3 direction, float angle ) {
    // division by zero protection 
    if( Mathf.Approximately( direction.z, 0f )) {
        direction.z = 0.0001f; 
    }
    // 1 get some orthogonal vector to direction ( solve direction and orthogonal dot product = 0, assume x = 1, y = 1, then z = as below )) 
    Vector3 orthogonal = new Vector3( 1f, 1f, - ( direction.x + direction.y ) / direction.z );
    // 2 get random vector from circle on flat orthogonal to direction vector. get full range to assume all cone space randomization (-180, 180 )
    float orthoAngle = UnityEngine.Random.Range( -180f, 180f );
    Quaternion rotateTowardsDirection = Quaternion.AngleAxis( orthoAngle, direction );
    Vector3 randomOrtho = rotateTowardsDirection * orthogonal;
    // 3 rotate direction towards random orthogonal vector by vector from our available range 
    float perturbAngle = UnityEngine.Random.Range( 0f, angle );   // range from (0, angle), full cone cover guarantees previous (-180,180) range   
    Quaternion rotateDirection = Quaternion.AngleAxis( perturbAngle, randomOrtho );
    Vector3 perturbedDirection = rotateDirection * direction;
    return perturbedDirection;
}

答案 5 :(得分:2)

这有效:

if __name__ == '__main__':
    import logging
    logFormatStr = '[%(asctime)s] p%(process)s {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s'
    logging.basicConfig(format = logFormatStr, filename = "global.log", level=logging.DEBUG)
    formatter = logging.Formatter(logFormatStr,'%m-%d %H:%M:%S')
    fileHandler = logging.FileHandler("summary.log")
    fileHandler.setLevel(logging.DEBUG)
    fileHandler.setFormatter(formatter)
    streamHandler = logging.StreamHandler()
    streamHandler.setLevel(logging.DEBUG)
    streamHandler.setFormatter(formatter)
    app.logger.addHandler(fileHandler)
    app.logger.addHandler(streamHandler)
    app.logger.info("Logging is set up.")
    app.run(host='0.0.0.0', port=8000, threaded=True)

答案 6 :(得分:0)

记录快速入门

-此代码不适用于一个类/或导入中的多个日志文件

import logging
import os # for Cwd path 
path = os.getcwd()


logFormatStr = '%(asctime)s  %(levelname)s - %(message)s'
logging.basicConfig(filename=path + '\logOne.log', format=logFormatStr, level=logging.DEBUG), logging.info('default message')

用于多个日志文件

使用logging.getLogger()方法创建日志记录实例---

  1. 每个记录器文件需要一个记录实例
  2. 我们可以创建多个日志文件,但不能使用同一实例

使用名称或Hardcore_String创建新的记录器实例 ----首选(名称),它将在调用时指定确切的类

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

日志类型-信息,调试,错误,严重,警告

调试 ----详细信息,通常仅在诊断问题时才有用。

INFO ----确认一切正常。

警告 ----表明发生了意外情况,或在不久的将来出现了某些问题(例如“磁盘空间不足”)。该软件仍按预期运行。

错误 ----由于存在更严重的问题,该软件无法执行某些功能。

CRITICAL ----严重错误,表明程序本身可能无法继续运行。

创建新格式器

format = logging.Formatter('%(asctime)s %(levelname)s - %(message)s')

创建新文件处理程序

file_handel = logging.FileHandler(path + '\logTwo.log')

将格式设置为FileHandler并将file_handler添加到日志记录实例[logger]

file_handel.setFormatter(format)
logger.addHandler(file_handel)

向具有单独设置级别的logOne.log文件和logTwo.log添加消息

logger.info("message for logOne")
logging.debug(" message for logTwo")

for more details