Skip to content

Commit f9e1b15

Browse files
authored
reduce CPU use in zapadapter (#3773)
* Improve zadapter benchmark. * Fix slow zadapter. * comments and changelog
1 parent a8a80f3 commit f9e1b15

File tree

3 files changed

+94
-31
lines changed

3 files changed

+94
-31
lines changed

‎CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ Main (unreleased)
2222

2323
- Fix `loki.source.firehose` to propagate specific cloudwatch event timestamps when useIncomingTs is set to true. (@michaelPotter)
2424

25+
- Fix elevated CPU usage when using some `otelcol` components due to debug logging. (@thampiotr)
26+
2527
v1.9.0
2628
-----------------
2729

‎internal/util/zapadapter/zapadapter.go‎

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func (lc *loggerCore) Enabled(zapcore.Level) bool {
4040
// With implements zapcore.Core, returning a new logger core with ff appended
4141
// to the list of fields.
4242
func (lc *loggerCore) With(ff []zapcore.Field) zapcore.Core {
43-
// Encode all of the fields so that they're go-kit compatible and create a
43+
// Encode all the fields so that they're go-kit compatible and create a
4444
// new logger from it.
4545
enc := newFieldEncoder()
4646
defer func() { _ = enc.Close() }()
@@ -132,30 +132,34 @@ func (fe *fieldEncoder) Close() error {
132132
}
133133

134134
func (fe *fieldEncoder) AddArray(key string, marshaler zapcore.ArrayMarshaler) error {
135-
enc := newArrayFieldEncoder()
136-
err := marshaler.MarshalLogArray(enc)
137-
if err != nil {
138-
return err
139-
}
140-
b, err := enc.jsonMarshal()
141-
if err != nil {
142-
return err
143-
}
144-
fe.fields = append(fe.fields, fe.keyName(key), string(b))
135+
fe.fields = append(fe.fields, fe.keyName(key), lazyStringer{f: func() string {
136+
enc := newArrayFieldEncoder()
137+
err := marshaler.MarshalLogArray(enc)
138+
if err != nil {
139+
return err.Error()
140+
}
141+
b, err := enc.jsonMarshal()
142+
if err != nil {
143+
return err.Error()
144+
}
145+
return string(b)
146+
}})
145147
return nil
146148
}
147149

148150
func (fe *fieldEncoder) AddObject(key string, marshaler zapcore.ObjectMarshaler) error {
149-
enc := newObjectFieldEncoder()
150-
err := marshaler.MarshalLogObject(enc)
151-
if err != nil {
152-
return err
153-
}
154-
b, err := enc.jsonMarshal()
155-
if err != nil {
156-
return err
157-
}
158-
fe.fields = append(fe.fields, fe.keyName(key), string(b))
151+
fe.fields = append(fe.fields, fe.keyName(key), lazyStringer{f: func() string {
152+
enc := newObjectFieldEncoder()
153+
err := marshaler.MarshalLogObject(enc)
154+
if err != nil {
155+
return err.Error()
156+
}
157+
b, err := enc.jsonMarshal()
158+
if err != nil {
159+
return err.Error()
160+
}
161+
return string(b)
162+
}})
159163
return nil
160164
}
161165

@@ -488,3 +492,11 @@ func (fe *arrayFieldEncoder) AppendReflected(value interface{}) error {
488492
fe.arr = append(fe.arr, value)
489493
return nil
490494
}
495+
496+
type lazyStringer struct {
497+
f func() string
498+
}
499+
500+
func (l lazyStringer) String() string {
501+
return l.f()
502+
}

‎internal/util/zapadapter/zapadapter_test.go‎

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ import (
99
"time"
1010

1111
"github.com/go-kit/log"
12-
"github.com/go-kit/log/level"
13-
"github.com/grafana/alloy/internal/util/zapadapter"
1412
"github.com/stretchr/testify/require"
1513
"go.uber.org/zap"
1614
"go.uber.org/zap/zapcore"
15+
16+
"github.com/grafana/alloy/internal/runtime/logging"
17+
"github.com/grafana/alloy/internal/util/zapadapter"
1718
)
1819

1920
func Test(t *testing.T) {
@@ -121,6 +122,39 @@ func Test(t *testing.T) {
121122
}
122123
}
123124

125+
/*
126+
As of 2025-06-04:
127+
128+
goos: darwin
129+
goarch: arm64
130+
pkg: github.com/grafana/alloy/internal/util/zapadapter
131+
cpu: Apple M2
132+
Benchmark
133+
Benchmark/No_fields_enabled-8 1352374 864.7 ns/op
134+
Benchmark/No_fields_disabled-8 6223372 193.1 ns/op
135+
Benchmark/Any_enabled-8 1000000 1332 ns/op
136+
Benchmark/Any_disabled-8 4654744 240.5 ns/op
137+
Benchmark/Bool_enabled-8 1000000 1015 ns/op
138+
Benchmark/Bool_disabled-8 5353936 253.2 ns/op
139+
Benchmark/Duration_enabled-8 1000000 1062 ns/op
140+
Benchmark/Duration_disabled-8 5175646 238.0 ns/op
141+
Benchmark/Error_enabled-8 1000000 1105 ns/op
142+
Benchmark/Error_disabled-8 4905226 267.0 ns/op
143+
Benchmark/Float32_enabled-8 1000000 1203 ns/op
144+
Benchmark/Float32_disabled-8 4813323 233.6 ns/op
145+
Benchmark/Float64_enabled-8 1000000 1037 ns/op
146+
Benchmark/Float64_disabled-8 5130016 232.8 ns/op
147+
Benchmark/Int_enabled-8 1000000 1065 ns/op
148+
Benchmark/Int_disabled-8 5154585 241.0 ns/op
149+
Benchmark/String_enabled-8 1000000 1025 ns/op
150+
Benchmark/String_disabled-8 5105998 233.3 ns/op
151+
Benchmark/Time_enabled-8 1000000 1143 ns/op
152+
Benchmark/Time_disabled-8 4857289 248.3 ns/op
153+
Benchmark/Array_enabled-8 327018 3529 ns/op
154+
Benchmark/Array_disabled-8 4889307 252.8 ns/op
155+
Benchmark/Object_enabled-8 285597 4187 ns/op
156+
Benchmark/Object_disabled-8 4864752 245.3 ns/op
157+
*/
124158
func Benchmark(b *testing.B) {
125159
// Benchmark various fields that may be commonly printed.
126160

@@ -134,30 +168,45 @@ func Benchmark(b *testing.B) {
134168
runBenchmark(b, "Int", zap.Int("key", 1234))
135169
runBenchmark(b, "String", zap.String("key", "test"))
136170
runBenchmark(b, "Time", zap.Time("key", time.Date(2022, 12, 1, 1, 1, 1, 1, time.UTC)))
137-
runBenchmark(b, "Array", zap.Strings("key", []string{"foo", "bar"}))
171+
runBenchmark(b, "Array", zap.Strings("key", []string{"foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar"}))
138172
runBenchmark(b, "Object", zap.Object("key", testObject{
139173
obj: map[string]any{
140-
"foo": "bar",
141-
"bar": 123,
142-
"baz": true,
174+
"foo": "car",
175+
"bar": 123,
176+
"baz": true,
177+
"foo2": "bar2",
178+
"bar2": 123,
179+
"baz2": true,
143180
"qux": map[string]any{
144-
"foo": "car",
181+
"foo": "car",
182+
"bar": 123,
183+
"baz": true,
184+
"foo2": "bar2",
185+
"bar2": 123,
186+
"baz2": true,
145187
},
146188
},
147189
}))
148190
}
149191

150192
func runBenchmark(b *testing.B, name string, fields ...zap.Field) {
151-
innerLogger := log.NewLogfmtLogger(io.Discard)
152-
innerLogger = level.NewFilter(innerLogger, level.AllowAll())
193+
innerLogger, err := logging.NewDeferred(io.Discard)
194+
require.NoError(b, err)
195+
err = innerLogger.Update(logging.Options{Level: logging.LevelInfo, Format: logging.FormatLogfmt})
196+
require.NoError(b, err)
153197

154198
zapLogger := zapadapter.New(innerLogger)
155199

156-
b.Run(name, func(b *testing.B) {
200+
b.Run(name+" enabled", func(b *testing.B) {
157201
for i := 0; i < b.N; i++ {
158202
zapLogger.Info("Hello, world!", fields...)
159203
}
160204
})
205+
b.Run(name+" disabled", func(b *testing.B) {
206+
for i := 0; i < b.N; i++ {
207+
zapLogger.Debug("Hello, world!", fields...)
208+
}
209+
})
161210
}
162211

163212
type testObject struct {

0 commit comments

Comments
 (0)