Files
galaxy-game/notification/internal/logging/logger.go
T
2026-04-22 08:49:45 +02:00

113 lines
2.9 KiB
Go

// Package logging configures the Notification Service process logger and
// provides context-aware helpers for trace fields.
package logging
import (
"context"
"fmt"
"log/slog"
"os"
"strings"
"galaxy/notification/internal/api/intentstream"
"go.opentelemetry.io/otel/trace"
)
// New constructs the process-wide JSON logger from level.
func New(level string) (*slog.Logger, error) {
var slogLevel slog.Level
if err := slogLevel.UnmarshalText([]byte(strings.TrimSpace(level))); err != nil {
return nil, fmt.Errorf("build logger: %w", err)
}
return slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slogLevel,
})), nil
}
// TraceAttrsFromContext returns slog key-value pairs for the active
// OpenTelemetry span when ctx carries a valid span context.
func TraceAttrsFromContext(ctx context.Context) []any {
if ctx == nil {
return nil
}
spanContext := trace.SpanContextFromContext(ctx)
if !spanContext.IsValid() {
return nil
}
return []any{
"otel_trace_id", spanContext.TraceID().String(),
"otel_span_id", spanContext.SpanID().String(),
}
}
// NotificationAttrs returns structured notification-identifying log fields.
func NotificationAttrs(
notificationID string,
notificationType intentstream.NotificationType,
producer intentstream.Producer,
audienceKind intentstream.AudienceKind,
idempotencyKey string,
requestID string,
traceID string,
) []any {
attrs := []any{
"notification_id", notificationID,
"notification_type", string(notificationType),
"producer", string(producer),
"audience_kind", string(audienceKind),
"idempotency_key", idempotencyKey,
}
if strings.TrimSpace(requestID) != "" {
attrs = append(attrs, "request_id", requestID)
}
if strings.TrimSpace(traceID) != "" {
attrs = append(attrs, "trace_id", traceID)
}
return attrs
}
// IntentAttrs returns structured intent-identifying log fields when a durable
// notification record does not yet exist.
func IntentAttrs(intent intentstream.Intent) []any {
attrs := []any{
"notification_type", string(intent.NotificationType),
"producer", string(intent.Producer),
"audience_kind", string(intent.AudienceKind),
"idempotency_key", intent.IdempotencyKey,
}
if strings.TrimSpace(intent.RequestID) != "" {
attrs = append(attrs, "request_id", intent.RequestID)
}
if strings.TrimSpace(intent.TraceID) != "" {
attrs = append(attrs, "trace_id", intent.TraceID)
}
return attrs
}
// RouteAttrs returns structured route-identifying log fields.
func RouteAttrs(
notificationID string,
notificationType intentstream.NotificationType,
producer intentstream.Producer,
audienceKind intentstream.AudienceKind,
idempotencyKey string,
requestID string,
traceID string,
routeID string,
channel intentstream.Channel,
) []any {
attrs := NotificationAttrs(notificationID, notificationType, producer, audienceKind, idempotencyKey, requestID, traceID)
attrs = append(attrs,
"route_id", routeID,
"channel", string(channel),
)
return attrs
}