BiSheng v2.6.0 指标日志可观测性(F042)实战:结构化日志埋点与监控层解析全解析
2026/9/16 19:16:46 网站建设 项目流程

BiSheng v2.6.0 指标日志可观测性(F042)实战:结构化日志埋点与监控层解析全解析

【免费下载链接】bishengBISHENG is an open LLM devops platform for next generation Enterprise AI applications. Powerful and comprehensive features include: GenAI workflow, RAG, Agent, Unified model management, Evaluation, SFT, Dataset Management, Enterprise-level System Management, Observability and more.项目地址: https://gitcode.com/GitHub_Trending/bi/bisheng

导读

本文围绕 BiSheng(开源 LLM DevOps 平台)v2.6.0 的042-metric-log-observability(F042)Feature,完整讲解"结构化日志埋点 + 监控层解析"这一可观测性方案的落地全貌:后端如何以零侵入、零阻塞的方式,把数据库、对象存储、模型调用、E+ 站内信四类子系统的原始测量打成契约化的BS_METRIC日志行,再由外部日志管线(ELK / Loki / ES)聚合成 P95、QPS、成功率等指标。读完本文,你将掌握MetricLogConf配置、emit_metric埋点工具、四类埋点接入位置、日志契约字段字典、监控层查询示例,以及本方案背后 12 个"反直觉事实"与设计决策,可直接用于部署观测或复刻同类埋点体系。


1. Feature 定位:做什么、不做什么

F042 的目标非常聚焦:为四类子系统埋结构化指标日志,由外部监控层采集并解析出指标。

指标域要覆盖的指标
数据库查询耗时 P95、QPS、慢查询明细、连接池等待数
对象存储上传/下载请求成功率(排除 401/403 签证过期,只计超时/5xx/连接失败)、上传/下载耗时
模型调用TTFT(首 Token 延迟)、调用成功/失败
E+ 站内信接口调用事件(成功/失败/被短路、耗时)

同时明确列出非目标(design.md §1):

  • 引入prometheus_client/metrics端点、搭 Grafana——指标的聚合、存储、看板、告警全部是监控层的职责,后端只负责"把每条原始测量打成契约化日志";
  • 做进程内百分位计算(预算好的 P95 跨进程不可合并,详见 §3 决策 2);
  • 改动模型调用现有的 ES telemetry 链路(仅并行补一行日志)。

该 Feature 的交付状态:design.md 与 tasks.md 均已过审,实现 12/12 完成(Wave 1 + Wave 2 + Wave 3 全部 ✅),各任务说明见 tasks.md。


2. 方案选型:为什么是"写日志"而不是 Prometheus

design.md §3 对三种交付方式做了完整对比(决策 1),这是理解整个 Feature 的前提:

方案优点缺点
A. 结构化日志 + 监控层解析零新依赖;对多进程天然透明;复用已有HTTP_ACCESS_METRIC先例依赖外部日志管线的解析/聚合能力;指标有采集延迟
B. Prometheus/metrics进程内注册表业界标准,histogram_quantile算 P95多进程需 multiproc 共享目录;Celery/Linsight worker 非 HTTP 服务,暴露/metrics需额外端口或 Pushgateway;引入新依赖
C. 复用现有 ES telemetry 事件流模型调用已在用,跨进程天然DB 超高频不能逐条写 ES;需进程内预聚合反而更重

选定 A。核心原因是 BiSheng 采用多进程部署(FastAPI web 进程 + Celery workers + Linsight worker),大量 DB 查询、模型调用、文件存储发生在 worker 内,而日志方案各进程各写各的、采集端集中汇,一次性绕开了多进程/metrics聚合的全部复杂度。

