golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/quic/qlog/json_writer.go (about) 1 // Copyright 2023 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build go1.21 6 7 package qlog 8 9 import ( 10 "bytes" 11 "fmt" 12 "io" 13 "log/slog" 14 "strconv" 15 "sync" 16 "time" 17 ) 18 19 // A jsonWriter writes JSON-SEQ (RFC 7464). 20 // 21 // A JSON-SEQ file consists of a series of JSON text records, 22 // each beginning with an RS (0x1e) character and ending with LF (0x0a). 23 type jsonWriter struct { 24 mu sync.Mutex 25 w io.WriteCloser 26 buf bytes.Buffer 27 } 28 29 // writeRecordStart writes the start of a JSON-SEQ record. 30 func (w *jsonWriter) writeRecordStart() { 31 w.mu.Lock() 32 w.buf.WriteByte(0x1e) 33 w.buf.WriteByte('{') 34 } 35 36 // writeRecordEnd finishes writing a JSON-SEQ record. 37 func (w *jsonWriter) writeRecordEnd() { 38 w.buf.WriteByte('}') 39 w.buf.WriteByte('\n') 40 w.w.Write(w.buf.Bytes()) 41 w.buf.Reset() 42 w.mu.Unlock() 43 } 44 45 func (w *jsonWriter) writeAttrs(attrs []slog.Attr) { 46 w.buf.WriteByte('{') 47 for _, a := range attrs { 48 w.writeAttr(a) 49 } 50 w.buf.WriteByte('}') 51 } 52 53 func (w *jsonWriter) writeAttr(a slog.Attr) { 54 if a.Key == "" { 55 return 56 } 57 w.writeName(a.Key) 58 w.writeValue(a.Value) 59 } 60 61 // writeAttr writes a []slog.Attr as an object field. 62 func (w *jsonWriter) writeAttrsField(name string, attrs []slog.Attr) { 63 w.writeName(name) 64 w.writeAttrs(attrs) 65 } 66 67 func (w *jsonWriter) writeValue(v slog.Value) { 68 v = v.Resolve() 69 switch v.Kind() { 70 case slog.KindAny: 71 switch v := v.Any().(type) { 72 case []slog.Value: 73 w.writeArray(v) 74 case interface{ AppendJSON([]byte) []byte }: 75 w.buf.Write(v.AppendJSON(w.buf.AvailableBuffer())) 76 default: 77 w.writeString(fmt.Sprint(v)) 78 } 79 case slog.KindBool: 80 w.writeBool(v.Bool()) 81 case slog.KindDuration: 82 w.writeDuration(v.Duration()) 83 case slog.KindFloat64: 84 w.writeFloat64(v.Float64()) 85 case slog.KindInt64: 86 w.writeInt64(v.Int64()) 87 case slog.KindString: 88 w.writeString(v.String()) 89 case slog.KindTime: 90 w.writeTime(v.Time()) 91 case slog.KindUint64: 92 w.writeUint64(v.Uint64()) 93 case slog.KindGroup: 94 w.writeAttrs(v.Group()) 95 default: 96 w.writeString("unhandled kind") 97 } 98 } 99 100 // writeName writes an object field name followed by a colon. 101 func (w *jsonWriter) writeName(name string) { 102 if b := w.buf.Bytes(); len(b) > 0 && b[len(b)-1] != '{' { 103 // Add the comma separating this from the previous field. 104 w.buf.WriteByte(',') 105 } 106 w.writeString(name) 107 w.buf.WriteByte(':') 108 } 109 110 func (w *jsonWriter) writeObject(f func()) { 111 w.buf.WriteByte('{') 112 f() 113 w.buf.WriteByte('}') 114 } 115 116 // writeObject writes an object-valued object field. 117 // The function f is called to write the contents. 118 func (w *jsonWriter) writeObjectField(name string, f func()) { 119 w.writeName(name) 120 w.writeObject(f) 121 } 122 123 func (w *jsonWriter) writeArray(vals []slog.Value) { 124 w.buf.WriteByte('[') 125 for i, v := range vals { 126 if i != 0 { 127 w.buf.WriteByte(',') 128 } 129 w.writeValue(v) 130 } 131 w.buf.WriteByte(']') 132 } 133 134 func (w *jsonWriter) writeRaw(v string) { 135 w.buf.WriteString(v) 136 } 137 138 // writeRawField writes a field with a raw JSON value. 139 func (w *jsonWriter) writeRawField(name, v string) { 140 w.writeName(name) 141 w.writeRaw(v) 142 } 143 144 func (w *jsonWriter) writeBool(v bool) { 145 if v { 146 w.buf.WriteString("true") 147 } else { 148 w.buf.WriteString("false") 149 } 150 } 151 152 // writeBoolField writes a bool-valued object field. 153 func (w *jsonWriter) writeBoolField(name string, v bool) { 154 w.writeName(name) 155 w.writeBool(v) 156 } 157 158 // writeDuration writes a duration as milliseconds. 159 func (w *jsonWriter) writeDuration(v time.Duration) { 160 if v < 0 { 161 w.buf.WriteByte('-') 162 v = -v 163 } 164 fmt.Fprintf(&w.buf, "%d.%06d", v.Milliseconds(), v%time.Millisecond) 165 } 166 167 // writeDurationField writes a millisecond duration-valued object field. 168 func (w *jsonWriter) writeDurationField(name string, v time.Duration) { 169 w.writeName(name) 170 w.writeDuration(v) 171 } 172 173 func (w *jsonWriter) writeFloat64(v float64) { 174 w.buf.Write(strconv.AppendFloat(w.buf.AvailableBuffer(), v, 'f', -1, 64)) 175 } 176 177 // writeFloat64Field writes an float64-valued object field. 178 func (w *jsonWriter) writeFloat64Field(name string, v float64) { 179 w.writeName(name) 180 w.writeFloat64(v) 181 } 182 183 func (w *jsonWriter) writeInt64(v int64) { 184 w.buf.Write(strconv.AppendInt(w.buf.AvailableBuffer(), v, 10)) 185 } 186 187 // writeInt64Field writes an int64-valued object field. 188 func (w *jsonWriter) writeInt64Field(name string, v int64) { 189 w.writeName(name) 190 w.writeInt64(v) 191 } 192 193 func (w *jsonWriter) writeUint64(v uint64) { 194 w.buf.Write(strconv.AppendUint(w.buf.AvailableBuffer(), v, 10)) 195 } 196 197 // writeUint64Field writes a uint64-valued object field. 198 func (w *jsonWriter) writeUint64Field(name string, v uint64) { 199 w.writeName(name) 200 w.writeUint64(v) 201 } 202 203 // writeTime writes a time as seconds since the Unix epoch. 204 func (w *jsonWriter) writeTime(v time.Time) { 205 fmt.Fprintf(&w.buf, "%d.%06d", v.UnixMilli(), v.Nanosecond()%int(time.Millisecond)) 206 } 207 208 // writeTimeField writes a time-valued object field. 209 func (w *jsonWriter) writeTimeField(name string, v time.Time) { 210 w.writeName(name) 211 w.writeTime(v) 212 } 213 214 func jsonSafeSet(c byte) bool { 215 // mask is a 128-bit bitmap with 1s for allowed bytes, 216 // so that the byte c can be tested with a shift and an and. 217 // If c > 128, then 1<<c and 1<<(c-64) will both be zero, 218 // and this function will return false. 219 const mask = 0 | 220 (1<<(0x22-0x20)-1)<<0x20 | 221 (1<<(0x5c-0x23)-1)<<0x23 | 222 (1<<(0x7f-0x5d)-1)<<0x5d 223 return ((uint64(1)<<c)&(mask&(1<<64-1)) | 224 (uint64(1)<<(c-64))&(mask>>64)) != 0 225 } 226 227 func jsonNeedsEscape(s string) bool { 228 for i := range s { 229 if !jsonSafeSet(s[i]) { 230 return true 231 } 232 } 233 return false 234 } 235 236 // writeString writes an ASCII string. 237 // 238 // qlog fields should never contain anything that isn't ASCII, 239 // so we do the bare minimum to avoid producing invalid output if we 240 // do write something unexpected. 241 func (w *jsonWriter) writeString(v string) { 242 w.buf.WriteByte('"') 243 if !jsonNeedsEscape(v) { 244 w.buf.WriteString(v) 245 } else { 246 for i := range v { 247 if jsonSafeSet(v[i]) { 248 w.buf.WriteByte(v[i]) 249 } else { 250 fmt.Fprintf(&w.buf, `\u%04x`, v[i]) 251 } 252 } 253 } 254 w.buf.WriteByte('"') 255 } 256 257 // writeStringField writes a string-valued object field. 258 func (w *jsonWriter) writeStringField(name, v string) { 259 w.writeName(name) 260 w.writeString(v) 261 }