依据 coord-final-decisions §3.8 W1-W8 裁决与 president-final-rulings §2.15/§2.16/§2.19 完整实现网关硬化: - W1/W2: 错误码 GW_ 前缀 + ActionState 信封响应体 - W3: 全量替换为 log/slog 结构化日志 - W4: /readyz 并行 ping 9 下游 + 软失败规则 - W5: 7 个业务 Prometheus 指标 + /metrics 端点 - W6: tracer 资源属性补全(name/version/env/host) - W7: DevMode=true && ENV=production panic 防护 - W8: 保持共享 downstream 熔断 P2 RS256 升级:接入 shared-go/jwks.Fetcher(TTL 5min)。 P2.7+P3-P5 路由扩展:student/parent/messages/dashboard。 文档同步:README/01/02/known-issues,arch.db 已更新。 质量校验:go vet + build + test 均通过。
88 lines
2.5 KiB
Go
88 lines
2.5 KiB
Go
package observability
|
||
|
||
import (
|
||
"context"
|
||
"log/slog"
|
||
"net/url"
|
||
"time"
|
||
|
||
"go.opentelemetry.io/otel"
|
||
"go.opentelemetry.io/otel/attribute"
|
||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
|
||
"go.opentelemetry.io/otel/propagation"
|
||
"go.opentelemetry.io/otel/sdk/resource"
|
||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
|
||
)
|
||
|
||
// InitTracer 初始化 OpenTelemetry tracer(W6 裁决:资源属性补全)。
|
||
//
|
||
// 资源属性包含:service.name / service.version / deployment.environment / host.name。
|
||
// endpoint 为 "http://host:port" 格式(如 "http://localhost:4318");
|
||
// 为空时跳过初始化(tracing disabled)。
|
||
//
|
||
// 返回 shutdown 函数,应在服务退出时调用以 flush 待发送 span。
|
||
func InitTracer(serviceName, endpoint, env, version, hostName string) func() {
|
||
if endpoint == "" {
|
||
slog.Info("OTEL endpoint not set, tracing disabled")
|
||
return func() {}
|
||
}
|
||
|
||
u, err := url.Parse(endpoint)
|
||
if err != nil || u.Host == "" {
|
||
slog.Warn("invalid OTEL endpoint, tracing disabled", "endpoint", endpoint)
|
||
return func() {}
|
||
}
|
||
|
||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||
defer cancel()
|
||
|
||
exporter, err := otlptracehttp.New(ctx,
|
||
otlptracehttp.WithEndpoint(u.Host),
|
||
otlptracehttp.WithInsecure(),
|
||
)
|
||
if err != nil {
|
||
slog.Error("failed to create OTLP exporter, tracing disabled", "error", err)
|
||
return func() {}
|
||
}
|
||
|
||
// W6 裁决:资源属性补全(service.name + version + env + host)
|
||
res, err := resource.New(ctx,
|
||
resource.WithAttributes(
|
||
semconv.ServiceName(serviceName),
|
||
attribute.String("service.version", version),
|
||
attribute.String("deployment.environment", env),
|
||
attribute.String("host.name", hostName),
|
||
),
|
||
)
|
||
if err != nil {
|
||
slog.Error("failed to create resource", "error", err)
|
||
return func() {}
|
||
}
|
||
|
||
tp := sdktrace.NewTracerProvider(
|
||
sdktrace.WithBatcher(exporter),
|
||
sdktrace.WithResource(res),
|
||
)
|
||
otel.SetTracerProvider(tp)
|
||
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
|
||
propagation.TraceContext{},
|
||
propagation.Baggage{},
|
||
))
|
||
|
||
slog.Info("OpenTelemetry tracer initialized",
|
||
"service", serviceName,
|
||
"endpoint", u.Host,
|
||
"env", env,
|
||
"version", version,
|
||
"host", hostName,
|
||
)
|
||
return func() {
|
||
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||
defer cancel()
|
||
if err := tp.Shutdown(shutdownCtx); err != nil {
|
||
slog.Error("failed to shutdown tracer", "error", err)
|
||
}
|
||
}
|
||
}
|