三个派生决策同样关键:

  • 决策 2(打原始测量值):进程内算好的 P95 跨进程不可合并——把各 worker 的 P95 再平均/取 max 都是错的;只有原始样本(或直方图桶计数)能跨进程汇总成一个池子算真实全局 P95。这正是"写日志、监控层聚合"分工的技术前提。
  • 决策 3(DB 指标拆两种日志行):高 QPS 下每条 SQL 一行日志量不可接受;纯"仅慢查询"又算不出 QPS 和完整 P95。最终采用"慢查询明细行 + 周期汇总行":慢查询逐条打(含op/elapsed_ms供排查),另每进程每 N 秒打一行汇总,携带count(→QPS)+ 延迟直方图桶计数(→P95)。
  • 决策 4(对象存储成功率口径):401/403 是签证/权限过期的预期场景,不代表存储服务不可用,单列result=excluded不进失败分母,只有超时/5xx/连接失败算result=error

另外还有决策 5(埋点收敛点)决策 6(连接池等待数用饱和度反推),分别见 §5 与 §6.2。


3. 配置:MetricLogConf参数详解与运维开关

F042 的所有行为都由MetricLogConf配置类驱动(T001),定义于 src/backend/bisheng/core/config/settings.py#L665-L689,作为metric_log字段挂到全局Settings(settings.py#L756)。

配置键类型默认值说明
enabledbooltrue总开关,关闭后不打任何BS_METRIC
dbbooltrue域开关:控制db_query/db_query_agg/db_pool三类 DB 指标
obj_storagebooltrue域开关:控制对象存储 put/get 指标
model_invokebooltrue域开关:控制模型调用 TTFT/status 指标
eplusbooltrue域开关:控制 E+ 站内信调用事件指标
db_slow_query_msint200慢查询明细阈值(毫秒),查询耗时 ≥ 该值才打db_query明细行
db_agg_window_sint10db_query_agg汇总行 flush 与db_pool池采样窗口(秒)

config.yaml中对应写入(docker/bisheng/config/config.yaml 的部署形态参考):

metric_log: enabled: true db: true obj_storage: true model_invoke: true eplus: true db_slow_query_ms: 200 db_agg_window_s: 10

