Python日志和配置库
dynaconf是一个配置管理包,支持多种配置文件格式,如:toml、yaml、json、ini及环境变量等
pip install dynaconf mkdir config cd config dynaconf init -f toml
命令生成的目录结构如下:
config
├── .gitignore
├── .secrets.toml
├── config.py
└── settings.toml
.secrets.toml用于存放敏感信息,默认被添加到.gitignore中,不会提交到代码仓库。
从环境变量读取配置:
# 环境变量前缀在config.py中设置
export PYDEMO_tag=dynaconf
from src.config.config import settings print(settings.tag) # dynaconf
config.py中可以指定读取的配置文件:
from dynaconf import Dynaconf settings = Dynaconf( # 环境变量前缀 envvar_prefix="PYDEMO", # 可以指定多个配置文件,如:settings.dev.toml settings_files=['settings.toml', '.secrets.toml'], )
在settinngs.toml中写入配置:
[person] name = "eason" age = 30
读取配置数据:
from dataclasses import dataclass from src.config.config import settings print(settings.person) # 输出:{'name': 'eason', 'age': 30}
绑定配置到类型
也可以定义具体的类型来绑定配置数据,这样在使用时更方便
from dataclasses import dataclass from src.config.config import settings @dataclass class Person: name: str age: int p = Person(**settings.person) print(p.name)
多环境配置
单个配置文件
不同环境读取不同的配置:
[production] person = { name = "prod", age = 100 } [development] person = { name = "dev", age = 100 }
config.py中设置environments的值是True
settings = Dynaconf( envvar_prefix="PYDEMO", settings_files=['settings.toml', '.secrets.toml'], environments=True )
环境变量settings.ENV_FOR_DYNACONF的值默认是development,dynaconf会读取[development]节点下的配置
# 设置环境变量,从production节点下读取配置 export ENV_FOR_DYNACONF=production # unset ENV_FOR_DYNACONF
或者
import os os.environ["ENV_FOR_DYNACONF"] = "production"
输出结果
from src.config.config import settings print(settings.ENV_FOR_DYNACONF) # 输出production p = Person(**settings.person) # 输出prod print(p.name)
多个配置文件
from dynaconf import Dynaconf _cfg_files = ['settings.pro.toml', '.secrets.pro.toml'] if __debug__: _cfg_files = ['settings.dev.toml', '.secrets.dev.toml'] settings = Dynaconf( envvar_prefix="DYNACONF", settings_files=_cfg_files )
这样我们就可以把不同环境下的配置项写入到不同的配置文件中了
loguru是一个易于配置和使用的Python日志库
安装:
pip install loguru
默认输出日志到控制台:
from loguru import logger logger.info("一条日志信息")
日志输出结果如下:
2023-06-07 21:06:04.154 | INFO | __main__:<module>:3 - 一条日志信息
loguru输出的日志带有颜色,不仅美观,还易于阅读,如下图所示
结构化日志
除了简单输出日志外,还可以记录结构化日志,示例如下:
from dataclasses import dataclass from loguru import logger @dataclass class Person: name: str age: int person = Person("eason", 30) logger.info("人员信息:{p}", p=person)
日志输出结果:
2023-06-19 20:48:10.900 | INFO | __main__:<module>:14 - 人员信息:Person(name='eason', age=30)
对于异常,loguru也可以输出详细的调用堆栈:
from loguru import logger def throws(): raise NotImplementedError("未实现的函数") try: throws() except Exception as err: logger.exception("出错了:{err}", err=err)
输出日志:
滚动日志
在生产环境,日志通常会记录到文本中而非仅仅打印到控制台。文本日志要考虑到磁盘占用问题,通常会采用滚动日志,配置如下:
# 禁用控制台日志 logger.remove(0) logger.add("/Users/Eason/Desktop/pdemo/logs/log.log" , encoding="utf-8" , level="INFO" # 输出的最小日志级别 , rotation="1 MB" # 每个日志文件的大小,超过该大小则创建新文件 , retention=10 # 保留的日志文件数量,不超过10个 , enqueue=True) # 多进程安全
替换FastAPI中的日志模块
.
├── README.md
├── docs
│ └── dev
│ └── local.http
├── mypy.ini
├── requirements.txt
├── ruff.toml
├── src
├── config
│ ├── _config.py
│ ├── app_options.py
│ └── settings.yaml
├── my_logger.py
└── main.py
配置如下:
server: port: 9001 log: level: INFO encoding: utf-8 rotation: 10 MB retention: 20 enqueue: True file_path: /Users/Eason/Desktop/pdemo/logs/log.log
在app_options.py中定义配置模型:
from __future__ import annotations from dataclasses import dataclass from src.config._config import settings @dataclass class AppOptions: log: LogOptions server: ServerOptions @dataclass class LogOptions: level: str file_path: str enqueue: bool rotation: int | str retention: int encoding: str @dataclass class ServerOptions: port: int options = AppOptions(log=LogOptions(**settings.log), server=ServerOptions(**settings.server))
在my_logger.py中添加配置相关代码:
from __future__ import annotations from dataclasses import dataclass from src.config._config import settings @dataclass class AppOptions: log: LogOptions server: ServerOptions @dataclass class LogOptions: level: str file_path: str enqueue: bool rotation: int | str retention: int encoding: str @dataclass class ServerOptions: port: int options = AppOptions(log=LogOptions(**settings.log), server=ServerOptions(**settings.server))
在main.py文件中定义API:
import uvicorn from fastapi import FastAPI, Response, status import src.my_logger as mylogger from src.config.app_options import options app = FastAPI(title="API-Demo", debug=__debug__) @app.get("/healthcheck") @app.head("/healthcheck") def health_check(): return "healthcheck" if __name__ == "__main__": mylogger.init_log() log_config = mylogger.replace_uvicorn_log(uvicorn.config.LOGGING_CONFIG) mylogger.logger.info("服务已启动,监听端口:{port}", port=options.server.port) uvicorn.run(app, host="0.0.0.0", port=options.server.port, log_config=log_config)
启动API服务,输出如下日志:
可以看到,已经使用loguru成功替换掉了内置的logging模块。