github.com/waldiirawan/apm-agent-go/v2@v2.2.2/model/marshal.go (about)

     1  // Licensed to Elasticsearch B.V. under one or more contributor
     2  // license agreements. See the NOTICE file distributed with
     3  // this work for additional information regarding copyright
     4  // ownership. Elasticsearch B.V. licenses this file to you under
     5  // the Apache License, Version 2.0 (the "License"); you may
     6  // not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing,
    12  // software distributed under the License is distributed on an
    13  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14  // KIND, either express or implied.  See the License for the
    15  // specific language governing permissions and limitations
    16  // under the License.
    17  
    18  package model // import "github.com/waldiirawan/apm-agent-go/v2/model"
    19  
    20  import (
    21  	"encoding/hex"
    22  	"encoding/json"
    23  	"net/http"
    24  	"net/url"
    25  	"sort"
    26  	"strings"
    27  	"time"
    28  
    29  	"github.com/pkg/errors"
    30  
    31  	"github.com/waldiirawan/apm-agent-go/v2/internal/apmstrings"
    32  	"go.elastic.co/fastjson"
    33  )
    34  
    35  //go:generate sh generate.sh
    36  
    37  // MarshalFastJSON writes the JSON representation of t to w.
    38  func (t Time) MarshalFastJSON(w *fastjson.Writer) error {
    39  	w.Int64(time.Time(t).UnixNano() / int64(time.Microsecond))
    40  	return nil
    41  }
    42  
    43  // UnmarshalJSON unmarshals the JSON data into t.
    44  func (t *Time) UnmarshalJSON(data []byte) error {
    45  	var usec int64
    46  	if err := json.Unmarshal(data, &usec); err != nil {
    47  		return err
    48  	}
    49  	*t = Time(time.Unix(usec/1000000, (usec%1000000)*1000).UTC())
    50  	return nil
    51  }
    52  
    53  // UnmarshalJSON unmarshals the JSON data into v.
    54  func (v *HTTPSpanContext) UnmarshalJSON(data []byte) error {
    55  	var httpSpanContext struct {
    56  		URL        string
    57  		StatusCode int `json:"status_code"`
    58  	}
    59  	if err := json.Unmarshal(data, &httpSpanContext); err != nil {
    60  		return err
    61  	}
    62  	u, err := url.Parse(httpSpanContext.URL)
    63  	if err != nil {
    64  		return err
    65  	}
    66  	v.URL = u
    67  	v.StatusCode = httpSpanContext.StatusCode
    68  	return nil
    69  }
    70  
    71  // MarshalFastJSON writes the JSON representation of v to w.
    72  func (v *HTTPSpanContext) MarshalFastJSON(w *fastjson.Writer) error {
    73  	w.RawByte('{')
    74  	first := true
    75  	if v.URL != nil {
    76  		beforeURL := w.Size()
    77  		w.RawString(`"url":"`)
    78  		if v.marshalURL(w) {
    79  			w.RawByte('"')
    80  			first = false
    81  		} else {
    82  			w.Rewind(beforeURL)
    83  		}
    84  	}
    85  	if v.StatusCode > 0 {
    86  		if !first {
    87  			w.RawByte(',')
    88  		}
    89  		w.RawString(`"status_code":`)
    90  		w.Int64(int64(v.StatusCode))
    91  	}
    92  	w.RawByte('}')
    93  	return nil
    94  }
    95  
    96  func (v *HTTPSpanContext) marshalURL(w *fastjson.Writer) bool {
    97  	if v.URL.Scheme != "" {
    98  		if !marshalScheme(w, v.URL.Scheme) {
    99  			return false
   100  		}
   101  		w.RawString("://")
   102  	} else {
   103  		w.RawString("http://")
   104  	}
   105  	w.StringContents(v.URL.Host)
   106  	if v.URL.Path == "" {
   107  		w.RawByte('/')
   108  	} else {
   109  		if v.URL.Path[0] != '/' {
   110  			w.RawByte('/')
   111  		}
   112  		w.StringContents(v.URL.Path)
   113  	}
   114  	if v.URL.RawQuery != "" {
   115  		w.RawByte('?')
   116  		w.StringContents(v.URL.RawQuery)
   117  	}
   118  	if v.URL.Fragment != "" {
   119  		w.RawByte('#')
   120  		w.StringContents(v.URL.Fragment)
   121  	}
   122  	return true
   123  }
   124  
   125  // MarshalFastJSON writes the JSON representation of v to w.
   126  func (v *URL) MarshalFastJSON(w *fastjson.Writer) error {
   127  	w.RawByte('{')
   128  	first := true
   129  	if v.Hash != "" {
   130  		const prefix = ",\"hash\":"
   131  		if first {
   132  			first = false
   133  			w.RawString(prefix[1:])
   134  		} else {
   135  			w.RawString(prefix)
   136  		}
   137  		w.String(v.Hash)
   138  	}
   139  	if v.Hostname != "" {
   140  		const prefix = ",\"hostname\":"
   141  		if first {
   142  			first = false
   143  			w.RawString(prefix[1:])
   144  		} else {
   145  			w.RawString(prefix)
   146  		}
   147  		w.String(v.Hostname)
   148  	}
   149  	if v.Path != "" {
   150  		const prefix = `,"pathname":"`
   151  		if first {
   152  			first = false
   153  			w.RawString(prefix[1:])
   154  		} else {
   155  			w.RawString(prefix)
   156  		}
   157  		if v.Path[0] != '/' {
   158  			w.RawByte('/')
   159  		}
   160  		w.StringContents(v.Path)
   161  		w.RawByte('"')
   162  	}
   163  	if v.Port != "" {
   164  		const prefix = ",\"port\":"
   165  		if first {
   166  			first = false
   167  			w.RawString(prefix[1:])
   168  		} else {
   169  			w.RawString(prefix)
   170  		}
   171  		w.String(v.Port)
   172  	}
   173  	schemeBegin := -1
   174  	schemeEnd := -1
   175  	if v.Protocol != "" {
   176  		before := w.Size()
   177  		const prefix = ",\"protocol\":\""
   178  		if first {
   179  			first = false
   180  			w.RawString(prefix[1:])
   181  		} else {
   182  			w.RawString(prefix)
   183  		}
   184  		schemeBegin = w.Size()
   185  		if marshalScheme(w, v.Protocol) {
   186  			schemeEnd = w.Size()
   187  			w.RawByte('"')
   188  		} else {
   189  			w.Rewind(before)
   190  		}
   191  	}
   192  	if v.Search != "" {
   193  		const prefix = ",\"search\":"
   194  		if first {
   195  			first = false
   196  			w.RawString(prefix[1:])
   197  		} else {
   198  			w.RawString(prefix)
   199  		}
   200  		w.String(v.Search)
   201  	}
   202  	if schemeEnd != -1 && v.Hostname != "" {
   203  		before := w.Size()
   204  		w.RawString(",\"full\":")
   205  		if !v.marshalFullURL(w, w.Bytes()[schemeBegin:schemeEnd]) {
   206  			w.Rewind(before)
   207  		}
   208  	}
   209  	w.RawByte('}')
   210  	return nil
   211  }
   212  
   213  func marshalScheme(w *fastjson.Writer, scheme string) bool {
   214  	// Canonicalize the scheme to lowercase. Don't use
   215  	// strings.ToLower, as it's too general and requires
   216  	// additional memory allocations.
   217  	//
   218  	// The scheme should start with a letter, and may
   219  	// then be followed by letters, digits, '+', '-',
   220  	// and '.'. We don't validate the scheme here, we
   221  	// just use those restrictions as a basis for
   222  	// optimization; anything not in that set will
   223  	// mean the full URL is omitted.
   224  	for i := 0; i < len(scheme); i++ {
   225  		c := scheme[i]
   226  		switch {
   227  		case c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c == '+' || c == '-' || c == '.':
   228  			w.RawByte(c)
   229  		case c >= 'A' && c <= 'Z':
   230  			w.RawByte(c + 'a' - 'A')
   231  		default:
   232  			return false
   233  		}
   234  	}
   235  	return true
   236  }
   237  
   238  func (v *URL) marshalFullURL(w *fastjson.Writer, scheme []byte) bool {
   239  	w.RawByte('"')
   240  	before := w.Size()
   241  	w.RawBytes(scheme)
   242  	w.RawString("://")
   243  
   244  	const maxRunes = 1024
   245  	runes := w.Size() - before // scheme is known to be all single-byte runes
   246  	if runes >= maxRunes {
   247  		// Pathological case, scheme >= 1024 runes.
   248  		w.Rewind(before + maxRunes)
   249  		w.RawByte('"')
   250  		return true
   251  	}
   252  
   253  	// Track how many runes we encode, and stop once we've hit the limit.
   254  	rawByte := func(v byte) {
   255  		if runes == maxRunes {
   256  			return
   257  		}
   258  		w.RawByte(v)
   259  		runes++
   260  	}
   261  	stringContents := func(v string) {
   262  		remaining := maxRunes - runes
   263  		truncated, n := apmstrings.Truncate(v, remaining)
   264  		if n > 0 {
   265  			w.StringContents(truncated)
   266  			runes += n
   267  		}
   268  	}
   269  
   270  	if strings.IndexByte(v.Hostname, ':') == -1 {
   271  		stringContents(v.Hostname)
   272  	} else {
   273  		rawByte('[')
   274  		stringContents(v.Hostname)
   275  		rawByte(']')
   276  	}
   277  	if v.Port != "" {
   278  		rawByte(':')
   279  		stringContents(v.Port)
   280  	}
   281  	if v.Path != "" {
   282  		if !strings.HasPrefix(v.Path, "/") {
   283  			rawByte('/')
   284  		}
   285  		stringContents(v.Path)
   286  	}
   287  	if v.Search != "" {
   288  		rawByte('?')
   289  		stringContents(v.Search)
   290  	}
   291  	if v.Hash != "" {
   292  		rawByte('#')
   293  		stringContents(v.Hash)
   294  	}
   295  	w.RawByte('"')
   296  	return true
   297  }
   298  
   299  func (l *Log) isZero() bool {
   300  	return l.Message == ""
   301  }
   302  
   303  func (e *Exception) isZero() bool {
   304  	return e.Message == ""
   305  }
   306  
   307  func (c Cookies) isZero() bool {
   308  	return len(c) == 0
   309  }
   310  
   311  // MarshalFastJSON writes the JSON representation of c to w.
   312  func (c Cookies) MarshalFastJSON(w *fastjson.Writer) error {
   313  	w.RawByte('{')
   314  	first := true
   315  outer:
   316  	for i := len(c) - 1; i >= 0; i-- {
   317  		for j := i + 1; j < len(c); j++ {
   318  			if c[i].Name == c[j].Name {
   319  				continue outer
   320  			}
   321  		}
   322  		if first {
   323  			first = false
   324  		} else {
   325  			w.RawByte(',')
   326  		}
   327  		w.String(c[i].Name)
   328  		w.RawByte(':')
   329  		w.String(c[i].Value)
   330  	}
   331  	w.RawByte('}')
   332  	return nil
   333  }
   334  
   335  // UnmarshalJSON unmarshals the JSON data into c.
   336  func (c *Cookies) UnmarshalJSON(data []byte) error {
   337  	m := make(map[string]string)
   338  	if err := json.Unmarshal(data, &m); err != nil {
   339  		return err
   340  	}
   341  	*c = make([]*http.Cookie, 0, len(m))
   342  	for k, v := range m {
   343  		*c = append(*c, &http.Cookie{
   344  			Name:  k,
   345  			Value: v,
   346  		})
   347  	}
   348  	sort.Slice(*c, func(i, j int) bool {
   349  		return (*c)[i].Name < (*c)[j].Name
   350  	})
   351  	return nil
   352  }
   353  
   354  func (hs Headers) isZero() bool {
   355  	return len(hs) == 0
   356  }
   357  
   358  // MarshalFastJSON writes the JSON representation of h to w.
   359  func (hs Headers) MarshalFastJSON(w *fastjson.Writer) error {
   360  	w.RawByte('{')
   361  	for i, h := range hs {
   362  		if i != 0 {
   363  			w.RawByte(',')
   364  		}
   365  		w.String(h.Key)
   366  		w.RawByte(':')
   367  		if len(h.Values) == 1 {
   368  			// Just one item, add the item directly.
   369  			w.String(h.Values[0])
   370  		} else {
   371  			// Zero or multiple items, include them all.
   372  			w.RawByte('[')
   373  			for i, v := range h.Values {
   374  				if i != 0 {
   375  					w.RawByte(',')
   376  				}
   377  				w.String(v)
   378  			}
   379  			w.RawByte(']')
   380  		}
   381  	}
   382  	w.RawByte('}')
   383  	return nil
   384  }
   385  
   386  // MarshalFastJSON writes the JSON representation of h to w.
   387  func (*Header) MarshalFastJSON(w *fastjson.Writer) error {
   388  	panic("unreachable")
   389  }
   390  
   391  // UnmarshalJSON unmarshals the JSON data into c.
   392  func (hs *Headers) UnmarshalJSON(data []byte) error {
   393  	var m map[string]interface{}
   394  	if err := json.Unmarshal(data, &m); err != nil {
   395  		return err
   396  	}
   397  	for k, v := range m {
   398  		switch v := v.(type) {
   399  		case string:
   400  			*hs = append(*hs, Header{Key: k, Values: []string{v}})
   401  		case []interface{}:
   402  			var values []string
   403  			for _, v := range v {
   404  				switch v := v.(type) {
   405  				case string:
   406  					values = append(values, v)
   407  				default:
   408  					return errors.Errorf("expected string, got %T", v)
   409  				}
   410  			}
   411  			*hs = append(*hs, Header{Key: k, Values: values})
   412  		default:
   413  			return errors.Errorf("expected string or []string, got %T", v)
   414  		}
   415  	}
   416  	sort.Slice(*hs, func(i, j int) bool {
   417  		return (*hs)[i].Key < (*hs)[j].Key
   418  	})
   419  	return nil
   420  }
   421  
   422  // MarshalFastJSON writes the JSON representation of c to w.
   423  func (c *ExceptionCode) MarshalFastJSON(w *fastjson.Writer) error {
   424  	if c.String != "" {
   425  		w.String(c.String)
   426  	} else {
   427  		w.Float64(c.Number)
   428  	}
   429  	return nil
   430  }
   431  
   432  // UnmarshalJSON unmarshals the JSON data into c.
   433  func (c *ExceptionCode) UnmarshalJSON(data []byte) error {
   434  	var v interface{}
   435  	if err := json.Unmarshal(data, &v); err != nil {
   436  		return err
   437  	}
   438  	switch v := v.(type) {
   439  	case string:
   440  		c.String = v
   441  	case float64:
   442  		c.Number = v
   443  	default:
   444  		return errors.Errorf("expected string or number, got %T", v)
   445  	}
   446  	return nil
   447  }
   448  
   449  // isZero is used by fastjson to implement omitempty.
   450  func (c *ExceptionCode) isZero() bool {
   451  	return c.String == "" && c.Number == 0
   452  }
   453  
   454  // MarshalFastJSON writes the JSON representation of b to w.
   455  func (b *RequestBody) MarshalFastJSON(w *fastjson.Writer) error {
   456  	if b.Form != nil {
   457  		w.RawByte('{')
   458  		first := true
   459  		for k, v := range b.Form {
   460  			if first {
   461  				first = false
   462  			} else {
   463  				w.RawByte(',')
   464  			}
   465  			w.String(k)
   466  			w.RawByte(':')
   467  			if len(v) == 1 {
   468  				// Just one item, add the item directly.
   469  				w.String(v[0])
   470  			} else {
   471  				// Zero or multiple items, include them all.
   472  				w.RawByte('[')
   473  				first := true
   474  				for _, v := range v {
   475  					if first {
   476  						first = false
   477  					} else {
   478  						w.RawByte(',')
   479  					}
   480  					w.String(v)
   481  				}
   482  				w.RawByte(']')
   483  			}
   484  		}
   485  		w.RawByte('}')
   486  	} else {
   487  		w.String(b.Raw)
   488  	}
   489  	return nil
   490  }
   491  
   492  // UnmarshalJSON unmarshals the JSON data into b.
   493  func (b *RequestBody) UnmarshalJSON(data []byte) error {
   494  	var v interface{}
   495  	if err := json.Unmarshal(data, &v); err != nil {
   496  		return err
   497  	}
   498  	switch v := v.(type) {
   499  	case string:
   500  		b.Raw = v
   501  		return nil
   502  	case map[string]interface{}:
   503  		form := make(url.Values, len(v))
   504  		for k, v := range v {
   505  			switch v := v.(type) {
   506  			case string:
   507  				form.Set(k, v)
   508  			case []interface{}:
   509  				for _, v := range v {
   510  					switch v := v.(type) {
   511  					case string:
   512  						form.Add(k, v)
   513  					default:
   514  						return errors.Errorf("expected string, got %T", v)
   515  					}
   516  				}
   517  			default:
   518  				return errors.Errorf("expected string or []string, got %T", v)
   519  			}
   520  		}
   521  		b.Form = form
   522  	default:
   523  		return errors.Errorf("expected string or map, got %T", v)
   524  	}
   525  	return nil
   526  }
   527  
   528  func (m StringMap) isZero() bool {
   529  	return len(m) == 0
   530  }
   531  
   532  // MarshalFastJSON writes the JSON representation of m to w.
   533  func (m StringMap) MarshalFastJSON(w *fastjson.Writer) (firstErr error) {
   534  	w.RawByte('{')
   535  	first := true
   536  	for _, item := range m {
   537  		if first {
   538  			first = false
   539  		} else {
   540  			w.RawByte(',')
   541  		}
   542  		w.String(item.Key)
   543  		w.RawByte(':')
   544  		if err := fastjson.Marshal(w, item.Value); err != nil && firstErr == nil {
   545  			firstErr = err
   546  		}
   547  	}
   548  	w.RawByte('}')
   549  	return nil
   550  }
   551  
   552  // UnmarshalJSON unmarshals the JSON data into m.
   553  func (m *StringMap) UnmarshalJSON(data []byte) error {
   554  	var mm map[string]string
   555  	if err := json.Unmarshal(data, &mm); err != nil {
   556  		return err
   557  	}
   558  	*m = make(StringMap, 0, len(mm))
   559  	for k, v := range mm {
   560  		*m = append(*m, StringMapItem{Key: k, Value: v})
   561  	}
   562  	sort.Slice(*m, func(i, j int) bool {
   563  		return (*m)[i].Key < (*m)[j].Key
   564  	})
   565  	return nil
   566  }
   567  
   568  // MarshalFastJSON exists to prevent code generation for StringMapItem.
   569  func (*StringMapItem) MarshalFastJSON(*fastjson.Writer) error {
   570  	panic("unreachable")
   571  }
   572  
   573  func (m IfaceMap) isZero() bool {
   574  	return len(m) == 0
   575  }
   576  
   577  // MarshalFastJSON writes the JSON representation of m to w.
   578  func (m IfaceMap) MarshalFastJSON(w *fastjson.Writer) (firstErr error) {
   579  	w.RawByte('{')
   580  	first := true
   581  	for _, item := range m {
   582  		if first {
   583  			first = false
   584  		} else {
   585  			w.RawByte(',')
   586  		}
   587  		w.String(item.Key)
   588  		w.RawByte(':')
   589  		if err := fastjson.Marshal(w, item.Value); err != nil && firstErr == nil {
   590  			firstErr = err
   591  		}
   592  	}
   593  	w.RawByte('}')
   594  	return nil
   595  }
   596  
   597  // UnmarshalJSON unmarshals the JSON data into m.
   598  func (m *IfaceMap) UnmarshalJSON(data []byte) error {
   599  	var mm map[string]interface{}
   600  	if err := json.Unmarshal(data, &mm); err != nil {
   601  		return err
   602  	}
   603  	*m = make(IfaceMap, 0, len(mm))
   604  	for k, v := range mm {
   605  		*m = append(*m, IfaceMapItem{Key: k, Value: v})
   606  	}
   607  	sort.Slice(*m, func(i, j int) bool {
   608  		return (*m)[i].Key < (*m)[j].Key
   609  	})
   610  	return nil
   611  }
   612  
   613  // MarshalFastJSON exists to prevent code generation for IfaceMapItem.
   614  func (*IfaceMapItem) MarshalFastJSON(*fastjson.Writer) error {
   615  	panic("unreachable")
   616  }
   617  
   618  func (id *TraceID) isZero() bool {
   619  	return *id == TraceID{}
   620  }
   621  
   622  // MarshalFastJSON writes the JSON representation of id to w.
   623  func (id *TraceID) MarshalFastJSON(w *fastjson.Writer) error {
   624  	w.RawByte('"')
   625  	writeHex(w, id[:])
   626  	w.RawByte('"')
   627  	return nil
   628  }
   629  
   630  // UnmarshalJSON unmarshals the JSON data into id.
   631  func (id *TraceID) UnmarshalJSON(data []byte) error {
   632  	_, err := hex.Decode(id[:], data[1:len(data)-1])
   633  	return err
   634  }
   635  
   636  func (id *SpanID) isZero() bool {
   637  	return *id == SpanID{}
   638  }
   639  
   640  // UnmarshalJSON unmarshals the JSON data into id.
   641  func (id *SpanID) UnmarshalJSON(data []byte) error {
   642  	_, err := hex.Decode(id[:], data[1:len(data)-1])
   643  	return err
   644  }
   645  
   646  // MarshalFastJSON writes the JSON representation of id to w.
   647  func (id *SpanID) MarshalFastJSON(w *fastjson.Writer) error {
   648  	w.RawByte('"')
   649  	writeHex(w, id[:])
   650  	w.RawByte('"')
   651  	return nil
   652  }
   653  
   654  func (t *ErrorTransaction) isZero() bool {
   655  	return *t == ErrorTransaction{}
   656  }
   657  
   658  func (t *MetricsTransaction) isZero() bool {
   659  	return *t == MetricsTransaction{}
   660  }
   661  
   662  func (s *MetricsSpan) isZero() bool {
   663  	return *s == MetricsSpan{}
   664  }
   665  
   666  func writeHex(w *fastjson.Writer, v []byte) {
   667  	const hextable = "0123456789abcdef"
   668  	for _, v := range v {
   669  		w.RawByte(hextable[v>>4])
   670  		w.RawByte(hextable[v&0x0f])
   671  	}
   672  }
   673  
   674  // MarshalFastJSON writes the JSON representation of v to w.
   675  func (v *Metric) MarshalFastJSON(w *fastjson.Writer) error {
   676  	w.RawByte('{')
   677  	if len(v.Counts) > 0 {
   678  		w.RawString("\"values\":")
   679  		w.RawByte('[')
   680  		for i, v := range v.Values {
   681  			if i != 0 {
   682  				w.RawByte(',')
   683  			}
   684  			w.Float64(v)
   685  		}
   686  		w.RawByte(']')
   687  		w.RawString(",\"counts\":")
   688  		w.RawByte('[')
   689  		for i, v := range v.Counts {
   690  			if i != 0 {
   691  				w.RawByte(',')
   692  			}
   693  			w.Uint64(v)
   694  		}
   695  		w.RawByte(']')
   696  	} else {
   697  		w.RawString("\"value\":")
   698  		w.Float64(v.Value)
   699  	}
   700  	if v.Type != "" {
   701  		w.RawString(",\"type\":")
   702  		w.String(v.Type)
   703  	}
   704  	w.RawByte('}')
   705  	return nil
   706  }