从源码实现看,配置读取是懒加载 + fail-open_get_conf()在启动极早期配置未就绪时返回None,此时_domain_enabled默认放行(conf is None → True),避免启动阶段埋点静默丢失;未知/新增 domain 也默认放行(见 metric_log.py#L44-L62)。record_db_query在读开关时同样做了双重判定——enableddb任一关闭即整条链路零开销返回(metric_log.py#L267-L269)。


4. 核心工具:metric_log.py的格式化、直方图与池采样

T002/T003 交付了埋点核心工具模块 src/backend/bisheng/common/services/metric_log.py,职责边界极其清晰:只负责格式化并输出一行日志,所有聚合(P95/QPS/成功率)都留给监控层

4.1emit_metric:统一入口与 logfmt 转义

def emit_metric(domain: str, **fields) -> None: """Emit one ``BS_METRIC`` line for ``domain``. Best-effort: never raises."""

格式化规则(与测试 test_metric_log.py 一一对应):

  • 固定 markerBS_METRIC+domain=<域>开头;
  • None字段省略不打(解析时按缺省处理);
  • bool 渲染为1/0
  • float 四舍五入到 3 位小数(5200.456789 → 5200.457),且不出现科学计数法;
  • 含空格、"=、换行、制表符等特殊字符的字符串值加双引号并转义(\\\"\"、换行/制表符→空格),保证整行 logfmt 可解析。

最关键的设计约束——零阻塞与异常隔离(design §5 坑 4)emit_metric整体包裹 try/except,埋点任何失败只打一条 debug 日志、绝不向调用方抛异常,绝不改变任何原有控制流/返回值/异常语义。测试test_emit_metric_never_raises_on_bad_value专门验证了这一点(传入__str__会抛异常的畸形对象也不会导致崩溃)。这是因为 DM 的dmAsync是"假异步"(内部同步阻塞事件循环),埋点里任何同步 IO / 锁竞争都会放大成 worker 冻结——所以埋点只做内存计数 + 非阻塞日志,周期 flush 不加全局锁。

4.2DbQueryHistogram:用日志搬运 Prometheus histogram

DB 查询延迟直方图是"DB 双行"方案(决策 3)的核心数据结构(metric_log.py#L111-L163):

  • 桶上界(毫秒):5, 10, 25, 50, 100, 250, 500, 1000++inf
  • record(elapsed_ms, now):O(log 桶数) 的bisect_left定位,边界值归属当前桶(elapsed == bound落在该桶,Prometheus<=语义),全程加锁线程安全(cursor 事件在多线程触发);
  • maybe_flush(now, window_s):窗口期满返回window_s / count / sum_ms / le_5 ... le_inf聚合字典并清零,否则返回Nonele_*是累积桶le_10le_5le_inf == count),累积计数跨进程、跨窗口可直接相加——这正是"监控层 sum 后histogram_quantile(0.95)得真实全局 P95、count/window得 QPS"的技术前提;
  • 计数器挂在模块级单例db_histogram(design §5 坑 7:SQLAlchemysessionmaker每次调用新建、只有 engine 是单例,计数器若挂 session 会丢数据)。

时间注入约定nowwindow_s均由调用方传入,本模块不读时钟——保证窗口可确定性测试(测试中window_s=0可强制每次查询立即 flush)。

4.3maybe_emit_pool_gauge:连接池饱和度采样

连接池"等待数"用饱和度反推而非直接计数(决策 6,design §5 坑 8):SQLAlchemy 不提供"当前排队数"的公开 API,PoolEvents.checkout拿到连接之后才触发、拿不到等待时长。实现(metric_log.py#L198-L230):

  • _PoolSampler每窗口至多采样一次;
  • 只读稳定的公开方法checkedout() / checkedin() / size()(坑 10:pool.overflow()语义随版本可能为负,max_overflow无公开方法,从DatabasePoolConf读);
  • capacity = size + max_overflowat_capacity = checked_out >= capacity
  • 异步 engine 的池要从async_engine.sync_engine.pool(坑 9),AsyncAdaptedQueuePool同样有这三个方法;
  • 没有checkedout方法的池(SQLite 开发/测试用的StaticPool/SingletonThreadPool)静默跳过,避免日志噪音。

4.4sql_op:只取首关键词,永不记 SQL 原文

坑 11 是最重要的安全约束:db_query若打完整 SQL 文本/bind 参数,会泄露密码等敏感值(违反docs/constitution.mdC6)、PII,且大IN(...)参数序列化本身极慢(实测 memory 场景 3.6w 参数序列化耗时 11.9s)。因此sql_op(statement)只取 SQL 前缀首个关键词(SELECT/INSERT/UPDATE/DELETE,大小写不敏感),否则返回OTHER,语句体与参数值立即丢弃、永不打日志(metric_log.py#L240-L252)。

4.5record_db_query:DB 指标完整管线

这是 DB 埋点的统一接线函数(metric_log.py#L255-L287),一次查询事件驱动整条链路:

  • status="error":只打db_query ... status=error明细行并提前返回——失败查询不污染延迟直方图
  • 成功路径:耗时 ≥db_slow_query_msdb_query明细 → 直方图record→ 窗口期满maybe_flushdb_query_agg汇总行 → 触发db_pool池采样(同一时间窗驱动)。

5. 四类埋点接入:最细统一 choke point(Wave 2)

决策 5 是埋点落地的关键:既不在 ASGI/中间件层按请求埋(拿不到子系统内部粒度,且 Celery/Linsight worker 无请求上下文、完全覆盖不到),也不在每个调用点手动埋(调用点极多、必漏、维护灾难),而是选在各子系统唯一无旁路的统一边界

5.1 DB:SQLAlchemy cursor 事件 + session 超时捕获(T005)

在 src/backend/bisheng/core/database/connection.py 中,sync/async engine 创建后立即安装事件监听(sync engine 在 connection.py#L201、async engine 走async_engine.sync_engine在 connection.py#L218):

  • _install_db_metric_eventsbefore_cursor_execute记开始时间,after_cursor_execute计算耗时并调用record_db_query,异常路径以status="error"进入(connection.py#L22-L58);
  • _emit_pool_wait_timeout_if_needed:session 上下文捕获sqlalchemy.exc.TimeoutErrordb_pool engine=... result=wait_timeout后再重抛,不改原有异常语义(connection.py#L64-L70)。

事件监听走 SQLAlchemy 通用 event,对达梦(DM) + MySQL 双 DB 方言透明。

5.2 对象存储:MinioStorage方法层切面(T007)

在 src/backend/bisheng/core/storage/minio/minio_storage.py 的put_object* / get_object* / download_object_sync方法层统一切面:计时 + 分类 result——http_statusS3Error.response.statuserr_codeS3Error.code;401/403 →result=excluded(不进失败分母,决策 4);NoSuchKey归 ok 语义(业务侧已处理,不算 error)。

坑 6 的边界处理:minioS3Error是 frozen dataclass,逃逸存储层后重抛会掩盖真错——所以埋点必须在存储层边界内读取.code/.response.status(此时尚未逃逸),避免埋点读到已冻结的异常字段而崩溃。

5.3 模型调用:复用既有 finally,并行补一行(T009)

模型调用的 TTFT / status / is_stream早已在采集(写 ESMODEL_INVOKE)。T009 在 src/backend/bisheng/llm/domain/utils.py 的 4 个 wrapper 的 finally 中,与既有upload_telemetry_log并行补一行emit_metric("model_invoke", ...),复用已采集的first_token_cost_time / status / is_stream——不重复造轮子、不动 ES 链路(坑 5)。TelemetryCallback.on_llm_new_token提供首 token 时间点。

5.4 E+ 站内信:client 真实调用 + forwarder 白名单后 skip(T011)

  • src/backend/bisheng/notification/external/cofco_eplus_client.py 的send_textcard:成功/失败/异常分别打eplus_notify result=ok/error,携带biz_code / http_status / elapsed_ms / action
  • src/backend/bisheng/notification/forwarder.py 的maybe_forward_external过白名单后的收件人解析 skip 打result=skipped

坑 12(T011 偏差)maybe_forward_external每条站内信都会调用,而feature_disabled/not_in_whitelist分支几乎每条都命中——在这两个分支打 metric 会刷屏、淹没真实 E+ 事件。因此只 meter 过白名单后的收件人解析 skip(低频,仅 forwardable 消息才到),client 只 meter 真实调用(ok/error);eplus_notify另加reason(skipped) /err_code(异常) 字段。

四组埋点均受MetricLogConf对应域开关控制,且全部 additive + 异常隔离——共享基础设施文件的改动不改变任何原有控制流。验收标准统一为:关闭对应开关后,行为与改动前完全一致(tasks.md 开发模式)。


6. 对外契约:BS_METRIC日志行字典与监控层算法口径(T012)

契约文档为 docs/observability/metric-log-contract.md(新增交付物),是给监控团队的解析依据。统一行格式:

BS_METRIC domain=<域> key=value key=value ...

采集正则建议锚定BS_METRIC domain=,并用domain=精确 token(后接空格或行尾)分流,避免db_query误匹配db_query_agg

6.1 字段字典

domain触发时机字段
db_query单条 SQL 且elapsed_ms >= db_slow_query_ms(慢查询明细);失败查询也打op(SELECT/INSERT/UPDATE/DELETE/OTHER)elapsed_msstatus(ok/error)
db_query_agg每进程每db_agg_window_swindow_scountsum_msle_5 le_10 le_25 le_50 le_100 le_250 le_500 le_1000 le_inf(累积桶计数,单位 ms)
db_pool周期采样(由查询驱动);池等待超时时engine(sync/async)checked_outidlesizecapacityat_capacity(0/1)result(可选=wait_timeout)
obj_storage每次上传/下载完成op(put/get)result(ok/error/excluded)http_statuserr_codeelapsed_ms
model_invoke每次模型调用结束model_idstatus(success/failed)is_stream(0/1)ttft_mstotal_ms
eplus_notifyE+ 每次真实调用(ok/error);forwarder 过白名单后的收件人 skip(skipped)result(ok/error/skipped)http_statusbiz_codeerr_codeelapsed_msactionreason(skipped 时)

真实样例行(与测试断言完全一致):

BS_METRIC domain=db_query op=SELECT elapsed_ms=253.1 status=ok BS_METRIC domain=db_query_agg window_s=10 count=8421 sum_ms=41230 le_5=6100 le_10=7300 le_25=8000 le_50=8250 le_100=8360 le_250=8408 le_500=8418 le_1000=8420 le_inf=8421 BS_METRIC domain=db_pool engine=async checked_out=37 idle=63 size=100 capacity=120 at_capacity=0 BS_METRIC domain=obj_storage op=put result=ok http_status=200 elapsed_ms=45.2 BS_METRIC domain=obj_storage op=get result=error http_status=500 err_code=InternalError elapsed_ms=1203.4 BS_METRIC domain=model_invoke model_id=123 status=success is_stream=1 ttft_ms=340.0 total_ms=5200.0 BS_METRIC domain=eplus_notify result=ok http_status=200 biz_code=0 elapsed_ms=88 action=request_channel

6.2 指标算法口径(供监控层参考)

  • DB QPS=sum(db_query_agg.count) / 时间跨度秒(跨进程直接加);
  • DB 查询耗时 P95= 对db_query_aggle_*桶按进程/窗口求和后histogram_quantile(0.95, buckets),P50/P99 同理;
  • DB 慢查询 TopN / 错误率=db_query明细行按op分组;错误率 =count(status=error) / count(*)
  • 连接池饱和度=db_pool.checked_out / capacity排队告警=at_capacity==1持续 N 个采样,或出现db_pool result=wait_timeout(池已耗尽、等待超时);
  • 存储成功率=count(result=ok) / (count(result=ok) + count(result=error)),按op(put/get) 分;result=excluded不进分母(401/403 签证过期);
  • 存储耗时= 对obj_storage.elapsed_ms原始样本按op分组算分位;
  • 模型 TTFT P95= 对model_invoke.ttft_ms原始样本算分位;调用成功率 =count(status=success) / count(*)
  • E+ 调用事件=eplus_notifyresult分组计数;elapsed_ms分位;错误细分看biz_code/err_code

6.3 查询示例(Loki LogQL / ES)

DB QPS(5 分钟速率):

sum(rate({app="bisheng"} |= "BS_METRIC domain=db_query_agg" | logfmt | unwrap count [5m]))

存储成功率(put,排除 excluded):

sum(count_over_time({app="bisheng"} |= "BS_METRIC domain=obj_storage" | logfmt | op="put" | result="ok" [5m])) / sum(count_over_time({app="bisheng"} |= "BS_METRIC domain=obj_storage" | logfmt | op="put" | result=~"ok|error" [5m]))

模型 TTFT P95:

quantile_over_time(0.95, {app="bisheng"} |= "BS_METRIC domain=model_invoke" | logfmt | unwrap ttft_ms [5m])

ES 侧用 ingest/grok 把BS_METRIC domain=... k=v解析成结构化字段后:成功率filter result:ok / (result:ok OR result:error);耗时分位用percentilesagg onelapsed_ms;DB P95 对db_query_aggle_*sumagg 后在可视化层做 histogram_quantile(或转 Prometheus remote-write)。

契约变更约束:新增字段向后兼容可直接加;重命名/删除字段、改domain、改 markerBS_METRIC属破坏性变更,必须通知监控团队同步解析/告警规则。字段单位固定:*_ms恒为毫秒,le_*桶恒为累积计数。


7. 测试体系:从单元到真实引擎的验证

Feature 采用后端 Test-First(务实版)开发模式,测试全部落在新增目录 src/backend/test/metric_log/,asyncio_mode=auto

测试文件覆盖内容(对应任务)
test_metric_log.py(T002)emit_metric格式化/转义/字段省略/布尔与浮点渲染;总开关与域开关 gating(含db_query/db_query_agg/db_pool共享db开关);never-raises 兜底;DbQueryHistogram桶归类(含边界值elapsed==bound)、窗口 flush/清零、空窗口不 flush;sql_op首关键词解析(含WITH cte→ OTHER、空串 → OTHER)
test_db_metric.py(T004)record_db_query慢查询明细/快查询只进直方图/错误行不进直方图/关闭开关零输出;在真实 SQLite engine上安装事件后跑真实 SQL 断言db_query+db_query_agg;池采样字段齐全;TimeoutErrordb_pool result=wait_timeout(非 TimeoutError 不打)
test_storage_metric.py(T006)mock minio client:put/get 成功打result=ok http_status=200;500/超时/连接错 →result=errorerr_code/http_status401/403 →result=excludedelapsed_ms存在
test_model_metric.py(T008)mock 流式/非流式模型调用,断言 finally 产model_invoke行含ttft_ms/status/is_stream/total_ms/model_id;失败路径status=failed
test_eplus_metric.py(T010)mock httpx:send_textcard成功打eplus_notify result=ok;失败/异常 →result=errormaybe_forward_external各短路分支 →result=skipped
test_e2e_metric_log.pye2e 场景验证(CI 运行)

值得一提的测试设计细节:capture_metric_logs用 loguru sink 捕获日志消息体做断言;_line_for用精确 domain token 边界匹配避免db_query误命中db_query_agg;fixture 自动重置模块级db_histogram/_pool_sampler单例,保证测试间隔离——这些细节本身也在验证"契约可被精确解析"。


8. 手动验证与运维排障

design.md §7 给出了完整的手动验证路径:本地起后端后制造流量,再 grep 日志。

cd src/backend && uv run uvicorn bisheng.main:app grep 'BS_METRIC domain=' <stdout / 日志文件>

分域确认:

  • domain=db_query_agg:每db_agg_window_s秒一行、桶计数非零 → QPS/P95 数据源正常;
  • domain=db_pool:采样字段齐全(checked_out/idle/size/capacity/at_capacity);
  • 上传/下载文件看domain=obj_storage
  • 跑一次模型调用看domain=model_invoke(与既有 ES telemetry 并存);
  • 触发一次站内信看domain=eplus_notify

强制产出慢查询明细的小技巧:把db_slow_query_ms调小(如 1ms),几乎所有查询都会打db_query明细行,便于排查。可观测性自身的健康度看emit_metric是否有异常被吞没(应只有 debug 级兜底日志,业务主流程不受任何影响)。


9. 已知坑与反直觉事实速查(design §5 全表)

#反直觉事实后果处理
1进程内算好的 P95 跨进程不可合并多 worker 下 P95 完全失真只打原始值/桶计数,聚合交监控层
2纯"仅慢查询"日志算不出 QPS 与完整 P95监控层拿不到 QPSDB 必须配周期汇总行db_query_agg
3401/403 是签证过期的预期场景,非服务故障计入失败率污染 SLA、误告警result=excluded单列,不进失败分母
4DMdmAsync是假异步(内部同步阻塞事件循环)埋点里任何同步 IO/锁竞争放大成 worker 冻结emit_metric零阻塞:内存计数 + 非阻塞日志,flush 不加全局锁
5模型调用 TTFT/status 已在采集(写 ES)重复造轮子、埋错地方复用 4 wrapper 的 finally,仅并行补日志行
6minioS3Error是 frozen dataclass,逃逸后重抛掩盖真错埋点在存储层外读字段会崩在存储层边界内读.code/.response.status
7sessionmaker每次新建,只有 engine 是单例计数器挂 session 会丢数据计数器/直方图挂模块级单例,按进程聚合
8SQLAlchemy 无"当前排队数"公开 API,checkout在拿到连接后触发直接拿等待数/等待时长无门饱和度反推 + 捕获TimeoutError;精确wait_ms需子类化QueuePool._do_get(档位 2)
9异步池要从async_engine.sync_engine.pool直接取报错/拿错对象采样处对 async 走sync_engine.pool
10pool.overflow()语义随版本可能为负;max_overflow无公开方法容量算错只用checkedout()/checkedin()/size()max_overflowDatabasePoolConf
11打完整 SQL/参数会泄密(C6)且大IN序列化极慢(3.6w 参数 11.9s)泄密 + 日志爆炸 + 拖慢主流程只提取op首关键词,永不记参数值
12maybe_forward_external对每条站内信调用,feature_disabled/not_in_whitelist几乎每条命中高频刷屏淹没真实事件只 meter 过白名单后的收件人 skip;client 只 meter 真实调用

10. 后续规划与当前边界(design §8)

  • 暂不做:进程内/metrics端点、Grafana 看板、告警规则(监控层职责);
  • 暂不做:存储/模型也走"周期直方图汇总"——当前它们是低中频,原始样本足够;若未来 QPS 上升、日志量成问题,再照 DB 的*_agg模式扩展;
  • 暂不做:连接池精确等待时长wait_ms全分布 / "此刻几个在排队"精确计数(档位 2,需子类化QueuePool._do_get,同步+异步各注入一次);当前饱和度 Gauge(档位 1)+ 等待超时计数已足够;
  • 触发重写:若企业无集中日志管线、要求进程内暴露指标端点 → 迁移到 Prometheus multiproc 方案(决策 1 的方案 B)。

11. 实现偏差记录(tasks.md 附录)

  • T011 偏差:E+ skipped 只在 forwarder 过白名单后的收件人解析 skip 打,feature_disabled/not_in_whitelist不打(高频刷屏,坑 12);eplus_notify增加reason(skipped) /err_code(异常) 字段。
  • T003 实现细节:直方图类公开命名为DbQueryHistogram(便于单测)+ 模块级单例db_histogram;方法签名为record(elapsed_ms, now)/maybe_flush(now, window_s)/maybe_emit_pool_gauge(now, pools, window_s)——now/window_s均由调用方传入以保证窗口可确定性测试;另新增sql_op(statement)(只取首关键词)。

以上偏差均为纯实现细节,不改变 design.md 的任何决策。


总结

F042 是一个"约定大于实现"的可观测性落地范本:后端侧用不到 300 行的metric_log.py+ 4 处收敛点埋点 + 1 份契约文档,换来了 DB/对象存储/模型/E+ 四类子系统的完整指标覆盖,且全程零新依赖、零阻塞、不改变任何业务语义。其精髓在于三条边界划分:埋点与聚合的边界(后端打原始测量,监控层算 P95/QPS/成功率)、慢查询明细与周期汇总的边界(解决高 QPS 下的日志量与指标可计算性的矛盾)、契约与实现的边界BS_METRIC一经发布即为对外契约,变更需通知监控团队)。对于任何需要为多进程架构补充可观测性的团队,这份 design + tasks + contract 的组合都是值得直接参考的实践。

【免费下载链接】bishengBISHENG is an open LLM devops platform for next generation Enterprise AI applications. Powerful and comprehensive features include: GenAI workflow, RAG, Agent, Unified model management, Evaluation, SFT, Dataset Management, Enterprise-level System Management, Observability and more.项目地址: https://gitcode.com/GitHub_Trending/bi/bisheng

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询