• ML 系统的日志记录


    Intuition

    日志记录是跟踪和记录应用程序中发生的关键事件的过程,用于检查、调试等。它们比print语句更强大,因为它们允许将特定的信息片段发送到具有自定义功能的特定位置格式化、共享接口等。这使得日志记录成为能够从应用程序的内部流程中发现有洞察力的信息的关键支持者。

    成分

    有几个总体概念需要注意:

    • Logger: 从应用程序发出日志消息。
    • Handler:将日志记录发送到特定位置。
    • Formatter: 格式化和样式化日志记录。

    日志记录还有很多,例如过滤器、异常日志记录等,但这些基础知识将使能够为应用程序做需要的一切。

    级别

    在创建专门的配置记录器之前,让看看使用基本配置记录的消息是什么样子的。

    import logging
    import sys

    # Create super basic logger
    logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)

    # Logging levels (from lowest to highest priority)
    logging.debug("Used for debugging your code.")
    logging.info("Informative messages from your code.")
    logging.warning("Everything works but there is something to be aware of.")
    logging.error("There's been a mistake with the process.")
    logging.critical("There is something terribly wrong and process may terminate.")
    • 1
    DEBUG:root:Used for debugging your code.
    INFO:root:Informative messages from your code.
    WARNING:root:Everything works but there is something to be aware of.
    ERROR:root:There's been a mistake with the process.
    CRITICAL:root:There is something terribly wrong and process may terminate.
    • 1

    这些是日志记录的基本级别,其中DEBUG优先级最低,优先级CRITICAL最高。定义了记录器,basicConfig用于将日志消息发送到标准输出(即终端控制台),但也可以写入任何其他流甚至文件。还将日志定义为对从 level 开始的日志消息敏感DEBUG。这意味着所有记录的消息都将显示,因为DEBUG它是最低级别。如果设置了 level ,ERROR那么只会显示日志消息。

    import logging
    import sys

    # Create super basic logger
    logging.basicConfig(stream=sys.stdout, level=logging.ERROR)

    # Logging levels (from lowest to highest priority)
    logging.debug("Used for debugging your code.")
    logging.info("Informative messages from your code.")
    logging.warning("Everything works but there is something to be aware of.")
    logging.error("There's been a mistake with the process.")
    logging.critical("There is something terribly wrong and process may terminate.")
    • 1
    ERROR:root:There's been a mistake with the process.
    CRITICAL:root:There is something terribly wrong and process may terminate.
    • 1

    配置

    首先,将在config.py脚本中设置日志的位置:

    # config/config.py
    LOGS_DIR = Path(BASE_DIR, "logs")
    LOGS_DIR.mkdir(parents=True, exist_ok=True)
    • 1

    接下来,将为应用程序配置记录器:

    # config/config.py
    import logging
    import sys
    logging_config = {
        "version": 1,
        "disable_existing_loggers": False,
        "formatters": {
            "minimal": {"format""%(message)s"},
            "detailed": {
                "format""%(levelname)s %(asctime)s [%(name)s:%(filename)s:%(funcName)s:%(lineno)d]\n%(message)s\n"
            },
        },
        "handlers": {
            "console": {
                "class""logging.StreamHandler",
                "stream": sys.stdout,
                "formatter""minimal",
                "level": logging.DEBUG,
            },
            "info": {
                "class""logging.handlers.RotatingFileHandler",
                "filename": Path(LOGS_DIR, "info.log"),
                "maxBytes": 10485760,  # 1 MB
                "backupCount": 10,
                "formatter""detailed",
                "level": logging.INFO,
            },
            "error": {
                "class""logging.handlers.RotatingFileHandler",
                "filename": Path(LOGS_DIR, "error.log"),
                "maxBytes": 10485760,  # 1 MB
                "backupCount": 10,
                "formatter""detailed",
                "level": logging.ERROR,
            },
        },
        "root": {
            "handlers": ["console""info""error"],
            "level": logging.INFO,
            "propagate": True,
        },
    }
    • 1
    1. [Lines 6-11]:定义两个不同的 Formatters(确定日志消息的格式和样式),minimal 和 detailed,它们使用各种 LogRecord 属性为日志消息创建格式化模板。
    2. [Lines 12-35]:定义不同的 处理程序(有关发送日志消息的位置的详细信息):
      • console:将日志消息(使用 minimal格式化程序)发送到 stdout高于级别的消息流 DEBUG(即所有记录的消息)。
      • info:将日志消息(使用 detailed格式化程序)发送到 logs/info.log(一个可以达到的文件, 1 MB将备份 10它的最新版本)以获得级别以上的消息 INFO
      • error:将日志消息(使用 detailed格式化程序)发送到 logs/error.log(一个可以达到的文件, 1 MB将备份 10它的最新版本)以获得级别以上的消息 ERROR
    3. [Lines 36-40]:将不同的处理程序附加到根 Logger

    选择使用字典来配置记录器,但还有其他方法,例如 Python 脚本、配置文件等。单击下面的不同选项以展开并查看各自的实现。

    Python 脚本

    import logging
    from rich.logging import RichHandler
    • 1
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
    console_handler = RichHandler(markup=True)
    console_handler.setLevel(logging.DEBUG)
    info_handler = logging.handlers.RotatingFileHandler(
        filename=Path(LOGS_DIR, "info.log"),
        maxBytes=10485760,  # 1 MB
        backupCount=10,
    )
    info_handler.setLevel(logging.INFO)
    error_handler = logging.handlers.RotatingFileHandler(
        filename=Path(LOGS_DIR, "error.log"),
        maxBytes=10485760,  # 1 MB
        backupCount=10,
    )
    error_handler.setLevel(logging.ERROR)

    minimal_formatter = logging.Formatter(fmt="%(message)s")
    detailed_formatter = logging.Formatter(
        fmt="%(levelname)s %(asctime)s [%(name)s:%(filename)s:%(funcName)s:%(lineno)d]\n%(message)s\n"
    )

    console_handler.setFormatter(fmt=minimal_formatter)
    info_handler.setFormatter(fmt=detailed_formatter)
    error_handler.setFormatter(fmt=detailed_formatter)
    logger.addHandler(hdlr=console_handler)
    logger.addHandler(hdlr=info_handler)
    logger.addHandler(hdlr=error_handler)
    • 1
    import logging
    from rich.logging import RichHandler

    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)

    console_handler = RichHandler(markup=True)
    console_handler.setLevel(logging.DEBUG)
    info_handler = logging.handlers.RotatingFileHandler(
        filename=Path(LOGS_DIR, "info.log"),
        maxBytes=10485760,  # 1 MB
        backupCount=10,
    )
    info_handler.setLevel(logging.INFO)
    error_handler = logging.handlers.RotatingFileHandler(
        filename=Path(LOGS_DIR, "error.log"),
        maxBytes=10485760,  # 1 MB
        backupCount=10,
    )
    error_handler.setLevel(logging.ERROR)

    minimal_formatter = logging.Formatter(fmt="%(message)s")
    detailed_formatter = logging.Formatter(
        fmt="%(levelname)s %(asctime)s [%(name)s:%(filename)s:%(funcName)s:%(lineno)d]\n%(message)s\n"
    )

    console_handler.setFormatter(fmt=minimal_formatter)
    info_handler.setFormatter(fmt=detailed_formatter)
    error_handler.setFormatter(fmt=detailed_formatter)
    logger.addHandler(hdlr=console_handler)
    logger.addHandler(hdlr=info_handler)
    logger.addHandler(hdlr=error_handler)
    • 1
    1. 把它放在一个logging.config文件中:

      [formatters]
      keys=minimal,detailed

      [formatter_minimal]
      format=%(message)s

      [formatter_detailed]
      format=
          %(levelname)s %(asctime)s [%(name)s:%(filename)s:%(funcName)s:%(lineno)d]
          %(message)s

      [handlers]
      keys=console,info,error

      [handler_console]
      class=StreamHandler
      level=DEBUG
      formatter=minimal
      args=(sys.stdout,)

      [handler_info]
      class=handlers.RotatingFileHandler
      level=INFO
      formatter=detailed
      backupCount=10
      maxBytes=10485760
      args=("logs/info.log",)

      [handler_error]
      class=handlers.RotatingFileHandler
      level=ERROR
      formatter=detailed
      backupCount=10
      maxBytes=10485760
      args=("logs/error.log",)

      [loggers]
      keys=root

      [logger_root]
      level=INFO
      handlers=console,info,error
      • 1
    2. 把它放在你的 Python 脚本中

      import logging
      import logging.config
      from rich.logging import RichHandler

      # Use config file to initialize logger
      logging.config.fileConfig(Path(CONFIG_DIR, "logging.config"))
      logger = logging.getLogger()
      logger.handlers[0] = RichHandler(markup=True)  # set rich handler
      • 1

    可以像这样加载记录器配置字典:

    # config/config.py
    from rich.logging import RichHandler
    logging.config.dictConfig(logging_config)
    logger = logging.getLogger()
    logger.handlers[0] = RichHandler(markup=True)  # pretty formatting

    # Sample messages (note that we use configured `logger` now)
    logger.debug("Used for debugging your code.")
    logger.info("Informative messages from your code.")
    logger.warning("Everything works but there is something to be aware of.")
    logger.error("There's been a mistake with the process.")
    logger.critical("There is something terribly wrong and process may terminate.")
    • 1
    DEBUG    Used for debugging your code.                                 config.py:71
    INFO     Informative messages from your code.                          config.py:72
    WARNING  Everything works but there is something to be aware of.       config.py:73
    ERROR    There's been a mistake with the process.                      config.py:74
    CRITICAL There is something terribly wrong and process may terminate.  config.py:75

    • 1

    使用RichHandler作为console处理程序来为日志消息获取漂亮的格式。这不是预安装的库,因此需要安装并添加到requirements.txt

    pip install rich==12.4.4

    # Add to requirements.txt rich==12.4.4

    记录的消息存储在日志目录中的相应文件中:

    logs/
        ├── info.log
        └── error.log

    • 1

    由于定义了详细的格式化程序,因此会看到如下信息丰富的日志消息:

    应用

    在项目中,可以将所有打印语句替换为日志语句:

    print("✅ Saved data!")

    • 1

    ──── 变成:────

    from config.config import logger
    logger.info("✅ Saved data!")

    • 1

    所有的日志消息都在INFO级别上,但在开发过程中可能不得不使用级别,如果系统以意外的方式运行DEBUG,还会添加一些ERROR或日志消息。CRITICAL

    • what:记录您希望从应用程序中显示的所有必要详细信息,这些详细信息将在开发期间之后的回顾性检查中有用。

    • 其中:最佳做法是不要将模块化函数与日志语句混为一谈。相反,应该在小函数之外和更大的工作流程中记录消息。例如,除了main.pytrain.py文件之外,任何脚本中都没有日志消息。这是因为这些脚本使用了其他脚本(data.py、evaluate.py 等)中定义的较小函数。如果觉得需要在其他功能中登录,那么它通常表明该功能需要进一步分解。

    Elastic stack(以前称为 ELK stack)是生产级日志记录的常用选项。它结合了Elasticsearch(分布式搜索引擎)、Logstash(摄取管道)和Kibana(可定制可视化)的特性。也可以简单地将日志上传到云博客存储(例如 S3、谷歌云存储等)。


    本文主体源自以下链接:

    @article{madewithml,
        author       = {Goku Mohandas},
        title        = { Made With ML },
        howpublished = {\url{https://madewithml.com/}},
        year         = {2022}
    }
    • 1

    本文由 mdnice 多平台发布

  • 相关阅读:
    iOS模拟器 Unable to boot the Simulator —— Ficow笔记
    [前端]开启VUE之路-NODE.js版本管理
    弹框确认按钮,请求两个接口跳转刷新页面,并使用async和await将异步改成同步的数据?
    vite.config.js文件配置代理设置VITE_APP_BASE_API
    程序员可以包装吗?
    钉钉开放平台创建企业内部H5微应用或者小程序
    基于nginx的tomcat负载均衡和集群(超简单)
    react-native 打包报错 android-gradle-plugin-requires-java-11
    常用的国内镜像源
    2022腾讯云年终双十一配置表汇总-云服务器CVM+轻量应用服务器
  • 原文地址:https://blog.csdn.net/hengfanz/article/details/128115917