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  }