Skip to content

Escritura de logs asincronica #7

@UrielPaluch

Description

@UrielPaluch

Objetivo

Utilizando py-spy encontré que la escritura de logs es sumamente costosa, pero es algo necesario. Podría intentar implementar una escritura de logs asincrónica. La idea es que se escriban los logs cuando haya algún hueco en cola de ejecución.

Hay dos formas de hacerlo, una es con asyncio y la otra con threads. Creo que asyncio es el enfoque correcto, porque la idea sería mandar a escribir y continuar con la ejecución (como si fuera un db).

Planificación

Tengo una lista donde voy appendeando los logs y luego que escirba cuando encuentra un hueco. Hay que pensar bien como se integra con el módulo de logging de toolbox.

  • A tener en cuenta:
    • Sacar (levelname)s del yaml
    • A los logs les tengo que agregar un timestamp para poder tener el momento real de cuando se ejecuta.

resultados

Tiempo de Logging

Testeando el tiempo que demora en loggear, para el MemoryHandler comparado con el FileHandler, obtengo estos resultados loggeando 400.000 logs, con una capacidad de 10000 para el MemoryHandler:

Batch Logger: log time 5345ms, flush time 2ms
Normal Logger: 6729ms
Batch Logger is 1.26 times faster than the normal logger.

Simulación con .log file real

Ahora, testero lo mismo que antes, pero loggeando mensajes reales de un log file de ejemplo.
El log file tiene 460.000 líneas. El código corre 10 veces la simulación de loggear todas las líneas, inicializando los loggers a cada vez.
La capacity la settee a 20.000, que sería el valor por defecto.

Normal Logger: 14493ms
Batch Logger: 7624ms
Batch Logger is 1.90 times faster than the normal logger.

Next Steps

  • Cambiar el método ".log()" por ".info()", ".debug()" y el resto.
  • Correr un test más exhaustivo del tiempo
  • Buscar e implementar forma más eficiente de concatenar strings. Update: Parece que sumar strings es más eficiente.
  • Investigar cantidad razonable de Logs para que funcione bien (capacidad máxima). Haciendo un cálculo rápido, viendo que nos gustaría consumir como máximo 2 megas en memoria, se seteó la capacidad por defecto en 20.000 logs.
  • Las funciones de log tienen que quedar asi, ejemplo:
def info(self, message: str, *args) -> None:
        """
        Loggea el mensaje en memoria con nivel info.

        Parameters
        ----------
        message : str
            Mensaje a loggear.
        """
        formatted_message = self.format_log_message(message.format(*args))
        self.logger.info(formatted_message)
        print(formatted_message)

Porque si no no se puede logguear lazy, logger.info("El entorno de ejecucion es %s", entorno.name), y no imprime los logs.

  • Llevarlo a producción. Cuando lo llevemos a producción hay que cambiar en el requirements.txt como se instala Toolbox porque yo (Uri) lo tuve que cambiar porque lo llevamos a prod antes de que corresponda. (este comentario es para hacerte que me acuerde de contarte bien que es lo que hicimos mal, mas allá de que te lo escribí por wp). Llevarlo a prod implica hacerlo en Tito y en CaucionColocadora.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions