Skip to content

refactor(go)!: 日志改 stdlib log/slog 仅 kv 传参 + spec 契约收敛与四语言 span_id 预留 - #4

Merged
TangJia025 merged 5 commits into
mainfrom
feat/2061-log-kv-spanid
Sep 10, 2026
Merged

refactor(go)!: 日志改 stdlib log/slog 仅 kv 传参 + spec 契约收敛与四语言 span_id 预留#4
TangJia025 merged 5 commits into
mainfrom
feat/2061-log-kv-spanid

Conversation

@TangJia025

Copy link
Copy Markdown
Contributor

承接 #2061。PR #1 合入后,在 spec 契约与四语言 SDK 上做的第二轮收敛,含破坏性 API 变更(Go 侧)。

spec 契约

  • time 固定毫秒精度:Go 由 RFC3339Nano 改为固定 3 位小数,与 Python(.%03dZ) / Node(toISOString) 对齐。纳秒对日志无实际收益,毫秒换来稳定的 LTS 正则。
  • 新增 span_id 预留位log-format.md / common-fields.md 增加字段,二期 trace 接入前恒空、有值才输出;并明确 request_id / trace_id / span_id 均为高基数值,禁止用作 metrics label
  • error 字段放宽:不再限定「原因链文本」,允许带异常上下文的语言(Python 等)写入完整 traceback(多行,JSON 转义为 \n,仍是单行 JSON)。同时补注该字段不适合聚合(取值逐次不同),按错误类型聚合请用 msg 常量 + 业务字段另作维度。
  • community 取源修正:旧 infra-common / service.md 已过时,改取 infrastructureservice.yaml 顶层 communities,枚举刷新为 18 项,附可复现的 awk 同步命令。
  • 新增「msg 常量 + kv 承载数据」传参约定:msg 内嵌值会让每行都不同,无法聚合计数、告警规则失效,禁止 printf 风格

