setlevel - Python: módulo de registro-globalmente
python logging example (4)
Como no he encontrado una respuesta satisfactoria, me gustaría profundizar un poco en la respuesta a la pregunta para dar una idea del funcionamiento y las intenciones de la biblioteca de logging
, que viene con la biblioteca estándar de Python.
A diferencia del enfoque del OP (póster original), la biblioteca separa claramente la interfaz del registrador y la configuración del propio registrador.
La configuración de los controladores es una prerrogativa del desarrollador de la aplicación que usa su biblioteca.
Eso significa que no debe crear una clase de registrador personalizada y configurar el registrador dentro de esa clase agregando cualquier configuración o lo que sea.
La biblioteca de logging
presenta cuatro componentes: registradores , manejadores , filtros y formateadores .
- Los madereros exponen la interfaz que utiliza directamente el código de la aplicación.
- Los manejadores envían los registros de registro (creados por los registradores) al destino apropiado.
- Los filtros proporcionan una instalación de grano más fino para determinar qué registro registra para producir.
- Los formateadores especifican el diseño de los registros en el resultado final.
Una estructura de proyecto común se ve así:
Project/
|-- .../
| |-- ...
|
|-- project/
| |-- package/
| | |-- __init__.py
| | |-- module.py
| |
| |-- __init__.py
| |-- project.py
|
|-- ...
|-- ...
Dentro de su código (como en module.py ) se refiere a la instancia de registro de su módulo para registrar los eventos en sus niveles específicos.
Una buena convención para usar al nombrar registradores es usar un registrador de nivel de módulo, en cada módulo que utiliza el registro, nombrado de la siguiente manera:
logger = logging.getLogger(__name__)
La variable especial __name__
refiere al nombre de su módulo y se ve como project.package.module
dependiendo de la estructura del código de su aplicación.
module.py (y cualquier otra clase) podría ser esencialmente así:
import logging
...
log = logging.getLogger(__name__)
class ModuleClass:
def do_something(self):
log.debug(''do_something() has been called!'')
¡El registrador en cada módulo propagará cualquier evento al registrador padre que a su vez pasa la información a su manejador adjunto! De forma análoga a la estructura del paquete / módulo de python, el registrador principal está determinado por el espacio de nombres utilizando "nombres de módulo de puntos". Es por eso que tiene sentido inicializar el registrador con la variable especial __name__
(en el ejemplo anterior el nombre coincide con la cadena "project.package.module" ).
Hay dos opciones para configurar el registrador globalmente:
Cree una instancia de un registrador en project.py con el nombre
__package__
que equivale a "proyecto" en este ejemplo y, por lo tanto, es el registrador padre de los registradores de todos los submódulos. Solo es necesario agregar un manejador y formateador apropiado a este registrador.Configure un registrador con un manejador y formateador en el script de ejecución (como main.py ) con el nombre del paquete superior.
Al desarrollar una biblioteca que utiliza el registro, debe tener cuidado de documentar cómo la biblioteca utiliza el registro, por ejemplo, los nombres de los registradores utilizados.
El script de ejecución, como main.py por ejemplo, finalmente podría verse más o menos así:
import logging
from project import App
def setup_logger():
# create logger
logger = logging.getLogger(''project'')
logger.setLevel(logging.DEBUG)
# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(level)
# create formatter
formatter = logging.Formatter(''%(asctime)s [%(levelname)s] %(name)s: %(message)s'')
# add formatter to ch
ch.setFormatter(formatter)
# add ch to logger
logger.addHandler(ch)
if __name__ == ''__main__'' and __package__ is None:
setup_logger()
app = App()
app.do_some_funny_stuff()
El método call log.setLevel(...)
especifica el mensaje de registro de menor gravedad que manejará un registrador, pero no necesariamente el resultado. Simplemente significa que el mensaje se pasa al manejador siempre que el nivel de gravedad del mensaje sea mayor que (o igual) que el establecido. Pero el controlador es responsable de manejar el mensaje de registro (imprimiéndolo o guardándolo, por ejemplo).
Por lo tanto, la biblioteca de logging
ofrece un enfoque estructurado y modular que solo necesita ser explotado de acuerdo con sus necesidades.
Hola, me preguntaba cómo implementar un registrador global que podría usarse en todas partes con su propia configuración:
yo tengo
class customLogger(logging.Logger):
...
en un archivo con sus formateadores y otras cosas. El registrador funciona perfectamente por sí mismo.
Importe este módulo en mi archivo main.py y creo un objeto como este:
self.log = log.customLogger(arguments)
Pero obviamente no puedo acceder a este objeto desde otras partes de mi código. ¿Estoy usando un enfoque equivocado? ¿Hay una mejor manera de hacer esto?
Cree una instancia de customLogger
en su módulo de registro y customLogger
como singleton; solo use la instancia importada, en lugar de la clase.
Puede pasarle una cadena con una subcadena común antes del primer período. Las partes de la cadena separadas por el punto (".") Se pueden usar para diferentes clases / módulos / archivos / etc. Al igual que (específicamente la parte logger = logging.getLogger(loggerName)
):
def getLogger(name, logdir=LOGDIR_DEFAULT, level=logging.DEBUG, logformat=FORMAT):
base = os.path.basename(__file__)
loggerName = "%s.%s" % (base, name)
logFileName = os.path.join(logdir, "%s.log" % loggerName)
logger = logging.getLogger(loggerName)
logger.setLevel(level)
i = 0
while os.path.exists(logFileName) and not os.access(logFileName, os.R_OK | os.W_OK):
i += 1
logFileName = "%s.%s.log" % (logFileName.replace(".log", ""), str(i).zfill((len(str(i)) + 1)))
try:
#fh = logging.FileHandler(logFileName)
fh = RotatingFileHandler(filename=logFileName, mode="a", maxBytes=1310720, backupCount=50)
except IOError, exc:
errOut = "Unable to create/open log file /"%s/"." % logFileName
if exc.errno is 13: # Permission denied exception
errOut = "ERROR ** Permission Denied ** - %s" % errOut
elif exc.errno is 2: # No such directory
errOut = "ERROR ** No such directory /"%s/"** - %s" % (os.path.split(logFileName)[0], errOut)
elif exc.errno is 24: # Too many open files
errOut = "ERROR ** Too many open files ** - Check open file descriptors in /proc/<PID>/fd/ (PID: %s)" % os.getpid()
else:
errOut = "Unhandled Exception ** %s ** - %s" % (str(exc), errOut)
raise LogException(errOut)
else:
formatter = logging.Formatter(logformat)
fh.setLevel(level)
fh.setFormatter(formatter)
logger.addHandler(fh)
return logger
class MainThread:
def __init__(self, cfgdefaults, configdir, pidfile, logdir, test=False):
self.logdir = logdir
logLevel = logging.DEBUG
logPrefix = "MainThread_TEST" if self.test else "MainThread"
try:
self.logger = getLogger(logPrefix, self.logdir, logLevel, FORMAT)
except LogException, exc:
sys.stderr.write("%s/n" % exc)
sys.stderr.flush()
os._exit(0)
else:
self.logger.debug("-------------------- MainThread created. Starting __init__() --------------------")
def run(self):
self.logger.debug("Initializing ReportThreads..")
for (group, cfg) in self.config.items():
self.logger.debug(" ------------------------------ GROUP ''%s'' CONFIG ------------------------------ " % group)
for k2, v2 in cfg.items():
self.logger.debug("%s <==> %s: %s" % (group, k2, v2))
try:
rt = ReportThread(self, group, cfg, self.logdir, self.test)
except LogException, exc:
sys.stderr.write("%s/n" % exc)
sys.stderr.flush()
self.logger.exception("Exception when creating ReportThread (%s)" % group)
logging.shutdown()
os._exit(1)
else:
self.threads.append(rt)
self.logger.debug("Threads initialized.. /"%s/"" % ", ".join([t.name for t in self.threads]))
for t in self.threads:
t.Start()
if not self.test:
self.loop()
class ReportThread:
def __init__(self, mainThread, name, config, logdir, test):
self.mainThread = mainThread
self.name = name
logLevel = logging.DEBUG
self.logger = getLogger("MainThread%s.ReportThread_%s" % ("_TEST" if self.test else "", self.name), logdir, logLevel, FORMAT)
self.logger.info("init database...")
self.initDB()
# etc....
if __name__ == "__main__":
# .....
MainThread(cfgdefaults=options.cfgdefaults, configdir=options.configdir, pidfile=options.pidfile, logdir=options.logdir, test=options.test)
Use logging.getLogger(name)
para crear un registrador global nombrado.
main.py
import log
logger = log.setup_custom_logger(''root'')
logger.debug(''main message'')
import submodule
log.py
import logging
def setup_custom_logger(name):
formatter = logging.Formatter(fmt=''%(asctime)s - %(levelname)s - %(module)s - %(message)s'')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG)
logger.addHandler(handler)
return logger
submodule.py
import logging
logger = logging.getLogger(''root'')
logger.debug(''submodule message'')
Salida
2011-10-01 20:08:40,049 - DEBUG - main - main message
2011-10-01 20:08:40,050 - DEBUG - submodule - submodule message