Repository navigation
Expand file tree
/
Copy pathwire.go
More file actions
115 lines (107 loc) · 3.33 KB
/
Copy pathwire.go
File metadata and controls
115 lines (107 loc) · 3.33 KB
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
package log
import "encoding/binary"
// Wire format: each record is a contiguous, 4-byte-aligned byte sequence
// (quill-style packing — a record touches 1-2 cache lines instead of one
// per field):
//
// u32 size (total, incl. header)
// u8 rtype (0 native, 1 slog)
// u8 flags (bit0: omit time)
// i16 level
// u64 key (TSC ticks / UnixNano: ordering + display time)
// u32 hidx (event registry index; native/slog: unused)
// u16 msgLen, msg bytes
// slog records then carry their handler context inline (so handlers are
// ordinary GC-managed objects — records never reference them):
// u16 nOpen, u16 nUnopened, (u16 len + name bytes) × nUnopened,
// u32 prefixLen, prefix bytes
// u16 nFields
// fields: u8 kind, u16 keyLen, key bytes, then per kind:
// string/json: u32 len + bytes; bool: u8; group: u16 count + nested;
// xform: u32 transform id + u32 len + raw bytes (applied on the
// consumer); other scalars: u64
const (
recTypeOff = 4
recFlagsOff = 5
recLevelOff = 6
recKeyOff = 8
recHidxOff = 16
recMsgOff = 20 // u16 len + bytes follow
recTypeNative = 0
recTypeSlog = 1
recTypeEvent = 2 // static-schema record: header + values only
flagOmitTime = 1
// Guards that bound the memory a single record can hold: values and
// the message are truncated at 8KB, keys at 1KB, native records at 64
// fields, slog WithAttrs prefixes at 128KB, and whole slog records at
// 512KB (attrs that would push past the cap are dropped).
maxStrLen = 8 << 10
maxKeyLen = 1 << 10
maxFieldsPerRecord = 64
maxPrefixLen = 128 << 10
maxRecordLen = 512 << 10
// Native records get a higher cap than slog ones: a group-free record
// maxes out at ~590KB (64 × 8KB values + 1KB keys), so this preserves
// pre-Group behavior while keeping group-bloated records bounded.
maxNativeRecordLen = 800 << 10
)
var le = binary.LittleEndian
// truncStr limits s to max bytes without splitting a UTF-8 rune.
func truncStr(s string, max int) string {
if len(s) <= max {
return s
}
for max > 0 && s[max]&0xC0 == 0x80 {
max--
}
return s[:max]
}
// appendFieldWire encodes one native field. kindAny values are formatted
// here, at the call site, into JSON bytes (kindJSON on the wire).
func appendFieldWire(b []byte, f *Field) []byte {
k := f.kind
if k == kindAny {
k = kindJSON
}
b = append(b, k)
key := truncStr(f.key, maxKeyLen)
b = le.AppendUint16(b, uint16(len(key)))
b = append(b, key...)
switch k {
case kindString:
v := truncStr(f.str, maxStrLen)
b = le.AppendUint32(b, uint32(len(v)))
b = append(b, v...)
case kindXform:
src, _ := f.iface.([]byte)
if len(src) > maxStrLen {
src = src[:maxStrLen]
}
b = le.AppendUint32(b, uint32(f.num))
b = le.AppendUint32(b, uint32(len(src)))
b = append(b, src...)
case kindBool:
b = append(b, byte(f.num&1))
case kindGroup:
fs, _ := f.iface.([]Field)
if len(fs) > maxFieldsPerRecord {
fs = fs[:maxFieldsPerRecord]
}
b = le.AppendUint16(b, uint16(len(fs)))
for i := range fs {
b = appendFieldWire(b, &fs[i])
}
case kindJSON:
var sb [128]byte
js := appendAny(sb[:0], f.iface)
if len(js) > maxStrLen {
js = js[:0]
js = append(js, `"!value too large"`...)
}
b = le.AppendUint32(b, uint32(len(js)))
b = append(b, js...)
default: // 8-byte scalars
b = le.AppendUint64(b, f.num)
}
return b
}