Registro de flask: No se puede hacer que escriba en un archivo


Bien, aquí está el código donde configuré todo:

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')

Lo que sucede es

  1. error.log se crea
  2. Nunca se le escribe nada
  3. A pesar de no agregar un StreamHandler y establecer debug en false, todavía obtengo todo en STDOUT (esto podría ser correcto, pero aún parece raro)

¿Estoy totalmente fuera de aquí en algún lugar o qué está sucediendo?

Author: fleshgolem, 2013-07-19

6 answers

¿Por qué no hacerlo así:

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")

Si ahora inicia su aplicación, verá ese error.registro contiene:

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

Para más información, visite http://docs.python.org/2/howto/logging.html

Bien, como insistes en que no puedes tener dos manejadores con el método que te mostré, agregaré un ejemplo que lo deja bastante claro. Primero, agregue este código de registro a su principal:

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

Ahora también agregamos algún código de depuración, para que veamos que nuestra configuración obras:

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

Todo lo que queda es el "registro.conf " program. Usemos eso:

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]

Esta configuración es más complicada de lo necesario, pero también muestra algunas características del módulo de registro.

Ahora, cuando ejecutamos nuestra aplicación, vemos esta salida (werkzeug - y 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/

También tenga en cuenta que se utilizó el formateador personalizado con el "HI".

Ahora mira los "errores.archivo "log". Contiene:

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/
 58
Author: HolgerSchurig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-22 14:38:53

Ok, mi fracaso se derivó de dos conceptos erróneos:

1) Flask aparentemente ignorará todo su registro personalizado a menos que se esté ejecutando en modo de producción

2) debug=False no es suficiente para que se ejecute en modo de producción. Tienes que envolver la aplicación en cualquier tipo de servidor WSGI para hacerlo

Después de iniciar la aplicación desde el servidor WSGI de gevent (y mover la inicialización del registro a un lugar más apropiado) todo parece funcionar bien

 12
Author: fleshgolem,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-22 12:27:45

La salida que ve en la consola de su aplicación es del registrador Werkzeug subyacente al que se puede acceder a través del registro.getLogger ("werkzeug").

Su registro puede funcionar tanto en el desarrollo como en la liberación agregando también controladores a ese registrador, así como al Flask.

Más información y código de ejemplo: Escribir solicitudes de Flask en un Registro de Acceso.

 8
Author: AsksAnyway,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-19 18:20:58

No me gustaron las otras respuestas, así que me mantuve en ello y parece que tuve que hacer mi configuración de registro DESPUÉS de que Flask hiciera su propia configuración.

@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)

Mi aplicación está estructurada como

/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

Siguiendo estas instrucciones http://flask.pocoo.org/docs/0.10/patterns/packages/

 3
Author: David,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-23 21:11:30

Esto funciona:

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)
 2
Author: nicodjimenez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-17 13:36:07

¿Por qué no echar un vistazo en el código y ver...

El módulo en el que aterrizamos es flask.logging.py, que define una función llamada create_logger(app). La inspección de esa función dará algunas pistas sobre los posibles culpables al solucionar problemas de registro con Flask.

EDITAR: esta respuesta estaba destinada a Matraz antes de la versión 1. El módulo flask.logging.py ha cambiado considerablemente desde entonces. La respuesta todavía ayuda con algunas advertencias generales y consejos sobre el registro de python, pero tenga en cuenta que algunos de Las peculiaridades de Flask a este respecto se han abordado en la versión 1 y podrían dejar de aplicarse.

La primera causa posible de conflictos en esa función es esta línea:

logger = getLogger(app.logger_name)

Veamos por qué:

La variable app.logger_name se establece en el método Flask.__init__() al valor de import_name, que es a su vez el parámetro receptor de Flask(__name__). Es decir app.logger_name se le asigna el valor de __name__, que probablemente será el nombre de su paquete principal, vamos a llamarlo para este ejemplo "awesomeapp".

Ahora, imagine que decidió configurar y crear su propio registrador manualmente. ¿Cuáles crees que son las posibilidades de que si su proyecto se llama "awesomeapp" también utilizaría ese nombre para configurar su registrador, creo que es bastante probable.

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)

Tiene sentido hacer esto... excepto por algunos problemas.

Cuando la propiedad Flask.logger se invoca por primera vez, a su vez llamará a la función flask.logging.create_logger() y las siguientes acciones sigue:

logger = getLogger(app.logger_name)

¿Recuerdas cómo nombraste a tu registrador después del proyecto y cómo app.logger_name comparte ese nombre también? Lo que sucede en la línea de código anterior es que la función logging.getLogger() ahora ha recuperado su registrador creado anteriormente y las siguientes instrucciones están a punto de meterse con él de una manera que le hará rascarse la cabeza más tarde. Por ejemplo

del logger.handlers[:]

Poof, acaba de perder todos los controladores que puede haber registrado previamente con su registrador.

Otras cosas que suceder dentro de la función, sin entrar mucho en detalles. Crea y registra dos logging.StreamHandler los objetos que pueden escupir a sys.stderr y/o Response objetos. Uno para el nivel de registro 'debug' y otro para 'production'.

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)

Con los detalles anteriores a la luz, debería quedar más claro por qué nuestro registrador y manejadores configurados manualmente se comportan mal cuando Flask se involucra. Sin embargo, la nueva información nos da nuevas opciones. Si aún desea mantener controladores separados, el enfoque más simple es: nombre su registrador a algo diferente del proyecto (por ejemplo, my_logger = getLogger('awesomeapp_logger')). Otro enfoque, si desea ser consistente con los protocolos de registro en Flask, es registrar un objeto logging.FileHandler en Flask.logger usando un enfoque similar a Flask.

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')
 2
Author: Michael Ekoka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-13 00:14:55