|
11 | 11 | SpanKind = OpenTelemetry::Trace::SpanKind |
12 | 12 | Status = OpenTelemetry::Trace::Status |
13 | 13 | Context = OpenTelemetry::Context |
| 14 | + SpanLimits = OpenTelemetry::SDK::Trace::SpanLimits |
14 | 15 |
|
15 | 16 | let(:context) { OpenTelemetry::Trace::SpanContext.new } |
16 | 17 | let(:mock_span_processor) { Minitest::Mock.new } |
|
25 | 26 | ) |
26 | 27 | end |
27 | 28 | let(:span) do |
28 | | - Span.new(context, Context.empty, 'name', SpanKind::INTERNAL, nil, span_limits, |
| 29 | + Span.new(context, Context.empty, OpenTelemetry::Trace::Span::INVALID, 'name', SpanKind::INTERNAL, nil, span_limits, |
29 | 30 | [], nil, nil, Time.now, nil, nil) |
30 | 31 | end |
31 | 32 |
|
|
200 | 201 | _(events.first.attributes).must_equal(attrs) |
201 | 202 | end |
202 | 203 |
|
203 | | - it 'add event with timestamp' do |
204 | | - ts = Time.now |
| 204 | + it 'honours an explicit timestamp' do |
| 205 | + ts = Time.new('2021-11-23 12:00:00.000000 -0600') |
205 | 206 | span.add_event('added', timestamp: ts) |
206 | 207 | events = span.events |
207 | 208 | _(events.size).must_equal(1) |
208 | 209 | _(events.first.timestamp).must_equal(exportable_timestamp(ts)) |
209 | 210 | end |
210 | 211 |
|
| 212 | + it 'sets the implicit event timestamp relative to the span start' do |
| 213 | + # Create a span with deterministic time values stored on it. |
| 214 | + test_span = mock_gettime(monotonic: 100, realtime: 1_000) do |
| 215 | + Span.new(context, Context.empty, OpenTelemetry::Trace::Span::INVALID, 'span', SpanKind::INTERNAL, nil, span_limits, [], nil, nil, nil, nil, nil) |
| 216 | + end |
| 217 | + |
| 218 | + mock_gettime(monotonic: 200, realtime: 1_000_000) do |
| 219 | + test_span.add_event('record something with relative time') |
| 220 | + event = test_span.events[0] |
| 221 | + _(event).wont_be_nil |
| 222 | + |
| 223 | + # The expect timestamp is that of the parent offset by |
| 224 | + # the drift in the monotonic clock |
| 225 | + _(event.timestamp).must_equal(1_000 + 100) |
| 226 | + end |
| 227 | + end |
| 228 | + |
211 | 229 | it 'does not add an event if span is ended' do |
212 | 230 | span.finish |
213 | 231 | span.add_event('will_not_be_added') |
|
372 | 390 | _(span.finish).must_equal(span) |
373 | 391 | end |
374 | 392 |
|
375 | | - it 'sets the end timestamp' do |
376 | | - span.finish |
377 | | - _(span.to_span_data.end_timestamp).wont_be_nil |
| 393 | + it 'honours an explicit end timestamp' do |
| 394 | + ts = Time.new('2021-11-23 12:00:00.000000 -0600') |
| 395 | + _(span.finish(end_timestamp: ts)).must_equal(span) |
| 396 | + _(span.end_timestamp).must_equal(exportable_timestamp(ts)) |
| 397 | + end |
| 398 | + |
| 399 | + it 'sets the end timestamp relative to the start time' do |
| 400 | + # Create a span with deterministic time values stored on it. |
| 401 | + test_span = mock_gettime(monotonic: 100, realtime: 1_000) do |
| 402 | + Span.new(context, Context.empty, OpenTelemetry::Trace::Span::INVALID, 'span', SpanKind::INTERNAL, nil, span_limits, [], nil, nil, nil, nil, nil) |
| 403 | + end |
| 404 | + |
| 405 | + mock_gettime(monotonic: 200, realtime: 1_000_000) do |
| 406 | + test_span.finish |
| 407 | + # The expect timestamp is that of the parent offset by |
| 408 | + # the drift in the monotonic clock |
| 409 | + _(test_span.end_timestamp).must_equal(1_000 + 100) |
| 410 | + end |
378 | 411 | end |
379 | 412 |
|
380 | 413 | it 'calls the span processor #on_finish callback' do |
381 | 414 | mock_span_processor.expect(:on_start, nil) { |_| true } |
382 | | - span = Span.new(context, Context.empty, |
| 415 | + span = Span.new(context, Context.empty, OpenTelemetry::Trace::Span::INVALID, |
383 | 416 | 'name', SpanKind::INTERNAL, nil, span_limits, |
384 | 417 | [mock_span_processor], nil, nil, Time.now, nil, nil) |
385 | 418 | mock_span_processor.expect(:on_finish, nil, [span]) |
|
405 | 438 | it 'calls the span processor #on_start callback' do |
406 | 439 | yielded_span = nil |
407 | 440 | mock_span_processor.expect(:on_start, nil) { |s| yielded_span = s } |
408 | | - span = Span.new(context, Context.empty, 'name', SpanKind::INTERNAL, nil, span_limits, |
| 441 | + span = Span.new(context, Context.empty, OpenTelemetry::Trace::Span::INVALID, 'name', SpanKind::INTERNAL, nil, span_limits, |
409 | 442 | [mock_span_processor], nil, nil, Time.now, nil, nil) |
410 | 443 | _(yielded_span).must_equal(span) |
411 | 444 | mock_span_processor.verify |
412 | 445 | end |
413 | 446 |
|
414 | 447 | it 'trims excess attributes' do |
415 | 448 | attributes = { 'foo': 'bar', 'other': 'attr' } |
416 | | - span = Span.new(context, Context.empty, 'name', SpanKind::INTERNAL, nil, span_limits, |
| 449 | + span = Span.new(context, Context.empty, OpenTelemetry::Trace::Span::INVALID, 'name', SpanKind::INTERNAL, nil, span_limits, |
417 | 450 | [], attributes, nil, Time.now, nil, nil) |
418 | 451 | _(span.to_span_data.total_recorded_attributes).must_equal(2) |
419 | 452 | _(span.attributes.length).must_equal(1) |
420 | 453 | end |
421 | 454 |
|
422 | 455 | it 'truncates attributes if configured' do |
423 | 456 | attributes = { 'foo': 'oldbaroldbaroldbaroldbaroldbaroldbar' } |
424 | | - span = Span.new(context, Context.empty, 'name', SpanKind::INTERNAL, nil, span_limits, |
| 457 | + span = Span.new(context, Context.empty, OpenTelemetry::Trace::Span::INVALID, 'name', SpanKind::INTERNAL, nil, span_limits, |
425 | 458 | [], attributes, nil, Time.now, nil, nil) |
426 | 459 | _(span.attributes[:foo]).must_equal('oldbaroldbaroldbaroldbaroldba...') |
427 | 460 | end |
428 | 461 |
|
429 | 462 | it 'counts attributes' do |
430 | 463 | attributes = { 'foo': 'bar', 'other': 'attr' } |
431 | | - span = Span.new(context, Context.empty, 'name', SpanKind::INTERNAL, nil, span_limits, |
| 464 | + span = Span.new(context, Context.empty, OpenTelemetry::Trace::Span::INVALID, 'name', SpanKind::INTERNAL, nil, span_limits, |
432 | 465 | [], attributes, nil, Time.now, nil, nil) |
433 | 466 | _(span.to_span_data.total_recorded_attributes).must_equal(2) |
434 | 467 | end |
435 | 468 |
|
436 | 469 | it 'counts links' do |
437 | 470 | links = [OpenTelemetry::Trace::Link.new(context), OpenTelemetry::Trace::Link.new(context)] |
438 | | - span = Span.new(context, Context.empty, 'name', SpanKind::INTERNAL, nil, span_limits, |
| 471 | + span = Span.new(context, Context.empty, OpenTelemetry::Trace::Span::INVALID, 'name', SpanKind::INTERNAL, nil, span_limits, |
439 | 472 | [], nil, links, Time.now, nil, nil) |
440 | 473 | _(span.to_span_data.total_recorded_links).must_equal(2) |
441 | 474 | end |
442 | 475 |
|
443 | 476 | it 'trims excess links' do |
444 | 477 | links = [OpenTelemetry::Trace::Link.new(context), OpenTelemetry::Trace::Link.new(context)] |
445 | | - span = Span.new(context, Context.empty, 'name', SpanKind::INTERNAL, nil, span_limits, |
| 478 | + span = Span.new(context, Context.empty, OpenTelemetry::Trace::Span::INVALID, 'name', SpanKind::INTERNAL, nil, span_limits, |
446 | 479 | [], nil, links, Time.now, nil, nil) |
447 | 480 | _(span.links.size).must_equal(1) |
448 | 481 | end |
449 | 482 |
|
450 | 483 | it 'prunes invalid links' do |
451 | 484 | invalid_context = OpenTelemetry::Trace::SpanContext.new(trace_id: OpenTelemetry::Trace::INVALID_TRACE_ID) |
452 | 485 | links = [OpenTelemetry::Trace::Link.new(context), OpenTelemetry::Trace::Link.new(invalid_context)] |
453 | | - span = Span.new(context, Context.empty, 'name', SpanKind::INTERNAL, nil, span_limits, |
| 486 | + span = Span.new(context, Context.empty, OpenTelemetry::Trace::Span::INVALID, 'name', SpanKind::INTERNAL, nil, span_limits, |
454 | 487 | [], nil, links, Time.now, nil, nil) |
455 | 488 | _(span.links.size).must_equal(1) |
456 | 489 | end |
| 490 | + |
| 491 | + it 'honours an explicit timestamp' do |
| 492 | + timestamp = Time.new('2021-11-23 12:00:00.000000 -0600') |
| 493 | + test_span = Span.new(context, Context.empty, OpenTelemetry::Trace::Span::INVALID, 'child span', SpanKind::INTERNAL, nil, span_limits, [], nil, [], timestamp, nil, nil) |
| 494 | + _(test_span.start_timestamp).must_equal(exportable_timestamp(timestamp)) |
| 495 | + end |
| 496 | + |
| 497 | + it 'uses the monotonic offset from the parent_span realtime start timestamp when parent_span is recording' do |
| 498 | + parent_span = mock_gettime(monotonic: 100, realtime: 1_000) do |
| 499 | + Span.new(context, Context.empty, OpenTelemetry::Trace::Span::INVALID, 'parent span', SpanKind::INTERNAL, nil, span_limits, [], nil, nil, nil, nil, nil) |
| 500 | + end |
| 501 | + |
| 502 | + _(parent_span.recording?).must_equal(true) |
| 503 | + mock_gettime(monotonic: 200, realtime: 1_000_000) do |
| 504 | + test_span = Span.new(context, Context.empty, parent_span, 'child span', SpanKind::INTERNAL, nil, span_limits, [], nil, [], nil, nil, nil) |
| 505 | + |
| 506 | + # We expect to see the start_timestamp to be the parent span timestamp |
| 507 | + # with the same offset we returned with our stubbed |
| 508 | + # monotonic_now value we returned above |
| 509 | + _(test_span.start_timestamp).must_equal(1_000 + 100) |
| 510 | + end |
| 511 | + end |
| 512 | + |
| 513 | + it 'uses the realtime clock when the parent_span is not recording' do |
| 514 | + non_recording_span = OpenTelemetry::Trace.non_recording_span(span.context) |
| 515 | + |
| 516 | + mock_gettime(monotonic: 100, realtime: 1_000) do |
| 517 | + test_span = Span.new(context, Context.empty, non_recording_span, 'name', SpanKind::INTERNAL, nil, span_limits, [], nil, [], nil, nil, nil) |
| 518 | + |
| 519 | + # We expect for the timestamp to be the value returned from |
| 520 | + # the call to realtime_now as we have no timestamp and no parent |
| 521 | + # span time to try and get a relative offset from. |
| 522 | + _(test_span.start_timestamp).must_equal(1_000) |
| 523 | + end |
| 524 | + end |
| 525 | + end |
| 526 | + |
| 527 | + def mock_gettime(monotonic:, realtime:) |
| 528 | + timestamps = { |
| 529 | + Process::CLOCK_MONOTONIC => monotonic, |
| 530 | + Process::CLOCK_REALTIME => realtime |
| 531 | + } |
| 532 | + |
| 533 | + clock_gettime_mock = lambda do |clock_id, unit| |
| 534 | + _(timestamps).must_include(clock_id) |
| 535 | + _(unit).must_equal(:nanosecond) |
| 536 | + timestamps[clock_id] |
| 537 | + end |
| 538 | + |
| 539 | + Process.stub(:clock_gettime, clock_gettime_mock) { yield } |
457 | 540 | end |
458 | 541 | end |
0 commit comments