python - sirve - Registro de Flask-No se puede hacer que escriba en un archivo
para que sirve python flask (6)
¿Por qué no hacer una inmersión 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.
EDIT: esta respuesta fue para Flask antes de la versión 1. El módulo flask.logging.py
ha cambiado considerablemente desde entonces. La respuesta aún ayuda con algunas advertencias generales y consejos sobre el registro de Python, pero tenga en cuenta que algunas de las peculiaridades de Flask en ese sentido se han abordado en la versión 1 y es posible que ya no se apliquen.
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__()
en el valor de import_name
, que a su vez es el parámetro de recepción de Flask(__name__)
. Eso es app.logger_name
se le asigna el valor de __name__
, que probablemente será el nombre de su paquete principal, vamos a llamarlo ''awesomeapp'' para este ejemplo.
Ahora, imagine que decidió configurar y configurar su propio registrador manualmente. ¿Cuál cree que es la probabilidad de que si su proyecto se llama "awesomeapp" también use 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 se invoca la propiedad Flask.logger
por primera vez, a su vez llamará a la función flask.logging.create_logger()
y se producirán las siguientes acciones:
logger = getLogger(app.logger_name)
¿Recuerda cómo llamó a su 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 el registrador creado anteriormente y las siguientes instrucciones están a punto de meterse con ello de una manera que hará que te rasques la cabeza más tarde. Por ejemplo
del logger.handlers[:]
Poof, acabas de perder todos los manejadores que hayas registrado previamente en tu registrador.
Otras cosas que suceden dentro de la función, sin entrar demasiado en detalles. Crea y registra dos objetos logging.StreamHandler
que pueden escupir a sys.stderr
y / u objetos de Response
. Uno para el nivel de registro ''depuración'' y otro para ''producción''.
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é nuestros registradores y manipuladores configurados manualmente se comportan mal cuando se involucra Flask. La nueva información nos da nuevas opciones sin embargo. Si aún desea mantener manejadores separados, el enfoque más simple es nombrar su registrador a algo diferente al proyecto (por ejemplo, my_logger = getLogger(''awesomeapp_logger'')
). Otro enfoque, si desea ser coherente con los protocolos de registro en Flask, es registrar un objeto Flask.logger
en Flask.logger
utilizando un enfoque similar al 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'')
Ok, aquí está el código donde configuro 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 ocurre es
- error.log se crea
- Nada está escrito en él
- A pesar de no agregar un StreamHandler y establecer la depuración en falso, sigo recibiendo todo en STDOUT (esto podría ser correcto, pero aún así parece extraño)
¿Estoy totalmente fuera de aquí o en lo que está sucediendo?
¿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á que error.log contiene:
INFO:werkzeug: * Running on http://0.0.0.0:5000/
Para obtener más información, visite http://docs.python.org/2/howto/logging.html
De acuerdo, como insistes en que no puedes tener dos controladores con el método que te mostré, agregaré un ejemplo que lo aclare. Primero, agrega este código de registro a tu principal:
import logging, logging.config, yaml
logging.config.dictConfig(yaml.load(open(''logging.conf'')))
Ahora también agregue un código de depuración para que veamos que nuestra configuración funciona:
logfile = logging.getLogger(''file'')
logconsole = logging.getLogger(''console'')
logfile.debug("Debug FILE")
logconsole.debug("Debug CONSOLE")
Todo lo que queda es el programa "logging.conf". 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 el archivo "errors.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/
Bien, mi fracaso se originó en dos conceptos erróneos:
1) Aparentemente, Flask ignorará todo el registro personalizado a menos que se ejecute en modo de producción.
2) debug = False no es suficiente para permitir que se ejecute en modo de producción. Debe 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
El resultado que ve en la consola de su aplicación proviene del registrador Werkzeug subyacente al que se puede acceder a través de logging.getLogger (''werkzeug'').
Su registro puede funcionar tanto en el desarrollo como en el lanzamiento al agregar controladores a ese registrador, así como también al Flask.
Más información y código de ejemplo: Escribir solicitudes de frasco en un registro de acceso .
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)
No me gustaron las otras respuestas, así que seguí y parece que tuve que hacer que mi configuración de registro AFTER 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/