Files
galaxy-game/gateway/internal/logging/logger.go
T
2026-04-02 19:18:42 +02:00

85 lines
2.0 KiB
Go

// Package logging configures the gateway structured logger and provides
// context-aware helpers for attaching OpenTelemetry trace identifiers.
package logging
import (
"context"
"strings"
"galaxy/gateway/internal/config"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// New constructs the process-wide JSON logger from cfg.
func New(cfg config.LoggingConfig) (*zap.Logger, error) {
level := zap.NewAtomicLevel()
if err := level.UnmarshalText([]byte(strings.TrimSpace(cfg.Level))); err != nil {
return nil, err
}
zapCfg := zap.NewProductionConfig()
zapCfg.Level = level
zapCfg.Sampling = nil
zapCfg.Encoding = "json"
zapCfg.EncoderConfig.TimeKey = "timestamp"
zapCfg.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
zapCfg.OutputPaths = []string{"stdout"}
zapCfg.ErrorOutputPaths = []string{"stderr"}
return zapCfg.Build()
}
// TraceFieldsFromContext returns zap fields for the active OpenTelemetry span
// when ctx carries a valid span context.
func TraceFieldsFromContext(ctx context.Context) []zap.Field {
if ctx == nil {
return nil
}
spanContext := trace.SpanContextFromContext(ctx)
if !spanContext.IsValid() {
return nil
}
return []zap.Field{
zap.String("otel_trace_id", spanContext.TraceID().String()),
zap.String("otel_span_id", spanContext.SpanID().String()),
}
}
// Sync flushes logger and ignores the benign stdout or stderr sync errors
// commonly returned by containerized or redirected process outputs.
func Sync(logger *zap.Logger) error {
if logger == nil {
return nil
}
err := logger.Sync()
if err == nil || isIgnorableSyncError(err) {
return nil
}
return err
}
func isIgnorableSyncError(err error) bool {
if err == nil {
return false
}
message := strings.ToLower(err.Error())
switch {
case strings.Contains(message, "invalid argument"):
return true
case strings.Contains(message, "bad file descriptor"):
return true
case strings.Contains(message, "inappropriate ioctl for device"):
return true
default:
return false
}
}