github.com/qioalice/ekago/v3@v3.3.2-0.20221202205325-5c262d586ee4/ekalog/encoder_json_private.go (about) 1 // Copyright © 2020-2021. All rights reserved. 2 // Author: Ilya Stroy. 3 // Contacts: iyuryevich@pm.me, https://github.com/qioalice 4 // License: https://opensource.org/licenses/MIT 5 6 package ekalog 7 8 import ( 9 "math" 10 "strconv" 11 "strings" 12 "time" 13 14 "github.com/qioalice/ekago/v3/ekasys" 15 "github.com/qioalice/ekago/v3/internal/ekaletter" 16 17 "github.com/json-iterator/go" 18 ) 19 20 var ( 21 // Make sure we won't break API by declaring package's console encoder 22 defaultJSONEncoder CI_Encoder 23 ) 24 25 // doBuild builds the current CI_JSONEncoder only if it has not built yet. 26 // There is no-op if encoder already built. 27 func (je *CI_JSONEncoder) doBuild() *CI_JSONEncoder { 28 29 switch { 30 case je == nil: 31 return nil 32 33 case je.api != nil: 34 // do not build if it's so already 35 return je 36 } 37 38 je.api = jsoniter.Config{ 39 IndentionStep: je.indent, 40 MarshalFloatWith6Digits: true, 41 ObjectFieldMustBeSimpleString: true, 42 }.Froze() 43 44 preEncodedFieldsApi := jsoniter.Config{ 45 IndentionStep: je.indent * 2, 46 MarshalFloatWith6Digits: true, 47 ObjectFieldMustBeSimpleString: true, 48 }.Froze() 49 50 je.preEncodedFieldsStreamIndentX1 = je.api.BorrowStream(nil) 51 je.preEncodedFieldsStreamIndentX2 = preEncodedFieldsApi.BorrowStream(nil) 52 53 if je.fieldNames == nil { 54 je.fieldNames = make(map[CI_JSONEncoder_Field]string) 55 } 56 57 dvn := func(je *CI_JSONEncoder, v CI_JSONEncoder_Field, val string) { 58 if _, ok := je.fieldNames[v]; !ok { 59 je.fieldNames[v] = val 60 } 61 } 62 63 dvn(je, CI_JSON_ENCODER_FIELD_LEVEL, 64 CI_JSON_ENCODER_FIELD_DEFAULT_LEVEL) 65 66 dvn(je, CI_JSON_ENCODER_FIELD_LEVEL_VALUE, 67 CI_JSON_ENCODER_FIELD_DEFAULT_LEVEL_VALUE) 68 69 dvn(je, CI_JSON_ENCODER_FIELD_TIME, 70 CI_JSON_ENCODER_FIELD_DEFAULT_TIME) 71 72 dvn(je, CI_JSON_ENCODER_FIELD_MESSAGE, 73 CI_JSON_ENCODER_FIELD_DEFAULT_MESSAGE) 74 75 dvn(je, CI_JSON_ENCODER_FIELD_ERROR_ID, 76 CI_JSON_ENCODER_FIELD_DEFAULT_ERROR_ID) 77 78 dvn(je, CI_JSON_ENCODER_FIELD_ERROR_CLASS_ID, 79 CI_JSON_ENCODER_FIELD_DEFAULT_ERROR_CLASS_ID) 80 81 dvn(je, CI_JSON_ENCODER_FIELD_ERROR_CLASS_NAME, 82 CI_JSON_ENCODER_FIELD_DEFAULT_ERROR_CLASS_NAME) 83 84 dvn(je, CI_JSON_ENCODER_FIELD_STACKTRACE, 85 CI_JSON_ENCODER_FIELD_DEFAULT_STACKTRACE) 86 87 dvn(je, CI_JSON_ENCODER_FIELD_1DL_STACKTRACE_MESSAGES, 88 CI_JSON_ENCODER_FIELD_DEFAULT_1DL_STACKTRACE_MESSAGES) 89 90 dvn(je, CI_JSON_ENCODER_FIELD_FIELDS, 91 CI_JSON_ENCODER_FIELD_DEFAULT_FIELDS) 92 93 dvn(je, CI_JSON_ENCODER_FIELD_1DL_LOG_FIELDS_PREFIX, 94 CI_JSON_ENCODER_FIELD_DEFAULT_1DL_LOG_FIELDS_PREFIX) 95 96 dvn(je, CI_JSON_ENCODER_FIELD_1DL_STACKTRACE_FIELDS_PREFIX, 97 CI_JSON_ENCODER_FIELD_DEFAULT_1DL_STACKTRACE_FIELDS_PREFIX) 98 99 if je.timeFormatter == nil { 100 je.timeFormatter = je.timeFormatterDefault 101 } 102 103 return je 104 } 105 106 func (_ *CI_JSONEncoder) timeFormatterDefault(t time.Time) string { 107 return t.Format(time.RFC3339) 108 } 109 110 // encodeBase encodes Entry's level, timestamp, message to s. 111 func (je *CI_JSONEncoder) encodeBase(s *jsoniter.Stream, e *Entry) { 112 113 s.WriteObjectField(je.fieldNames[CI_JSON_ENCODER_FIELD_LEVEL]) 114 s.WriteString(e.Level.String()) 115 s.WriteMore() 116 117 s.WriteObjectField(je.fieldNames[CI_JSON_ENCODER_FIELD_LEVEL_VALUE]) 118 s.WriteUint8(uint8(e.Level)) 119 s.WriteMore() 120 121 s.WriteObjectField(je.fieldNames[CI_JSON_ENCODER_FIELD_TIME]) 122 s.WriteString(je.timeFormatter(e.Time)) 123 124 s.WriteMore() 125 s.WriteObjectField(je.fieldNames[CI_JSON_ENCODER_FIELD_MESSAGE]) 126 s.WriteString(e.LogLetter.Messages[0].Body) 127 128 if e.ErrLetter != nil { 129 s.WriteMore() 130 je.encodeErrorHeader(s, e.ErrLetter) 131 } 132 } 133 134 // encodeErrorHeader writes ekaerr.Error's header object treating provided 135 // ekaletter.Letter as ekaerr.Error's one. 136 // 137 // It won't encode stacktrace, neither its messages nor fields. 138 // encodeStackFrame() does that. 139 func (je *CI_JSONEncoder) encodeErrorHeader(s *jsoniter.Stream, errLetter *ekaletter.Letter) { 140 141 for i, n := 0, len(errLetter.SystemFields); i < n; i++ { 142 switch errLetter.SystemFields[i].BaseType() { 143 144 case ekaletter.KIND_SYS_TYPE_EKAERR_UUID: 145 s.WriteObjectField(je.fieldNames[CI_JSON_ENCODER_FIELD_ERROR_ID]) 146 s.WriteString(errLetter.SystemFields[i].SValue) 147 148 case ekaletter.KIND_SYS_TYPE_EKAERR_CLASS_ID: 149 s.WriteObjectField(je.fieldNames[CI_JSON_ENCODER_FIELD_ERROR_CLASS_ID]) 150 s.WriteInt64(errLetter.SystemFields[i].IValue) 151 152 case ekaletter.KIND_SYS_TYPE_EKAERR_CLASS_NAME: 153 s.WriteObjectField(je.fieldNames[CI_JSON_ENCODER_FIELD_ERROR_CLASS_NAME]) 154 s.WriteString(errLetter.SystemFields[i].SValue) 155 156 default: 157 continue 158 } 159 160 if i < n-1 { 161 s.WriteMore() 162 } 163 } 164 165 to := s.Buffer() 166 if l := len(to); to[l-1] == ',' { 167 s.SetBuffer(to[:l-1]) 168 } 169 } 170 171 func (je *CI_JSONEncoder) encodeStacktrace(s *jsoniter.Stream, e *Entry) (wasAdded bool) { 172 173 stacktrace := e.LogLetter.StackTrace 174 if len(stacktrace) == 0 && e.ErrLetter != nil { 175 stacktrace = e.ErrLetter.StackTrace 176 } 177 178 n := int16(len(stacktrace)) 179 if n == 0 { 180 return false 181 } 182 183 var ( 184 fields []ekaletter.LetterField 185 messages []ekaletter.LetterMessage 186 ) 187 188 if e.ErrLetter != nil { 189 fields = e.ErrLetter.Fields 190 messages = e.ErrLetter.Messages 191 } 192 193 if je.oneDepthLevel { 194 var sb strings.Builder 195 196 s.WriteObjectField(je.fieldNames[CI_JSON_ENCODER_FIELD_STACKTRACE]) 197 s.WriteArrayStart() 198 199 for i := int16(0); i < n; i++ { 200 frame := &stacktrace[i] 201 frame.DoFormat() 202 203 sb.Reset() 204 sb.Grow(len(frame.Format) + 10) 205 206 sb.WriteByte('[') 207 sb.WriteString(strconv.Itoa(int(i))) 208 sb.WriteString("]: ") 209 210 sb.WriteString(frame.Format[frame.FormatFullPathOffset:]) 211 sb.WriteByte('/') 212 sb.WriteString(frame.Format[:frame.FormatFileOffset-1]) 213 sb.WriteByte(' ') 214 sb.WriteString(frame.Format[frame.FormatFileOffset : frame.FormatFullPathOffset-1]) 215 216 s.WriteString(sb.String()) 217 218 if i < n-1 { 219 s.WriteMore() 220 } 221 } 222 223 s.WriteArrayEnd() 224 225 if len(messages) > 0 && messages[0].Body != "" { 226 227 s.WriteMore() 228 s.WriteObjectField(je.fieldNames[CI_JSON_ENCODER_FIELD_1DL_STACKTRACE_MESSAGES]) 229 s.WriteArrayStart() 230 231 mi := 0 232 for i, n := int16(0), int16(len(stacktrace)); i < n; i++ { 233 if mi < len(messages) && messages[mi].StackFrameIdx == i { 234 sb.Reset() 235 sb.Grow(len(messages[mi].Body) + 10) 236 237 sb.WriteByte('[') 238 sb.WriteString(strconv.Itoa(int(i))) 239 sb.WriteString("]: ") 240 sb.WriteString(messages[mi].Body) 241 242 s.WriteString(sb.String()) 243 mi++ 244 245 s.WriteMore() 246 } 247 } 248 249 to := s.Buffer() 250 if l := len(to); to[l-1] == ',' { 251 s.SetBuffer(to[:l-1]) 252 } 253 s.WriteArrayEnd() 254 } 255 256 if len(fields) > 0 { 257 s.WriteMore() 258 259 for i, n := 0, len(fields); i < n; i++ { 260 keyBak := fields[i].Key 261 262 key := je.fieldNames[CI_JSON_ENCODER_FIELD_1DL_STACKTRACE_FIELDS_PREFIX] 263 key = strings.Replace(key, "{{num}}", strconv.Itoa(int(fields[i].StackFrameIdx)), 1) 264 key += fields[i].Key 265 266 fields[i].Key = key 267 268 if wasAdded := je.encodeField(s, fields[i]); wasAdded { 269 s.WriteMore() 270 } 271 272 fields[i].Key = keyBak 273 } 274 275 to := s.Buffer() 276 if l := len(to); to[l-1] == ',' { 277 s.SetBuffer(to[:l-1]) 278 } 279 } 280 281 } else { 282 fi := 0 // fi for fields' index 283 mi := 0 // mi for messages' index 284 285 s.WriteObjectField(je.fieldNames[CI_JSON_ENCODER_FIELD_STACKTRACE]) 286 s.WriteArrayStart() 287 288 for i := int16(0); i < n; i++ { 289 frame := &stacktrace[i] 290 291 messageForStackFrame := ekaletter.LetterMessage{} 292 fieldsForStackFrame := []ekaletter.LetterField(nil) 293 fiEnd := 0 294 295 //goland:noinspection GoNilness 296 if mi < len(messages) && messages[mi].StackFrameIdx == i { 297 messageForStackFrame = messages[mi] 298 mi++ 299 } 300 301 if fi < len(fields) && fields[fi].StackFrameIdx == i { 302 fiEnd = fi + 1 303 for fiEnd < len(fields) && fields[fiEnd].StackFrameIdx == i { 304 fiEnd++ 305 } 306 } 307 308 if fiEnd != 0 { 309 fieldsForStackFrame = fields[fi:fiEnd] 310 } 311 312 je.encodeStackFrame(s, frame, fieldsForStackFrame, messageForStackFrame) 313 314 if i < n-1 { 315 s.WriteMore() 316 } 317 } 318 319 s.WriteArrayEnd() 320 } 321 322 return true 323 } 324 325 func (je *CI_JSONEncoder) encodeStackFrame( 326 327 s *jsoniter.Stream, 328 frame *ekasys.StackFrame, 329 fields []ekaletter.LetterField, 330 message ekaletter.LetterMessage, 331 332 ) { 333 frame.DoFormat() 334 335 s.WriteObjectStart() 336 337 s.WriteObjectField("func") 338 s.WriteString(frame.Format[:frame.FormatFileOffset-1]) 339 s.WriteMore() 340 341 s.WriteObjectField("file") 342 s.WriteString(frame.Format[frame.FormatFileOffset+1 : frame.FormatFullPathOffset-2]) 343 s.WriteMore() 344 345 s.WriteObjectField("package") 346 s.WriteString(frame.Format[frame.FormatFullPathOffset:]) 347 348 if message.Body != "" { 349 s.WriteMore() 350 s.WriteObjectField("message") 351 s.WriteString(message.Body) 352 } 353 354 if len(fields) > 0 { 355 s.WriteMore() 356 if wasAdded := je.encodeFields(s, fields, nil, false); !wasAdded { 357 b := s.Buffer() 358 s.SetBuffer(b[:len(b)-1]) 359 } 360 } 361 362 s.WriteObjectEnd() 363 } 364 365 func (je *CI_JSONEncoder) encodeFields(s *jsoniter.Stream, fs, addFs []ekaletter.LetterField, addPreEncoded bool) (wasAdded bool) { 366 367 if len(fs) == 0 && len(addFs) == 0 { 368 return false 369 } 370 371 var ( 372 unnamedFieldIdx, writtenFields int16 373 prefix string 374 ) 375 376 if je.oneDepthLevel { 377 prefix = je.fieldNames[CI_JSON_ENCODER_FIELD_1DL_LOG_FIELDS_PREFIX] 378 } else { 379 s.WriteObjectField(je.fieldNames[CI_JSON_ENCODER_FIELD_FIELDS]) 380 s.WriteObjectStart() 381 } 382 383 addField := func(s *jsoniter.Stream, f *ekaletter.LetterField, prefix string, unnamedFieldIdx, writtenFields *int16) { 384 if strings.HasPrefix(f.Key, "sys.") { 385 return 386 } 387 388 keyBak := f.Key 389 390 var sb strings.Builder 391 sb.Grow(len(prefix) + len(f.Key) + 10) 392 sb.WriteString(prefix) 393 394 if f.Key == "" && !f.IsSystem() { 395 sb.WriteString(f.KeyOrUnnamed(unnamedFieldIdx)) 396 } else { 397 sb.WriteString(f.Key) 398 } 399 f.Key = sb.String() 400 401 if wasAdded = je.encodeField(s, *f); wasAdded { 402 s.WriteMore() 403 *writtenFields++ 404 } 405 406 f.Key = keyBak 407 } 408 409 for i, n := int16(0), int16(len(fs)); i < n; i++ { 410 addField(s, &fs[i], prefix, &unnamedFieldIdx, &writtenFields) 411 } 412 for i, n := int16(0), int16(len(addFs)); i < n; i++ { 413 addField(s, &addFs[i], prefix, &unnamedFieldIdx, &writtenFields) 414 } 415 416 to := s.Buffer() 417 418 // Write pre-encoded fields in "fields" section 419 if addPreEncoded { 420 to = bufw2(to, je.preEncodedFieldsStreamIndentX2.Buffer()) 421 } 422 423 i := len(to) - 1 424 425 // Remove last comma. 426 if to[i] == ',' { 427 i-- 428 } 429 430 if !je.oneDepthLevel && writtenFields == 0 { 431 // Maybe no fields were added? 432 for i >= 0 && to[i] != 'f' { // start of "fields" 433 i-- 434 } 435 i -= 2 // we need also ignore quote (-1) and also (-1) because of +1 below 436 } 437 438 s.SetBuffer(to[:i+1]) 439 440 if !je.oneDepthLevel && writtenFields > 0 { 441 s.WriteObjectEnd() 442 } 443 444 return writtenFields > 0 445 } 446 447 func (je *CI_JSONEncoder) encodeField(s *jsoniter.Stream, f ekaletter.LetterField) (wasAdded bool) { 448 s.WriteObjectField(f.Key) 449 je.encodeFieldValue(s, f) 450 return true 451 } 452 453 func (je *CI_JSONEncoder) encodeFieldValue(s *jsoniter.Stream, f ekaletter.LetterField) { 454 455 if f.Kind.IsSystem() { 456 switch f.Kind.BaseType() { 457 458 case ekaletter.KIND_SYS_TYPE_EKAERR_UUID, ekaletter.KIND_SYS_TYPE_EKAERR_CLASS_NAME: 459 s.WriteString(f.SValue) 460 461 case ekaletter.KIND_SYS_TYPE_EKAERR_CLASS_ID: 462 b := s.Buffer() 463 b = strconv.AppendInt(b, f.IValue, 10) 464 s.SetBuffer(b) 465 466 default: 467 s.WriteString("<unsupported system field>") 468 } 469 470 } else if f.Kind.IsNil() { 471 s.WriteNil() 472 473 } else if f.Kind.IsInvalid() { 474 s.WriteString("<invalid_field>") 475 476 } else { 477 switch f.Kind.BaseType() { 478 479 case ekaletter.KIND_TYPE_BOOL: 480 b := s.Buffer() 481 b = strconv.AppendBool(b, f.IValue != 0) 482 s.SetBuffer(b) 483 484 case ekaletter.KIND_TYPE_INT, 485 ekaletter.KIND_TYPE_INT_8, ekaletter.KIND_TYPE_INT_16, 486 ekaletter.KIND_TYPE_INT_32, ekaletter.KIND_TYPE_INT_64: 487 b := s.Buffer() 488 b = strconv.AppendInt(b, f.IValue, 10) 489 s.SetBuffer(b) 490 491 case ekaletter.KIND_TYPE_UINT, 492 ekaletter.KIND_TYPE_UINT_8, ekaletter.KIND_TYPE_UINT_16, 493 ekaletter.KIND_TYPE_UINT_32, ekaletter.KIND_TYPE_UINT_64: 494 b := s.Buffer() 495 b = strconv.AppendUint(b, uint64(f.IValue), 10) 496 s.SetBuffer(b) 497 498 case ekaletter.KIND_TYPE_FLOAT_32: 499 b := s.Buffer() 500 f := float64(math.Float32frombits(uint32(f.IValue))) 501 b = strconv.AppendFloat(b, f, 'f', 2, 32) 502 s.SetBuffer(b) 503 504 case ekaletter.KIND_TYPE_FLOAT_64: 505 b := s.Buffer() 506 f := math.Float64frombits(uint64(f.IValue)) 507 b = strconv.AppendFloat(b, f, 'f', 2, 64) 508 s.SetBuffer(b) 509 510 case ekaletter.KIND_TYPE_UINTPTR, ekaletter.KIND_TYPE_ADDR: 511 b := s.Buffer() 512 b = bufw(b, "0x") 513 b = strconv.AppendInt(b, f.IValue, 16) 514 s.SetBuffer(b) 515 516 case ekaletter.KIND_TYPE_STRING: 517 s.WriteString(f.SValue) 518 519 case ekaletter.KIND_TYPE_COMPLEX_64: 520 b := s.Buffer() 521 r := math.Float32frombits(uint32(f.IValue >> 32)) 522 i := math.Float32frombits(uint32(f.IValue)) 523 c := complex128(complex(r, i)) 524 // TODO: Use strconv.AppendComplex() when it will be released. 525 b = bufw(b, strconv.FormatComplex(c, 'f', 2, 32)) 526 s.SetBuffer(b) 527 528 case ekaletter.KIND_TYPE_COMPLEX_128: 529 b := s.Buffer() 530 c := f.Value.(complex128) 531 // TODO: Use strconv.AppendComplex() when it will be released. 532 b = bufw(b, strconv.FormatComplex(c, 'f', 2, 64)) 533 s.SetBuffer(b) 534 535 case ekaletter.KIND_TYPE_UNIX: 536 s.WriteString(time.Unix(f.IValue, 0).Format("Jan 2 15:04:05")) 537 538 case ekaletter.KIND_TYPE_UNIX_NANO: 539 s.WriteString(time.Unix(0, f.IValue).Format("Jan 2 15:04:05.000000000")) 540 541 case ekaletter.KIND_TYPE_DURATION: 542 s.WriteString(time.Duration(f.IValue).String()) 543 544 case ekaletter.KIND_TYPE_MAP, ekaletter.KIND_TYPE_EXTMAP, 545 ekaletter.KIND_TYPE_STRUCT, ekaletter.KIND_TYPE_ARRAY: 546 // TODO: Add support of extracted maps. 547 s.WriteVal(f.Value) 548 549 default: 550 s.WriteString("<unsupported_field>") 551 } 552 } 553 }