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 }