recorrer leer filtrar filas datos data con como columnas cargar archivo python logging filter

python - leer - pandas filtrar filas



¿Cuál es la forma correcta de filtrar diferentes registradores utilizando el registro de Python? (1)

Mi propósito es hacer un registro de múltiples módulos con filtrado jerárquico

la forma en que lo propone el autor de registro Vinay Sajip, al menos, supongo

Puedes saltar a " Cómo quiero que funcione ".

Desafortunadamente, aprendí muy rápidamente que trabajar con las instalaciones de registro es mucho más sofisticado que la mayoría de mi experiencia con el idioma y ya cometí muchos errores comunes (diseño), por ejemplo, tratando de lograr un registro centralizado de clase de registrador único para múltiples módulos o incluso esquemas como ( usando la clase de registrador Python para generar múltiples registros para diferentes niveles de registro ). Pero al parecer hay espacio para un mejor diseño, y podría ser peor gastar tiempo en encontrarlo y aprenderlo. Entonces, ahora mismo, espero estar en el camino correcto. De lo contrario Vinaj deberá aclarar el resto ;-)

Organizo mi registro como esto:

  • Cada módulo de python tiene su propio logger.
  • Cada registrador tiene un nombre igual al módulo donde está definido, por ejemplo, logger = logging.getLogger(__name__)
  • De esta forma, el código dentro de cada módulo puede usar su propio registrador (definido localmente) para enviar mensajes de registro (logging.LogRecord) a los manejadores (logging.Handler)
  • Utilice logging.config para lograr una total flexibilidad en la configuración del registro (Nota: en el código a continuación, simplemente comienzo con basicConfig )

Dicho enfoque es un enfoque recomendado y estoy de acuerdo con sus posibles ventajas. Por ejemplo, puedo activar / desactivar DEBUG de bibliotecas externas utilizando nombres de módulos completamente calificados (la jerarquía de nombres que ya existe en el código).

Ahora, para tener un mayor nivel de control, quiero usar logging. Clase de filtro, para poder filtrar (permitir) solo un subárbol seleccionado dentro de la jerarquía de registradores.

Todo esto está bien, pero el filtrado como se describe aquí

Filter instances are used to perform arbitrary filtering of LogRecords. Loggers and Handlers can optionally use Filter instances to filter records as desired. The base filter class only allows events which are below a certain point in the logger hierarchy. For example, a filter initialized with "A.B" will allow events logged by loggers "A.B", "A.B.C", "A.B.C.D", "A.B.D" etc. but not "A.BB", "B.A.B" etc. If initialized with the empty string, all events are passed.

Todavía no está funcionando para mí.

Mi conjetura es que mi falta de comprensión de los detalles detrás de la propagación de LogRecords es la fuente del problema. Antes de saltar al código, quiero mostrar aquí un diagrama de flujo (del tutorial del libro de cocina que al principio no pude descubrir de inmediato):

Código de ejemplo

Comienzo con dos módulos de ejemplo, cada uno usa su propio registrador llamado:

bar.py:

import logging logger = logging.getLogger(__name__) def bar(): logger.info(''hello from '' + __name__)

foo.py:

import logging from bar import bar, logger as bar_logger logger = logging.getLogger(''foo'') def foo(): logger.info(''hello from foo'') if __name__ == ''__main__'': # Trivial logging setup. logging.basicConfig( level=logging.INFO, format=''%(asctime)s %(name)-20s %(levelname)-8s %(message)s'', datefmt=''%m-%d %H:%M'' ) # Do some work. foo() bar()

El registro se construye primero con logging.basicConfig (el registrador de raíz, que se creó después de que el import logging __main__ obtiene un controlador de flujo adjunto, para que tengamos una consola), está habilitado (respectivo Logger.disabled = False) y ambos registradores de módulos bar y foo se propagan al registrador raíz (así que tenemos tres registradores en total).

print logger print bar_logger print logging.root # Prints #<logging.Logger object at 0x7f0cfd520790> #<logging.Logger object at 0x7f0cfd55d710> #<logging.RootLogger object at 0x7f0cfd520550>

El caso de uso real es cuando bar es una biblioteca externa que quiero silenciar (filtrar).

Cómo funciona, pero "a mí" no me gusta.

# Don''t like it bar_logger.addFilter(logging.Filter(''foo'')) # Do some work. foo() bar()

solo impresiones

06-24 14:08 foo INFO hello from foo

Como quiero que funcione

Quiero filtrarlo centralmente, es decir, en mi root logger sin la necesidad de importar todos los loggers de todos los módulos externos.

logging.root.addFilter(logging.Filter(''foo''))

huellas dactilares

06-24 14:17 foo INFO hello from foo 06-24 14:17 bar INFO hello from bar

Debe haber algún error obvio / estúpido que echo de menos: no quiero ningún mensaje del bar logger. Oye, pero ¿cuál es una mejor manera de encontrarlo que resumir todo sobre SO, amigos? ;-)

Intentaré encontrar la manera en que bar_logger espera la decisión del root logger, antes de emitir algo. Solo espero que así sea como se supone que funcione en primer lugar.


Solución

Agregue el filtro al controlador en lugar del registrador:

handler.addFilter(logging.Filter(''foo''))

Explicación

En el diagrama de flujo que publicó, observe que hay dos diamantes:

  • ¿Un filtro adjunto al registrador rechaza el registro?
  • ¿Un filtro adjunto a hander rechaza el registro?

Por lo tanto, obtienes dos cambios al rechazar un LogRecord. Si adjunta el filtro al registrador raíz, pero inicia LogRecord a través de, digamos, foo o bar loggers, entonces LogRecord no se filtra porque LogRecord pasa a través de foo o bar loggers libremente y el filtro del registrador raíz nunca entra jugar. (Mira el diagrama de flujo de nuevo.)

En contraste, el StreamHandler definido por basicConfig es capaz de filtrar cualquier LogRecord que se le pase.

Entonces: agregue el filtro al controlador en lugar del registrador:

# foo.py import logging import bar logger = logging.getLogger(''foo'') def foo(): logger.info(''hello from foo'') if __name__ == ''__main__'': # Trivial logging setup. logging.basicConfig( level=logging.INFO, format=''%(asctime)s %(name)-20s %(levelname)-8s %(message)s'', datefmt=''%m-%d %H:%M'') for handler in logging.root.handlers: handler.addFilter(logging.Filter(''foo'')) foo() bar.bar()

rendimientos

06-24 09:17 foo INFO hello from foo

Si desea permitir el registro de registradores cuyo nombre comience con foo o bar , pero no de ningún otro registrador, puede crear un filtro de lista blanca como este:

import logging foo_logger = logging.getLogger(''foo'') bar_logger = logging.getLogger(''bar'') baz_logger = logging.getLogger(''baz'') class Whitelist(logging.Filter): def __init__(self, *whitelist): self.whitelist = [logging.Filter(name) for name in whitelist] def filter(self, record): return any(f.filter(record) for f in self.whitelist) logging.basicConfig( level=logging.INFO, format=''%(asctime)s %(name)-20s %(levelname)-8s %(message)s'', datefmt=''%m-%d %H:%M'') for handler in logging.root.handlers: handler.addFilter(Whitelist(''foo'', ''bar'')) foo_logger.info(''hello from foo'') # 06-24 09:41 foo INFO hello from foo bar_logger.info(''hello from bar'') # 06-24 09:41 bar INFO hello from bar baz_logger.info(''hello from baz'') # No output since Whitelist filters if record.name not begin with ''foo'' or ''bar''

Y de manera similar, podría incluir en la lista negra los nombres de los registradores con esto:

class Blacklist(Whitelist): def filter(self, record): return not Whitelist.filter(self, record)