-
Notifications
You must be signed in to change notification settings - Fork 3
/
writer.go
128 lines (113 loc) · 3.27 KB
/
writer.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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package zlg
import (
"context"
"encoding/json"
"sync"
"cloud.google.com/go/logging"
"github.com/rs/zerolog"
"google.golang.org/api/option"
)
type cloudLoggingWriter struct {
ctx context.Context
firstBlockingWrite sync.Once
logger *logging.Logger
severityMap map[zerolog.Level]logging.Severity
zerolog.LevelWriter
}
// DefaultSeverityMap contains the default zerolog.Level -> logging.Severity mappings.
var DefaultSeverityMap = map[zerolog.Level]logging.Severity{
zerolog.DebugLevel: logging.Debug,
zerolog.InfoLevel: logging.Info,
zerolog.WarnLevel: logging.Warning,
zerolog.ErrorLevel: logging.Error,
zerolog.PanicLevel: logging.Critical,
zerolog.FatalLevel: logging.Critical,
}
// secretly, we keep tabs of all loggers
var loggersWeMade = make([]*logging.Logger, 0, 1)
func (c *cloudLoggingWriter) Write(p []byte) (int, error) {
// writing to stackdriver without levels? o-okay...
entry := logging.Entry{Payload: json.RawMessage(p)}
c.logger.Log(entry)
var err error
c.firstBlockingWrite.Do(func() {
err = c.logger.Flush()
})
if err != nil {
return 0, err
}
return len(p), nil
}
func (c *cloudLoggingWriter) WriteLevel(level zerolog.Level, payload []byte) (int, error) {
entry := logging.Entry{
Severity: c.severityMap[level],
Payload: json.RawMessage(payload),
}
c.logger.Log(entry)
var err error
c.firstBlockingWrite.Do(func() {
err = c.logger.Flush()
})
if err != nil {
return 0, err
}
if level == zerolog.FatalLevel || level == zerolog.PanicLevel {
// ensure that any pending logs are written before exit
err := c.logger.Flush()
if err != nil {
return 0, err
}
}
return len(payload), nil
}
// CloudLoggingOptions specifies some optional configuration.
type CloudLoggingOptions struct {
// Specify this to override DefaultSeverityMap.
SeverityMap map[zerolog.Level]logging.Severity
// Used during *logging.Client construction.
ClientOptions []option.ClientOption
// Used during *logging.Client construction.
ClientOnError func(error)
// Specify this to override the default of constructing a *logging.Logger on the caller's behalf.
Logger *logging.Logger
// Used during GCP Logger construction.
LoggerOptions []logging.LoggerOption
}
// NewCloudLoggingWriter creates a LevelWriter that logs only to GCP Cloud Logging using non-blocking calls.
func NewCloudLoggingWriter(ctx context.Context, projectID, logID string, opts CloudLoggingOptions) (writer zerolog.LevelWriter, err error) {
logger := opts.Logger
if opts.Logger == nil {
var client *logging.Client
client, err = logging.NewClient(ctx, projectID, opts.ClientOptions...)
if err != nil {
return
}
if opts.ClientOnError != nil {
client.OnError = opts.ClientOnError
}
logger = client.Logger(logID, opts.LoggerOptions...)
loggersWeMade = append(loggersWeMade, logger)
}
severityMap := opts.SeverityMap
if severityMap == nil {
severityMap = DefaultSeverityMap
}
writer = &cloudLoggingWriter{
ctx: ctx,
logger: logger,
severityMap: severityMap,
}
return
}
// Flush blocks while flushing all loggers this module created.
func Flush() []error {
var errs []error
for _, logger := range loggersWeMade {
if logger != nil {
if err := logger.Flush(); err != nil {
errs = append(errs, err)
}
}
}
return errs
}