github.com/qioalice/ekago/v3@v3.3.2-0.20221202205325-5c262d586ee4/ekalog/encoder_console_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 "bytes" 10 "io" 11 "math" 12 "strconv" 13 "strings" 14 "time" 15 16 "github.com/qioalice/ekago/v3/ekamath" 17 "github.com/qioalice/ekago/v3/ekasys" 18 "github.com/qioalice/ekago/v3/internal/ekaletter" 19 20 "github.com/json-iterator/go" 21 ) 22 23 // noinspection GoSnakeCaseUsage 24 type ( 25 // _CICE_FormatPart represents built format's part (parsed RAW format string). 26 // - for 'typ' == '_CICE_FPT_VERB_JUST_TEXT', 'value' is original RAW format string's part; 27 // - for 'typ' == '_CICE_FPT_VERB_COLOR_CUSTOM', 'value' is calculated bash escape sequence 28 // (like "\033[01;03;38;05;144m"); 29 // - for other 'typ' variants, 'value' is empty, 'cause it's log's verbs 30 // and they're runtime calculated entities. 31 _CICE_FormatPart struct { 32 typ _CICE_FormatPartType 33 value string 34 } 35 36 // _CICE_FormatPartType is a special type of _CICE_FormatPart's field 'typ' that contains 37 // an info what kind of format verb current _CICE_FormatPart object is 38 // and how exactly it will be converted to the text at the runtime. 39 _CICE_FormatPartType uint16 40 41 _CICE_FieldsFormat struct { 42 isSet bool 43 beforeFields string 44 afterFields string 45 beforeKey string 46 afterKey string 47 afterValue string 48 afterNewLine string 49 afterNewLineForError string 50 itemsPerLine int16 51 } 52 53 _CICE_BodyFormat struct { 54 isSet bool 55 beforeBody string 56 afterBody string 57 } 58 59 _CICE_CallerFormat struct { 60 isSet bool 61 isDefault bool 62 parts [10]struct { 63 typ int16 64 val string 65 } 66 } 67 68 _CICE_StacktraceFormat struct { 69 isSet bool 70 beforeStack string 71 afterStack string 72 } 73 74 _CICE_ErrorFormat struct { 75 isSet bool 76 beforeError string 77 afterError string 78 } 79 80 _CICE_DropColors struct { 81 buf bytes.Buffer 82 dest io.Writer 83 } 84 ) 85 86 // noinspection GoSnakeCaseUsage 87 const ( 88 // Common Integrator Console Encoder Format Part Type (CICE FPT) 89 // predefined constants. 90 91 _CICE_FPT_MASK_TYPE _CICE_FormatPartType = 0x00_FF 92 _CICE_FPT_MASK_DATA _CICE_FormatPartType = 0xFF_00 93 94 _CICE_FPT_VERB_JUST_TEXT _CICE_FormatPartType = 0x01 95 _CICE_FPT_VERB_COLOR_CUSTOM _CICE_FormatPartType = 0x02 96 _CICE_FPT_VERB_COLOR_FOR_LEVEL _CICE_FormatPartType = 0x03 97 _CICE_FPT_VERB_BODY _CICE_FormatPartType = 0x0A 98 _CICE_FPT_VERB_TIME _CICE_FormatPartType = 0x0B 99 _CICE_FPT_VERB_LEVEL _CICE_FormatPartType = 0x0C 100 _CICE_FPT_VERB_STACKTRACE _CICE_FormatPartType = 0x1A 101 _CICE_FPT_VERB_FIELDS _CICE_FormatPartType = 0x2A 102 _CICE_FPT_VERB_CALLER _CICE_FormatPartType = 0x3A 103 104 // Common Integrator Console Encoder Level Format (CICE LF) 105 // type constants. 106 107 _CICE_LF_NUMBER _CICE_FormatPartType = 1 108 _CICE_LF_SHORT_NORMAL _CICE_FormatPartType = 2 109 _CICE_LF_SHORT_UPPER_CASE _CICE_FormatPartType = 3 110 _CICE_LF_FULL_NORMAL _CICE_FormatPartType = 4 111 _CICE_LF_FULL_UPPER_CASE _CICE_FormatPartType = 5 112 113 // Common Integrator Console Encoder Time Format (CICE TF) 114 // type constants. 115 116 _CICE_TF_TIMESTAMP _CICE_FormatPartType = 1 117 _CICE_TF_ANSIC _CICE_FormatPartType = 2 118 _CICE_TF_UNIXDATE _CICE_FormatPartType = 3 119 _CICE_TF_RUBYDATE _CICE_FormatPartType = 4 120 _CICE_TF_RFC822 _CICE_FormatPartType = 5 121 _CICE_TF_RFC822_Z _CICE_FormatPartType = 6 122 _CICE_TF_RFC850 _CICE_FormatPartType = 7 123 _CICE_TF_RFC1123 _CICE_FormatPartType = 8 124 _CICE_TF_RFC1123_Z _CICE_FormatPartType = 9 125 _CICE_TF_RFC3339 _CICE_FormatPartType = 10 126 127 // Common Integrator Console Encoder Caller Format (CICE CF) 128 // type constants. 129 130 _CICE_CF_TYPE_SEPARATOR int16 = -1 131 _CICE_CF_TYPE_FUNC_SHORT int16 = 1 132 _CICE_CF_TYPE_FUNC_FULL int16 = 2 133 _CICE_CF_TYPE_FILE_SHORT int16 = 3 134 _CICE_CF_TYPE_FILE_FULL int16 = 4 135 _CICE_CF_TYPE_LINE_NUM int16 = 5 136 _CICE_CF_TYPE_PKG_SHORT int16 = 6 // unused 137 _CICE_CF_TYPE_PKG_FULL int16 = 7 138 139 // Common Integrator Console Encoder (CICE) verb predefined constants. 140 141 _CICE_VERB_START_INDICATOR rune = '{' 142 _CICE_VERB_END_INDICATOR rune = '}' 143 _CICE_VERB_SEPARATOR byte = '/' 144 145 // Common Integrator Console Integrator Standard Colors (CICE SC) 146 // predefined constants. 147 148 _CICE_SC_DEBUG string = "c/fg:#b2b2b2" 149 _CICE_SC_INFO string = "c/fg:#87d7ff" 150 _CICE_SC_NOTICE string = "c/fg:#00d700" 151 _CICE_SC_WARNING string = "c/fg:#ff8700" 152 _CICE_SC_ERROR string = "c/fg:#ff0000" 153 _CICE_SC_CRITICAL string = "c/fg:#ffaf00/b" 154 _CICE_SC_ALERT string = "c/fg:#ffaf00/b/u" 155 _CICE_SC_EMERGENCY string = "c/fg:#ff00ff/b/u" 156 157 // Common Integrator Console Encoder (CICE) defaults. 158 159 _CICE_DEFAULT_FORMAT string = "{{c}}[{{l/SS}}] <{{t}}>:{{c/0}} " + // include colored level, colored time 160 "{{m/?$\n}}" + // include message with \n if non-empty 161 "{{f/?$\n/v = /e, /l\t/le\t\t}}" + // include fields with " = " as key-value separator 162 "{{s/?$\n/e, }}" + // include stacktrace with \n if non-empty 163 "{{w/0/fd}}" + // omit caller, specify each stacktrace's frame format 164 "\n" 165 166 _CICE_DEFAULT_TIME_FORMAT string = "Mon Jan 02 15:04:05" 167 ) 168 169 // CommonIntegrator CI_ConsoleEncoder Verb Types (CICE VT) 170 // that are used in format string to determine what kind of verb must be used. 171 var ( 172 cevtCaller = []string{"caller", "who", "w"} 173 cevtColor = []string{"color", "c"} 174 cevtLevel = []string{"level", "lvl", "l"} 175 cevtTime = []string{"time", "t"} 176 cevtMessage = []string{"message", "body", "m", "b"} 177 cevtFields = []string{"fields", "f"} 178 cevtStacktrace = []string{"stacktrace", "s"} 179 ) 180 181 var ( 182 // Make sure we won't break API by declaring package's console encoder. 183 defaultConsoleEncoder CI_Encoder 184 ) 185 186 // Type extracts verb's type from current _CICE_FormatPartType that can be compared 187 // with _CEFPT_VERB... constants. 188 func (fpt _CICE_FormatPartType) Type() _CICE_FormatPartType { 189 return fpt & _CICE_FPT_MASK_TYPE 190 } 191 192 // Data extracts verb's type data from current _CICE_FormatPartType that is needed 193 // for some internal verb's encoders. 194 func (fpt _CICE_FormatPartType) Data() _CICE_FormatPartType { 195 return (fpt & _CICE_FPT_MASK_DATA) >> 8 196 } 197 198 // doBuild builds the current CI_ConsoleEncoder only if it has not built yet. 199 // There is no-op if format string is empty or encoder already built. 200 func (ce *CI_ConsoleEncoder) doBuild() *CI_ConsoleEncoder { 201 202 switch { 203 case ce == nil: 204 return nil 205 206 case len(ce.formatParts) > 0: 207 // do not build if it's so already 208 return ce 209 } 210 211 ce.format = strings.TrimSpace(ce.format) 212 if ce.format == "" { 213 ce.format = _CICE_DEFAULT_FORMAT 214 } 215 216 // start parsing ce.format 217 // all parsing loops are for-range based (because there is UTF-8 support) 218 // (yes, you can use not only ASCII parts in your format string, 219 // and yes if you do it, you are mad. stop it!). 220 for rest := ce.format; rest != ""; rest = ce.parseFirstVerb(rest) { 221 } 222 223 ce.uniteJustTextVerbs() 224 ce.setStandardParts() 225 226 return ce 227 } 228 229 // uniteJustTextVerbs unites "just text" verbs in 'ce.formatParts' 230 // that follows each other. It may happen when something with bad verbs 231 // were included in 'ce.format'. 232 func (ce *CI_ConsoleEncoder) uniteJustTextVerbs() *CI_ConsoleEncoder { 233 234 var ( 235 // idx of "just text" verb next "just text" verbs will be united with 236 justTextVerbIdx = -1 237 // new out slice of verbs 238 newFormatParts = make([]_CICE_FormatPart, 0, len(ce.formatParts)) 239 ) 240 241 for idx, verb := range ce.formatParts { 242 switch verbTyp := verb.typ.Type(); { 243 244 case justTextVerbIdx == -1 && verbTyp == _CICE_FPT_VERB_JUST_TEXT: 245 justTextVerbIdx = idx 246 247 case justTextVerbIdx != -1 && verbTyp == _CICE_FPT_VERB_JUST_TEXT: 248 ce.formatParts[justTextVerbIdx].value += verb.value 249 250 case justTextVerbIdx != -1 && verbTyp != _CICE_FPT_VERB_JUST_TEXT: 251 newFormatParts = append(newFormatParts, ce.formatParts[justTextVerbIdx]) 252 justTextVerbIdx = -1 253 fallthrough 254 255 case justTextVerbIdx == -1 && verbTyp != _CICE_FPT_VERB_JUST_TEXT: 256 newFormatParts = append(newFormatParts, verb) 257 } 258 } 259 260 // it was definitely the last "just text" verb we should add 261 if justTextVerbIdx != -1 { 262 newFormatParts = append(newFormatParts, ce.formatParts[justTextVerbIdx]) 263 } 264 265 ce.formatParts = newFormatParts[:] 266 return ce 267 } 268 269 // setStandardParts saves standard colors for standard log levels 270 // if they has not been set yet. 271 func (ce *CI_ConsoleEncoder) setStandardParts() *CI_ConsoleEncoder { 272 273 if ce.colorMap == nil { 274 ce.colorMap = make(map[Level]string) 275 } 276 277 if ce.colorMap[LEVEL_DEBUG] == "" { 278 ce.colorMap[LEVEL_DEBUG] = ce.rvColorHelper(_CICE_SC_DEBUG) 279 } 280 if ce.colorMap[LEVEL_INFO] == "" { 281 ce.colorMap[LEVEL_INFO] = ce.rvColorHelper(_CICE_SC_INFO) 282 } 283 if ce.colorMap[LEVEL_NOTICE] == "" { 284 ce.colorMap[LEVEL_NOTICE] = ce.rvColorHelper(_CICE_SC_NOTICE) 285 } 286 if ce.colorMap[LEVEL_WARNING] == "" { 287 ce.colorMap[LEVEL_WARNING] = ce.rvColorHelper(_CICE_SC_WARNING) 288 } 289 if ce.colorMap[LEVEL_ERROR] == "" { 290 ce.colorMap[LEVEL_ERROR] = ce.rvColorHelper(_CICE_SC_ERROR) 291 } 292 if ce.colorMap[LEVEL_CRITICAL] == "" { 293 ce.colorMap[LEVEL_CRITICAL] = ce.rvColorHelper(_CICE_SC_CRITICAL) 294 } 295 if ce.colorMap[LEVEL_ALERT] == "" { 296 ce.colorMap[LEVEL_ALERT] = ce.rvColorHelper(_CICE_SC_ALERT) 297 } 298 if ce.colorMap[LEVEL_EMERGENCY] == "" { 299 ce.colorMap[LEVEL_EMERGENCY] = ce.rvColorHelper(_CICE_SC_EMERGENCY) 300 } 301 302 if !ce.cf.isSet { 303 ce.cf.isDefault = true 304 } 305 306 return ce 307 } 308 309 // parseFirstVerb parses 'format', extracts first verb (even if it's "just text" 310 // verb), saves it to ce.formatParts and then returns the rest of 'format' string. 311 func (ce *CI_ConsoleEncoder) parseFirstVerb(format string) (rest string) { 312 313 var ( 314 i = 0 315 pc rune // prev char 316 wv bool // true if current parsing verb is complex verb (not "just text") 317 wve bool // true if complex verb has been closed correctly 318 ) 319 320 // loop is for-range based (because there is UTF-8 support) 321 // (yes, you can use not only ASCII parts in your format string, 322 // and yes if you do it, you are mad. stop it!). 323 for _, c := range format { 324 switch { 325 case c == _CICE_VERB_START_INDICATOR && pc == _CICE_VERB_START_INDICATOR && wv: 326 // unexpected "{{" inside complex verb, treat all prev as "just text", 327 // try to treat as starting complex verb 328 wv = false 329 i-- 330 331 case c == _CICE_VERB_START_INDICATOR && pc == _CICE_VERB_START_INDICATOR && i > 1: 332 // > 1 (not > 0) because if string started with "{{", after first "{" 333 // i already == 1. 334 // 335 // was "just text", found complex verb start 336 i-- 337 338 case c == _CICE_VERB_END_INDICATOR && pc == _CICE_VERB_END_INDICATOR && wv: 339 // ending of complex verb 340 wve = true 341 i++ 342 343 case c == _CICE_VERB_START_INDICATOR && pc == _CICE_VERB_START_INDICATOR: 344 // this is the beginning of 'format' and of complex verb 345 wv = true 346 i++ 347 continue 348 349 default: 350 pc = c 351 i++ 352 continue 353 } 354 break 355 } 356 357 // what kind of verb did we parse and whether verb has been closed correctly? 358 if wv && wve { 359 ce.minimumBufferLen += ce.rv(format[:i]) 360 } else { 361 ce.minimumBufferLen += ce.rvJustText(format[:i]) 362 } 363 364 return format[i:] 365 } 366 367 // rv (resolve verb) tries to determine what kind of complex verb 'verb' is, 368 // creates related '_CICE_FormatPart', fills it and adds to 'ce.formatParts'. 369 // Returned predicted minimum length of bytes that required in buffer to store 370 // formatted verb. 371 func (ce *CI_ConsoleEncoder) rv(verb string) (predictedLen int) { 372 373 // applyOnce is a helper func to avoid many if-else statements in tht switch below. 374 applyOnce := func(isSet *bool, fallback, applicator func(string) int, verb string) int { 375 if !*isSet { 376 *isSet = true 377 return applicator(verb) 378 } else { 379 return fallback(verb) 380 } 381 } 382 383 // it guarantees that "verb" starts from "{{", 384 // so we can remove leading "{{" and trailing "}}" 385 switch verb = verb[2 : len(verb)-2]; { 386 387 case hpm(verb, cevtCaller): 388 return applyOnce(&ce.cf.isSet, ce.rvJustText, ce.rvCaller, verb) 389 390 case hpm(verb, cevtColor): 391 return ce.rvColor(verb) 392 393 case hpm(verb, cevtLevel): 394 return ce.rvLevel(verb) 395 396 case hpm(verb, cevtTime): 397 return ce.rvTime(verb) 398 399 case hpm(verb, cevtMessage): 400 return applyOnce(&ce.bf.isSet, ce.rvJustText, ce.rvBody, verb) 401 402 case hpm(verb, cevtFields): 403 return applyOnce(&ce.ff.isSet, ce.rvJustText, ce.rvFields, verb) 404 405 case hpm(verb, cevtStacktrace): 406 return applyOnce(&ce.sf.isSet, ce.rvJustText, ce.rvStacktrace, verb) 407 408 default: 409 // incorrect verb, treat it as "just text" verb 410 return ce.rvJustText(verb) 411 } 412 } 413 414 // rvHelper is a part of "resolve verb" functions but moreover it's a helper. 415 // This function literally have no recognition algorithm but splits 'verb' 416 // to verb parts (ignoring first part, assuming that its verb's name) and then 417 // calls 'resolver' for each that part. 418 // Stops splitting and calling 'resolver' if it's return 'false' one time. 419 // 420 // It guarantees that if 'verbPart' is empty, 'resolver' won't be called for that 421 // and processing will stopped. 422 // 423 // Requirements: 424 // 'verb' != "", 'resolver' != nil. Otherwise no-op. 425 func (_ *CI_ConsoleEncoder) rvHelper(verb string, resolver func(verbPart string) (continue_ bool)) { 426 427 if verb == "" || resolver == nil { 428 return 429 } 430 431 // skip verb name 432 if idx := strings.IndexByte(verb, _CICE_VERB_SEPARATOR); idx != -1 { 433 verb = verb[idx+1:] 434 } else { 435 // there is no verb separator, it's simple verb. Do nothing 436 return 437 } 438 439 for idx := strings.IndexByte(verb, _CICE_VERB_SEPARATOR); idx != -1; { 440 if idx == 0 || !resolver(verb[:idx]) { 441 goto STOP 442 } 443 verb = verb[idx+1:] 444 idx = strings.IndexByte(verb, _CICE_VERB_SEPARATOR) 445 } 446 447 // parse last entity 448 if !resolver(verb) { 449 goto STOP 450 } 451 452 STOP: 453 return 454 } 455 456 func (ce *CI_ConsoleEncoder) rvJustText(text string) (predictedLen int) { 457 458 if text != "" { 459 ce.formatParts = append(ce.formatParts, _CICE_FormatPart{ 460 typ: _CICE_FPT_VERB_JUST_TEXT, 461 value: text, 462 }) 463 } 464 465 return len(text) 466 } 467 468 func (ce *CI_ConsoleEncoder) rvLevel(verb string) (predictedLen int) { 469 470 formattedLevel := _CICE_LF_FULL_NORMAL 471 predictedLen = 9 472 473 if idx := strings.IndexByte(verb, _CICE_VERB_SEPARATOR); idx != -1 { 474 switch verb[idx+1:] { 475 case "d", "D": 476 formattedLevel = _CICE_LF_NUMBER 477 predictedLen = 1 478 case "s": 479 formattedLevel = _CICE_LF_SHORT_NORMAL 480 predictedLen = 5 481 case "S": 482 formattedLevel = _CICE_LF_SHORT_UPPER_CASE 483 predictedLen = 5 484 case "ss": 485 formattedLevel = _CICE_LF_FULL_NORMAL 486 predictedLen = 9 487 case "SS": 488 formattedLevel = _CICE_LF_FULL_UPPER_CASE 489 predictedLen = 9 490 } 491 } 492 493 ce.formatParts = append(ce.formatParts, _CICE_FormatPart{ 494 typ: _CICE_FPT_VERB_LEVEL | (formattedLevel << 8), 495 }) 496 497 return predictedLen 498 } 499 500 func (ce *CI_ConsoleEncoder) rvTime(verb string) (predictedLen int) { 501 502 format := _CICE_DEFAULT_TIME_FORMAT 503 formattedTime := _CICE_FormatPartType(0) 504 505 (*CI_ConsoleEncoder)(nil).rvHelper(verb, func(verbPart string) (continue_ bool) { 506 if verbPart = strings.TrimSpace(format); verbPart != "" { 507 switch predefined := strings.ToUpper(verbPart); predefined { 508 case "UNIX", "TIMESTAMP": 509 formattedTime = _CICE_TF_TIMESTAMP 510 case "ANSIC": 511 formattedTime = _CICE_TF_ANSIC 512 case "UNIXDATE", "UNIX_DATE": 513 formattedTime = _CICE_TF_UNIXDATE 514 case "RUBYDATE", "RUBY_DATE": 515 formattedTime = _CICE_TF_RUBYDATE 516 case "RFC822": 517 formattedTime = _CICE_TF_RFC822 518 case "RFC822Z": 519 formattedTime = _CICE_TF_RFC822_Z 520 case "RFC850": 521 formattedTime = _CICE_TF_RFC850 522 case "RFC1123": 523 formattedTime = _CICE_TF_RFC1123 524 case "RFC1123Z": 525 formattedTime = _CICE_TF_RFC1123_Z 526 case "RFC3339": 527 formattedTime = _CICE_TF_RFC3339 528 default: 529 format = verbPart 530 } 531 } 532 return false // only first time verb is allowed and will be parsed 533 }) 534 535 ce.formatParts = append(ce.formatParts, _CICE_FormatPart{ 536 typ: _CICE_FPT_VERB_TIME | (formattedTime << 8), 537 value: format, 538 }) 539 540 return len(time.Now().Format(format)) + 10 // stock for some weekdays 541 } 542 543 func (ce *CI_ConsoleEncoder) rvColor(verb string) (predictedLen int) { 544 545 if idx := strings.IndexByte(verb, _CICE_VERB_SEPARATOR); idx == -1 { 546 ce.formatParts = append(ce.formatParts, _CICE_FormatPart{ 547 typ: _CICE_FPT_VERB_COLOR_FOR_LEVEL, 548 }) 549 return ce.colorMapMax 550 } 551 552 if encodedColor := ce.rvColorHelper(verb); encodedColor != "" { 553 ce.formatParts = append(ce.formatParts, _CICE_FormatPart{ 554 typ: _CICE_FPT_VERB_COLOR_CUSTOM, 555 value: encodedColor, 556 }) 557 return len(encodedColor) 558 } else { 559 return ce.rvJustText(verb) 560 } 561 } 562 563 func (_ *CI_ConsoleEncoder) rvColorHelper(colorVerb string) string { 564 565 cb := colorBuilder{} 566 cb.init() 567 568 (*CI_ConsoleEncoder)(nil).rvHelper(colorVerb, func(verbPart string) (continue_ bool) { 569 return cb.parseEntity(verbPart) 570 }) 571 572 return cb.encode() 573 } 574 575 // rvBody is a part of "resolve verb" functions. 576 // rvBody tries to parse 'verb' as logger Entry's body and anyway indicates that 577 // here will be stored Entry's body. 578 // 579 // Verb's arguments: 580 // - For non-empty body: 581 // - "?^<text>": <text> will be prepended to the Entry's body at the runtime. 582 // - "?$<text>": <text> will be appended to the Entry's body at the runtime. 583 func (ce *CI_ConsoleEncoder) rvBody(verb string) (predictedLen int) { 584 585 (*CI_ConsoleEncoder)(nil).rvHelper(verb, func(verbPart string) (continue_ bool) { 586 switch { 587 case strings.HasPrefix(verbPart, "?^"): 588 ce.bf.beforeBody = verbPart[2:] 589 case strings.HasPrefix(verbPart, "?$"): 590 ce.bf.afterBody = verbPart[2:] 591 default: 592 return false 593 } 594 return true 595 }) 596 597 ce.formatParts = append(ce.formatParts, _CICE_FormatPart{ 598 typ: _CICE_FPT_VERB_BODY, 599 }) 600 601 return 256 + len(ce.bf.beforeBody) + len(ce.bf.afterBody) 602 } 603 604 func (ce *CI_ConsoleEncoder) rvCaller(verb string) (predictedLen int) { 605 606 isAdd := true 607 formatPrefixes := []string{"f", "F"} 608 609 (*CI_ConsoleEncoder)(nil).rvHelper(verb, func(verbPart string) (continue_ bool) { 610 switch { 611 case verbPart == "0": 612 isAdd = false 613 case hpm(verbPart, formatPrefixes): 614 predictedLen += ce.rvCallerFormat(verbPart[1:]) 615 default: 616 return false 617 } 618 return true 619 }) 620 621 if isAdd { 622 ce.formatParts = append(ce.formatParts, _CICE_FormatPart{ 623 typ: _CICE_FPT_VERB_CALLER, 624 }) 625 return predictedLen 626 } else { 627 return 0 628 } 629 } 630 631 func (ce *CI_ConsoleEncoder) rvCallerFormat(f string) (predictedLen int) { 632 633 if f == "" || f == "d" || f == "D" { 634 ce.cf.isDefault = true 635 return 256 636 } 637 638 j := 0 // index of ce.cf.parts 639 for _, fc := range f { 640 641 t := _CICE_CF_TYPE_SEPARATOR // by default threat it as a separator 642 switch fc { 643 644 case 'w': 645 t = _CICE_CF_TYPE_FUNC_SHORT 646 case 'W': 647 t = _CICE_CF_TYPE_FUNC_FULL 648 case 'f': 649 t = _CICE_CF_TYPE_FILE_SHORT 650 case 'F': 651 t = _CICE_CF_TYPE_FILE_FULL 652 case 'l', 'L': 653 t = _CICE_CF_TYPE_LINE_NUM 654 case 'p', 'P': 655 t = _CICE_CF_TYPE_PKG_FULL 656 657 default: 658 switch ce.cf.parts[j].typ { 659 default: 660 j++ 661 fallthrough 662 case 0: 663 ce.cf.parts[j].typ = _CICE_CF_TYPE_SEPARATOR 664 fallthrough 665 case -1: 666 ce.cf.parts[j].val += string(fc) 667 } 668 continue 669 } 670 671 // maybe it's already existed? skip it if it so 672 for i, n := 0, len(ce.cf.parts); i < n && ce.cf.parts[i].typ != 0; i++ { 673 if ce.cf.parts[i].typ == t { 674 t = _CICE_CF_TYPE_SEPARATOR 675 } 676 } 677 678 if t != _CICE_CF_TYPE_SEPARATOR { 679 if ce.cf.parts[j].typ != 0 { 680 j++ 681 } 682 ce.cf.parts[j].typ = t 683 } 684 } 685 686 return 256 687 } 688 689 // rvFields is a part of "resolve verb" functions. 690 // rvFields tries to parse 'verb' as logger Entry's fields (not attached Error's) 691 // but anyway indicates that here will be stored Entry's body. 692 // 693 // Verb's arguments: 694 // - If at least one LetterField presented: 695 // - "?^<text>": <text> will be write before any LetterField is written at the runtime. 696 // - "?$<text>": <text> will be appended to the end of last LetterField at the runtime. 697 // - "k<text>": <text> will be written before LetterField's keys is written. 698 // - "v<text>": <text> will be written before LetterField's value is written. 699 // - "e<text>": <text> will be written after LetterField's value excluding last. 700 // - "l<text>": <text> will be written at the each new line of fields' part set. 701 // - "*<int>": <int> is how much fields are placed at the one line 702 // (by default: 4. Use <= 0 value to place all fields at the one line). 703 func (ce *CI_ConsoleEncoder) rvFields(verb string) (predictedLen int) { 704 705 ce.ff.itemsPerLine = 4 706 707 (*CI_ConsoleEncoder)(nil).rvHelper(verb, func(verbPart string) (continue_ bool) { 708 switch upperCased := strings.ToUpper(verbPart); { 709 710 case strings.HasPrefix(verbPart, "?^"): 711 ce.ff.beforeFields = verbPart[2:] 712 case strings.HasPrefix(verbPart, "?$"): 713 ce.ff.afterFields = verbPart[2:] 714 case strings.HasPrefix(upperCased, "LE"): 715 ce.ff.afterNewLineForError = verbPart[2:] 716 case upperCased[0] == 'L': 717 ce.ff.afterNewLine = verbPart[1:] 718 case upperCased[0] == 'K': 719 ce.ff.beforeKey = verbPart[1:] 720 case upperCased[0] == 'V': 721 ce.ff.afterKey = verbPart[1:] 722 case upperCased[0] == 'E': 723 ce.ff.afterValue = verbPart[1:] 724 725 case verbPart[0] == '*': 726 if perLine_, err := strconv.Atoi(verbPart[1:]); err == nil { 727 if perLine_ < 0 { 728 ce.ff.itemsPerLine = 0 729 } else { 730 ce.ff.itemsPerLine = int16(perLine_) 731 } 732 } 733 734 default: 735 return false 736 } 737 return true 738 }) 739 740 ce.formatParts = append(ce.formatParts, _CICE_FormatPart{ 741 typ: _CICE_FPT_VERB_FIELDS, 742 }) 743 744 return 512 + len(ce.ff.beforeFields) + len(ce.ff.afterFields) + len(ce.ff.beforeKey) + 745 len(ce.ff.afterKey) + len(ce.ff.afterValue) + len(ce.ff.afterNewLine) 746 } 747 748 func (ce *CI_ConsoleEncoder) rvStacktrace(verb string) (predictedLen int) { 749 750 (*CI_ConsoleEncoder)(nil).rvHelper(verb, func(verbPart string) (continue_ bool) { 751 switch { 752 case strings.HasPrefix(verbPart, "?^"): 753 ce.sf.beforeStack = verbPart[2:] 754 case strings.HasPrefix(verbPart, "?$"): 755 ce.sf.afterStack = verbPart[2:] 756 default: 757 return false 758 } 759 return true 760 }) 761 762 ce.formatParts = append(ce.formatParts, _CICE_FormatPart{ 763 typ: _CICE_FPT_VERB_STACKTRACE, 764 }) 765 766 return 2048 767 } 768 769 func (ce *CI_ConsoleEncoder) encodeJustText(to []byte, fp _CICE_FormatPart) []byte { 770 return bufw(to, fp.value) 771 } 772 773 func (ce *CI_ConsoleEncoder) encodeLevel(to []byte, fp _CICE_FormatPart, e *Entry) []byte { 774 775 formattedLevel := "" 776 switch fp.typ.Data() { 777 778 case _CICE_LF_NUMBER: 779 formattedLevel = strconv.Itoa(int(e.Level)) 780 case _CICE_LF_SHORT_NORMAL: 781 formattedLevel = e.Level.String3() 782 case _CICE_LF_SHORT_UPPER_CASE: 783 formattedLevel = e.Level.ToUpper3() 784 case _CICE_LF_FULL_NORMAL: 785 formattedLevel = e.Level.String() 786 case _CICE_LF_FULL_UPPER_CASE: 787 formattedLevel = strings.ToUpper(e.Level.String()) 788 } 789 790 return bufw(to, formattedLevel) 791 } 792 793 func (ce *CI_ConsoleEncoder) encodeTime(e *Entry, fp _CICE_FormatPart, to []byte) []byte { 794 795 formattedTime := "" 796 797 switch fp.typ.Data() { 798 case _CICE_TF_TIMESTAMP: 799 formattedTime = strconv.FormatInt(e.Time.Unix(), 10) 800 case _CICE_TF_ANSIC: 801 formattedTime = e.Time.Format(time.ANSIC) 802 case _CICE_TF_UNIXDATE: 803 formattedTime = e.Time.Format(time.UnixDate) 804 case _CICE_TF_RUBYDATE: 805 formattedTime = e.Time.Format(time.RubyDate) 806 case _CICE_TF_RFC822: 807 formattedTime = e.Time.Format(time.RFC822) 808 case _CICE_TF_RFC822_Z: 809 formattedTime = e.Time.Format(time.RFC822Z) 810 case _CICE_TF_RFC850: 811 formattedTime = e.Time.Format(time.RFC850) 812 case _CICE_TF_RFC1123: 813 formattedTime = e.Time.Format(time.RFC1123) 814 case _CICE_TF_RFC1123_Z: 815 formattedTime = e.Time.Format(time.RFC1123Z) 816 case _CICE_TF_RFC3339: 817 formattedTime = e.Time.Format(time.RFC3339) 818 default: 819 formattedTime = e.Time.Format(fp.value) 820 } 821 822 return bufw(to, formattedTime) 823 } 824 825 func (ce *CI_ConsoleEncoder) encodeColor(to []byte, fp _CICE_FormatPart) []byte { 826 return bufw(to, fp.value) 827 } 828 829 func (ce *CI_ConsoleEncoder) encodeColorForLevel(to []byte, e *Entry) []byte { 830 if color := ce.colorMap[e.Level]; color != "" { 831 return bufw(to, color) 832 } 833 return to 834 } 835 836 func (ce *CI_ConsoleEncoder) encodeBody(to []byte, e *Entry) []byte { 837 838 body := e.LogLetter.Messages[0].Body 839 if body == "" { 840 return to 841 } 842 843 if ce.bf.beforeBody != "" { 844 to = bufw(to, ce.bf.beforeBody) 845 } 846 847 to = bufw(to, body) 848 849 if ce.bf.afterBody != "" { 850 to = bufw(to, ce.bf.afterBody) 851 } 852 853 return to 854 } 855 856 func (ce *CI_ConsoleEncoder) encodeCaller(to []byte, e *Entry) []byte { 857 858 var frame *ekasys.StackFrame 859 860 switch { 861 case len(e.LogLetter.StackTrace) > 0: 862 frame = &e.LogLetter.StackTrace[0] 863 864 case e.ErrLetter != nil: 865 frame = &e.ErrLetter.StackTrace[0] 866 867 default: 868 return to 869 } 870 871 return ce.encodeStackFrame(to, frame, nil, ekaletter.LetterMessage{}) 872 } 873 874 func (ce *CI_ConsoleEncoder) encodeFields(to []byte, fs, addFs []ekaletter.LetterField, isErrors, addPreEncoded bool) []byte { 875 876 if len(fs) == 0 && len(addFs) == 0 { 877 return to 878 } 879 880 if !isErrors && ce.ff.beforeFields != "" { 881 to = bufw(to, ce.ff.beforeFields) 882 } 883 884 var ( 885 unnamedFieldIdx, writtenFields int16 886 ) 887 888 addField := func(to []byte, f *ekaletter.LetterField, isErrors bool, unnamedFieldIex, writtenFields *int16) []byte { 889 if strings.HasPrefix(f.Key, "sys.") { 890 return to 891 } 892 893 keyBak := f.Key 894 895 if f.Key == "" && !f.IsSystem() { 896 f.Key = f.KeyOrUnnamed(&unnamedFieldIdx) 897 } 898 899 toLenBak := len(to) 900 to = ce.encodeField(to, *f, isErrors, *writtenFields) 901 if len(to) != toLenBak { 902 *writtenFields++ 903 } 904 905 f.Key = keyBak 906 return to 907 } 908 909 for i, n := int16(0), int16(len(fs)); i < n; i++ { 910 to = addField(to, &fs[i], isErrors, &unnamedFieldIdx, &writtenFields) 911 } 912 for i, n := int16(0), int16(len(addFs)); i < n; i++ { 913 to = addField(to, &addFs[i], isErrors, &unnamedFieldIdx, &writtenFields) 914 } 915 916 if addPreEncoded && ce.preEncodedFieldsWritten > 0 { 917 if ce.ff.afterValue != "" { 918 to = to[:len(to)-len(ce.ff.afterValue)] 919 } 920 if l := len(to); to[l-1] != '\n' { 921 to = bufw(to, "\n") 922 } 923 to = bufw2(to, ce.preEncodedFields) 924 } 925 926 if writtenFields > 0 { 927 928 // remove last "after value" and write "after fields" 929 if ce.ff.afterValue != "" { 930 to = to[:len(to)-len(ce.ff.afterValue)] 931 } 932 933 if !isErrors && ce.ff.afterFields != "" { 934 to = bufw(to, ce.ff.afterFields) 935 } 936 937 } else { 938 939 // no fields were written 940 // remove 'beforeFields' part 941 to = to[:len(to)-len(ce.ff.beforeFields)] 942 } 943 944 return to 945 } 946 947 func (ce *CI_ConsoleEncoder) encodeField(to []byte, f ekaletter.LetterField, isErrors bool, fieldNum int16) []byte { 948 949 if f.IsSystem() && f.BaseType() == ekaletter.KIND_SYS_TYPE_EKAERR_CLASS_ID { 950 return to 951 } 952 953 // Maybe field wants to be started with new line? 954 oldKey := f.Key 955 if f.Key = strings.TrimSpace(f.Key); len(oldKey) != len(f.Key) { 956 to = bufw(to, "\n") 957 } 958 959 // write new line and new line title 960 if ce.ff.itemsPerLine > 0 && fieldNum != 0 && fieldNum%ce.ff.itemsPerLine == 0 { 961 to = bufw(to, "\n") 962 } 963 964 if wasNewLine := to[len(to)-1] == '\n'; wasNewLine && !isErrors && len(ce.ff.afterNewLine) > 0 { 965 to = bufw(to, ce.ff.afterNewLine) 966 } else if wasNewLine && isErrors && len(ce.ff.afterNewLineForError) > 0 { 967 to = bufw(to, ce.ff.afterNewLineForError) 968 } 969 970 // Write "before key", key, "after key", value and "after value" 971 972 if ce.ff.beforeKey != "" { 973 to = bufw(to, ce.ff.beforeKey) 974 } 975 to = bufw(to, f.Key) 976 if ce.ff.afterKey != "" { 977 to = bufw(to, ce.ff.afterKey) 978 } 979 to = ce.encodeFieldValue(to, f) 980 if ce.ff.afterValue != "" { 981 to = bufw(to, ce.ff.afterValue) 982 } 983 984 return to 985 } 986 987 func (ce *CI_ConsoleEncoder) encodeFieldValue(to []byte, f ekaletter.LetterField) []byte { 988 989 if f.Kind.IsSystem() { 990 switch f.Kind.BaseType() { 991 992 case ekaletter.KIND_SYS_TYPE_EKAERR_UUID, ekaletter.KIND_SYS_TYPE_EKAERR_CLASS_NAME: 993 to = bufw(to, `"`) 994 to = bufw(to, f.SValue) 995 to = bufw(to, `"`) 996 997 case ekaletter.KIND_SYS_TYPE_EKAERR_CLASS_ID: 998 to = strconv.AppendInt(to, f.IValue, 10) 999 1000 default: 1001 to = bufw(to, `"<unsupported system field>"`) 1002 } 1003 1004 } else if f.Kind.IsNil() { 1005 to = bufw(to, "null") 1006 1007 } else if f.Kind.IsInvalid() { 1008 to = bufw(to, "<invalid_field>") 1009 1010 } else { 1011 switch f.Kind.BaseType() { 1012 1013 case ekaletter.KIND_TYPE_BOOL: 1014 to = strconv.AppendBool(to, f.IValue != 0) 1015 1016 case ekaletter.KIND_TYPE_INT, 1017 ekaletter.KIND_TYPE_INT_8, ekaletter.KIND_TYPE_INT_16, 1018 ekaletter.KIND_TYPE_INT_32, ekaletter.KIND_TYPE_INT_64: 1019 to = strconv.AppendInt(to, f.IValue, 10) 1020 1021 case ekaletter.KIND_TYPE_UINT, 1022 ekaletter.KIND_TYPE_UINT_8, ekaletter.KIND_TYPE_UINT_16, 1023 ekaletter.KIND_TYPE_UINT_32, ekaletter.KIND_TYPE_UINT_64: 1024 to = strconv.AppendUint(to, uint64(f.IValue), 10) 1025 1026 case ekaletter.KIND_TYPE_FLOAT_32: 1027 f := float64(math.Float32frombits(uint32(f.IValue))) 1028 to = strconv.AppendFloat(to, f, 'f', 2, 32) 1029 1030 case ekaletter.KIND_TYPE_FLOAT_64: 1031 f := math.Float64frombits(uint64(f.IValue)) 1032 to = strconv.AppendFloat(to, f, 'f', 2, 64) 1033 1034 case ekaletter.KIND_TYPE_UINTPTR, ekaletter.KIND_TYPE_ADDR: 1035 to = bufw(to, "0x") 1036 to = strconv.AppendInt(to, f.IValue, 16) 1037 1038 case ekaletter.KIND_TYPE_STRING: 1039 to = strconv.AppendQuote(to, f.SValue) 1040 1041 case ekaletter.KIND_TYPE_COMPLEX_64: 1042 r := math.Float32frombits(uint32(f.IValue >> 32)) 1043 i := math.Float32frombits(uint32(f.IValue)) 1044 c := complex128(complex(r, i)) 1045 // TODO: Use strconv.AppendComplex() when it will be released. 1046 to = bufw(to, strconv.FormatComplex(c, 'f', 2, 32)) 1047 1048 case ekaletter.KIND_TYPE_COMPLEX_128: 1049 c := f.Value.(complex128) 1050 // TODO: Use strconv.AppendComplex() when it will be released. 1051 to = bufw(to, strconv.FormatComplex(c, 'f', 2, 64)) 1052 1053 case ekaletter.KIND_TYPE_UNIX: 1054 to = bufw(to, time.Unix(f.IValue, 0).Format("Jan 2 15:04:05")) 1055 1056 case ekaletter.KIND_TYPE_UNIX_NANO: 1057 to = bufw(to, time.Unix(0, f.IValue).Format("Jan 2 15:04:05.000000000")) 1058 1059 case ekaletter.KIND_TYPE_DURATION: 1060 to = bufw(to, time.Duration(f.IValue).String()) 1061 1062 case ekaletter.KIND_TYPE_MAP, ekaletter.KIND_TYPE_EXTMAP: 1063 // TODO: Add support of extracted maps. 1064 if jsonedMap, legacyErr := jsoniter.Marshal(f.Value); legacyErr == nil { 1065 to = bufw2(to, jsonedMap) 1066 } else { 1067 to = bufw(to, "<unsupported_map>") 1068 } 1069 1070 case ekaletter.KIND_TYPE_STRUCT: 1071 if jsonedStruct, legacyErr := jsoniter.Marshal(f.Value); legacyErr == nil { 1072 to = bufw2(to, jsonedStruct) 1073 } else { 1074 to = bufw(to, "<unsupported_struct>") 1075 } 1076 1077 case ekaletter.KIND_TYPE_ARRAY: 1078 if jsonedArray, legacyErr := jsoniter.Marshal(f.Value); legacyErr == nil { 1079 to = bufw2(to, jsonedArray) 1080 } else { 1081 to = bufw(to, "<unsupported_array>") 1082 } 1083 1084 default: 1085 to = bufw(to, "<unsupported_field>") 1086 } 1087 } 1088 1089 return to 1090 } 1091 1092 func (ce *CI_ConsoleEncoder) encodeStacktrace(to []byte, e *Entry) []byte { 1093 1094 trace := e.LogLetter.StackTrace 1095 isLightweightError := false 1096 1097 if len(trace) == 0 && e.ErrLetter != nil { 1098 trace = e.ErrLetter.StackTrace 1099 isLightweightError = len(trace) == 0 1100 } 1101 1102 n := int16(len(trace)) 1103 if n == 0 && !isLightweightError { 1104 return to 1105 } 1106 1107 if ce.sf.beforeStack != "" { 1108 to = bufw(to, ce.sf.beforeStack) 1109 } 1110 1111 var ( 1112 fi = 0 // fi for fields' index 1113 mi = 0 // mi for messages' index 1114 1115 fields []ekaletter.LetterField // We don't use log's fields and messages here 1116 messages []ekaletter.LetterMessage // 'cause they will be written in a different place. 1117 ) 1118 1119 if e.ErrLetter != nil { 1120 fields = e.ErrLetter.Fields 1121 messages = e.ErrLetter.Messages 1122 } 1123 1124 // Simulate stacktrace's length if it's a lightweight error. 1125 1126 if isLightweightError { 1127 1128 var fieldGreatestFrameIdx int16 = 0 1129 if nf := len(fields); nf > 0 { 1130 fieldGreatestFrameIdx = fields[nf-1].StackFrameIdx 1131 } 1132 1133 var messageGreatestFrameIdx int16 = 0 1134 if nm := len(messages); nm > 0 { 1135 messageGreatestFrameIdx = messages[nm-1].StackFrameIdx 1136 } 1137 1138 n = ekamath.Max(fieldGreatestFrameIdx, messageGreatestFrameIdx) 1139 } 1140 1141 for i := int16(0); i < n; i++ { 1142 messageForFrame := ekaletter.LetterMessage{} 1143 fieldsForFrame := []ekaletter.LetterField(nil) 1144 fiEnd := 0 1145 1146 //goland:noinspection GoNilness 1147 if mi < len(messages) && messages[mi].StackFrameIdx == i { 1148 messageForFrame = messages[mi] 1149 mi++ 1150 } 1151 1152 if fi < len(fields) && fields[fi].StackFrameIdx == i { 1153 fiEnd = fi + 1 1154 for fiEnd < len(fields) && fields[fiEnd].StackFrameIdx == i { 1155 fiEnd++ 1156 } 1157 } 1158 1159 if fiEnd != 0 { 1160 fieldsForFrame = fields[fi:fiEnd] 1161 } 1162 1163 var frame *ekasys.StackFrame = nil 1164 if !isLightweightError { 1165 frame = &trace[i] 1166 } 1167 1168 to = ce.encodeStackFrame(to, frame, fieldsForFrame, messageForFrame) 1169 } 1170 1171 if nt := len(to) - 1; to[nt] == '\n' { 1172 to = to[:nt] 1173 } 1174 1175 if ce.sf.afterStack != "" { 1176 to = bufw(to, ce.sf.afterStack) 1177 } 1178 1179 return to 1180 } 1181 1182 func (ce *CI_ConsoleEncoder) encodeStackFrame( 1183 1184 to []byte, 1185 frame *ekasys.StackFrame, 1186 fields []ekaletter.LetterField, 1187 message ekaletter.LetterMessage, 1188 1189 ) []byte { 1190 1191 lToAtStart := len(to) 1192 1193 switch { 1194 case frame == nil: 1195 // It's a lightweight error's frame. Do nothing. 1196 1197 case !ce.cf.isDefault: 1198 // Reminder: frame.DoFormat does once: 1199 // "<package>/<func> (<short_file>:<file_line>) <full_package_path>". 1200 1201 for i, n := 0, len(ce.cf.parts); i < n && ce.cf.parts[i].typ != 0; i++ { 1202 switch ce.cf.parts[i].typ { 1203 1204 case _CICE_CF_TYPE_SEPARATOR: 1205 to = bufw(to, ce.cf.parts[i].val) 1206 1207 case _CICE_CF_TYPE_FUNC_SHORT: 1208 frame.DoFormat() 1209 to = bufw(to, frame.Format[:frame.FormatFileOffset-1]) 1210 1211 case _CICE_CF_TYPE_FUNC_FULL: 1212 to = bufw(to, frame.Function) 1213 1214 case _CICE_CF_TYPE_FILE_SHORT: 1215 frame.DoFormat() 1216 i := strings.IndexByte(frame.Format, ':') 1217 to = bufw(to, frame.Format[frame.FormatFileOffset+1:i]) 1218 1219 case _CICE_CF_TYPE_FILE_FULL: 1220 to = bufw(to, frame.File) 1221 1222 case _CICE_CF_TYPE_LINE_NUM: 1223 to = bufw(to, strconv.Itoa(frame.Line)) 1224 1225 case _CICE_CF_TYPE_PKG_FULL: 1226 to = bufw(to, frame.Format[frame.FormatFullPathOffset:]) 1227 } 1228 } 1229 1230 default: 1231 to = bufw(to, frame.DoFormat()) 1232 } 1233 1234 if message.Body != "" || len(fields) > 0 { 1235 1236 if frame != nil { 1237 to = bufwc(to, '\n') 1238 } 1239 1240 if ce.ff.afterNewLineForError != "" { 1241 to = bufw(to, ce.ff.afterNewLineForError) 1242 } 1243 1244 if message.Body != "" { 1245 to = bufw(to, message.Body) 1246 to = bufwc(to, '\n') 1247 } 1248 1249 lToBefore := len(to) 1250 to = ce.encodeFields(to, fields, nil, true, false) 1251 1252 // ce.encodeFields may write no fields. Then we must clear last "\n" 1253 if len(to) == lToBefore { 1254 to = to[:len(to)-1] 1255 } 1256 } 1257 1258 if lToAtStart != len(to) { 1259 to = bufwc(to, '\n') 1260 } 1261 1262 return to 1263 } 1264 1265 func (dc *_CICE_DropColors) Write(p []byte) (n int, err error) { 1266 if n, err = dc.buf.Write(p); err != nil && err != io.EOF { 1267 return n, err 1268 } 1269 b := dc.buf.Bytes() 1270 j := 0 1271 for i, n, write := 0, len(b), true; i < n; i++ { 1272 if write && b[i] == '\033' { 1273 write = false 1274 i += 2 1275 } else if !write && b[i] == 'm' { 1276 write = true 1277 } else if write { 1278 b[j] = b[i] 1279 j++ 1280 } 1281 } 1282 return dc.dest.Write(b[:j]) 1283 }