Skip to content

Commit f7e60f5

Browse files
authored
New log encoder to report errors through stackdriver (#42)
* new encoder, zapdriver config * Update logger.go * stacktrace into message only on ErrorLevel * inherit from zapcore.Encoder * alter error with >= zapcore.ErrorLevel * if stacktrace is present put it in the message * test of grpc interceptor report on error * Revert "test of grpc interceptor report on error" This reverts commit 5accb62. * implement clone function
1 parent 225ad19 commit f7e60f5

File tree

2 files changed

+62
-36
lines changed

2 files changed

+62
-36
lines changed

encoder.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package zapvml
2+
3+
import (
4+
"regexp"
5+
6+
"go.uber.org/zap/buffer"
7+
"go.uber.org/zap/zapcore"
8+
)
9+
10+
func newEncoder(cfg zapcore.EncoderConfig) (zapcore.Encoder, error) {
11+
return &Encoder{zapcore.NewJSONEncoder(cfg)}, nil
12+
}
13+
14+
// Wraps zapcore.Encoder to customize stack traces to be picked up by Stackdriver error reporting.
15+
// The following issue might make this unnecessary:
16+
// https://github.com/uber-go/zap/issues/514
17+
type Encoder struct {
18+
zapcore.Encoder
19+
}
20+
21+
// multiline pattern to match the function name line
22+
var functionNamePattern = regexp.MustCompile(`(?m)^(\S+)$`)
23+
24+
func (s *Encoder) EncodeEntry(ent zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
25+
if ent.Stack != "" {
26+
// Make the message look like a real panic, so Stackdriver error reporting picks it up.
27+
// This used to need the string "panic: " at the beginning, but no longer seems to need it!
28+
// ent.Message = "panic: " + ent.Message + "\n\ngoroutine 1 [running]:\n"
29+
ent.Message = ent.Message + "\n\ngoroutine 1 [running]:\n"
30+
31+
// Trial-and-error: On App Engine Standard go111 the () are needed after function calls
32+
// zap does not add them, so hack it with a regexp
33+
replaced := functionNamePattern.ReplaceAllString(ent.Stack, "$1(...)")
34+
ent.Message += replaced
35+
ent.Stack = ""
36+
}
37+
return s.Encoder.EncodeEntry(ent, fields)
38+
}
39+
40+
func (s *Encoder) Clone() zapcore.Encoder {
41+
return &Encoder{s.Encoder.Clone()}
42+
}

logger.go

Lines changed: 20 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package zapvml
22

33
import (
4-
"os"
5-
64
"github.com/blendle/zapdriver"
75
"go.uber.org/zap"
86
"go.uber.org/zap/zapcore"
@@ -18,8 +16,9 @@ var (
1816
)
1917

2018
type Config struct {
21-
Level zapcore.Level `required:"true" default:"warn"`
22-
Debug bool `required:"true" default:"false"`
19+
Level zapcore.Level `required:"true" default:"warn"`
20+
Debug bool `required:"true" default:"false"`
21+
ServiceName string `required:"true" default:"default_service"`
2322
}
2423

2524
func Init(globalLevel zapcore.Level) {
@@ -34,43 +33,28 @@ func init() {
3433
panic(err)
3534
}
3635

37-
Level = zap.NewAtomicLevelAt(cfg.Level)
38-
39-
// High-priority output should also go to standard error, and low-priority
40-
// output should also go to standard out.
41-
// It is useful for Kubernetes deployment.
42-
// Kubernetes interprets os.Stdout log items as INFO and os.Stderr log items
43-
// as ERROR by default.
44-
highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
45-
return lvl >= zapcore.ErrorLevel
46-
})
47-
lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
48-
return lvl >= Level.Level() && lvl < zapcore.ErrorLevel
49-
})
50-
51-
// Output channels
52-
consoleInfos := zapcore.Lock(os.Stdout)
53-
consoleErrors := zapcore.Lock(os.Stderr)
36+
err := zap.RegisterEncoder("stackdriver-json", newEncoder)
37+
if err != nil {
38+
panic(err)
39+
}
5440

55-
// Setup Config and Encoder
56-
var ecfg zapcore.EncoderConfig
57-
var enc zapcore.Encoder
41+
var config zap.Config
5842
if cfg.Debug {
59-
ecfg = zapdriver.NewDevelopmentEncoderConfig()
60-
enc = zapcore.NewConsoleEncoder(ecfg)
43+
config = zapdriver.NewDevelopmentConfig()
6144
} else {
62-
ecfg = zapdriver.NewProductionEncoderConfig()
63-
enc = zapcore.NewJSONEncoder(ecfg)
45+
config = zapdriver.NewProductionConfig()
46+
}
47+
48+
config.Encoding = "stackdriver-json"
49+
50+
Log, err = config.Build(zapdriver.WrapCore(
51+
zapdriver.ReportAllErrors(true),
52+
zapdriver.ServiceName(cfg.ServiceName),
53+
))
54+
if err != nil {
55+
panic(err)
6456
}
6557

66-
// Join the outputs, encoders, and level-handling functions into
67-
// zapcore.
68-
core := zapcore.NewTee(
69-
zapcore.NewCore(enc, consoleErrors, highPriority),
70-
zapcore.NewCore(enc, consoleInfos, lowPriority),
71-
)
72-
// From a zapcore.Core, it's easy to construct a Logger.
73-
Log = zap.New(core)
7458
zap.RedirectStdLog(Log)
7559
}
7660

0 commit comments

Comments
 (0)