Skip to content

Commit

Permalink
Add remote? to SpanContext (#76)
Browse files Browse the repository at this point in the history
  • Loading branch information
fbogsany authored Sep 13, 2019
1 parent 4bf3933 commit 741ca61
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,27 @@ module Propagation
# Propagation is usually implemented via library-specific request interceptors, where the client-side injects values
# and the server-side extracts them.
class HTTPTextFormat
# extract will return a SpanContext from the supplied carrier
# invalid headers will result in a new SpanContext
# @param [Carrier] the carrier to get the header from
# @yield [Carrier, String] the header key
# @return [SpanContext] the span context from the header, or a new one if parsing fails
# Return a remote {Trace::SpanContext} extracted from the supplied carrier.
# Invalid headers will result in a new, valid, non-remote {Trace::SpanContext}.
#
# @param [Carrier] carrier The carrier to get the header from.
# @yield [Carrier, String] the carrier and the header key.
# @return [SpanContext] the span context from the header, or a new one if parsing fails.
def extract(carrier)
raise ArgumentError, 'block must be supplied' unless block_given?

header = yield carrier, TraceParent::TRACE_PARENT_HEADER
tp = TraceParent.from_string(header)

SpanContext.new(trace_id: tp.trace_id, span_id: tp.span_id, flags: tp.flags)
Trace::SpanContext.new(trace_id: tp.trace_id, span_id: tp.span_id, trace_flags: tp.flags, remote: true)
rescue OpenTelemetry::Error
SpanContext.new
Trace::SpanContext.new
end

# inject will set the span context on the supplied carrier
# @param [Context] the carrier
# @yield [Carrier, String, String] carrier, header key, header value
# Set the span context on the supplied carrier.
#
# @param [SpanContext] context The active {Trace::SpanContext}.
# @yield [Carrier, String, String] carrier, header key, header value.
def inject(context, carrier)
raise ArgumentError, 'block must be supplied' unless block_given?

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module DistributedContext
module Propagation
# A TraceParent is an implementation of the W3C trace context specification
# https://www.w3.org/TR/trace-context/
# {SpanContext}
# {Trace::SpanContext}
class TraceParent
InvalidFormatError = Class.new(Error)
InvalidVersionError = Class.new(Error)
Expand All @@ -25,15 +25,15 @@ class TraceParent
private_constant :REGEXP

class << self
# Creates a new {TraceParent} from a supplied {SpanContext}
# @param [SpanContext] the context
# Creates a new {TraceParent} from a supplied {Trace::SpanContext}
# @param [SpanContext] ctx The context
# @return [TraceParent] a trace parent
def from_context(ctx)
new(trace_id: ctx.trace_id, span_id: ctx.span_id, flags: ctx.trace_flags)
end

# Deserializes the {TraceParent} from the string representation
# @param [String] the serialized trace parent
# @param [String] string The serialized trace parent
# @return [TraceParent] a trace_parent
# @raise [InvalidFormatError] on an invalid format
# @raise [InvalidVerionError] on an invalid version
Expand Down
26 changes: 23 additions & 3 deletions api/lib/opentelemetry/trace/span_context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,45 @@
module OpenTelemetry
module Trace
# A SpanContext contains the state that must propagate to child {Span}s and across process boundaries.
# It contains the identifiers (a trace ID and span ID) associated with the {Span} and a set of
# {TraceFlags}.
# It contains the identifiers (a trace ID and span ID) associated with the {Span}, a set of
# {TraceFlags}, and a boolean indicating that the SpanContext was extracted from the wire.
class SpanContext
attr_reader :trace_id, :span_id, :trace_flags

# Returns a new {SpanContext}.
#
# @param [optional String] trace_id The trace ID associated with a {Span}.
# @param [optional String] span_id The span ID associated with a {Span}.
# @param [optional TraceFlags] trace_flags The trace flags associated with a {Span}.
# @param [optional Boolean] remote Whether the {SpanContext} was extracted from the wire.
# @return [SpanContext]
def initialize(
trace_id: Trace.generate_trace_id,
span_id: Trace.generate_span_id,
trace_flags: TraceFlags::DEFAULT
trace_flags: TraceFlags::DEFAULT,
remote: false
)
@trace_id = trace_id
@span_id = span_id
@trace_flags = trace_flags
@remote = remote
end

# Returns true if the {SpanContext} has a non-zero trace ID and non-zero span ID.
#
# @return [Boolean]
def valid?
@trace_id != INVALID_TRACE_ID && @span_id != INVALID_SPAN_ID
end

# Returns true if the {SpanContext} was propagated from a remote parent.
#
# @return [Boolean]
def remote?
@remote
end

# Represents an invalid {SpanContext}, with an invalid trace ID and an invalid span ID.
INVALID = new(trace_id: INVALID_TRACE_ID, span_id: INVALID_SPAN_ID)
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# frozen_string_literal: true

# Copyright 2019 OpenTelemetry Authors
#
# SPDX-License-Identifier: Apache-2.0

require 'test_helper'

describe OpenTelemetry::DistributedContext::Propagation::HTTPTextFormat do
SpanContext = OpenTelemetry::Trace::SpanContext
HTTPTextFormat =
OpenTelemetry::DistributedContext::Propagation::HTTPTextFormat

let(:formatter) { HTTPTextFormat.new }
let(:valid_header) do
'00-000000000000000000000000000000AA-00000000000000ea-01'
end
let(:invalid_header) do
'FF-000000000000000000000000000000AA-00000000000000ea-01'
end

describe '#extract' do
it 'requires a block' do
proc { formatter.extract({}) }.must_raise(ArgumentError)
end

it 'yields the carrier and the header key' do
carrier = {}
yielded = false
formatter.extract(carrier) do |c, key|
c.must_equal(carrier)
key.must_equal('traceparent')
yielded = true
valid_header
end
yielded.must_equal(true)
end

it 'returns a remote SpanContext with fields from the traceparent header' do
context = formatter.extract({}) { valid_header }
context.must_be :remote?
context.trace_id.must_equal('000000000000000000000000000000aa')
context.span_id.must_equal('00000000000000ea')
context.trace_flags.must_be :sampled?
end

it 'returns a valid non-remote SpanContext on error' do
context = formatter.extract({}) { invalid_header }
context.wont_be :remote?
context.must_be :valid?
end
end

describe '#inject' do
it 'requires a block' do
proc { formatter.inject(SpanContext.new, {}) }.must_raise(ArgumentError)
end

it 'yields the carrier, key, and traceparent value from the context' do
context = SpanContext.new(trace_id: 'f' * 32, span_id: '1' * 16)
carrier = {}
yielded = false
formatter.inject(context, carrier) do |c, k, v|
c.must_equal(carrier)
k.must_equal('traceparent')
v.must_equal('00-ffffffffffffffffffffffffffffffff-1111111111111111-00')
yielded = true
c
end
yielded.must_equal(true)
end
end

describe '#fields' do
it 'returns an array with the W3C traceparent header' do
formatter.fields.must_equal(['traceparent'])
end
end
end
11 changes: 11 additions & 0 deletions api/test/opentelemetry/trace/span_context_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@
end
end

describe '#remote?' do
it 'is false by default' do
span_context.wont_be(:remote?)
end

it 'reflects the value passed in' do
context = OpenTelemetry::Trace::SpanContext.new(remote: true)
context.must_be(:remote?)
end
end

describe '#trace_id' do
it 'reflects the value passed in' do
trace_id = OpenTelemetry::Trace.generate_trace_id
Expand Down

0 comments on commit 741ca61

Please sign in to comment.