ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

两个 Python 的冷技巧(2):用 ConfigParser 与 logging 配 TaoToken 的 ini 骨架

两个 Python 的冷技巧(2):用 ConfigParser 与 logging 配 TaoToken 的 ini 骨架 1. 为什么 ini logging 这套组合值得单独拎出来讲Python 项目里配置管理这件事很多人第一反应是.env或者 YAML但如果你写过那种「一个进程要按不同业务、不同房间、不同实例分别落日志」的服务就会发现ConfigParser加logging.config.fileConfig这套老组合其实非常顺手。它最大的价值在于配置文件里可以写占位符运行时再决定实际值日志文件名、日志级别、输出路径都能跟着命令行参数走不用改一行代码。这篇要落到的场景是 AI 工具接入 TaoToken。你可能会同时跑好几个小脚本一个做模型对话调试一个跑批量补全一个挂在后台做 Agent 任务。它们都需要读同一份 API 配置base_url、api_key、默认模型又都需要各自的日志文件方便排查。如果每个脚本里硬编码一遍改起来就是灾难。用一份config.ini管住公共配置用logging的 ini 配置管住日志分流是能直接抄走用的骨架。适合谁看已经会写 Python、但配置和日志还停留在print加手写open()的阶段或者你正在接 TaoToken 这类兼容 OpenAI 协议的服务想把手动测试脚本整理成稍微像样点的工程结构。下面所有代码都可以直接复制运行我按「先配 ini、再初始化 logging、然后读取校验、最后发一次真实请求验证」的顺序走每一步都有可观察的结果。2. TaoToken 前置拿到 base_url 和 key确认接入形态TaoToken 是一个大模型 API 聚合服务兼容 OpenAI 的接口协议所以你在 Python 里用openai这个库就能直接调不需要额外装什么奇怪的 SDK。对配置管理来说这意味着你只需要在 ini 里存三样东西base_url、api_key、model。先到控制台把 key 建出来。打开 https://taotoken.net/console 登录后在 API Keys 页面新建一个密钥复制出来先存到临时地方。这个 key 只会完整显示一次丢了就重新建。接入地址这块要分清两个用途地址官网入口https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentAPI 基址https://taotoken.net/api注意 API 基址后面不带 UTM 参数代码里填的就是https://taotoken.net/api。如果你用的是openai库base_url一般填到/api这一层就行库会自动拼/v1/chat/completions这类路径有些客户端要求填到/api/v1这个以你实际用的库文档为准报 404 的时候优先检查这里。模型名怎么选控制台里模型列表页会列出当前可用的模型标识直接抄那个字符串填进 ini 的model项。想先手动试一下模型通不通可以用模型对话页面 https://taotoken.net/model-chat 发一条消息确认账号和额度没问题再回到代码里配。如果你后面要长期跑编码类任务或者 Agent可以了解下 Coding Plan https://taotoken.net/coding-plan 它面向的是持续性的编码调用场景和这篇讲的配置骨架是互补的骨架负责把 key 和日志管好套餐负责把用量管好。3. 可复制配置config.ini 与 logging 初始化骨架先建目录结构我习惯这样放taotoken_demo/ ├── config.ini ├── app.py └── logs/logs/目录要提前建好logging不会自动帮你创建父目录这是新手最常踩的坑之一。3.1 config.ini 全文[DEFAULT] base_url https://taotoken.net/api model 你的模型名 log_dir logs log_level INFO [taotoken] api_key 你的APIKey timeout 30 max_retries 2 [logging] version 1 disable_existing_loggers false [formatters] keys standard [formatter_standard] format %(asctime)s | %(levelname)s | %(name)s | %(message)s datefmt %Y-%m-%d %H:%M:%S [handlers] keys console, file [handler_console] class StreamHandler level %(log_level)s formatter standard args (sys.stdout,) [handler_file] class handlers.TimedRotatingFileHandler level %(log_level)s formatter standard args (%(log_dir)s/taotoken.log, midnight, 1, 7, utf-8) [loggers] keys root, taotoken [logger_root] level %(log_level)s handlers console, file [logger_taotoken] level %(log_level)s handlers console, file qualname taotoken propagate 0这里有几个点值得单独说。[DEFAULT]节里的log_dir和log_level会被后面所有节继承所以handler_file的args里能直接写%(log_dir)slogging.config.fileConfig解析时会自动替换。TimedRotatingFileHandler的参数顺序是(filename, when, interval, backupCount, encoding)我写的是每天午夜切一次、保留 7 份、用 utf-8 编码中文日志不会乱码。propagate 0是为了防止taotoken这个 logger 的日志再往 root 冒泡一次否则你会看到每条日志打印两遍。3.2 app.py 初始化骨架import logging import logging.config import configparser from pathlib import Path BASE_DIR Path(__file__).resolve().parent CONFIG_PATH BASE_DIR / config.ini def load_config(path: Path) - configparser.ConfigParser: if not path.exists(): raise FileNotFoundError(f配置文件不存在: {path}) parser configparser.ConfigParser() parser.read(path, encodingutf-8) return parser def setup_logging(parser: configparser.ConfigParser) - logging.Logger: log_dir Path(parser.get(DEFAULT, log_dir)) if not log_dir.is_absolute(): log_dir BASE_DIR / log_dir log_dir.mkdir(parentsTrue, exist_okTrue) logging.config.fileConfig( CONFIG_PATH, defaults{log_dir: str(log_dir)}, disable_existing_loggersFalse, ) return logging.getLogger(taotoken) def main(): parser load_config(CONFIG_PATH) logger setup_logging(parser) api_key parser.get(taotoken, api_key) base_url parser.get(taotoken, base_url) model parser.get(taotoken, model) logger.info(配置加载完成 base_url%s model%s, base_url, model) logger.info(api_key 长度%d 前缀%s, len(api_key), api_key[:6]) if __name__ __main__: main()注意fileConfig的defaults参数它会把log_dir的绝对路径注入进去覆盖 ini 里的相对值。这样无论你从哪个工作目录启动脚本日志都落在项目下的logs/里不会跑到别处去。这就是ConfigParser那个defaults参数在运行时的用法和 ini 文件里[DEFAULT]节是两回事但效果是叠加的。4. 验证请求读取校验 日志落盘 真实调用光初始化不算数得跑出可观察的结果。分三步验证。4.1 第一步读取校验在main()里加一段校验逻辑把关键配置项检查一遍def validate(parser: configparser.ConfigParser, logger: logging.Logger) - None: required { taotoken: [api_key, base_url, model], } for section, keys in required.items(): if not parser.has_section(section): raise ValueError(f缺少配置节: {section}) for key in keys: value parser.get(section, key, fallback).strip() if not value or value.startswith(你的): raise ValueError(f配置项未填写: [{section}] {key}) logger.info(配置校验通过)运行python app.py控制台应该输出类似2025-01-01 12:00:00 | INFO | taotoken | 配置加载完成 base_urlhttps://taotoken.net/api modelxxx 2025-01-01 12:00:00 | INFO | taotoken | api_key 长度48 前缀sk-abc 2025-01-01 12:00:00 | INFO | taotoken | 配置校验通过同时logs/taotoken.log里应该有同样的三行。如果控制台有、文件没有检查logs/目录权限如果文件有、控制台没有检查handler_console的args是不是写成了(sys.stdout,)少了逗号会变成字符串而不是元组。4.2 第二步发一次真实请求装好openai库后把调用逻辑接上from openai import OpenAI def ask(parser, logger): client OpenAI( api_keyparser.get(taotoken, api_key), base_urlparser.get(taotoken, base_url), timeoutparser.getint(taotoken, timeout), max_retriesparser.getint(taotoken, max_retries), ) logger.info(开始请求模型) resp client.chat.completions.create( modelparser.get(taotoken, model), messages[{role: user, content: 用一句话说明什么是 ini 配置}], ) content resp.choices[0].message.content logger.info(请求成功返回长度%d, len(content)) logger.debug(返回内容: %s, content) return content跑通后日志里会出现「开始请求模型」和「请求成功返回长度xx」。如果卡在请求阶段先把timeout调大试试如果报 401说明 key 不对或者没填对如果报 404八成是base_url写错了确认是不是漏了/api或者多写了/v1。4.3 第三步确认日志轮转配置生效想验证TimedRotatingFileHandler真的在工作可以临时把 ini 里的when改成S、interval改成5跑几次请求后看logs/下是不是出现了带时间后缀的文件。验证完记得改回midnight不然日志文件会碎得很难看。5. 本篇常见错排查报错KeyError: formattersfileConfig要求 ini 里必须有[formatters]、[handlers]、[loggers]三个节且每个节里的keys要和你实际定义的节名对得上。少一个都会在初始化时直接抛异常。日志中文变乱码TimedRotatingFileHandler的args里最后一个参数是encoding不写就按系统默认编码走Windows 上容易出问题。加上utf-8就好。api_key读出来带引号ini 文件里不要给值加引号api_key sk-xxx读出来会连引号一起带上。直接写api_key sk-xxx。configparser把 key 名转成小写ConfigParser默认对选项名做小写化API_KEY和api_key会被当成同一个。如果你确实需要保留大小写初始化时传parser.optionxform str但一般没必要统一用小写更省心。fileConfig之后已有的 logger 失效disable_existing_loggers默认是True会把之前创建的 logger 全禁掉。我在 ini 里显式写了false如果你在别处也初始化过 logging记得保持这个设置。相对路径的坑ini 里写log_dir logsfileConfig解析时是相对于当前工作目录不是相对于 ini 文件。所以我在setup_logging里先把它转成绝对路径再通过defaults注入这样最稳。6. 把 key 和日志管好之后下一步做什么这套骨架跑通后你手里就有了一个能复用的配置底座换项目时只改config.ini代码不用动日志按天切、按模块分 logger排查问题时直接翻文件。接下来可以按你的实际用途分流如果你主要是在调模型、试 prompt直接去模型对话页面手动发几条比写代码快https://taotoken.net/model-chat如果你要把这套配置接进正式的编码工作流比如让脚本长期跑补全或 Agent 任务建议把 key 单独管理、配合套餐控制用量Coding Plan 的入口在这里https://taotoken.net/coding-plan如果你在接入过程中遇到鉴权、路径拼接、超时这类问题先去 API Keys 页面确认 key 状态再对照接入文档检查base_url和请求头https://taotoken.net/api-keys 和 https://taotoken.net/doc最后留一个我自己的习惯config.ini永远不进版本库仓库里只放一份config.ini.example把api_key留空。这样别人 clone 下来照着填就行也不会有人手滑把 key 提交上去。
返回列表