Go:日志改用 stdlib log/slog 自定义 Handler(破坏性变更

对齐主流(stdlib log/slog、kratos v3)——日志 API 只保留 kv 传参Info(msg string, args ...any) 交替键值,去掉 printf 变体。

  • log/log.go 重写为 kratos v3 形态的包级 API:Level = slog.Level 别名、SetDefault/DefaultX/XContext 成对、ParseLevel(容错 FATAL/WARNING)、LevelFatal = slog.LevelError + 4
  • 新增 log/handler.go:自定义 slog.Handler,单行扁平 JSON → stdout。Handle(ctx, record) 能拿到 ctx,故 InfoContext(ctx, ...) 自动从 sdkctx.From(ctx) 附加请求字段,调用点无需 WithRequest 绑定
  • error 值统一转 .Error() 文本:encoding/json 此前把多数 error 渲染成 {},这是本次修掉的根因。
  • logger 字段(调用位置 service/webhook.go:51)用 runtime.CallersFrames 取帧 —— runtime.Callers 返回的是返回地址,FuncForPC(pc).FileLine 会归到上一层帧CallersFrames 才能正确解释(并展开内联帧)。代码注释已写明避免回归。路径只取末两段,不泄漏构建机绝对路径。
  • sdkctx 增加 SpanID 预留位,日志侧有值才输出。

⚠️ Go SDK 尚未发布、仓库内零外部引用,故未保留兼容 shim;业务侧 printf 调用需改为 kv。

Python / Node / Java:span_id 预留注入

首期只保证可写可透传、有值才输出:

  • Python_context.Requestspan_id 与访问器,bind() 合并;log.py 非空才写。
  • Nodecontext.jsspanId()bindRequest 合并;log.js 非空才写。
  • JavaRequestContextspanId(4 参 of/push 重载,保留 3 参兼容首期用法)、currentSpanId()asMdcFieldsspan_idObsLoggingMDC_SPAN_ID 写入与清理;logback-json.xmlmdc provider 增键。

验证

语言 命令 结果
Go go vet ./... && go test -race -count=1 ./... 5 包全 ok(含并发写测试)
Python pytest -q 15 passed
Node npm test 10 pass / 0 fail
Java mvn -B test Tests run: 12, Failures: 0, Errors: 0

Go 本地为 1.26.7,CI 走 go-version-file(1.22);新代码未使用任何 1.23+ 语言特性或标准库,结果可外推。

🤖 Generated with Claude Code

AI 使用声明

当前 PR 是否有 AI 参与:

    1. AI Agent 平台:Claude Code
    2. AI 模型:deepseek-v4-flash
    3. Prompt 上下文:承接 #2061 SDK 实现(PR feat: obs-sdk 实现 #2061(spec 契约 + Go/Python/Java/Node SDK + UT) #1 已合入)的后续收敛 —— 用户确认「日志仅支持 kv 与主流看齐」,并要求修正 community 取源、给四语言补 span_id 注入、放宽 error 字段契约以消除 Python traceback 冲突

TangJia025 and others added 3 commits September 10, 2026 16:00
四语言一致性收敛,spec 作为唯一契约源先行:

- time 固定毫秒精度(3 位小数):Go 由 RFC3339Nano 改为固定 3 位,与
  Python(%03d) / Node(toISOString) 对齐;纳秒对日志无实际收益,毫秒换来
  稳定的 LTS 正则。
- 新增 span_id:log-format / common-fields 增加预留位,二期 trace 接入前
  恒空、有值才输出;明确 request_id/trace_id/span_id 均为高基数值,
  禁止用作 metrics label。
- error 字段放宽:不再限定为「原因链文本」,允许带异常上下文的语言
  (Python 等)写入完整 traceback(多行,JSON 转义为 \n,仍是单行 JSON);
  同时补注该字段不适合聚合(取值逐次不同),按错误类型聚合请用 msg 常量
  + 业务字段另作维度。
- community 取源修正:旧 infra-common / service.md 已过时,改取
  opensourceways/infrastructure 仓 service.yaml 顶层 communities,
  枚举刷新为 18 项,并附可复现的 awk 同步命令(含 services: 缩进 - name:
  是服务名而非社区名的坑位说明)。
- 新增「msg 常量 + kv 承载数据」传参约定:msg 内嵌值会让每行都不同,
  无法聚合计数、告警规则失效,明确禁止 printf 风格。

Co-Authored-By: Claude Code <noreply@anthropic.com>
对齐主流(stdlib log/slog、kratos v3):日志 API 只保留 kv 传参,
Info(msg string, args ...any) 交替键值,去掉 printf 变体。msg 内嵌值会让
每行都不同、无法聚合计数,这是契约层已禁止 printf 风格的实现侧落地。

- log/log.go 重写为 kratos v3 形态的包级 API:Level = slog.Level 别名、
  SetDefault/Default、X/XContext 成对、ParseLevel(容错 FATAL/WARNING)、
  LevelFatal = slog.LevelError + 4。各导出helper 直接调用同一 emitter,
  保证 runtime.Callers 的固定 skip 计数成立。
- 新增 log/handler.go:自定义 slog.Handler,单行扁平 JSON 写 stdout。
  Handle(ctx, record) 能拿到 ctx,故 InfoContext(ctx, ...) 可自动从
  sdkctx.From(ctx) 附加请求字段,调用点无需 WithRequest 绑定 —— 这正是
  slog 方案相对旧 API 的 DX 收益。
- 字段严格按契约顺序输出;community 取请求上下文覆盖,未覆盖回退部署默认。
- logger 字段(调用位置 service/webhook.go:51)用 runtime.CallersFrames
  取帧:runtime.Callers 返回的是返回地址,FuncForPC(pc).FileLine 会归到
  上一层帧;CallersFrames 才能正确解释(并展开内联帧)。代码注释已写明,
  避免回归。路径只取末两段,不泄漏构建机绝对路径。
- error 值统一转 .Error() 文本:encoding/json 此前把多数 error 渲染成 {},
  这是本次修掉的根因。
- appendJSONString 不转义 <>&(与 slog JSONHandler 一致,可读性优先),
  但非法 UTF-8 回退 json.Marshal 以免产出非法 JSON。
- sdkctx 增加 SpanID 预留位,日志侧有值才输出。
- README 同步为 slog 方案与 kv 用法。

Co-Authored-By: Claude Code <noreply@anthropic.com>
三语言对齐 Go/spec 的 span_id 预留位,首期只保证可写可透传、有值才输出:

- Python:_context.Request 增 span_id 字段与 span_id() 访问器,bind() 合并;
  log.py 非空才写入 span_id。
- Node:context.js 增 spanId() 访问器,bindRequest 合并 spanId;
  log.js 非空才写入 span_id。
- Java:RequestContext 增 spanId(4 参 of/push 重载,保留 3 参以兼容首期用法)、
  currentSpanId()、asMdcFields 含 span_id;ObsLogging 增 MDC_SPAN_ID 写入与
  清理;logback-json.xml 的 mdc provider 增 span_id 键。
- 各语言补测试:含/不含 span_id 两条路径、与 trace_id 互不干扰、
  span-only bind 不误产出 trace_id;README 字段清单同步。

Co-Authored-By: Claude Code <noreply@anthropic.com>
@opensourceways-bot

Copy link
Copy Markdown

Welcome To opensourceways Community

Hey @TangJia025 , thanks for your contribution to the community.

Bot Usage Manual

I'm the Bot here serving you. You can find the instructions on how to interact with me at Here . That means you can comment below every pull request or issue to trigger Bot Commands.

Contact Guide

If you have any questions, please contact the SIG: infratructure ,
and any of the maintainers: @GeorgeCao-hw, @TangJia025, @pkking, @zhongjun2 ,
and any of the committers: @GeorgeCao-hw, @TangJia025, @pkking, @zkhzkhz .

@opensourceways-bot

Copy link
Copy Markdown

CLA Signature Pass

TangJia025, thanks for your pull request. All authors of the commits have signed the CLA. 👍

@opensourceways-bot

Copy link
Copy Markdown

Linking Issue Notice

@TangJia025 , the pull request must be linked to at least one issue.
If an issue has already been linked, but the needs-issue label remains, you can remove the label by commenting /check-issue .

TangJia025 and others added 2 commits September 10, 2026 16:14
Co-Authored-By: Claude Code <noreply@anthropic.com>
此前 Java 侧只把字段写进 MDC,真正决定输出的 encoder provider 用的是默认配置,
实测(examples/logback-json.xml)与契约存在五处不符:

  @timestamp(纳秒 + 本地时区 +08:00)、level 大写 INFO、message、logger_name,
  且未配 <stackTrace/> 时 throwable 被整条丢弃 —— 错误日志直接丢失原因。

纯 XML 配置无法修好:<logLevel/> 只能输出大写(7.4 无大小写配置项,
<logLevelValue/> 输出的是数字 20000),字段名走 LogstashFieldNames 而
LoggingEventCompositeJsonEncoder 没有 setFieldNames。故固定字段这一层的所有权
收到 SDK 内。

- 新增 log.ObsJsonProvider(logstash AbstractJsonProvider 实现):按契约顺序输出
  time/level/msg/service/env/instance/community/request_id/trace_id/span_id/logger/error。
  time 由 DateTimeFormatter 固定毫秒 UTC + XXX 输出 Z(不受 JVM 时区影响);
  level 小写(TRACE 归入 debug);logger 取调用位置 file:line,对齐 Go 侧语义;
  error 用 ThrowableProxyUtil.asString 输出完整堆栈。JSON 转义交给 Jackson 的
  JsonGenerator,不手写转义。空值省略,预留位(trace_id/span_id)无值即不出现。
  覆写 prepareForDeferredProcessing 提前提取调用点,兼容 AsyncAppender。
- examples/logback-json.xml 简化为一行 <provider class="...ObsJsonProvider"/>,
  并为业务字段留出「在本 provider 之后追加 <mdc>/<keyValuePairs/>」的注释位。
- pom 增 jackson-core(provided,2.15.2 与 logstash 7.4 对齐):provider 直接实现
  其接口、直接用 JsonGenerator,不应依赖传递依赖。
- 新增 ObsJsonProviderTest:直接加载 examples/logback-json.xml 跑真实 encoder,
  断言字段名/顺序/时间格式(单测内故意用非 UTC 时区验证)/级别小写/logger 为调用
  位置/MDC 取值/异常堆栈保留。仅断言 MDC 是测不出这批问题的。
- README 的示例 JSON 与字段说明同步(原示例即错误输出)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
@opensourceways-bot

Copy link
Copy Markdown

CLA Signature Pass

TangJia025, thanks for your pull request. All authors of the commits have signed the CLA. 👍

@TangJia025
TangJia025 merged commit 83eaf05 into main Sep 10, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants