feat: notification service
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user