lib/opentelemetry/sdk/trace/span.rb in opentelemetry-sdk-1.0.1 vs lib/opentelemetry/sdk/trace/span.rb in opentelemetry-sdk-1.0.2

- old
+ new

@@ -129,11 +129,11 @@ # values may be (array of) string, boolean or numeric type. # @param [optional Time] timestamp Optional timestamp for the event. # # @return [self] returns itself def add_event(name, attributes: nil, timestamp: nil) - event = Event.new(name, truncate_attribute_values(attributes), wall_clock(timestamp)) + event = Event.new(name, truncate_attribute_values(attributes), relative_timestamp(timestamp)) @mutex.synchronize do if @ended OpenTelemetry.logger.warn('Calling add_event on an ended Span.') else @@ -231,11 +231,11 @@ @mutex.synchronize do if @ended OpenTelemetry.logger.warn('Calling finish on an ended Span.') return self end - @end_timestamp = wall_clock(end_timestamp) + @end_timestamp = relative_timestamp(end_timestamp) @attributes = validated_attributes(@attributes).freeze @events.freeze @ended = true end @span_processors.each { |processor| processor.on_finish(self) } @@ -274,11 +274,11 @@ context.tracestate ) end # @api private - def initialize(context, parent_context, name, kind, parent_span_id, span_limits, span_processors, attributes, links, start_timestamp, resource, instrumentation_library) # rubocop:disable Metrics/AbcSize + def initialize(context, parent_context, parent_span, name, kind, parent_span_id, span_limits, span_processors, attributes, links, start_timestamp, resource, instrumentation_library) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity super(span_context: context) @mutex = Mutex.new @name = name @kind = kind @parent_span_id = parent_span_id.freeze || OpenTelemetry::Trace::INVALID_SPAN_ID @@ -289,21 +289,49 @@ @ended = false @status = DEFAULT_STATUS @total_recorded_events = 0 @total_recorded_links = links&.size || 0 @total_recorded_attributes = attributes&.size || 0 - @start_timestamp = wall_clock(start_timestamp) - @end_timestamp = nil @attributes = attributes.nil? ? nil : Hash[attributes] # We need a mutable copy of attributes. trim_span_attributes(@attributes) @events = nil @links = trim_links(links, span_limits.link_count_limit, span_limits.link_attribute_count_limit) + + # Times are hard. Whenever an explicit timestamp is provided + # (for Events or for the Span start_timestamp or end_timestamp), + # we use that as the recorded timestamp. An implicit Event timestamp + # and end_timestamp is computed as a monotonic clock offset from + # the realtime start_timestamp. The realtime start_timestamp is + # computed as a monotonic clock offset from the realtime + # start_timestamp of its parent span, if available, or it is + # fetched from the realtime system clock. + # + # We therefore have 3 start timestamps. The first two are used + # internally (and by child spans) to compute other timestamps. + # The last is the start timestamp actually recorded in the + # SpanData. + @monotonic_start_timestamp = monotonic_now + @realtime_start_timestamp = if parent_span.recording? + relative_realtime(parent_span.realtime_start_timestamp, parent_span.monotonic_start_timestamp) + else + realtime_now + end + @start_timestamp = if start_timestamp + time_in_nanoseconds(start_timestamp) + else + @realtime_start_timestamp + end + @end_timestamp = nil @span_processors.each { |processor| processor.on_start(self, parent_context) } end # TODO: Java implementation overrides finalize to log if a span isn't finished. + protected + + attr_reader :monotonic_start_timestamp, :realtime_start_timestamp + private def validated_attributes(attrs) return attrs if Internal.valid_attributes?(name, 'span', attrs) @@ -374,12 +402,29 @@ event = Event.new(event.name, attrs.freeze, event.timestamp) end events << event end - def wall_clock(timestamp) - timestamp = (timestamp.to_r * 1_000_000_000).to_i unless timestamp.nil? - timestamp || Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond) + def relative_timestamp(timestamp) + return time_in_nanoseconds(timestamp) unless timestamp.nil? + + relative_realtime(realtime_start_timestamp, monotonic_start_timestamp) + end + + def time_in_nanoseconds(timestamp) + (timestamp.to_r * 1_000_000_000).to_i + end + + def relative_realtime(realtime_base, monotonic_base) + realtime_base + (monotonic_now - monotonic_base) + end + + def realtime_now + Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond) + end + + def monotonic_now + Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond) end end # rubocop:enable Metrics/ClassLength end end