...
1
2 package slogstackdriver
3
4 import (
5 "context"
6 "encoding/json"
7 "fmt"
8 "io"
9 "strings"
10
11 "cloud.google.com/go/compute/metadata"
12 "go.opencensus.io/trace"
13 logpbtype "google.golang.org/genproto/googleapis/logging/type"
14 logpb "google.golang.org/genproto/googleapis/logging/v2"
15
16 "cdr.dev/slog"
17 "cdr.dev/slog/internal/syncwriter"
18 )
19
20
21
22
23
24 func Sink(w io.Writer) slog.Sink {
25 projectID, _ := metadata.ProjectID()
26
27 return stackdriverSink{
28 projectID: projectID,
29 w: syncwriter.New(w),
30 }
31 }
32
33 type stackdriverSink struct {
34 projectID string
35 w *syncwriter.Writer
36 }
37
38 func (s stackdriverSink) LogEntry(ctx context.Context, ent slog.SinkEntry) {
39
40
41
42
43 e := slog.M(
44 slog.F("logging.googleapis.com/severity", sev(ent.Level)),
45 slog.F("message", ent.Message),
46
47 slog.F("timestampSeconds", ent.Time.Unix()),
48 slog.F("timestampNanos", ent.Time.UnixNano()%1e9),
49 slog.F("logging.googleapis.com/sourceLocation", &logpb.LogEntrySourceLocation{
50 File: ent.File,
51 Line: int64(ent.Line),
52 Function: ent.Func,
53 }),
54 )
55
56 if len(ent.LoggerNames) > 0 {
57 e = append(e, slog.F("logging.googleapis.com/operation", &logpb.LogEntryOperation{
58 Producer: strings.Join(ent.LoggerNames, "."),
59 }))
60 }
61
62 if ent.SpanContext != (trace.SpanContext{}) {
63 e = append(e,
64 slog.F("logging.googleapis.com/trace", s.traceField(ent.SpanContext.TraceID)),
65 slog.F("logging.googleapis.com/spanId", ent.SpanContext.SpanID.String()),
66 slog.F("logging.googleapis.com/trace_sampled", ent.SpanContext.IsSampled()),
67 )
68 }
69
70 e = append(e, ent.Fields...)
71
72 buf, _ := json.Marshal(e)
73
74 buf = append(buf, '\n')
75 s.w.Write("slogstackdriver", buf)
76 }
77
78 func (s stackdriverSink) Sync() {
79 s.w.Sync("stackdriverSink")
80 }
81
82 func sev(level slog.Level) logpbtype.LogSeverity {
83 switch level {
84 case slog.LevelDebug:
85 return logpbtype.LogSeverity_DEBUG
86 case slog.LevelInfo:
87 return logpbtype.LogSeverity_INFO
88 case slog.LevelWarn:
89 return logpbtype.LogSeverity_WARNING
90 case slog.LevelError:
91 return logpbtype.LogSeverity_ERROR
92 default:
93 return logpbtype.LogSeverity_CRITICAL
94 }
95 }
96
97 func (s stackdriverSink) traceField(tID trace.TraceID) string {
98 return fmt.Sprintf("projects/%v/traces/%v", s.projectID, tID)
99 }
100
View as plain text