opensourceways 微服务可观测薄封装 SDK 的 Java 实现,契约见根目录 spec/。 语义与 Go / Python / Node SDK 对齐:
- community 双层注入:
service/env/instance为部署级 const label,community建模为普通可变 label, 值取请求上下文覆盖(可信判定点写入),未覆盖回退部署默认 —— 「注册一次两用」。 trace_id/span_id预留:首期只保证字段可写可透传,不落 span。- 日志:结构化 JSON(logback + logstash JSON encoder,MDC 输出固定键)。
- 指标:Micrometer + Prometheus registry 薄封装(业务 counter/gauge/histogram); HTTP 服务端指标不重复造轮子,Java 服务走 Spring Boot Actuator + Micrometer 官方 server instrumentation。
| 组件 | 说明 |
|---|---|
context.RequestContext |
请求级上下文(community/request_id/trace_id/span_id),ThreadLocal 作用域句柄,对齐其它语言的 sdkctx/contextvars/ALS |
log.ObsLogging |
部署默认字段 + 请求覆盖字段写入 SLF4J MDC |
log.ObsJsonProvider |
logstash-logback-encoder 的 provider:按契约输出固定字段(时间/级别/字段名/顺序/异常堆栈) |
ObsMetrics |
业务指标装配(common tags + community 动态 label + namespace 前缀) |
middleware.ObsFilter |
可选 Servlet Filter:注入 request_id + 可信判定点解析 community → RequestContext + MDC |
JDK 17 + Maven:
mvn test当前开发机无 JDK/Maven,Java 代码未在本机编译运行;已按 Micrometer 1.13 公开 API 编写, 由仓库 CI(.github/workflows/ci.yml 的 java job)负责编译 + 跑
mvn test验证。 如 CI 暴露问题,以 CI 输出为准修复。
ObsSdkConfig cfg = ObsSdkConfig.builder()
.service("review") // 生产走 OBS_SERVICE 等环境变量,ObsSdkConfig.fromEnvironment()
.env("test")
.instance("pod-1")
.community("openeuler") // 部署级默认 community
.build();
ObsMetrics m = ObsMetrics.of(cfg);
// 业务 counter —— community 自动排首位,值取请求上下文覆盖,否则回退默认
ObsMetrics.CounterVec built = m.counter("built_releases", "发布的构建数", "kind");
built.inc(1, "tag");
// 请求上下文内再埋 → 该条 series 的 community 被覆盖为 mindspore
try (RequestContext.Scope scope = RequestContext.push("mindspore", "req-1", null)) {
built.inc(1, "tag");
}
// gauge / histogram(Timer 自动产出 _seconds_count/_sum,可配桶)
m.gauge("in_flight", "在飞请求数").set(3);
m.histogram("review_duration", "评审耗时", new double[]{0.1, 0.5}).observe(0.05);
// Prometheus text 快照(供自检 / 测试)
String text = m.text();命名说明:Micrometer 会自动为 Counter 追加
_total、为 Timer(秒基)追加_seconds, 因此 Java 侧传基础名(不带_total/_seconds后缀),导出名与其它语言 SDK 一致 (如built_releases_total、review_duration_seconds_count)。 跨服务共享 SDK 时给namespace(如"obs"),导出名变为obs_<metric>_total。
// 服务启动:
ObsLogging.init(ObsSdkConfig.fromEnvironment());
// 请求处理:先 init() 后每次请求在可信判定点解析后 push 即可
RequestContext.push(community, requestId, traceId); // try-with-resource 作用域Java 服务的服务端指标交给 Actuator 暴露(stater 对齐,SDK 不重复埋 HTTP 指标):
- 依赖
micrometer-registry-prometheus+ 引入spring-boot-starter-actuator。 - 把 SDK 的 registry 暴露成 bean 让 Actuator 托管(
PrometheusMeterRegistry会被 actuator 自动发现并挂到/actuator/prometheus):
@Bean
public PrometheusMeterRegistry prometheusRegistry(ObsSdkConfig obsConfig) {
return ObsMetrics.of(obsConfig).meterRegistry();
}application.yml:management.endpoints.web.exposure.include: prometheus。
在接入服务里把 SDK 的 business 指标注册到同一个 PrometheusMeterRegistry(上面 bean 的实例),
即可与 Actuator 的服务端指标合并暴露给 AOM 抓取。
// Spring Boot 注册,community resolver 必须来自可信判定点(路由前缀/认证主体/白名单),
// 不能裸读 URL/Header 当 community(见 spec/community-values.md)
@Bean
public FilterRegistrationBean<ObsFilter> obsFilter() {
FilterRegistrationBean<ObsFilter> reg = new FilterRegistrationBean<>();
reg.setFilter(new ObsFilter(req ->
req.getRequestURI().startsWith("/mindspore") ? "mindspore" : null));
reg.addUrlPatterns("/*");
reg.setOrder(Ordered.HIGHEST_PRECEDENCE);
return reg;
}把 examples/logback-json.xml 拷成接入服务的 logback 配置并引入 logstash-logback-encoder,
日志即输出单行 JSON,例:
{"time":"2026-09-10T08:13:42.725Z","level":"info","msg":"job done","service":"review","env":"test","instance":"pod-1","community":"openEuler","request_id":"req-1","logger":"ReviewSvc.java:51"}固定字段的顺序、取值与缺失规则均由 SDK 的 ObsJsonProvider 保证,接入方无需(也无法)逐项配置:
| 字段 | 说明 |
|---|---|
time |
固定毫秒精度 UTC,以 Z 结尾(不受 JVM 时区影响) |
level |
小写 debug / info / warn / error(TRACE 归入 debug) |
msg |
格式化后的消息 |
service / env / instance / community |
来自 MDC(ObsLogging.init 登记,community 可被请求上下文覆盖) |
request_id / trace_id / span_id |
请求级,来自 MDC;空值省略(后两者为二期预留) |
logger |
调用位置 文件:行号(对齐 Go 侧语义) |
error |
异常完整堆栈,仅在有 throwable 时出现 |
为什么不用 encoder 自带的 provider:
<logLevel/>只能输出大写INFO(7.4 无配置项可改 大小写,<logLevelValue/>输出的是数字),字段名走LogstashFieldNames而LoggingEventCompositeJsonEncoder没有setFieldNames,且不配<stackTrace/>时 throwable 会被整条丢弃。故固定字段这一层由 SDK 自己的 provider 承担。业务字段不属于固定字段,可在
ObsJsonProvider之后追加<mdc>/<keyValuePairs/>等 provider,输出会落在固定字段之后。
依赖:
logstash-logback-encoder与jackson-core在本 SDK 中为providedscope (ObsJsonProvider需要它们编译),运行时由接入服务提供,不随 SDK 传递。