Enseñarle a iniciar sesión en Python

Prólogo

Este artículo es mi copia de otros, pero personalmente creo que es realmente muy detallado, hay principios y ejemplos, no solo te enseñan cómo usar el registro, sino que también te permiten conocer el principio del registro, es realmente genial, aunque el artículo es muy largo , Tal vez no lo lea en serio, pero este artículo definitivamente puede ayudarlo cuando encuentre problemas, porque también vi este artículo antes, es lo mismo, pero cuando busqué mucho, Cuando no obtuve una respuesta para un artículo similar al procesamiento de registros, después de leer este artículo cuidadosamente, el problema se resolvió y aprendí muchas cosas que no entendía antes. Espero ayudar a todos.

Adjunte la dirección original, si hay alguna infracción, contácteme para eliminar

El peor caso para un desarrollador es descubrir por qué una aplicación desconocida no funciona. A veces, ni siquiera sabe si el sistema se está ejecutando y si es consistente con el diseño original.

Las aplicaciones que se ejecutan en línea son cajas negras y deben rastrearse y monitorearse. La forma más fácil e importante es iniciar sesión. El registro nos permite desarrollar software y, al mismo tiempo, permitir que el programa envíe información mientras el sistema se está ejecutando. Esta información es útil para nosotros y los administradores del sistema.

Al igual que escribir documentación de código para futuros programadores, deberíamos permitir que el nuevo software genere suficientes registros para que los desarrolladores y administradores de sistemas los usen. El registro es una parte clave de los archivos del sistema sobre el estado de ejecución de la aplicación. Al agregar un registro al software para generar una oración, escriba el mismo archivo para el desarrollador y el administrador que mantendrá el sistema en el futuro.

Algunos puristas creen que un desarrollador capacitado casi no necesita un depurador interactivo cuando utiliza el registro y las pruebas. Si no podemos usar registros detallados para explicar la aplicación en el proceso de desarrollo, cuando el código se ejecute en línea, será más difícil explicarlos.

Este artículo presenta el módulo de registro de Python, incluido su diseño y métodos aplicables para casos más complejos. Este artículo no es un documento para desarrolladores, es más como una guía para explicar cómo se construye la plantilla de registro de Python, e inspira a las personas interesadas a estudiar en profundidad.

¿Por qué usar el módulo de registro?

Algunos desarrolladores pueden preguntar, ¿por qué no una simple declaración impresa? El módulo de registro tiene muchas ventajas, que incluyen:

1. Soporte multihilo

2. Clasificación de registros por diferentes niveles

3. Flexibilidad y configurabilidad

Separación de cómo registrar registros de lo que se registra

El último punto es separar realmente nuestro contenido grabado del método de grabación, asegurando la cooperación de diferentes partes del software. Por ejemplo, permite a los desarrolladores de un marco o biblioteca agregar registros y dejar que el administrador del sistema o la persona responsable de ejecutar la configuración decida qué se debe registrar más adelante.

¿Qué hay en el módulo de registro?

El módulo de registro separa perfectamente las responsabilidades de cada parte del mismo (siguiendo los métodos de la API de Apache Log4j). Veamos cómo pasa una línea de registro a través del código de este módulo y estudiemos las diferentes partes del mismo.

Registrador

La grabadora es un objeto con el que los desarrolladores a menudo interactúan. Las principales API explican lo que queremos grabar.

Como ejemplo de un registrador, podemos clasificar las solicitudes para enviar un mensaje sin preocuparnos de dónde fueron enviadas.

Por ejemplo, cuando escribimos logger.info ("Stock se vendió a% s", precio) tenemos en cuenta el siguiente módulo:

9e76456b2ed05063814316bfa417307b.jpguploading.4e448015.gifTransferencia fallida, re-carga cancelada9e76456b2ed05063814316bfa417307b.jpguploading.4e448015.gif Transferencia fallida,re-cargacancelada9e76456b2ed05063814316bfa417307b.jpguploading.4e448015.gif Transferencia fallida, re-carga cancelada1-dos

Necesitamos una linea. Suponga que se está ejecutando algún código en la grabadora, de modo que esta línea aparece en la consola o el archivo. Pero, ¿qué pasó realmente internamente?

Registro

El registro es un paquete utilizado por el módulo de registro para satisfacer toda la información requerida. Contienen información como dónde deben registrarse los registros, cadenas modificadas, parámetros, colas de información solicitadas, etc.

Todos son objetos grabados. Cada vez que llamamos a la grabadora, se generan estos objetos. Pero, ¿cómo se serializan estos objetos en la secuencia? A través del procesador!

Procesador

El procesador envía registros de registro a otros terminales de salida, obtienen los registros de registro y los procesan en funciones relacionadas.

Por ejemplo, un procesador de archivos buscará un registro y lo agregará al archivo.

El módulo de registro estándar ya tiene una variedad de procesadores integrados, como:

Se pueden escribir varios procesadores de archivos (TimeRotated, SizeRotated, Watched) en el archivo

  1. Secuencia de salida de StreamHandler como stdout o stderr
  2. SMTPHandler envía registros de registro por correo electrónico
  3. SocketHandler envía el archivo de registro al socket de transmisión
  4. SyslogHandler 、 NTEventHandler 、 HTTPHandler 及 MemoryHandler 等

Actualmente tenemos un modelo similar a la situación real:

4e99d9d29c2930c87fdaa43a4a1ee340.jpguploading.4e448015.gifTransferencia fallida, re-carga cancelada4e99d9d29c2930c87fdaa43a4a1ee340.jpguploading.4e448015.gif Transferencia fallida,re-cargacancelada4e99d9d29c2930c87fdaa43a4a1ee340.jpguploading.4e448015.gif Transferencia fallida, re-carga cancelada2-crmvrmf

La mayoría de los procesadores manejan cadenas (SMTPHandler, FileHandler, etc.). Tal vez desee saber cómo estos registros de registro estructurados se transforman en bytes que son fáciles de serializar.

Formateador

El formateador es responsable de convertir registros de registro de metadatos enriquecidos en cadenas, y si no se proporciona nada, habrá un formateador predeterminado.

La biblioteca de registro proporciona la clase de formateador general, utilizando plantillas y estilos como entrada. El marcador de posición puede declarar todas las propiedades en un objeto LogRecord.

Por ejemplo: '% (asctime) s% (levelname) s% (name) s:% (message) s' generará un registro similar al 2017-07-19 15: 31: 13,942 INFO parent.child: Hello EuroPython.

Tenga en cuenta: la información del atributo es el resultado de interpolar la plantilla original del registro a través de los parámetros proporcionados. (Por ejemplo, para logger.info ("Hola% s", "Laszlo") este mensaje será "Hola Laszlo")

Todos los atributos predeterminados se pueden encontrar en la documentación del registro.

Ok, ahora que entendemos el formateador, nuestro modelo ha cambiado nuevamente:

6d076b8eac2d78f9a68755f33b87b466.jpguploading.4e448015.gifTransferencia fallida, re-carga cancelada6d076b8eac2d78f9a68755f33b87b466.jpguploading.4e448015.gif Transferencia fallida,re-cargacancelada6d076b8eac2d78f9a68755f33b87b466.jpguploading.4e448015.gif Transferencia fallida, re-carga cancelada3-olzyr7p

Filtro

El último objeto de nuestra herramienta de registro es el filtro.

Los filtros permiten un control detallado de los registros que deben enviarse. Se pueden aplicar múltiples filtros a la grabadora y al procesador al mismo tiempo. Para un registro enviado, todos los filtros deben pasar este registro.

Los usuarios pueden declarar sus propios filtros como objetos, usar el método de filtro para obtener registros de registro como entrada y devolver True / False como salida.

Por este motivo, el siguiente es el flujo de trabajo de registro actual:

cc3b5f9462a604e197222c5cca493c82.jpguploading.4e448015.gifTransferencia fallida, re-carga canceladacc3b5f9462a604e197222c5cca493c82.jpguploading.4e448015.gif Transferencia fallida,re-cargacanceladacc3b5f9462a604e197222c5cca493c82.jpguploading.4e448015.gif Transferencia fallida, re-carga cancelada4-nfjk7uc

Nivel de registrador

En este punto, puede quedar impresionado por la gran cantidad de contenido complejo y las configuraciones de módulos inteligentemente ocultas, pero hay una cosa más a tener en cuenta: las capas de registrador.

