Skip to content
This repository was archived by the owner on Dec 30, 2024. It is now read-only.

Commit 1fa3486

Browse files
authored
Merge pull request #9 from krombel/useLevelWriterInterface
use zerolog.LevelWriter interface for performance
2 parents abe3e64 + e81a73f commit 1fa3486

File tree

3 files changed

+215
-26
lines changed

3 files changed

+215
-26
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
```go
77
import (
88
"errors"
9-
"io"
109
stdlog "log"
1110
"os"
1211

@@ -22,7 +21,8 @@ func main() {
2221

2322
defer w.Close()
2423

25-
logger := zerolog.New(io.MultiWriter(w, os.Stdout)).With().Timestamp().Logger()
24+
multi := zerolog.MultiLevelWriter(os.Stdout, w)
25+
logger := zerolog.New(multi).With().Timestamp().Logger()
2626

2727
logger.Error().Err(errors.New("dial timeout")).Msg("test message")
2828
}

writer.go

+44-23
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,21 @@ type Writer struct {
3333
}
3434

3535
// Write handles zerolog's json and sends events to sentry.
36-
func (w *Writer) Write(data []byte) (int, error) {
36+
func (w *Writer) Write(data []byte) (n int, err error) {
37+
n = len(data)
38+
39+
lvl, err := w.parseLogLevel(data)
40+
if err != nil {
41+
return n, nil
42+
}
43+
44+
if _, enabled := w.levels[lvl]; !enabled {
45+
return
46+
}
47+
3748
event, ok := w.parseLogEvent(data)
49+
event.Level = levelsMapping[lvl]
50+
3851
if ok {
3952
w.hub.CaptureEvent(event)
4053
// should flush before os.Exit
@@ -43,7 +56,27 @@ func (w *Writer) Write(data []byte) (int, error) {
4356
}
4457
}
4558

46-
return len(data), nil
59+
return
60+
}
61+
62+
// implements zerolog.LevelWriter
63+
func (w *Writer) WriteLevel(level zerolog.Level, p []byte) (n int, err error) {
64+
n = len(p)
65+
if _, enabled := w.levels[level]; !enabled {
66+
return
67+
}
68+
69+
event, ok := w.parseLogEvent(p)
70+
event.Level = levelsMapping[level]
71+
72+
if ok {
73+
w.hub.CaptureEvent(event)
74+
// should flush before os.Exit
75+
if event.Level == sentry.LevelFatal {
76+
w.hub.Flush(w.flushTimeout)
77+
}
78+
}
79+
return
4780
}
4881

4982
// Close forces client to flush all pending events.
@@ -53,37 +86,27 @@ func (w *Writer) Close() error {
5386
return nil
5487
}
5588

56-
func (w *Writer) parseLogEvent(data []byte) (*sentry.Event, bool) {
57-
const logger = "zerolog"
58-
89+
// parses the log level from the encoded log
90+
func (w *Writer) parseLogLevel(data []byte) (zerolog.Level, error) {
5991
lvlStr, err := jsonparser.GetUnsafeString(data, zerolog.LevelFieldName)
6092
if err != nil {
61-
return nil, false
62-
}
63-
64-
lvl, err := zerolog.ParseLevel(lvlStr)
65-
if err != nil {
66-
return nil, false
93+
return zerolog.Disabled, nil
6794
}
6895

69-
_, enabled := w.levels[lvl]
70-
if !enabled {
71-
return nil, false
72-
}
96+
return zerolog.ParseLevel(lvlStr)
97+
}
7398

74-
sentryLvl, ok := levelsMapping[lvl]
75-
if !ok {
76-
return nil, false
77-
}
99+
// parses the event except the log level
100+
func (w *Writer) parseLogEvent(data []byte) (*sentry.Event, bool) {
101+
const logger = "zerolog"
78102

79103
event := sentry.Event{
80104
Timestamp: now(),
81-
Level: sentryLvl,
82105
Logger: logger,
83106
Extra: map[string]interface{}{},
84107
}
85108

86-
err = jsonparser.ObjectEach(data, func(key, value []byte, vt jsonparser.ValueType, offset int) error {
109+
err := jsonparser.ObjectEach(data, func(key, value []byte, vt jsonparser.ValueType, offset int) error {
87110
switch string(key) {
88111
case zerolog.MessageFieldName:
89112
event.Message = bytesToStrUnsafe(value)
@@ -99,7 +122,6 @@ func (w *Writer) parseLogEvent(data []byte) (*sentry.Event, bool) {
99122

100123
return nil
101124
})
102-
103125
if err != nil {
104126
return nil, false
105127
}
@@ -296,7 +318,6 @@ func New(dsn string, opts ...WriterOption) (*Writer, error) {
296318
BeforeSend: cfg.beforeSend,
297319
TracesSampleRate: cfg.tracesSampleRate,
298320
})
299-
300321
if err != nil {
301322
return nil, err
302323
}

writer_test.go

+169-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package zlogsentry
22

33
import (
4+
"errors"
5+
"io"
46
"testing"
57
"time"
68

@@ -22,6 +24,9 @@ func TestParseLogEvent(t *testing.T) {
2224

2325
ev, ok := w.parseLogEvent(logEventJSON)
2426
require.True(t, ok)
27+
zLevel, err := w.parseLogLevel(logEventJSON)
28+
assert.Nil(t, err)
29+
ev.Level = levelsMapping[zLevel]
2530

2631
assert.Equal(t, ts, ev.Timestamp)
2732
assert.Equal(t, sentry.LevelError, ev.Level)
@@ -35,6 +40,125 @@ func TestParseLogEvent(t *testing.T) {
3540
assert.Equal(t, "bee07485-2485-4f64-99e1-d10165884ca7", ev.Extra["requestId"])
3641
}
3742

43+
func TestParseLogLevel(t *testing.T) {
44+
w, err := New("")
45+
require.Nil(t, err)
46+
47+
level, err := w.parseLogLevel(logEventJSON)
48+
require.Nil(t, err)
49+
assert.Equal(t, zerolog.ErrorLevel, level)
50+
}
51+
52+
func TestWrite(t *testing.T) {
53+
beforeSendCalled := false
54+
writer, err := New("", WithBeforeSend(func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event {
55+
assert.Equal(t, sentry.LevelError, event.Level)
56+
assert.Equal(t, "test message", event.Message)
57+
require.Len(t, event.Exception, 1)
58+
assert.Equal(t, "dial timeout", event.Exception[0].Value)
59+
assert.True(t, time.Since(event.Timestamp).Minutes() < 1)
60+
assert.Equal(t, "bee07485-2485-4f64-99e1-d10165884ca7", event.Extra["requestId"])
61+
beforeSendCalled = true
62+
return event
63+
}))
64+
require.Nil(t, err)
65+
66+
var zerologError error
67+
zerolog.ErrorHandler = func(err error) {
68+
zerologError = err
69+
}
70+
71+
// use io.MultiWriter to enforce using the Write() method
72+
log := zerolog.New(io.MultiWriter(writer)).With().Timestamp().
73+
Str("requestId", "bee07485-2485-4f64-99e1-d10165884ca7").
74+
Logger()
75+
log.Err(errors.New("dial timeout")).
76+
Msg("test message")
77+
78+
require.Nil(t, zerologError)
79+
require.True(t, beforeSendCalled)
80+
}
81+
82+
func TestWriteLevel(t *testing.T) {
83+
beforeSendCalled := false
84+
writer, err := New("", WithBeforeSend(func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event {
85+
assert.Equal(t, sentry.LevelError, event.Level)
86+
assert.Equal(t, "test message", event.Message)
87+
require.Len(t, event.Exception, 1)
88+
assert.Equal(t, "dial timeout", event.Exception[0].Value)
89+
assert.True(t, time.Since(event.Timestamp).Minutes() < 1)
90+
assert.Equal(t, "bee07485-2485-4f64-99e1-d10165884ca7", event.Extra["requestId"])
91+
beforeSendCalled = true
92+
return event
93+
}))
94+
require.Nil(t, err)
95+
96+
var zerologError error
97+
zerolog.ErrorHandler = func(err error) {
98+
zerologError = err
99+
}
100+
101+
log := zerolog.New(writer).With().Timestamp().
102+
Str("requestId", "bee07485-2485-4f64-99e1-d10165884ca7").
103+
Logger()
104+
log.Err(errors.New("dial timeout")).
105+
Msg("test message")
106+
107+
require.Nil(t, zerologError)
108+
require.True(t, beforeSendCalled)
109+
}
110+
111+
func TestWrite_Disabled(t *testing.T) {
112+
beforeSendCalled := false
113+
writer, err := New("",
114+
WithLevels(zerolog.FatalLevel),
115+
WithBeforeSend(func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event {
116+
beforeSendCalled = true
117+
return event
118+
}))
119+
require.Nil(t, err)
120+
121+
var zerologError error
122+
zerolog.ErrorHandler = func(err error) {
123+
zerologError = err
124+
}
125+
126+
// use io.MultiWriter to enforce using the Write() method
127+
log := zerolog.New(io.MultiWriter(writer)).With().Timestamp().
128+
Str("requestId", "bee07485-2485-4f64-99e1-d10165884ca7").
129+
Logger()
130+
log.Err(errors.New("dial timeout")).
131+
Msg("test message")
132+
133+
require.Nil(t, zerologError)
134+
require.False(t, beforeSendCalled)
135+
}
136+
137+
func TestWriteLevel_Disabled(t *testing.T) {
138+
beforeSendCalled := false
139+
writer, err := New("",
140+
WithLevels(zerolog.FatalLevel),
141+
WithBeforeSend(func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event {
142+
beforeSendCalled = true
143+
return event
144+
}))
145+
require.Nil(t, err)
146+
147+
var zerologError error
148+
zerolog.ErrorHandler = func(err error) {
149+
zerologError = err
150+
}
151+
152+
log := zerolog.New(writer).With().Timestamp().
153+
Str("requestId", "bee07485-2485-4f64-99e1-d10165884ca7").
154+
Logger()
155+
log.Err(errors.New("dial timeout")).
156+
Msg("test message")
157+
158+
require.Nil(t, zerologError)
159+
require.False(t, beforeSendCalled)
160+
}
161+
38162
func BenchmarkParseLogEvent(b *testing.B) {
39163
w, err := New("")
40164
if err != nil {
@@ -46,7 +170,7 @@ func BenchmarkParseLogEvent(b *testing.B) {
46170
}
47171
}
48172

49-
func BenchmarkParseLogEvent_DisabledLevel(b *testing.B) {
173+
func BenchmarkParseLogEvent_Disabled(b *testing.B) {
50174
w, err := New("", WithLevels(zerolog.FatalLevel))
51175
if err != nil {
52176
b.Errorf("failed to create writer: %v", err)
@@ -56,3 +180,47 @@ func BenchmarkParseLogEvent_DisabledLevel(b *testing.B) {
56180
w.parseLogEvent(logEventJSON)
57181
}
58182
}
183+
184+
func BenchmarkWriteLogEvent(b *testing.B) {
185+
w, err := New("")
186+
if err != nil {
187+
b.Errorf("failed to create writer: %v", err)
188+
}
189+
190+
for i := 0; i < b.N; i++ {
191+
_, _ = w.Write(logEventJSON)
192+
}
193+
}
194+
195+
func BenchmarkWriteLogEvent_Disabled(b *testing.B) {
196+
w, err := New("", WithLevels(zerolog.FatalLevel))
197+
if err != nil {
198+
b.Errorf("failed to create writer: %v", err)
199+
}
200+
201+
for i := 0; i < b.N; i++ {
202+
_, _ = w.Write(logEventJSON)
203+
}
204+
}
205+
206+
func BenchmarkWriteLogLevelEvent(b *testing.B) {
207+
w, err := New("")
208+
if err != nil {
209+
b.Errorf("failed to create writer: %v", err)
210+
}
211+
212+
for i := 0; i < b.N; i++ {
213+
_, _ = w.WriteLevel(zerolog.ErrorLevel, logEventJSON)
214+
}
215+
}
216+
217+
func BenchmarkWriteLogLevelEvent_Disabled(b *testing.B) {
218+
w, err := New("", WithLevels(zerolog.FatalLevel))
219+
if err != nil {
220+
b.Errorf("failed to create writer: %v", err)
221+
}
222+
223+
for i := 0; i < b.N; i++ {
224+
_, _ = w.WriteLevel(zerolog.ErrorLevel, logEventJSON)
225+
}
226+
}

0 commit comments

Comments
 (0)