Files
Edu/packages/shared-go/logger/logger.go
SpecialX faaaf29f67 docs: ai 协作文档体系重构与多 ai 仲裁结果落地
1.AI 协作文档体系重构(objections/worklines/contracts+matrix.md)

2.coord 仲裁文档(final-decisions/cross-review/final-rulings/orchestration)

3.各服务 01/02 文档补全

4.共享包初始化(shared-ts/shared-go/hooks/ui-components/ui-tokens)

5.Proto 契约补全

6.004 架构影响地图更新

7.端口分配表

8.设计规格文档
2026-07-10 12:58:22 +08:00

66 lines
2.2 KiB
Go

// Package logger provides a structured logger built on zap with OpenTelemetry
// trace correlation.
//
// In production (the default) logs are emitted as JSON at Info level. When the
// ENV environment variable is set to "development" the logger switches to a
// console encoder at Debug level for human-readable output.
package logger
import (
"context"
"os"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// ctxKey is an unexported type so context values cannot collide with callers.
type ctxKey struct{}
// fallback is returned by FromContext when no logger has been injected into
// the context; it discards all output so callers never receive a nil logger.
var fallback = zap.NewNop()
// New creates a structured logger tagged with serviceName. The encoder and
// level are selected from the ENV environment variable as described in the
// package documentation.
func New(serviceName string) *zap.Logger {
var cfg zap.Config
if os.Getenv("ENV") == "development" {
cfg = zap.NewDevelopmentConfig()
cfg.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
} else {
cfg = zap.NewProductionConfig()
cfg.Level = zap.NewAtomicLevelAt(zapcore.InfoLevel)
}
l, err := cfg.Build(zap.Fields(zap.String("service", serviceName)))
if err != nil {
// A logger that cannot be constructed is a fatal misconfiguration;
// there is no safe way to continue.
panic(err)
}
return l
}
// WithContext returns a copy of ctx that carries l so it can later be
// retrieved via FromContext.
func WithContext(ctx context.Context, l *zap.Logger) context.Context {
return context.WithValue(ctx, ctxKey{}, l)
}
// FromContext returns the logger stored in ctx. When an active OpenTelemetry
// span is present, the returned logger is decorated with a trace_id field so
// log lines can be correlated to traces. If no logger was stored in ctx a
// no-op logger is returned.
func FromContext(ctx context.Context) *zap.Logger {
if v, ok := ctx.Value(ctxKey{}).(*zap.Logger); ok && v != nil {
if sc := trace.SpanContextFromContext(ctx); sc.HasTraceID() {
return v.With(zap.String("trace_id", sc.TraceID().String()))
}
return v
}
return fallback
}