1// Copyright The OpenTelemetry Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package zipkin 16 17import ( 18 "fmt" 19 "net" 20 "strconv" 21 "testing" 22 "time" 23 24 "github.com/google/go-cmp/cmp" 25 zkmodel "github.com/openzipkin/zipkin-go/model" 26 "github.com/stretchr/testify/assert" 27 "github.com/stretchr/testify/require" 28 29 "go.opentelemetry.io/otel/attribute" 30 "go.opentelemetry.io/otel/codes" 31 "go.opentelemetry.io/otel/sdk/instrumentation" 32 "go.opentelemetry.io/otel/sdk/resource" 33 tracesdk "go.opentelemetry.io/otel/sdk/trace" 34 "go.opentelemetry.io/otel/semconv" 35 "go.opentelemetry.io/otel/trace" 36) 37 38func TestModelConversion(t *testing.T) { 39 resource := resource.NewWithAttributes( 40 semconv.ServiceNameKey.String("model-test"), 41 ) 42 43 inputBatch := []*tracesdk.SpanSnapshot{ 44 // typical span data 45 { 46 SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ 47 TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 48 SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, 49 }), 50 Parent: trace.NewSpanContext(trace.SpanContextConfig{ 51 TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 52 SpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, 53 }), 54 SpanKind: trace.SpanKindServer, 55 Name: "foo", 56 StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), 57 EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), 58 Attributes: []attribute.KeyValue{ 59 attribute.Int64("attr1", 42), 60 attribute.String("attr2", "bar"), 61 attribute.Array("attr3", []int{0, 1, 2}), 62 }, 63 MessageEvents: []trace.Event{ 64 { 65 Time: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), 66 Name: "ev1", 67 Attributes: []attribute.KeyValue{ 68 attribute.Int64("eventattr1", 123), 69 }, 70 }, 71 { 72 Time: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), 73 Name: "ev2", 74 Attributes: nil, 75 }, 76 }, 77 StatusCode: codes.Error, 78 StatusMessage: "404, file not found", 79 Resource: resource, 80 }, 81 // span data with no parent (same as typical, but has 82 // invalid parent) 83 { 84 SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ 85 TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 86 SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, 87 }), 88 SpanKind: trace.SpanKindServer, 89 Name: "foo", 90 StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), 91 EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), 92 Attributes: []attribute.KeyValue{ 93 attribute.Int64("attr1", 42), 94 attribute.String("attr2", "bar"), 95 }, 96 MessageEvents: []trace.Event{ 97 { 98 Time: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), 99 Name: "ev1", 100 Attributes: []attribute.KeyValue{ 101 attribute.Int64("eventattr1", 123), 102 }, 103 }, 104 { 105 Time: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), 106 Name: "ev2", 107 Attributes: nil, 108 }, 109 }, 110 StatusCode: codes.Error, 111 StatusMessage: "404, file not found", 112 Resource: resource, 113 }, 114 // span data of unspecified kind 115 { 116 SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ 117 TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 118 SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, 119 }), 120 Parent: trace.NewSpanContext(trace.SpanContextConfig{ 121 TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 122 SpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, 123 }), 124 SpanKind: trace.SpanKindUnspecified, 125 Name: "foo", 126 StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), 127 EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), 128 Attributes: []attribute.KeyValue{ 129 attribute.Int64("attr1", 42), 130 attribute.String("attr2", "bar"), 131 }, 132 MessageEvents: []trace.Event{ 133 { 134 Time: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), 135 Name: "ev1", 136 Attributes: []attribute.KeyValue{ 137 attribute.Int64("eventattr1", 123), 138 }, 139 }, 140 { 141 Time: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), 142 Name: "ev2", 143 Attributes: nil, 144 }, 145 }, 146 StatusCode: codes.Error, 147 StatusMessage: "404, file not found", 148 Resource: resource, 149 }, 150 // span data of internal kind 151 { 152 SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ 153 TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 154 SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, 155 }), 156 Parent: trace.NewSpanContext(trace.SpanContextConfig{ 157 TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 158 SpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, 159 }), 160 SpanKind: trace.SpanKindInternal, 161 Name: "foo", 162 StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), 163 EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), 164 Attributes: []attribute.KeyValue{ 165 attribute.Int64("attr1", 42), 166 attribute.String("attr2", "bar"), 167 }, 168 MessageEvents: []trace.Event{ 169 { 170 Time: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), 171 Name: "ev1", 172 Attributes: []attribute.KeyValue{ 173 attribute.Int64("eventattr1", 123), 174 }, 175 }, 176 { 177 Time: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), 178 Name: "ev2", 179 Attributes: nil, 180 }, 181 }, 182 StatusCode: codes.Error, 183 StatusMessage: "404, file not found", 184 Resource: resource, 185 }, 186 // span data of client kind 187 { 188 SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ 189 TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 190 SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, 191 }), 192 Parent: trace.NewSpanContext(trace.SpanContextConfig{ 193 TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 194 SpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, 195 }), 196 SpanKind: trace.SpanKindClient, 197 Name: "foo", 198 StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), 199 EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), 200 Attributes: []attribute.KeyValue{ 201 attribute.Int64("attr1", 42), 202 attribute.String("attr2", "bar"), 203 attribute.String("peer.hostname", "test-peer-hostname"), 204 attribute.String("net.peer.ip", "1.2.3.4"), 205 attribute.Int64("net.peer.port", 9876), 206 }, 207 MessageEvents: []trace.Event{ 208 { 209 Time: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), 210 Name: "ev1", 211 Attributes: []attribute.KeyValue{ 212 attribute.Int64("eventattr1", 123), 213 }, 214 }, 215 { 216 Time: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), 217 Name: "ev2", 218 Attributes: nil, 219 }, 220 }, 221 StatusCode: codes.Error, 222 StatusMessage: "404, file not found", 223 Resource: resource, 224 }, 225 // span data of producer kind 226 { 227 SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ 228 TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 229 SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, 230 }), 231 Parent: trace.NewSpanContext(trace.SpanContextConfig{ 232 TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 233 SpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, 234 }), 235 SpanKind: trace.SpanKindProducer, 236 Name: "foo", 237 StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), 238 EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), 239 Attributes: []attribute.KeyValue{ 240 attribute.Int64("attr1", 42), 241 attribute.String("attr2", "bar"), 242 }, 243 MessageEvents: []trace.Event{ 244 { 245 Time: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), 246 Name: "ev1", 247 Attributes: []attribute.KeyValue{ 248 attribute.Int64("eventattr1", 123), 249 }, 250 }, 251 { 252 Time: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), 253 Name: "ev2", 254 Attributes: nil, 255 }, 256 }, 257 StatusCode: codes.Error, 258 StatusMessage: "404, file not found", 259 Resource: resource, 260 }, 261 // span data of consumer kind 262 { 263 SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ 264 TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 265 SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, 266 }), 267 Parent: trace.NewSpanContext(trace.SpanContextConfig{ 268 TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 269 SpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, 270 }), 271 SpanKind: trace.SpanKindConsumer, 272 Name: "foo", 273 StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), 274 EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), 275 Attributes: []attribute.KeyValue{ 276 attribute.Int64("attr1", 42), 277 attribute.String("attr2", "bar"), 278 }, 279 MessageEvents: []trace.Event{ 280 { 281 Time: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), 282 Name: "ev1", 283 Attributes: []attribute.KeyValue{ 284 attribute.Int64("eventattr1", 123), 285 }, 286 }, 287 { 288 Time: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), 289 Name: "ev2", 290 Attributes: nil, 291 }, 292 }, 293 StatusCode: codes.Error, 294 StatusMessage: "404, file not found", 295 Resource: resource, 296 }, 297 // span data with no events 298 { 299 SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ 300 TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 301 SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, 302 }), 303 Parent: trace.NewSpanContext(trace.SpanContextConfig{ 304 TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 305 SpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, 306 }), 307 SpanKind: trace.SpanKindServer, 308 Name: "foo", 309 StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), 310 EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), 311 Attributes: []attribute.KeyValue{ 312 attribute.Int64("attr1", 42), 313 attribute.String("attr2", "bar"), 314 }, 315 MessageEvents: nil, 316 StatusCode: codes.Error, 317 StatusMessage: "404, file not found", 318 Resource: resource, 319 }, 320 // span data with an "error" attribute set to "false" 321 { 322 SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ 323 TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 324 SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, 325 }), 326 Parent: trace.NewSpanContext(trace.SpanContextConfig{ 327 TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, 328 SpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, 329 }), 330 SpanKind: trace.SpanKindServer, 331 Name: "foo", 332 StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), 333 EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), 334 Attributes: []attribute.KeyValue{ 335 attribute.String("error", "false"), 336 }, 337 MessageEvents: []trace.Event{ 338 { 339 Time: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), 340 Name: "ev1", 341 Attributes: []attribute.KeyValue{ 342 attribute.Int64("eventattr1", 123), 343 }, 344 }, 345 { 346 Time: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), 347 Name: "ev2", 348 Attributes: nil, 349 }, 350 }, 351 StatusCode: codes.Unset, 352 Resource: resource, 353 }, 354 } 355 356 expectedOutputBatch := []zkmodel.SpanModel{ 357 // model for typical span data 358 { 359 SpanContext: zkmodel.SpanContext{ 360 TraceID: zkmodel.TraceID{ 361 High: 0x001020304050607, 362 Low: 0x8090a0b0c0d0e0f, 363 }, 364 ID: zkmodel.ID(0xfffefdfcfbfaf9f8), 365 ParentID: zkmodelIDPtr(0x3f3e3d3c3b3a3938), 366 Debug: false, 367 Sampled: nil, 368 Err: nil, 369 }, 370 Name: "foo", 371 Kind: "SERVER", 372 Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), 373 Duration: time.Minute, 374 Shared: false, 375 LocalEndpoint: &zkmodel.Endpoint{ 376 ServiceName: "model-test", 377 }, 378 RemoteEndpoint: nil, 379 Annotations: []zkmodel.Annotation{ 380 { 381 Timestamp: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), 382 Value: `ev1: {"eventattr1":123}`, 383 }, 384 { 385 Timestamp: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), 386 Value: "ev2", 387 }, 388 }, 389 Tags: map[string]string{ 390 "attr1": "42", 391 "attr2": "bar", 392 "attr3": "[0,1,2]", 393 "otel.status_code": "Error", 394 "error": "404, file not found", 395 }, 396 }, 397 // model for span data with no parent 398 { 399 SpanContext: zkmodel.SpanContext{ 400 TraceID: zkmodel.TraceID{ 401 High: 0x001020304050607, 402 Low: 0x8090a0b0c0d0e0f, 403 }, 404 ID: zkmodel.ID(0xfffefdfcfbfaf9f8), 405 ParentID: nil, 406 Debug: false, 407 Sampled: nil, 408 Err: nil, 409 }, 410 Name: "foo", 411 Kind: "SERVER", 412 Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), 413 Duration: time.Minute, 414 Shared: false, 415 LocalEndpoint: &zkmodel.Endpoint{ 416 ServiceName: "model-test", 417 }, 418 RemoteEndpoint: nil, 419 Annotations: []zkmodel.Annotation{ 420 { 421 Timestamp: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), 422 Value: `ev1: {"eventattr1":123}`, 423 }, 424 { 425 Timestamp: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), 426 Value: "ev2", 427 }, 428 }, 429 Tags: map[string]string{ 430 "attr1": "42", 431 "attr2": "bar", 432 "otel.status_code": "Error", 433 "error": "404, file not found", 434 }, 435 }, 436 // model for span data of unspecified kind 437 { 438 SpanContext: zkmodel.SpanContext{ 439 TraceID: zkmodel.TraceID{ 440 High: 0x001020304050607, 441 Low: 0x8090a0b0c0d0e0f, 442 }, 443 ID: zkmodel.ID(0xfffefdfcfbfaf9f8), 444 ParentID: zkmodelIDPtr(0x3f3e3d3c3b3a3938), 445 Debug: false, 446 Sampled: nil, 447 Err: nil, 448 }, 449 Name: "foo", 450 Kind: "", 451 Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), 452 Duration: time.Minute, 453 Shared: false, 454 LocalEndpoint: &zkmodel.Endpoint{ 455 ServiceName: "model-test", 456 }, 457 RemoteEndpoint: nil, 458 Annotations: []zkmodel.Annotation{ 459 { 460 Timestamp: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), 461 Value: `ev1: {"eventattr1":123}`, 462 }, 463 { 464 Timestamp: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), 465 Value: "ev2", 466 }, 467 }, 468 Tags: map[string]string{ 469 "attr1": "42", 470 "attr2": "bar", 471 "otel.status_code": "Error", 472 "error": "404, file not found", 473 }, 474 }, 475 // model for span data of internal kind 476 { 477 SpanContext: zkmodel.SpanContext{ 478 TraceID: zkmodel.TraceID{ 479 High: 0x001020304050607, 480 Low: 0x8090a0b0c0d0e0f, 481 }, 482 ID: zkmodel.ID(0xfffefdfcfbfaf9f8), 483 ParentID: zkmodelIDPtr(0x3f3e3d3c3b3a3938), 484 Debug: false, 485 Sampled: nil, 486 Err: nil, 487 }, 488 Name: "foo", 489 Kind: "", 490 Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), 491 Duration: time.Minute, 492 Shared: false, 493 LocalEndpoint: &zkmodel.Endpoint{ 494 ServiceName: "model-test", 495 }, 496 RemoteEndpoint: nil, 497 Annotations: []zkmodel.Annotation{ 498 { 499 Timestamp: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), 500 Value: `ev1: {"eventattr1":123}`, 501 }, 502 { 503 Timestamp: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), 504 Value: "ev2", 505 }, 506 }, 507 Tags: map[string]string{ 508 "attr1": "42", 509 "attr2": "bar", 510 "otel.status_code": "Error", 511 "error": "404, file not found", 512 }, 513 }, 514 // model for span data of client kind 515 { 516 SpanContext: zkmodel.SpanContext{ 517 TraceID: zkmodel.TraceID{ 518 High: 0x001020304050607, 519 Low: 0x8090a0b0c0d0e0f, 520 }, 521 ID: zkmodel.ID(0xfffefdfcfbfaf9f8), 522 ParentID: zkmodelIDPtr(0x3f3e3d3c3b3a3938), 523 Debug: false, 524 Sampled: nil, 525 Err: nil, 526 }, 527 Name: "foo", 528 Kind: "CLIENT", 529 Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), 530 Duration: time.Minute, 531 Shared: false, 532 LocalEndpoint: &zkmodel.Endpoint{ 533 ServiceName: "model-test", 534 }, 535 RemoteEndpoint: &zkmodel.Endpoint{ 536 IPv4: net.ParseIP("1.2.3.4"), 537 Port: 9876, 538 }, 539 Annotations: []zkmodel.Annotation{ 540 { 541 Timestamp: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), 542 Value: `ev1: {"eventattr1":123}`, 543 }, 544 { 545 Timestamp: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), 546 Value: "ev2", 547 }, 548 }, 549 Tags: map[string]string{ 550 "attr1": "42", 551 "attr2": "bar", 552 "net.peer.ip": "1.2.3.4", 553 "net.peer.port": "9876", 554 "peer.hostname": "test-peer-hostname", 555 "otel.status_code": "Error", 556 "error": "404, file not found", 557 }, 558 }, 559 // model for span data of producer kind 560 { 561 SpanContext: zkmodel.SpanContext{ 562 TraceID: zkmodel.TraceID{ 563 High: 0x001020304050607, 564 Low: 0x8090a0b0c0d0e0f, 565 }, 566 ID: zkmodel.ID(0xfffefdfcfbfaf9f8), 567 ParentID: zkmodelIDPtr(0x3f3e3d3c3b3a3938), 568 Debug: false, 569 Sampled: nil, 570 Err: nil, 571 }, 572 Name: "foo", 573 Kind: "PRODUCER", 574 Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), 575 Duration: time.Minute, 576 Shared: false, 577 LocalEndpoint: &zkmodel.Endpoint{ 578 ServiceName: "model-test", 579 }, 580 RemoteEndpoint: nil, 581 Annotations: []zkmodel.Annotation{ 582 { 583 Timestamp: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), 584 Value: `ev1: {"eventattr1":123}`, 585 }, 586 { 587 Timestamp: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), 588 Value: "ev2", 589 }, 590 }, 591 Tags: map[string]string{ 592 "attr1": "42", 593 "attr2": "bar", 594 "otel.status_code": "Error", 595 "error": "404, file not found", 596 }, 597 }, 598 // model for span data of consumer kind 599 { 600 SpanContext: zkmodel.SpanContext{ 601 TraceID: zkmodel.TraceID{ 602 High: 0x001020304050607, 603 Low: 0x8090a0b0c0d0e0f, 604 }, 605 ID: zkmodel.ID(0xfffefdfcfbfaf9f8), 606 ParentID: zkmodelIDPtr(0x3f3e3d3c3b3a3938), 607 Debug: false, 608 Sampled: nil, 609 Err: nil, 610 }, 611 Name: "foo", 612 Kind: "CONSUMER", 613 Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), 614 Duration: time.Minute, 615 Shared: false, 616 LocalEndpoint: &zkmodel.Endpoint{ 617 ServiceName: "model-test", 618 }, 619 RemoteEndpoint: nil, 620 Annotations: []zkmodel.Annotation{ 621 { 622 Timestamp: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), 623 Value: `ev1: {"eventattr1":123}`, 624 }, 625 { 626 Timestamp: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), 627 Value: "ev2", 628 }, 629 }, 630 Tags: map[string]string{ 631 "attr1": "42", 632 "attr2": "bar", 633 "otel.status_code": "Error", 634 "error": "404, file not found", 635 }, 636 }, 637 // model for span data with no events 638 { 639 SpanContext: zkmodel.SpanContext{ 640 TraceID: zkmodel.TraceID{ 641 High: 0x001020304050607, 642 Low: 0x8090a0b0c0d0e0f, 643 }, 644 ID: zkmodel.ID(0xfffefdfcfbfaf9f8), 645 ParentID: zkmodelIDPtr(0x3f3e3d3c3b3a3938), 646 Debug: false, 647 Sampled: nil, 648 Err: nil, 649 }, 650 Name: "foo", 651 Kind: "SERVER", 652 Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), 653 Duration: time.Minute, 654 Shared: false, 655 LocalEndpoint: &zkmodel.Endpoint{ 656 ServiceName: "model-test", 657 }, 658 RemoteEndpoint: nil, 659 Annotations: nil, 660 Tags: map[string]string{ 661 "attr1": "42", 662 "attr2": "bar", 663 "otel.status_code": "Error", 664 "error": "404, file not found", 665 }, 666 }, 667 // model for span data with an "error" attribute set to "false" 668 { 669 SpanContext: zkmodel.SpanContext{ 670 TraceID: zkmodel.TraceID{ 671 High: 0x001020304050607, 672 Low: 0x8090a0b0c0d0e0f, 673 }, 674 ID: zkmodel.ID(0xfffefdfcfbfaf9f8), 675 ParentID: zkmodelIDPtr(0x3f3e3d3c3b3a3938), 676 Debug: false, 677 Sampled: nil, 678 Err: nil, 679 }, 680 Name: "foo", 681 Kind: "SERVER", 682 Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), 683 Duration: time.Minute, 684 Shared: false, 685 LocalEndpoint: &zkmodel.Endpoint{ 686 ServiceName: "model-test", 687 }, 688 RemoteEndpoint: nil, 689 Annotations: []zkmodel.Annotation{ 690 { 691 Timestamp: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), 692 Value: `ev1: {"eventattr1":123}`, 693 }, 694 { 695 Timestamp: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), 696 Value: "ev2", 697 }, 698 }, 699 Tags: nil, // should be omitted 700 }, 701 } 702 gottenOutputBatch := toZipkinSpanModels(inputBatch) 703 require.Equal(t, expectedOutputBatch, gottenOutputBatch) 704} 705 706func zkmodelIDPtr(n uint64) *zkmodel.ID { 707 id := zkmodel.ID(n) 708 return &id 709} 710 711func TestTagsTransformation(t *testing.T) { 712 keyValue := "value" 713 doubleValue := 123.456 714 uintValue := int64(123) 715 statusMessage := "this is a problem" 716 instrLibName := "instrumentation-library" 717 instrLibVersion := "semver:1.0.0" 718 719 tests := []struct { 720 name string 721 data *tracesdk.SpanSnapshot 722 want map[string]string 723 }{ 724 { 725 name: "attributes", 726 data: &tracesdk.SpanSnapshot{ 727 Attributes: []attribute.KeyValue{ 728 attribute.String("key", keyValue), 729 attribute.Float64("double", doubleValue), 730 attribute.Int64("uint", uintValue), 731 attribute.Bool("ok", true), 732 }, 733 }, 734 want: map[string]string{ 735 "double": fmt.Sprint(doubleValue), 736 "key": keyValue, 737 "ok": "true", 738 "uint": strconv.FormatInt(uintValue, 10), 739 }, 740 }, 741 { 742 name: "no attributes", 743 data: &tracesdk.SpanSnapshot{}, 744 want: nil, 745 }, 746 { 747 name: "omit-noerror", 748 data: &tracesdk.SpanSnapshot{ 749 Attributes: []attribute.KeyValue{ 750 attribute.Bool("error", false), 751 }, 752 }, 753 want: nil, 754 }, 755 { 756 name: "statusCode", 757 data: &tracesdk.SpanSnapshot{ 758 Attributes: []attribute.KeyValue{ 759 attribute.String("key", keyValue), 760 attribute.Bool("error", true), 761 }, 762 StatusCode: codes.Error, 763 StatusMessage: statusMessage, 764 }, 765 want: map[string]string{ 766 "error": statusMessage, 767 "key": keyValue, 768 "otel.status_code": codes.Error.String(), 769 }, 770 }, 771 { 772 name: "instrLib-empty", 773 data: &tracesdk.SpanSnapshot{ 774 InstrumentationLibrary: instrumentation.Library{}, 775 }, 776 want: nil, 777 }, 778 { 779 name: "instrLib-noversion", 780 data: &tracesdk.SpanSnapshot{ 781 Attributes: []attribute.KeyValue{}, 782 InstrumentationLibrary: instrumentation.Library{ 783 Name: instrLibName, 784 }, 785 }, 786 want: map[string]string{ 787 "otel.library.name": instrLibName, 788 }, 789 }, 790 { 791 name: "instrLib-with-version", 792 data: &tracesdk.SpanSnapshot{ 793 Attributes: []attribute.KeyValue{}, 794 InstrumentationLibrary: instrumentation.Library{ 795 Name: instrLibName, 796 Version: instrLibVersion, 797 }, 798 }, 799 want: map[string]string{ 800 "otel.library.name": instrLibName, 801 "otel.library.version": instrLibVersion, 802 }, 803 }, 804 } 805 for _, tt := range tests { 806 t.Run(tt.name, func(t *testing.T) { 807 got := toZipkinTags(tt.data) 808 if diff := cmp.Diff(got, tt.want); diff != "" { 809 t.Errorf("Diff%v", diff) 810 } 811 }) 812 } 813} 814 815func TestRemoteEndpointTransformation(t *testing.T) { 816 tests := []struct { 817 name string 818 data *tracesdk.SpanSnapshot 819 want *zkmodel.Endpoint 820 }{ 821 { 822 name: "nil-not-applicable", 823 data: &tracesdk.SpanSnapshot{ 824 SpanKind: trace.SpanKindClient, 825 Attributes: []attribute.KeyValue{}, 826 }, 827 want: nil, 828 }, 829 { 830 name: "nil-not-found", 831 data: &tracesdk.SpanSnapshot{ 832 SpanKind: trace.SpanKindConsumer, 833 Attributes: []attribute.KeyValue{ 834 attribute.String("attr", "test"), 835 }, 836 }, 837 want: nil, 838 }, 839 { 840 name: "peer-service-rank", 841 data: &tracesdk.SpanSnapshot{ 842 SpanKind: trace.SpanKindProducer, 843 Attributes: []attribute.KeyValue{ 844 semconv.PeerServiceKey.String("peer-service-test"), 845 semconv.NetPeerNameKey.String("peer-name-test"), 846 semconv.HTTPHostKey.String("http-host-test"), 847 }, 848 }, 849 want: &zkmodel.Endpoint{ 850 ServiceName: "peer-service-test", 851 }, 852 }, 853 { 854 name: "http-host-rank", 855 data: &tracesdk.SpanSnapshot{ 856 SpanKind: trace.SpanKindProducer, 857 Attributes: []attribute.KeyValue{ 858 semconv.HTTPHostKey.String("http-host-test"), 859 semconv.DBNameKey.String("db-name-test"), 860 }, 861 }, 862 want: &zkmodel.Endpoint{ 863 ServiceName: "http-host-test", 864 }, 865 }, 866 { 867 name: "db-name-rank", 868 data: &tracesdk.SpanSnapshot{ 869 SpanKind: trace.SpanKindProducer, 870 Attributes: []attribute.KeyValue{ 871 attribute.String("foo", "bar"), 872 semconv.DBNameKey.String("db-name-test"), 873 }, 874 }, 875 want: &zkmodel.Endpoint{ 876 ServiceName: "db-name-test", 877 }, 878 }, 879 { 880 name: "peer-hostname-rank", 881 data: &tracesdk.SpanSnapshot{ 882 SpanKind: trace.SpanKindProducer, 883 Attributes: []attribute.KeyValue{ 884 keyPeerHostname.String("peer-hostname-test"), 885 keyPeerAddress.String("peer-address-test"), 886 semconv.HTTPHostKey.String("http-host-test"), 887 semconv.DBNameKey.String("http-host-test"), 888 }, 889 }, 890 want: &zkmodel.Endpoint{ 891 ServiceName: "peer-hostname-test", 892 }, 893 }, 894 { 895 name: "peer-address-rank", 896 data: &tracesdk.SpanSnapshot{ 897 SpanKind: trace.SpanKindProducer, 898 Attributes: []attribute.KeyValue{ 899 keyPeerAddress.String("peer-address-test"), 900 semconv.HTTPHostKey.String("http-host-test"), 901 semconv.DBNameKey.String("http-host-test"), 902 }, 903 }, 904 want: &zkmodel.Endpoint{ 905 ServiceName: "peer-address-test", 906 }, 907 }, 908 { 909 name: "net-peer-invalid-ip", 910 data: &tracesdk.SpanSnapshot{ 911 SpanKind: trace.SpanKindProducer, 912 Attributes: []attribute.KeyValue{ 913 semconv.NetPeerIPKey.String("INVALID"), 914 }, 915 }, 916 want: nil, 917 }, 918 { 919 name: "net-peer-ipv6-no-port", 920 data: &tracesdk.SpanSnapshot{ 921 SpanKind: trace.SpanKindProducer, 922 Attributes: []attribute.KeyValue{ 923 semconv.NetPeerIPKey.String("0:0:1:5ee:bad:c0de:0:0"), 924 }, 925 }, 926 want: &zkmodel.Endpoint{ 927 IPv6: net.ParseIP("0:0:1:5ee:bad:c0de:0:0"), 928 }, 929 }, 930 { 931 name: "net-peer-ipv4-port", 932 data: &tracesdk.SpanSnapshot{ 933 SpanKind: trace.SpanKindProducer, 934 Attributes: []attribute.KeyValue{ 935 semconv.NetPeerIPKey.String("1.2.3.4"), 936 semconv.NetPeerPortKey.Int(9876), 937 }, 938 }, 939 want: &zkmodel.Endpoint{ 940 IPv4: net.ParseIP("1.2.3.4"), 941 Port: 9876, 942 }, 943 }, 944 } 945 for _, tt := range tests { 946 t.Run(tt.name, func(t *testing.T) { 947 got := toZipkinRemoteEndpoint(tt.data) 948 if diff := cmp.Diff(got, tt.want); diff != "" { 949 t.Errorf("Diff%v", diff) 950 } 951 }) 952 } 953} 954 955func TestServiceName(t *testing.T) { 956 attrs := []attribute.KeyValue{} 957 assert.Equal(t, defaultServiceName, getServiceName(attrs)) 958 959 attrs = append(attrs, attribute.String("test_key", "test_value")) 960 assert.Equal(t, defaultServiceName, getServiceName(attrs)) 961 962 attrs = append(attrs, semconv.ServiceNameKey.String("my_service")) 963 assert.Equal(t, "my_service", getServiceName(attrs)) 964} 965