Podemos crear un registrador mediante logging.getLogger (). Este personaje pasa un parámetro a getLogger, que se puede usar para definir una jerarquía separando elementos con puntos.

Por ejemplo, logging.getLogger ("parent.child") creará un registrador "hijo" cuyo registrador padre se llama "padre". El registrador es un objeto global administrado por el módulo de registro, por lo que podemos Recuperalos fácilmente en cualquier parte del proyecto.

El ejemplo del registrador también se considera comúnmente un canal. La jerarquía permite a los desarrolladores definir canales y sus jerarquías.

Cuando el registro de registro se pasa a todos los procesadores en el registrador, el procesador principal procesará recursivamente hasta que lleguemos al registrador de nivel superior (definido como una cadena vacía), o haya un propagador de conjunto de registrador = Falso Podemos ver en el diagrama actualizado:

c8b964ba3363d7ac320a4a98af10c51c.jpguploading.4e448015.gifTransferencia fallida, re-carga canceladac8b964ba3363d7ac320a4a98af10c51c.jpguploading.4e448015.gif Transferencia fallida,re-cargacanceladac8b964ba3363d7ac320a4a98af10c51c.jpguploading.4e448015.gif Transferencia fallida, re-carga cancelada5-sjkrpt3

Tenga en cuenta que no se llama a la grabadora principal, solo se llama a su procesador. Esto significa que los filtros y otro código en la clase de registrador no se ejecutarán en el padre. Cuando agregamos filtros a la grabadora, esto suele ser una trampa.

Resumen de flujo de trabajo

Hemos aclarado la división de responsabilidades y cómo ajustamos el filtrado de registros. Sin embargo, hay otros dos atributos que no hemos mencionado:

  1. La grabadora puede estar incompleta, lo que no permite enviar ningún registro desde aquí.
  2. Se puede establecer un nivel efectivo tanto en la grabadora como en el procesador.

Por ejemplo, cuando una grabadora se establece en el nivel INFO, solo se pasará el nivel INFO y superior. Las mismas reglas se aplican a los procesadores.

Basado en todas las consideraciones anteriores, el diagrama de flujo del registro final se ve así:

45c58254965c82c504774ecdac0334d0.jpguploading.4e448015.gifVolcado fallado a volver a subir Cancelar45c58254965c82c504774ecdac0334d0.jpguploading.4e448015.gif Subiendo ...volver a subirCancelar45c58254965c82c504774ecdac0334d0.jpguploading.4e448015.gif volcado falló a volver a subir cancelado6-kd6eiyr

Cómo usar el módulo de registro

Ahora que hemos entendido la parte y el diseño del módulo de registro, es hora de comprender cómo interactúa un desarrollador con él. El siguiente es un ejemplo de código:

 

 

1

2

3

4 4

5 5

6 6

7 7

8

9 9

10

11

12

13

14

15

registro de importación

 

def sample_function (secret_parameter):

    logger = logging.getLogger (__ name__) # __name __ = projectA.moduleB

    logger.debug ("Va a realizar magia con '% s'", parámetro_secreto)

    ...

    tratar:

        resultado = do_magic (parámetro_secreto)

    excepto IndexError:

        logger.exception ("OMG sucedió de nuevo, alguien por favor dile a Laszlo")

    excepto:

        logger.info ("Excepción inesperada", exc_info = True)

        aumento

    más:

        logger.info ("La magia con '% s' resultó en '% s'", parámetro_secreto, resultado, stack_info = True)

 Creó un registrador con el módulo  __ nombre __ . Creará canales y niveles basados ​​en la estructura del proyecto, tal como los módulos Pyhon están conectados con puntos.

La variable del registrador se refiere al "módulo" del registrador, con "projectA" como padre y "root" como padre del padre.

En la quinta línea, vemos cómo ejecutar la llamada para enviar el registro. Podemos usar uno de depuración, información, error o métodos críticos para registrar registros en un nivel apropiado.

Al grabar un mensaje, además de los parámetros de la plantilla, podemos pasar los parámetros de contraseña con significados especiales, los más interesantes son exc_info y stack_info. Agregarán información sobre la excepción actual y el marco de la pila, respectivamente. Por conveniencia, hay una excepción de método en el objeto registrador, tal como este error llama a exc_info = True.

