[#12] Use buffer pool for message encoding
``` goos: linux goarch: amd64 pkg: git.frostfs.info/TrueCloudLab/zapjournald cpu: 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz │ 1 │ 2 │ │ sec/op │ sec/op vs base │ Logger/standard/no_fields-8 450.3n ± 2% 452.9n ± 3% ~ (p=0.138 n=10) Logger/standard/application_fields-8 610.4n ± 1% 608.4n ± 1% ~ (p=0.325 n=10) Logger/standard/journald_fields-8 595.8n ± 5% 604.5n ± 1% ~ (p=0.075 n=10) Logger/journald/no_fields-8 1809.0n ± 4% 900.4n ± 1% -50.23% (p=0.000 n=10) Logger/journald/application_fields-8 2.037µ ± 5% 1.087µ ± 1% -46.62% (p=0.000 n=10) Logger/journald/journald_fields-8 2.054µ ± 4% 1.119µ ± 1% -45.52% (p=0.000 n=10) geomean 1.036µ 753.1n -27.33% │ 1 │ 2 │ │ B/op │ B/op vs base │ Logger/standard/no_fields-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Logger/standard/application_fields-8 128.0 ± 0% 128.0 ± 0% ~ (p=1.000 n=10) ¹ Logger/standard/journald_fields-8 64.00 ± 0% 64.00 ± 0% ~ (p=1.000 n=10) ¹ Logger/journald/no_fields-8 1206.000 ± 0% 5.000 ± 0% -99.59% (p=0.000 n=10) Logger/journald/application_fields-8 1494.0 ± 0% 133.0 ± 0% -91.10% (p=0.000 n=10) Logger/journald/journald_fields-8 1478.00 ± 0% 85.00 ± 0% -94.25% (p=0.000 n=10) geomean ² -83.36% ² ¹ all samples are equal ² summaries must be >0 to compute geomean │ 1 │ 2 │ │ allocs/op │ allocs/op vs base │ Logger/standard/no_fields-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Logger/standard/application_fields-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹ Logger/standard/journald_fields-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹ Logger/journald/no_fields-8 29.000 ± 0% 1.000 ± 0% -96.55% (p=0.000 n=10) Logger/journald/application_fields-8 30.000 ± 0% 2.000 ± 0% -93.33% (p=0.000 n=10) Logger/journald/journald_fields-8 31.000 ± 0% 3.000 ± 0% -90.32% (p=0.000 n=10) geomean ² -75.38% ² ¹ all samples are equal ² summaries must be >0 to compute geomean ``` Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
This commit is contained in:
parent
8c4cf38d6a
commit
d6ea4d0bbf
2 changed files with 82 additions and 8 deletions
58
encoder.go
Normal file
58
encoder.go
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package zapjournald
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.uber.org/zap/buffer"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pool = buffer.NewPool()
|
||||||
|
|
||||||
|
func writeFieldBytes(buf *buffer.Buffer, name string, value []byte) {
|
||||||
|
buf.Write([]byte(name))
|
||||||
|
if bytes.ContainsRune(value, '\n') {
|
||||||
|
// According to the format, if the value includes a newline
|
||||||
|
// need to write the field name, plus a newline, then the
|
||||||
|
// size (64bit LE), the field data and a final newline.
|
||||||
|
|
||||||
|
buf.Write([]byte{'\n'})
|
||||||
|
appendUint64Binary(buf, uint64(len(value)))
|
||||||
|
} else {
|
||||||
|
buf.Write([]byte{'='})
|
||||||
|
}
|
||||||
|
buf.Write(value)
|
||||||
|
buf.Write([]byte{'\n'})
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeField(buf *buffer.Buffer, name string, value string) {
|
||||||
|
buf.Write([]byte(name))
|
||||||
|
if strings.ContainsRune(value, '\n') {
|
||||||
|
// According to the format, if the value includes a newline
|
||||||
|
// need to write the field name, plus a newline, then the
|
||||||
|
// size (64bit LE), the field data and a final newline.
|
||||||
|
|
||||||
|
buf.Write([]byte{'\n'})
|
||||||
|
// 1 allocation here.
|
||||||
|
// binary.Write(w, binary.LittleEndian, uint64(len(value)))
|
||||||
|
appendUint64Binary(buf, uint64(len(value)))
|
||||||
|
} else {
|
||||||
|
buf.Write([]byte{'='})
|
||||||
|
}
|
||||||
|
buf.WriteString(value)
|
||||||
|
buf.Write([]byte{'\n'})
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendUint64Binary(buf *buffer.Buffer, v uint64) {
|
||||||
|
// Copied from https://github.com/golang/go/blob/go1.21.3/src/encoding/binary/binary.go#L119
|
||||||
|
buf.Write([]byte{
|
||||||
|
byte(v),
|
||||||
|
byte(v >> 8),
|
||||||
|
byte(v >> 16),
|
||||||
|
byte(v >> 24),
|
||||||
|
byte(v >> 32),
|
||||||
|
byte(v >> 40),
|
||||||
|
byte(v >> 48),
|
||||||
|
byte(v >> 56),
|
||||||
|
})
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package zapjournald
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/ssgreg/journald"
|
"github.com/ssgreg/journald"
|
||||||
"go.uber.org/zap/zapcore"
|
"go.uber.org/zap/zapcore"
|
||||||
|
@ -70,14 +71,10 @@ func (core *Core) Write(entry zapcore.Entry, fields []zapcore.Field) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the message.
|
b := pool.Get()
|
||||||
buffer, err := core.encoder.EncodeEntry(entry, fields)
|
defer b.Free()
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to encode log entry: %w", err)
|
|
||||||
}
|
|
||||||
defer buffer.Free()
|
|
||||||
|
|
||||||
message := buffer.String()
|
writeField(b, "PRIORITY", strconv.Itoa(int(prio)))
|
||||||
|
|
||||||
structuredFields := maps.Clone(core.contextStructuredFields)
|
structuredFields := maps.Clone(core.contextStructuredFields)
|
||||||
for _, field := range fields {
|
for _, field := range fields {
|
||||||
|
@ -85,9 +82,28 @@ func (core *Core) Write(entry zapcore.Entry, fields []zapcore.Field) error {
|
||||||
structuredFields[field.Key] = getFieldValue(field)
|
structuredFields[field.Key] = getFieldValue(field)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for k, v := range structuredFields {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case []byte:
|
||||||
|
writeFieldBytes(b, k, v)
|
||||||
|
case string:
|
||||||
|
writeField(b, k, v)
|
||||||
|
default:
|
||||||
|
writeField(b, k, fmt.Sprint(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the message.
|
||||||
|
buffer, err := core.encoder.EncodeEntry(entry, fields)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to encode log entry: %w", err)
|
||||||
|
}
|
||||||
|
defer buffer.Free()
|
||||||
|
|
||||||
|
writeFieldBytes(b, "MESSAGE", buffer.Bytes())
|
||||||
|
|
||||||
// Write the message.
|
// Write the message.
|
||||||
return core.j.Send(message, prio, structuredFields)
|
return core.j.WriteMsg(b.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync flushes buffered logs (not used).
|
// Sync flushes buffered logs (not used).
|
||||||
|
|
Loading…
Reference in a new issue