github.com/waldiirawan/apm-agent-go/v2@v2.2.2/model/marshal_test.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_test 19 20 import ( 21 "encoding/json" 22 "net/http" 23 "net/url" 24 "strings" 25 "testing" 26 "time" 27 "unicode/utf8" 28 29 "github.com/stretchr/testify/assert" 30 "github.com/stretchr/testify/require" 31 32 "github.com/waldiirawan/apm-agent-go/v2/model" 33 "go.elastic.co/fastjson" 34 ) 35 36 func TestMarshalTransaction(t *testing.T) { 37 tx := fakeTransaction() 38 39 var w fastjson.Writer 40 tx.MarshalFastJSON(&w) 41 42 decoded := mustUnmarshalJSON(w) 43 expect := map[string]interface{}{ 44 "trace_id": "0102030405060708090a0b0c0d0e0f10", 45 "id": "0102030405060708", 46 "parent_id": "0001020304050607", 47 "name": "GET /foo/bar", 48 "type": "request", 49 "timestamp": float64(123000000), 50 "duration": 123.456, 51 "result": "418", 52 "context": map[string]interface{}{ 53 "custom": map[string]interface{}{ 54 "bar": true, 55 "baz": 3.45, 56 "foo": "one", 57 "qux": map[string]interface{}{"quux": float64(6)}, 58 }, 59 "service": map[string]interface{}{ 60 "framework": map[string]interface{}{ 61 "name": "framework-name", 62 "version": "framework-version", 63 }, 64 }, 65 "request": map[string]interface{}{ 66 "url": map[string]interface{}{ 67 "full": "https://testing.invalid/foo/bar?baz#qux", 68 "protocol": "https", 69 "hostname": "testing.invalid", 70 "pathname": "/foo/bar", 71 "search": "baz", 72 "hash": "qux", 73 }, 74 "method": "GET", 75 "headers": map[string]interface{}{ 76 "User-Agent": "Mosaic/0.2 (Windows 3.1)", 77 "Cookie": "monster=yumyum; random=junk", 78 }, 79 "body": "ahoj", 80 "http_version": "1.1", 81 "cookies": map[string]interface{}{ 82 "monster": "yumyum", 83 "random": "junk", 84 }, 85 "socket": map[string]interface{}{ 86 "remote_address": "[::1]", 87 }, 88 }, 89 "response": map[string]interface{}{ 90 "status_code": float64(418), 91 "headers": map[string]interface{}{ 92 "Content-Type": "text/html", 93 }, 94 }, 95 "user": map[string]interface{}{ 96 "username": "wanda", 97 }, 98 "tags": map[string]interface{}{ 99 "tag": "urit", 100 }, 101 }, 102 "span_count": map[string]interface{}{ 103 "started": float64(99), 104 "dropped": float64(4), 105 }, 106 "dropped_spans_stats": []interface{}{ 107 map[string]interface{}{ 108 "destination_service_resource": "http://elasticsearch:9200", 109 "duration": map[string]interface{}{ 110 "count": float64(4), 111 "sum": map[string]interface{}{ 112 "us": float64(1000), 113 }, 114 }, 115 "outcome": "success", 116 }, 117 }, 118 "otel": map[string]interface{}{ 119 "span_kind": "MESSAGING", 120 "attributes": map[string]interface{}{ 121 "messaging.system": "messaging", 122 }, 123 }, 124 } 125 assert.Equal(t, expect, decoded) 126 } 127 128 func TestMarshalSpan(t *testing.T) { 129 var w fastjson.Writer 130 span := fakeSpan() 131 span.Context = fakeDatabaseSpanContext() 132 span.MarshalFastJSON(&w) 133 134 decoded := mustUnmarshalJSON(w) 135 assert.Equal(t, map[string]interface{}{ 136 "trace_id": "000102030405060708090a0b0c0d0e0f", 137 "id": "0001020304050607", 138 "parent_id": "0001020304050607", 139 "transaction_id": "0001020304050607", 140 "name": "SELECT FROM bar", 141 "timestamp": float64(123000000), 142 "duration": float64(3), 143 "type": "db.postgresql.query", 144 "context": map[string]interface{}{ 145 "db": map[string]interface{}{ 146 "instance": "wat", 147 "statement": `SELECT foo FROM bar WHERE baz LIKE 'qu%x'`, 148 "type": "sql", 149 "user": "barb", 150 }, 151 }, 152 "otel": map[string]interface{}{ 153 "span_kind": "MESSAGING", 154 "attributes": map[string]interface{}{ 155 "messaging.system": "messaging", 156 }, 157 }, 158 }, decoded) 159 160 w.Reset() 161 span.Duration = 4 162 span.Name = "GET testing.invalid:8000" 163 span.Type = "ext.http" 164 span.ParentID = model.SpanID{} // parent_id is optional 165 span.TransactionID = model.SpanID{} // transaction_id is optional 166 span.Context = fakeHTTPSpanContext() 167 span.OTel = &model.OTel{ 168 SpanKind: "SERVER", 169 Attributes: map[string]interface{}{ 170 "numeric.data": 123.456, 171 "boolean.data": true, 172 "slice.data": []string{"one", "two"}, 173 }, 174 } 175 span.MarshalFastJSON(&w) 176 177 decoded = mustUnmarshalJSON(w) 178 assert.Equal(t, map[string]interface{}{ 179 "trace_id": "000102030405060708090a0b0c0d0e0f", 180 "id": "0001020304050607", 181 "name": "GET testing.invalid:8000", 182 "timestamp": float64(123000000), 183 "duration": float64(4), 184 "type": "ext.http", 185 "context": map[string]interface{}{ 186 "http": map[string]interface{}{ 187 "url": "http://testing.invalid:8000/path?query#fragment", 188 }, 189 }, 190 "otel": map[string]interface{}{ 191 "span_kind": "SERVER", 192 "attributes": map[string]interface{}{ 193 "numeric.data": 123.456, 194 "boolean.data": true, 195 "slice.data": []interface{}{"one", "two"}, 196 }, 197 }, 198 }, decoded) 199 } 200 201 func TestMarshalSpanHTTPStatusCode(t *testing.T) { 202 var w fastjson.Writer 203 span := fakeSpan() 204 span.Context = &model.SpanContext{ 205 HTTP: &model.HTTPSpanContext{StatusCode: 200}, 206 } 207 span.MarshalFastJSON(&w) 208 209 decoded := mustUnmarshalJSON(w) 210 assert.Equal(t, map[string]interface{}{ 211 "trace_id": "000102030405060708090a0b0c0d0e0f", 212 "id": "0001020304050607", 213 "transaction_id": "0001020304050607", 214 "parent_id": "0001020304050607", 215 "name": "SELECT FROM bar", 216 "timestamp": float64(123000000), 217 "duration": float64(3), 218 "type": "db.postgresql.query", 219 "context": map[string]interface{}{ 220 "http": map[string]interface{}{ 221 "status_code": 200.0, 222 }, 223 }, 224 "otel": map[string]interface{}{ 225 "span_kind": "MESSAGING", 226 "attributes": map[string]interface{}{ 227 "messaging.system": "messaging", 228 }, 229 }, 230 }, decoded) 231 } 232 233 func TestMarshalMetrics(t *testing.T) { 234 metrics := fakeMetrics() 235 236 var w fastjson.Writer 237 metrics.MarshalFastJSON(&w) 238 239 decoded := mustUnmarshalJSON(w) 240 expect := map[string]interface{}{ 241 "timestamp": float64(123000000), 242 "tags": map[string]interface{}{ 243 "foo": "bar", 244 }, 245 "samples": map[string]interface{}{ 246 "metric_one": map[string]interface{}{ 247 "value": float64(1024), 248 }, 249 "metric_two": map[string]interface{}{ 250 "value": float64(-66.6), 251 }, 252 }, 253 } 254 assert.Equal(t, expect, decoded) 255 } 256 257 func TestMarshalError(t *testing.T) { 258 var e model.Error 259 time, err := time.Parse("2006-01-02T15:04:05.999Z", "1970-01-01T00:02:03Z") 260 assert.NoError(t, err) 261 e.Timestamp = model.Time(time) 262 263 // The primary error ID is required, all other IDs are optional 264 var w fastjson.Writer 265 e.MarshalFastJSON(&w) 266 assert.Equal(t, `{"id":"00000000000000000000000000000000","timestamp":123000000}`, string(w.Bytes())) 267 268 e.ID = model.TraceID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} 269 e.TransactionID = model.SpanID{1, 2, 3, 4, 5, 6, 7, 8} 270 e.TraceID = model.TraceID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} 271 e.ParentID = model.SpanID{1, 2, 3, 4, 5, 6, 7, 8} 272 w.Reset() 273 e.MarshalFastJSON(&w) 274 assert.Equal(t, 275 `{"id":"000102030405060708090a0b0c0d0e0f","timestamp":123000000,"parent_id":"0102030405060708","trace_id":"0102030405060708090a0b0c0d0e0f10","transaction_id":"0102030405060708"}`, 276 string(w.Bytes()), 277 ) 278 } 279 280 func TestMarshalErrorTransactionUnsampled(t *testing.T) { 281 var e model.Error 282 time, err := time.Parse("2006-01-02T15:04:05.999Z", "1970-01-01T00:02:03Z") 283 assert.NoError(t, err) 284 e.Timestamp = model.Time(time) 285 e.Transaction.Sampled = new(bool) 286 e.Transaction.Type = "foo" 287 288 var w fastjson.Writer 289 e.MarshalFastJSON(&w) 290 assert.Equal(t, `{"id":"00000000000000000000000000000000","timestamp":123000000,"transaction":{"sampled":false,"type":"foo"}}`, string(w.Bytes())) 291 } 292 293 func TestMarshalCookies(t *testing.T) { 294 cookies := model.Cookies{ 295 {Name: "foo", Value: "!"}, // eclipsed 296 {Name: "baz", Value: "qux"}, 297 {Name: "foo", Value: "bar"}, 298 } 299 var w fastjson.Writer 300 cookies.MarshalFastJSON(&w) 301 assert.Equal(t, `{"foo":"bar","baz":"qux"}`, string(w.Bytes())) 302 } 303 304 func TestMarshalRequestBody(t *testing.T) { 305 body := model.RequestBody{ 306 Raw: "rawr", 307 } 308 var w fastjson.Writer 309 body.MarshalFastJSON(&w) 310 assert.Equal(t, `"rawr"`, string(w.Bytes())) 311 312 body.Form = url.Values{ 313 "first": []string{"jackie"}, 314 "last": []string{"brown"}, 315 "keywords": []string{"rum", "punch"}, 316 } 317 w.Reset() 318 body.MarshalFastJSON(&w) 319 320 decoded := mustUnmarshalJSON(w) 321 expect := map[string]interface{}{ 322 "first": "jackie", 323 "last": "brown", 324 "keywords": []interface{}{"rum", "punch"}, 325 } 326 assert.Equal(t, expect, decoded) 327 } 328 329 func TestMarshalLog(t *testing.T) { 330 log := model.Log{ 331 Message: "foo", 332 Level: "bar", 333 LoggerName: "baz", 334 ParamMessage: "%s", 335 } 336 var w fastjson.Writer 337 log.MarshalFastJSON(&w) 338 339 assert.Equal(t, `{"message":"foo","level":"bar","logger_name":"baz","param_message":"%s"}`, string(w.Bytes())) 340 341 log = model.Log{ 342 Message: "foo", 343 LoggerName: "bar", 344 } 345 w.Reset() 346 log.MarshalFastJSON(&w) 347 assert.Equal(t, `{"message":"foo","logger_name":"bar"}`, string(w.Bytes())) 348 } 349 350 func TestMarshalException(t *testing.T) { 351 x := model.Exception{ 352 Message: "foo", 353 Type: "bar", 354 Module: "baz", 355 Attributes: map[string]interface{}{ 356 "qux": map[string]interface{}{ 357 "quux": "corge", 358 }, 359 }, 360 Handled: true, 361 } 362 var w fastjson.Writer 363 x.MarshalFastJSON(&w) 364 365 assert.Equal(t, 366 `{"handled":true,"message":"foo","attributes":{"qux":{"quux":"corge"}},"module":"baz","type":"bar"}`, 367 string(w.Bytes()), 368 ) 369 } 370 371 func TestMarshalExceptionCode(t *testing.T) { 372 code := model.ExceptionCode{ 373 String: "boom", 374 Number: 123, 375 } 376 var w fastjson.Writer 377 code.MarshalFastJSON(&w) 378 assert.Equal(t, `"boom"`, string(w.Bytes())) 379 380 w.Reset() 381 code.String = "" 382 code.MarshalFastJSON(&w) 383 assert.Equal(t, `123`, string(w.Bytes())) 384 } 385 386 func TestMarshalUser(t *testing.T) { 387 user := model.User{ 388 Email: "foo@example.com", 389 ID: "123", 390 Username: "bar", 391 } 392 var w fastjson.Writer 393 user.MarshalFastJSON(&w) 394 assert.Equal(t, `{"email":"foo@example.com","id":"123","username":"bar"}`, string(w.Bytes())) 395 } 396 397 func TestMarshalStacktraceFrame(t *testing.T) { 398 f := model.StacktraceFrame{ 399 File: "file.go", 400 Line: 123, 401 AbsolutePath: "fabulous", 402 Function: "wonderment", 403 } 404 var w fastjson.Writer 405 f.MarshalFastJSON(&w) 406 407 assert.Equal(t, 408 `{"filename":"file.go","lineno":123,"abs_path":"fabulous","function":"wonderment"}`, 409 string(w.Bytes()), 410 ) 411 412 f = model.StacktraceFrame{ 413 File: "file.go", 414 Line: 123, 415 LibraryFrame: true, 416 ContextLine: "0", 417 PreContext: []string{"-2", "-1"}, 418 PostContext: []string{"+1", "+2"}, 419 Vars: map[string]interface{}{ 420 "foo": []string{"bar", "baz"}, 421 }, 422 } 423 w.Reset() 424 f.MarshalFastJSON(&w) 425 assert.Equal(t, 426 `{"filename":"file.go","lineno":123,"context_line":"0","library_frame":true,"post_context":["+1","+2"],"pre_context":["-2","-1"],"vars":{"foo":["bar","baz"]}}`, 427 string(w.Bytes()), 428 ) 429 } 430 431 func TestMarshalResponse(t *testing.T) { 432 finished := true 433 headersSent := true 434 response := model.Response{ 435 Finished: &finished, 436 Headers: model.Headers{{ 437 Key: "Content-Type", 438 Values: []string{"text/plain"}, 439 }}, 440 HeadersSent: &headersSent, 441 StatusCode: 200, 442 } 443 var w fastjson.Writer 444 response.MarshalFastJSON(&w) 445 assert.Equal(t, 446 `{"finished":true,"headers":{"Content-Type":"text/plain"},"headers_sent":true,"status_code":200}`, 447 string(w.Bytes()), 448 ) 449 } 450 451 func TestMarshalURL(t *testing.T) { 452 in := model.URL{ 453 Path: "/", 454 Search: "abc=def", 455 Hash: strings.Repeat("x", 1000), // exceed "full" URL length 456 Hostname: "testing.invalid", 457 Port: "999", 458 Protocol: "http", 459 } 460 461 var w fastjson.Writer 462 in.MarshalFastJSON(&w) 463 464 var out model.URL 465 err := json.Unmarshal(w.Bytes(), &out) 466 require.NoError(t, err) 467 468 // The full URL should have been truncated to avoid a validation error. 469 assert.Equal(t, "http://testing.invalid:999/?abc=def#"+strings.Repeat("x", 988), out.Full) 470 out.Full = "" 471 472 assert.Equal(t, in, out) 473 } 474 475 func TestMarshalURLFullTruncated(t *testing.T) { 476 t.Run("escape", func(t *testing.T) { 477 testMarshalURLFullTruncated(t, '&') 478 }) 479 t.Run("unicode", func(t *testing.T) { 480 testMarshalURLFullTruncated(t, 'δΈ–') 481 }) 482 } 483 484 func testMarshalURLFullTruncated(t *testing.T, r rune) { 485 const maxRunes = 1024 486 in := model.URL{ 487 Hostname: "example.com", 488 Path: "/", 489 Protocol: "http", 490 Search: strings.Repeat(string(r), maxRunes), // should be truncated 491 } 492 493 var w fastjson.Writer 494 in.MarshalFastJSON(&w) 495 496 var out model.URL 497 err := json.Unmarshal(w.Bytes(), &out) 498 require.NoError(t, err) 499 500 assert.Equal(t, maxRunes, utf8.RuneCountInString(out.Full)) 501 502 const prefix = "http://example.com/?" 503 assert.Equal(t, prefix+strings.Repeat(string(r), maxRunes-len(prefix)), out.Full) 504 } 505 506 func TestMarshalURLPathEmpty(t *testing.T) { 507 in := model.URL{ 508 Hostname: "example.com", 509 Path: "", 510 Protocol: "http", 511 } 512 513 var w fastjson.Writer 514 in.MarshalFastJSON(&w) 515 516 var out model.URL 517 err := json.Unmarshal(w.Bytes(), &out) 518 require.NoError(t, err) 519 assert.Equal(t, "http://example.com", out.Full) 520 } 521 522 func TestMarshalURLPathLeadingSlashMissing(t *testing.T) { 523 in := model.URL{ 524 Path: "foo", 525 Search: "abc=def", 526 Hostname: "testing.invalid", 527 Port: "999", 528 Protocol: "http", 529 } 530 531 var w fastjson.Writer 532 in.MarshalFastJSON(&w) 533 534 var out model.URL 535 err := json.Unmarshal(w.Bytes(), &out) 536 require.NoError(t, err) 537 538 assert.Equal(t, "http://testing.invalid:999/foo?abc=def", out.Full) 539 out.Full = "" 540 541 // Leading slash should have been added during marshalling. We do it 542 // here rather than when building the model to avoid allocation. 543 in.Path = "/foo" 544 545 assert.Equal(t, in, out) 546 } 547 548 func TestMarshalHTTPSpanContextURLPathLeadingSlashMissing(t *testing.T) { 549 httpSpanContext := model.HTTPSpanContext{ 550 URL: mustParseURL("http://testing.invalid:8000/path?query#fragment"), 551 } 552 httpSpanContext.URL.Path = "path" 553 554 var w fastjson.Writer 555 httpSpanContext.MarshalFastJSON(&w) 556 557 var out model.HTTPSpanContext 558 err := json.Unmarshal(w.Bytes(), &out) 559 require.NoError(t, err) 560 561 // Leading slash should have been added during marshalling. 562 httpSpanContext.URL.Path = "/path" 563 assert.Equal(t, httpSpanContext.URL, out.URL) 564 } 565 566 func TestTransactionUnmarshalJSON(t *testing.T) { 567 tx := fakeTransaction() 568 var w fastjson.Writer 569 tx.MarshalFastJSON(&w) 570 571 var out model.Transaction 572 err := json.Unmarshal(w.Bytes(), &out) 573 require.NoError(t, err) 574 assert.Equal(t, tx, out) 575 } 576 577 func TestMarshalCloud(t *testing.T) { 578 cloud := fakeCloud() 579 580 var w fastjson.Writer 581 cloud.MarshalFastJSON(&w) 582 583 decoded := mustUnmarshalJSON(w) 584 expect := map[string]interface{}{ 585 "provider": "zeus", 586 "region": "troposphere", 587 "availability_zone": "torrid", 588 "instance": map[string]interface{}{ 589 "id": "instance_id", 590 "name": "instance_name", 591 }, 592 "machine": map[string]interface{}{ 593 "type": "machine_type", 594 }, 595 "account": map[string]interface{}{ 596 "id": "account_id", 597 "name": "account_name", 598 }, 599 "project": map[string]interface{}{ 600 "id": "project_id", 601 "name": "project_name", 602 }, 603 } 604 assert.Equal(t, expect, decoded) 605 } 606 607 func TestMarshalMetric(t *testing.T) { 608 histogram := &model.Metric{ 609 Type: "histogram", 610 Values: []float64{0.05, 0.1, 0.5, 1, 5}, 611 Counts: []uint64{1, 1, 5, 10, 5}, 612 } 613 614 var w fastjson.Writer 615 histogram.MarshalFastJSON(&w) 616 expect := `{"values":[0.05,0.1,0.5,1,5],"counts":[1,1,5,10,5],"type":"histogram"}` 617 618 assert.Equal(t, expect, string(w.Bytes())) 619 620 m := &model.Metric{Value: 1} 621 622 w.Reset() 623 m.MarshalFastJSON(&w) 624 expect = `{"value":1}` 625 626 assert.Equal(t, expect, string(w.Bytes())) 627 } 628 629 func fakeTransaction() model.Transaction { 630 return model.Transaction{ 631 TraceID: model.TraceID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, 632 ID: model.SpanID{1, 2, 3, 4, 5, 6, 7, 8}, 633 ParentID: model.SpanID{0, 1, 2, 3, 4, 5, 6, 7}, 634 Name: "GET /foo/bar", 635 Type: "request", 636 Timestamp: model.Time(time.Unix(123, 0).UTC()), 637 Duration: 123.456, 638 Result: "418", 639 Context: &model.Context{ 640 Request: &model.Request{ 641 URL: model.URL{ 642 Full: "https://testing.invalid/foo/bar?baz#qux", 643 Hostname: "testing.invalid", 644 Protocol: "https", 645 Path: "/foo/bar", 646 Search: "baz", 647 Hash: "qux", 648 }, 649 Method: "GET", 650 Headers: model.Headers{{ 651 Key: "Cookie", Values: []string{"monster=yumyum; random=junk"}, 652 }, { 653 Key: "User-Agent", Values: []string{"Mosaic/0.2 (Windows 3.1)"}, 654 }}, 655 Body: &model.RequestBody{ 656 Raw: "ahoj", 657 }, 658 HTTPVersion: "1.1", 659 Cookies: []*http.Cookie{ 660 {Name: "monster", Value: "yumyum"}, 661 {Name: "random", Value: "junk"}, 662 }, 663 Socket: &model.RequestSocket{ 664 RemoteAddress: "[::1]", 665 }, 666 }, 667 Response: &model.Response{ 668 StatusCode: 418, 669 Headers: model.Headers{{ 670 Key: "Content-Type", Values: []string{"text/html"}, 671 }}, 672 }, 673 Custom: model.IfaceMap{ 674 {Key: "bar", Value: true}, 675 {Key: "baz", Value: 3.45}, 676 {Key: "foo", Value: "one"}, 677 {Key: "qux", Value: map[string]interface{}{"quux": float64(6)}}, 678 }, 679 User: &model.User{ 680 Username: "wanda", 681 }, 682 Tags: model.IfaceMap{{ 683 Key: "tag", Value: "urit", 684 }}, 685 Service: &model.Service{ 686 Framework: &model.Framework{ 687 Name: "framework-name", 688 Version: "framework-version", 689 }, 690 }, 691 }, 692 SpanCount: model.SpanCount{ 693 Started: 99, 694 Dropped: 4, 695 }, 696 DroppedSpansStats: []model.DroppedSpansStats{ 697 { 698 DestinationServiceResource: "http://elasticsearch:9200", 699 Outcome: "success", 700 Duration: model.AggregateDuration{ 701 Count: 4, 702 Sum: model.DurationSum{Us: int64(time.Millisecond) / 1e3}, 703 }, 704 }, 705 }, 706 OTel: &model.OTel{ 707 SpanKind: "MESSAGING", 708 Attributes: map[string]interface{}{ 709 "messaging.system": "messaging", 710 }, 711 }, 712 } 713 } 714 715 func fakeSpan() model.Span { 716 return model.Span{ 717 Name: "SELECT FROM bar", 718 ID: model.SpanID{0, 1, 2, 3, 4, 5, 6, 7}, 719 ParentID: model.SpanID{0, 1, 2, 3, 4, 5, 6, 7}, 720 TransactionID: model.SpanID{0, 1, 2, 3, 4, 5, 6, 7}, 721 TraceID: model.TraceID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, 722 Timestamp: model.Time(time.Unix(123, 0).UTC()), 723 Duration: 3, 724 Type: "db.postgresql.query", 725 Context: fakeDatabaseSpanContext(), 726 OTel: &model.OTel{ 727 SpanKind: "MESSAGING", 728 Attributes: map[string]interface{}{ 729 "messaging.system": "messaging", 730 }, 731 }, 732 } 733 } 734 735 func fakeDatabaseSpanContext() *model.SpanContext { 736 return &model.SpanContext{ 737 Database: &model.DatabaseSpanContext{ 738 Instance: "wat", 739 Statement: `SELECT foo FROM bar WHERE baz LIKE 'qu%x'`, 740 Type: "sql", 741 User: "barb", 742 }, 743 } 744 } 745 746 func fakeHTTPSpanContext() *model.SpanContext { 747 return &model.SpanContext{ 748 HTTP: &model.HTTPSpanContext{ 749 URL: mustParseURL("http://testing.invalid:8000/path?query#fragment"), 750 }, 751 } 752 } 753 754 func fakeMetrics() *model.Metrics { 755 return &model.Metrics{ 756 Timestamp: model.Time(time.Unix(123, 0).UTC()), 757 Labels: model.StringMap{{Key: "foo", Value: "bar"}}, 758 Samples: map[string]model.Metric{ 759 "metric_one": {Value: 1024}, 760 "metric_two": {Value: -66.6}, 761 }, 762 } 763 } 764 765 func fakeService() *model.Service { 766 return &model.Service{ 767 Name: "fake-service", 768 Version: "1.0.0-rc1", 769 Environment: "dev", 770 Agent: &model.Agent{ 771 Name: "go", 772 Version: "0.1.0", 773 }, 774 Framework: &model.Framework{ 775 Name: "gin", 776 Version: "1.0", 777 }, 778 Language: &model.Language{ 779 Name: "go", 780 Version: "1.10", 781 }, 782 Runtime: &model.Runtime{ 783 Name: "go", 784 Version: "gc 1.10", 785 }, 786 } 787 } 788 789 func fakeSystem() *model.System { 790 return &model.System{ 791 Architecture: "x86_64", 792 Hostname: "host.example", 793 Platform: "linux", 794 } 795 } 796 797 func fakeProcess() *model.Process { 798 ppid := 1 799 return &model.Process{ 800 Pid: 1234, 801 Ppid: &ppid, 802 Title: "my-fake-service", 803 Argv: []string{"my-fake-service", "-f", "config.yml"}, 804 } 805 } 806 807 func fakeCloud() *model.Cloud { 808 return &model.Cloud{ 809 Provider: "zeus", 810 Region: "troposphere", 811 AvailabilityZone: "torrid", 812 Instance: &model.CloudInstance{ 813 ID: "instance_id", 814 Name: "instance_name", 815 }, 816 Machine: &model.CloudMachine{ 817 Type: "machine_type", 818 }, 819 Account: &model.CloudAccount{ 820 ID: "account_id", 821 Name: "account_name", 822 }, 823 Project: &model.CloudProject{ 824 ID: "project_id", 825 Name: "project_name", 826 }, 827 } 828 } 829 830 func mustParseURL(s string) *url.URL { 831 u, err := url.Parse(s) 832 if err != nil { 833 panic(err) 834 } 835 return u 836 } 837 838 func newUint64(v uint64) *uint64 { 839 return &v 840 } 841 842 func newFloat64(v float64) *float64 { 843 return &v 844 } 845 846 func mustUnmarshalJSON(w fastjson.Writer) interface{} { 847 var out interface{} 848 err := json.Unmarshal(w.Bytes(), &out) 849 if err != nil { 850 panic(err) 851 } 852 return out 853 }