-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlogging.go
104 lines (92 loc) · 3.32 KB
/
logging.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package telemetry
import (
"context"
"log/slog"
"os"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// handlerWithSpanContext adds attributes from the span context
// [START opentelemetry_instrumentation_spancontext_logger]
func handlerWithSpanContext(handler slog.Handler) *spanContextLogHandler {
return &spanContextLogHandler{Handler: handler}
}
// spanContextLogHandler is a slog.Handler which adds attributes from the
// span context.
type spanContextLogHandler struct {
slog.Handler
}
// Handle overrides slog.Handler's Handle method. This adds attributes from the
// span context to the slog.Record.
func (t *spanContextLogHandler) Handle(ctx context.Context, record slog.Record) error {
// Get the SpanContext from the context.
if s := trace.SpanContextFromContext(ctx); s.IsValid() {
// Add trace context attributes following Cloud Logging structured log format described
// in https://cloud.google.com/logging/docs/structured-logging#special-payload-fields
record.AddAttrs(
slog.Any("logging.googleapis.com/trace", s.TraceID()),
)
record.AddAttrs(
slog.Any("logging.googleapis.com/spanId", s.SpanID()),
)
record.AddAttrs(
slog.Bool("logging.googleapis.com/trace_sampled", s.TraceFlags().IsSampled()),
)
}
return t.Handler.Handle(ctx, record)
}
func replacer(groups []string, a slog.Attr) slog.Attr {
// Rename attribute keys to match Cloud Logging structured log format
switch a.Key {
case slog.LevelKey:
a.Key = "severity"
// Map slog.Level string values to Cloud Logging LogSeverity
// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity
if level := a.Value.Any().(slog.Level); level == slog.LevelWarn {
a.Value = slog.StringValue("WARNING")
}
case slog.TimeKey:
a.Key = "timestamp"
case slog.MessageKey:
a.Key = "message"
}
return a
}
// [END opentelemetry_instrumentation_spancontext_logger]
func SetupLogging() {
// Use json as our base logging format.
jsonHandler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ReplaceAttr: replacer})
// Add span context attributes when Context is passed to logging calls.
instrumentedHandler := handlerWithSpanContext(jsonHandler)
// Set this handler as the global slog handler.
slog.SetDefault(slog.New(instrumentedHandler))
}
// SetupZapLogging configures a Zap logger with OpenTelemetry integration
// and sets it as the global logger.
func SetupZapLogging() *zap.Logger {
config := zap.NewProductionConfig()
config.EncoderConfig.TimeKey = "timestamp"
config.EncoderConfig.MessageKey = "message"
config.EncoderConfig.LevelKey = "severity"
config.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
logger, err := config.Build()
if err != nil {
// Fallback to default logger if setup fails
logger, _ = zap.NewProduction()
}
// Set as global logger
zap.ReplaceGlobals(logger)
return logger
}
// WithTraceContext creates a new logger with OpenTelemetry trace context added as fields
func WithTraceContext(ctx context.Context, logger *zap.Logger) *zap.Logger {
if s := trace.SpanContextFromContext(ctx); s.IsValid() {
return logger.With(
zap.String("logging.googleapis.com/trace", s.TraceID().String()),
zap.String("logging.googleapis.com/spanId", s.SpanID().String()),
zap.Bool("logging.googleapis.com/trace_sampled", s.TraceFlags().IsSampled()),
)
}
return logger
}