logging 模块,stdin,stdout,stderr

文章目录

  • 快速入门
  • getLogger
  • logging.basicConfig
    • [stderr stdout stdin](#stderr stdout stdin)
      • [不管是 Python 还是 shell,本质都是"进程有 0/1/2 三个流",默认情况下:stdin 从终端读,stdout、stderr 都指向终端 → 你都能在屏幕上看到](#不管是 Python 还是 shell,本质都是“进程有 0/1/2 三个流”,默认情况下:stdin 从终端读,stdout、stderr 都指向终端 → 你都能在屏幕上看到)
      • 怎么从流里读出来
    • 配置文件和控制台
  • [日志冒泡 propagation](#日志冒泡 propagation)
  • [在同一个 Python 进程里,logging.getLogger("名称") 是"按名字全局唯一的单例"。进程间不共享](#在同一个 Python 进程里,logging.getLogger("名称") 是“按名字全局唯一的单例”。进程间不共享)
    • [防止 handler 重复添加](#防止 handler 重复添加)
  • 函数属于它定义的模块,不属于调用它的文件
    • [不只是 log,函数体里用到的所有"全局变量"都是先回自己定义所在的模块去找的,而不是去调用它的那个文件里找。](#不只是 log,函数体里用到的所有“全局变量”都是先回自己定义所在的模块去找的,而不是去调用它的那个文件里找。)
  • 只打印自己python文件的日志,可以多个进程

快速入门

快速入门
日志指南
日志进阶

getLogger

1、通过执行 logger = getLogger("名称") 创建一个该名称的日志记录器然后调用日志记录器的 debug(), info(), warning(), error() 和 critical() 方法来使用日志记录功能。

2、 logger = getLogger("") 或 logger = getLogger() 创建的是root日志记录器。

root logger 默认:level = WARNING,handlers = []

但 logging 模块有个 lastResort 兜底 handler,所以没配置控制台也能在控制台看到 WARNING 及以上的输出。

3、

python 复制代码
import logging
logging.info()
logging.debug()
# 等价于
logging.getLogger().info("hello")   # 注意:这里用的是"root logger"
# 或
logging.getLogger("").info("hello")
也就是说:

logging.info() / logging.debug() / logging.error() 这些函数
本质上就是 调用 root logger 的对应方法。

logging.basicConfig

python 复制代码
# 只配置了控制台(准确说是标准错误流 stderr),默认写到了stderr通道
logging.basicConfig(level=logging.INFO)
# 只配置了文件
logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S"
)

basicConfig 是在给 整个 logging 系统的"老大"------root 日志记录器 做初始化配置的,同时顺带创建一个默认的 Handler。

你这段代码等价于在说:

"把 root logger 的等级设成 DEBUG,并且给它加一个写文件的 handler,日志都按这个格式写到 app.log 里。"



stderr stdout stdin

不管是 shell 还是 Python,stdin / stdout / stderr 都是 3 条"管道"(标准流)。

具体往哪条管道走,不是看内容是对还是错,而是看程序怎么写的。

谁写到 stdout / stderr,不是看"内容像不像错误",而是看它到底往哪个文件描述符写:1(stdout)还是 2(stderr)。

在 shell / Linux 里,每个进程有三个默认"文件描述符":

0 👉 stdin(标准输入)

1 👉 stdout(标准输出)

2 👉 stderr(标准错误)

重定向的时候:

或 1> 是 重定向 stdout

2> 是 重定向 stderr

stdin/stdout/stderr 是 3 条独立的通道;

✅ 到底走哪条,是程序写输出时选了哪个流;


不管是 Python 还是 shell,本质都是"进程有 0/1/2 三个流",默认情况下:stdin 从终端读,stdout、stderr 都指向终端 → 你都能在屏幕上看到

✅ 默认情况下:控制台"接"着 stdout 和 stderr,所以你都能看到

❌ 但 stdout/stderr 仍然是两个不同的通道,只是默认连到了同一个"屏幕设备"上而已。


不管是 Python 还是 shell,本质都是"进程有 0/1/2 三个流"

默认情况下:

stdin 从终端读

stdout、stderr 都指向终端 → 你都能在屏幕上看到

怎么从流里读出来

程序自己怎么从流里读?(读 stdin)

别的东西怎么拿到这个程序写到 stdout / stderr 里的内容?(重定向、管道、捕获)




写到流里 = 写到一条"水管"里;

谁能拿出来,看谁在 另一头接这条管子:

程序自己:从 stdin 读

Shell:用 >、2>、| 把 stdout / stderr 接走

其他程序:用诸如 subprocess 之类的 API 捕获输出

配置文件和控制台


日志冒泡 propagation

日志从子 logger 一路往父 logger 传,最后传到 root 的过程,就像气泡往上浮一样。Python logging 里官方叫 propagation。





在同一个 Python 进程里,logging.getLogger("名称") 是"按名字全局唯一的单例"。进程间不共享



防止 handler 重复添加




函数属于它定义的模块,不属于调用它的文件

函数回家找"自己文件里的全局变量",不会跨文件去你调用它的地方要东西。




不只是 log,函数体里用到的所有"全局变量"都是先回自己定义所在的模块去找的,而不是去调用它的那个文件里找。

只打印自己python文件的日志,可以多个进程

python 复制代码
# logger_utils.py
import logging
from logging.handlers import TimedRotatingFileHandler
from pathlib import Path
from typing import Optional

APP_LOGGER_NAME = "ai_build"  # 你项目的总 logger 名字,随便起一个


def setup_app_logger(log_file: Optional[str] = None, level: int = logging.INFO):
    """
    配置业务日志记录器,只打印自己项目相关的日志。
    """
    logger = logging.getLogger(APP_LOGGER_NAME)
    logger.setLevel(level)
    logger.propagate = False  # 关键:不要往 root 传了

    # 避免重复添加处理器
    if logger.handlers:
        return

    formatter = logging.Formatter(
        "%(asctime)s - %(name)s - [%(levelname)s] "
        "[%(threadName)s] %(filename)s:%(lineno)d --- %(message)s"
    )

    # 控制台处理器
    console_handler = logging.StreamHandler()
    console_handler.setFormatter(formatter)
    logger.addHandler(console_handler)

    # 文件处理器(如果指定了文件)
    if log_file:
        Path(log_file).parent.mkdir(parents=True, exist_ok=True)
        file_handler = TimedRotatingFileHandler(
            log_file,
            when="midnight",
            interval=1,
            backupCount=7,
            encoding="utf-8",
        )
        file_handler.setFormatter(formatter)
        logger.addHandler(file_handler)


def get_logger(name: str) -> logging.Logger:
    """
    业务代码里用这个拿 logger,
    日志名会是: ai_build.xxx
    """
    return logging.getLogger(f"{APP_LOGGER_NAME}.{name}")
python 复制代码
"""
然后入口脚本设置自己的日志文件。这样从入口调到的所有python文件都写到了该日志文件中,因为非如入口文件是get_logger(__name__),返回logging.getLogger(f"{APP_LOGGER_NAME}.{name}"),然后也没配置自己的handle,所有冒泡到父亲APP_LOGGER_NAME
"""
from logger_utils import setup_app_logger, get_logger

setup_app_logger("logs/ci.log")
log = get_logger(__name__)

log.info("只会打印自己项目的日志")

另一个进程的脚本里就换下日志文件就好





相关推荐
其美杰布-富贵-李1 分钟前
TensorBoard 与 WandB 在 PyTorch Lightning 中的完整指南
人工智能·pytorch·python·监控·调优
Python永远的神2 分钟前
告别循环:Python 列表推导式,让你的代码飞起来!
python
Vic101016 分钟前
Spring AOP 高级陷阱:为什么 @Before 修改参数是“伪修改“?
java·python·spring
小智RE0-走在路上11 分钟前
Python学习笔记(12) --对象,类的成员方法,构造方法,其他内置方法,封装,继承,多态,类型注解
笔记·python·学习
执笔论英雄14 分钟前
[RL]协程asyncio.CancelledError
开发语言·python·microsoft
a_zzzzzzzz16 分钟前
Python 解释器 + Shell 脚本实现桌面打开软件
开发语言·python
小蒜学长21 分钟前
python基于Python的医疗机构药品及耗材信息管理系统(代码+数据库+LW)
数据库·spring boot·后端·python
AI_567824 分钟前
Python RPA解放Excel生产力
python·excel·rpa
智航GIS24 分钟前
3.1 字符串(String)
开发语言·python
至此流年莫相忘24 分钟前
python基础语法
前端·python