2008-12-04 24 views
40

Estoy desarrollando una aplicación de Django, y estoy tratando de usar el módulo de registro de Python para el registro de error/rastreo. Idealmente, me gustaría tener diferentes registradores configurados para diferentes áreas del sitio. Hasta ahora tengo todo esto funcionando, pero una cosa me tiene a mí rascándome la cabeza.Registro de Python en Django

Tengo el registrador de raíz va a sys.stderr, y he configurado otro registrador para escribir en un archivo. Esto está en mi archivo settings.py:

sviewlog = logging.getLogger('MyApp.views.scans') 
view_log_handler = logging.FileHandler('C:\\MyApp\\logs\\scan_log.log') 
view_log_handler.setLevel(logging.INFO) 
view_log_handler.setFormatter(logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s %(message)s')) 
sviewlog.addHandler(view_log_handler) 

Parece bastante simple. Sin embargo, este es el problema: todo lo que escribo en el sviewlog se escribe en el archivo de registro dos veces. El registrador de raíz solo lo imprime una vez. Es como addHandler() se llama dos veces. Y cuando paso mi código a través de un depurador, esto es exactamente lo que veo. El código en settings.py se ejecuta dos veces, por lo que se crean y añaden dos FileHandlers a la misma instancia del registrador. ¿Pero por qué? ¿Y cómo me las arreglo?

¿Alguien puede decirme qué está pasando aquí? Intenté mover el código de creación de instancias de logger/gestor de sviewlog al archivo en el que se usa (ya que en realidad me parece el lugar adecuado), pero tengo el mismo problema allí. La mayoría de los ejemplos que he visto en línea usan solo el registrador de raíz, y preferiría tener varios registradores.

Respuesta

30

Permítanme responder mi propia pregunta. El problema subyacente aquí es que settings.py se importa dos veces, o incluso más (consulte here). (Todavía no entiendo por qué es esto. Tal vez algún experto de Django podría explicarme eso). Esto también parece ser cierto para algunos otros módulos. En este punto, no creo que sea sensato hacer suposiciones sobre cuántas veces se importará settings.py. En realidad, tales suposiciones no son seguras en general. He tenido este código en lugares que no sean settings.py, y los resultados son similares.

Tienes que codificar esto. Es decir, debe verificar su registrador para los manejadores existentes antes de agregar controladores adicionales a él. Esto es un poco feo porque es perfectamente razonable tener múltiples manejadores, incluso del mismo tipo, conectados a un registrador. Hay algunas soluciones para lidiar con esto. Una de ellas es verificar la propiedad de los manipuladores de su objeto logger. Si solo quieres un manejador y tu longitud> 0, entonces no lo agregues. Personalmente, no me gusta esta solución, porque se complica con más controladores.

prefiero algo como esto (gracias a Thomas Guettler):

# file logconfig.py 
if not hasattr(logging, "set_up_done"): 
    logging.set_up_done=False 

def set_up(myhome): 
    if logging.set_up_done: 
     return 
    # set up your logging here 
    # ... 
    logging.set_up_done=True 

debo decir, me gustaría que el hecho de que las importaciones de Django settings.py varias veces estaban mejor documentado. Y me imagino que mi configuración es de alguna manera causa de esta importación múltiple, pero estoy teniendo problemas para descubrir qué está causando el problema y por qué. Tal vez no pude encontrar eso en sus documentos, pero creo que ese es el tipo de cosas que necesita advertir a sus usuarios.

+3

No soy un experto en Django, pero imagino que settings.py se importaría cada vez que se carga un módulo que tiene una declaración de importación con "configuraciones" en él. Cuantos más módulos use settings.py, más veces se importará. –

+2

@HartleyBrody [python caches modules] (http://docs.python.org/release/2.6/reference/simple_stmts.html#the-import-statement) a medida que se cargan, por lo que un módulo determinado se ejecuta solo una vez, independientemente del número de declaraciones de importación en que aparece. –

+1

De acuerdo con las notas de la versión de [Django 1.4] (https://docs.djangoproject.com/en/dev/releases/1.4/#updated-default-project-layout-and-manage-py) este problema puede haber sido abordado . –

1

¿Por qué utilizar Python Logger en lugar de django-logging? Pruébalo, podría resolver tu problema.

http://code.google.com/p/django-logging/wiki/Overview

Por el momento, sólo permitiría a ver el registrador de la raíz, pero seguro que puede escribir en varios registradores.

+5

Como yo lo entiendo, django-registro no tiene cualquier cosa que ver con el registro de archivos separados. Su uso principal es generar mensajes de registro en las páginas de su aplicación, que no es una función que necesito en este momento. Entonces no puedo pensar por qué resolvería mi problema. – Jeff

+1

django-logging ya no se mantiene. Hay una recomendación en la página del proyecto para usar https://github.com/robhudson/django-debug-toolbar para el registro del lado del cliente – michela

14

Difícil comentar sobre su caso específico. Si settings.py se ejecuta dos veces, entonces es normal que obtenga dos líneas por cada registro enviado.

Tuvimos el mismo problema, así que lo configuramos en nuestros proyectos para tener un módulo dedicado al registro. Los módulos tienen un patrón de "módulo único", por lo que solo ejecutaremos el código interesante una vez.

Parece que este:

def init_logging(): 
    stdoutHandler = logging.StreamHandler(sys.stdout) 
    stdoutHandler.setLevel(DEBUG) 
    stdoutHandler.setFormatter(logging.Formatter(LOG_FORMAT_WITH_TIME)) 
    logging.getLogger(LOG_AREA1).addHandler(stdoutHandler) 

logInitDone=False #global variable controlling the singleton. 
if not logInitDone: 
    logInitDone = True 
    init_logging() 

Importación de la log.py la primera vez que va a configurar el registro correctamente.

+1

¿Tengo razón? De esta manera tienes que 'importar el registro, el registro' que escribir smthng como 'logger = logging.getLogger (LOG_AREA1)' y solo entonces úselo como 'logger.info (bla-bla-bla)'? ¿Es mejor devolver el registrador de init_logging() ('return logging.getLogger ...') y configurarlo en el módulo var? Di 'logger = init_logging()'. De esta forma puede importar log.py y simplemente comenzar a utilizarlo 'import log; log.logger.info (bla-bla-bla) '? – NilColor

+1

¡Oh! Y, por supuesto, puede usar el registrador de nivel de módulo si desea: 'importar registro; module_logger = log.logger.getLogger (__ name __) ' – NilColor

+0

Todo este material de 'logInitDone' es inútil. Míralo, cuando se ejecuta dos veces 'logInitDone' siempre se establece en' False' para que 'init_logging()' todavía se llame dos veces. Esto solo funciona porque el módulo solo se ejecuta una vez mientras se importa como se explica en [esta respuesta] (http://stackoverflow.com/questions/10936709/why-does-a-python-module-act-like-a-singleton) – RickyA

0

Una manera hackosa, pero puede tratar de poner el código de registro dentro de un admin.py. Se supone que debe importarse solo una vez.

Alternativamente; ¿primero puede verificar si existe el registro MyApp.views.scans? Si existe (tal vez se genera un error), simplemente puede omitir la creación (y, por lo tanto, no agregar el controlador de nuevo). Una manera más limpia pero no lo he intentado sin embargo.

También debe haber un lugar más apropiado para poner este código (__init__.py?). settings.py es para la configuración.

4

Puede solucionar su problema comprobando el número de controladores cuando está realizando su inicialización.

def init_logging(): 
    stdoutHandler = logging.StreamHandler(sys.stdout) 
    stdoutHandler.setLevel(DEBUG) 
    stdoutHandler.setFormatter(logging.Formatter(LOG_FORMAT_WITH_TIME)) 
    logger = logging.getLogger(LOG_AREA1) 
    if len(logger.handlers) < 1: 
     logger.addHandler(stdoutHandler) 

No creo que esta sea una buena manera de manejarlo. Personalmente, para iniciar sesión en django con el módulo de registro de Python, creo un registrador en views.py para cada aplicación que me interesa, luego tomo el registrador en cada función de visualización.

from django.http import HttpResponse 
from magic import makeLogger 
from magic import getLogger 

makeLogger('myLogName', '/path/to/myLogName.log') 
def testLogger(request): 
    logger = getLogger('myLogName') 
    logger.debug('this worked') 
    return HttpResponse('TEXT, HTML or WHATEVER') 

Este es un muy buen artículo sobre la depuración de Django y cubre algunas de registro: http://simonwillison.net/2008/May/22/debugging/

3

Para responder a la pregunta de por qué lo hace "importaciones Django settings.py varias veces": no lo hace.

Probablemente esté ejecutando un servidor web multiproceso/multiproceso que crea varios subinterpretadores de python, donde cada uno de ellos importa el código de su aplicación django una vez.

Pruébelo en el servidor de prueba django y verá que la configuración no se importa muchas veces.

Hace algún tiempo, diseñé un sencillo singleton (versión idiomática de borg pitón para ser más preciso) con mi primera aplicación django/apache, antes de darme cuenta de que sí, tenía más de una instancia de mi singleton creado. ..

6

`` para responder a la pregunta de por qué lo hace "las importaciones de Django settings.py varias veces":. no lo hace ''

en realidad, no se importan dos veces (saltar más allá del primer fragmento de código para acéptelo pero una buena lectura si tiene tiempo):

http://blog.dscpl.com.au/2010/03/improved-wsgi-script-for-use-with.html

PD: Perdón por reactivar un hilo viejo.

24

A partir de la versión 1.3, Django usa el registro de python estándar, configurado con la configuración LOGGING (documentada aquí: 1.3, dev).

Django logging reference: 1.3, dev.

3

También puede utilizar un middleware run-once para obtener un efecto similar, sin las variables privadas. Tenga en cuenta que esto solo configurará el registro para las solicitudes web; necesitará encontrar una solución diferente si desea iniciar sesión en su shell o ejecuta el comando.

from django.conf import settings 
from django.core.exceptions import MiddlewareNotUsed 
import logging 
import logging.handlers 
import logging.config 

__all__ = ('LoggingConfigMiddleware',) 


class LoggingConfigMiddleware: 
    def __init__(self): 
     '''Initialise the logging setup from settings, called on first request.''' 
     if hasattr(settings, 'LOGGING'): 
      logging.config.dictConfig(settings.LOGGING) 
     elif getattr(settings, 'DEBUG', False): 
      print 'No logging configured.' 
     raise MiddlewareNotUsed('Logging setup only.') 
11

La reactivación de un viejo hilo, pero yo estaba experimentando mensajes duplicados durante el uso de Django 1.3 Python registro con el dictConfig format.

El disable_existing_loggers se deshace del problema manejador/registro duplicado con múltiples cargas settings.py, pero todavía se puede obtener mensajes de registro duplicados si no se especifica el booleano propagate apropiadamente en el logger específica. A saber, asegúrese de establecer propagate=False para registradores secundarios. Por ejemplo,

'loggers': { 
    'django': { 
     'handlers':['null'], 
     'propagate': True, 
     'level':'INFO', 
    }, 
    'django.request': { 
     'handlers': ['console'], 
     'level': 'ERROR', 
     'propagate': False, 
    }, 
    'project': { 
     'handlers': ['console', 'project-log-file'], 
     'level': 'DEBUG', 
     'propagate': True, 
    }, 
    'project.customapp': { 
     'handlers': ['console', 'customapp-log-file'], 
     'level': 'DEBUG', 
     'propagate': False, 
    }, 
} 

Aquí, project.customapp conjuntos propagate=False de modo que no serán atrapados por el registrador project también. Los Django logging docs son excelentes, como siempre.

+1

Esto se aplica a Django 1.4 también. –

+1

Del mismo modo para 1.6> BTW, gracias por el propagate = False tip, eso resolvió mi problema. –

0

Para añadir a A Lee correos, estados de documentación de registro pitón esto de propagar:

Logger.propagate

Si esto da como resultado false, los mensajes de registro no se pasan por este registrador o por sus registradores de niños a los manejadores de madereros de nivel superior (ancestro). El constructor establece este atributo en 1.

Esto significa que si propagate == False continuación niño registrador no pasará el registro de mensajes a su registrador padre

Cuestiones relacionadas