Estos son los fundamentos de cómo usar el módulo grabador, pero también vale la pena explicar algunas prácticas que generalmente se consideran malas operaciones.

Cadena sobreformateada

Loggger.info ("plantilla de cadena {}". El formato (argumento)) debe evitarse tanto como sea posible, y logger.info ("plantilla de cadena% s", argumento) debe usarse si es posible. Esta es una mejor práctica, porque solo cuando se envía el registro, la cadena realmente cambiará. Cuando el nivel de nuestro registro está por encima de INFO, no hacerlo resultará en una pérdida de ciclos, porque este cambio aún ocurrirá.

Captura y formateo de excepciones

Por lo general, queremos registrar la información de registro de la anomalía en el módulo de rastreo.

 

1

2

3

4 4

tratar:

    ...

excepto Excepción como error:

    logger.info ("Algo malo sucedió:% s", error)

Pero dicho código nos mostrará una línea de registro similar a Algo malo sucedió: "secret_key". Esto no es muy útil. Si usamos exc_info como explicación previa, se mostrará de la siguiente manera:

 

1

2

3

4 4

tratar:

    ...

excepto Excepción:

    logger.info ("Algo malo sucedió", exc_info = True)

 

1

2

3

4 4

5 5

6 6

7 7

Algo malo sucedio

Rastreo (llamadas recientes más última):

  Archivo "sample_project.py", línea 10, en código

    inner_code ()

  Archivo "sample_project.py", línea 6, en inner_code

    x = datos ["clave_secreta"]

KeyError: 'secret_key'

Esto incluirá no solo el recurso exacto de la excepción, sino también su tipo.

Configurar la grabadora

Equipar nuestro software es muy simple, necesitamos configurar la pila de registros y formular cómo se emiten estos registros.

Las siguientes son varias formas de configurar la pila de registros

Ajustes básicos

Esta es, con mucho, la forma más fácil de configurar el registro. Utilice logging.basicConfig (level = ”INFO”) para crear un StreamHandler básico, de modo que todo lo que se grabe en INFO se grabará en el nivel superior a la consola. Estos son algunos parámetros para escribir configuraciones básicas:

Parámetro Explicación Ejemplos
nombre del archivo Especifique el procesador de archivos creado, use un nombre de archivo específico en lugar de un procesador de flujo /var/logs/logs.txt
formato Use una cadena de formato específica para el procesador “'% (Asctime) s% (mensaje) s'”
datefmt Use un formato de fecha / hora específico "% H:% M:% S"
nivel Establecer un nivel específico para el nivel del grabador raíz "INFORMACIÓN"

Al configurar scripts simples, este es un método simple y útil.

请注意, basicConfig 仅仅在运行的一开始可以这么调用。如果你已经设置了你的根记录器,调用 basicConfig 将不会奏效。

字典设置

所有元素的设置以及如何连接它们可以作为字典来说明。这个字典应当由不同的部分组成,包括记录器、处理器、格式化以及一些基本的通用参数。

例子如下:

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

config = {

    'disable_existing_loggers': False,

    'version': 1,

    'formatters': {

        'short': {

            'format': '%(asctime)s %(levelname)s %(name)s: %(message)s'

        },

    },

    'handlers': {

        'console': {

            'level': 'INFO',

            'formatter': 'short',

            'class': 'logging.StreamHandler',

        },

    },

    'loggers': {

        '': {

            'handlers': ['console'],

            'level': 'ERROR',

        },

        'plugins': {

            'handlers': ['console'],

            'level': 'INFO',

            'propagate': False

        }

    },

}

import logging.config

logging.config.dictConfig(config)

当被引用时, dictConfig 将会禁用所有运行的记录器,除非 disable_existing_loggers 被设置为 false。这通常是需要的,因为很多模块声明了一个全球记录器,它在 dictConfig 被调用之前被导入的时候将会实例化。

你可以查看 schema that can be used for the dictConfig method(链接)。通常,这些设置将会存储在一个 YAML 文件中,并且从那里设置。很多开发者会倾向于使用这种方式而不是使用 fileConfig(链接),因为它为定制化提供了更好的支持。

拓展 logging

幸亏设计了这种方式,拓展 logging 模块很容易。让我们来看些例子:

logging JSON | 记录 JSON

只要我们想要记录,我们可以通过创建一种自定义格式化来记录 JSON ,它会将日志记录转化为 JSON 编码的字符串。

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

import logging

import logging.config

import json

ATTR_TO_JSON = ['created', 'filename', 'funcName', 'levelname', 'lineno', 'module', 'msecs', 'msg', 'name', 'pathname', 'process', 'processName', 'relativeCreated', 'thread', 'threadName']

class JsonFormatter:

    def format(self, record):

        obj = {attr: getattr(record, attr)

                  for attr in ATTR_TO_JSON}

        return json.dumps(obj, indent=4)

 

handler = logging.StreamHandler()

handler.formatter = JsonFormatter()

logger = logging.getLogger(__name__)

logger.addHandler(handler)

logger.error("Hello")

添加更多上下文

在格式化中,我们可以指定任何日志记录的属性。

我们可以通过多种方式增加属性,在这个例子中,我们用过滤器来丰富日志记录。

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

import logging

import logging.config

GLOBAL_STUFF = 1

 

class ContextFilter(logging.Filter):

    def filter(self, record):

        global GLOBAL_STUFF

        GLOBAL_STUFF += 1

        record.global_data = GLOBAL_STUFF

        return True

 

handler = logging.StreamHandler()

handler.formatter = logging.Formatter("%(global_data)s %(message)s")

handler.addFilter(ContextFilter())

logger = logging.getLogger(__name__)

logger.addHandler(handler)

 

logger.error("Hi1")

logger.error("Hi2")

这样有效地在所有日志记录中增加了一个属性,它可以通过记录器。格式化会在日志行中包含这个属性。

请注意这会在你的应用中影响所有的日志记录,包含你可能用到以及你发送日志的库和其他的框架。它可以用来记录类似于在所有日志行里的一个独立请求 ID ,去追踪请求或者去添加额外的上下文信息。

从 Python 3.2 开始,你可以使用 setLogRecordFactory 去获得所有日志的创建记录和增加额外的信息。这个 extra attribute 和 LoggerAdapter class 或许同样是有趣的。

缓冲日志

有时候当错误发生时,我们想要排除日志故障。创建一个缓冲的处理器,来记录当错误发生时的最新故障信息是一种可行的办法。下面的代码是个非人为策划的例子:

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

import logging

import logging.handlers

 

class SmartBufferHandler(logging.handlers.MemoryHandler):

    def __init__(self, num_buffered, *args, **kwargs):

        kwargs["capacity"] = num_buffered + 2  # +2 one for current, one for prepop

        super().__init__(*args, **kwargs)

 

    def emit(self, record):

        if len(self.buffer) == self.capacity - 1:

            self.buffer.pop(0)

        super().emit(record)

 

handler = SmartBufferHandler(num_buffered=2, target=logging.StreamHandler(), flushLevel=logging.ERROR)

logger = logging.getLogger(__name__)

logger.setLevel("DEBUG")

logger.addHandler(handler)

 

logger.error("Hello1")

logger.debug("Hello2")  # This line won't be logged

logger.debug("Hello3")

logger.debug("Hello4")

logger.error("Hello5")  # As error will flush the buffered logs, the two last debugs will be logged

更多信息

这篇关于日志记录库的灵活性和可配置性的介绍,目的在于证明它如何设计了分别的关注点的美学。它同样为任何对 logging documentation 和 how-to guide 感兴趣的人提供了一个坚实的基础。虽然这篇文章对于 Python 日志模块并不是一个综合性的知道,但是这里有一些针对于常见的问题的回答。

问:我的库发送了一个“ no logger configured” 的警告

答:从 The Hitchhiker’s Guide to Python 查阅 how to configure logging in a library

问:如果一个记录器没有层级设置会怎么样?

答:记录器的有效层级,会由它的父级递归定义。

问:我所有的日志都在本地时间,我如何记录在 UTC ?

答:格式化就是答案!你需要在你的格式化中设置 converter 属性为通用的 UTC 时间。使用 converter = time.gmtime 。

17 artículos originales publicados · Me gusta1 · Visitas 818

Supongo que te gusta

Origin blog.csdn.net/weixin_45433031/article/details/105445910
Recomendado
Clasificación