B3 context propagation (#86)
* Extract LS context propagation into configurable propagator class * Extract context prop test cases to LightStepPropagator spec * Make LightStepPropagator easier to subclass Constant lookup will search in Module.nesting before looking up the inheritance chain, which makes constants hard to override when subclassing. This commit prefixes constants in the LightStep propagator with self.class in order to steer lookup to the inheritance chain. * Introduce B3 Propagator This commit introduces a B3 Propagator where keys are properly injected and extracted under the proper names. Special handling of the trace id and sampled flag will come in subsequent commits. * Propagate 16 byte trace ids for B3; use 16 byte ids internally * Honor and propagate sampled flag for B3 * Clean up tests * Specify default propagator for Tracer#configure * Test against currently maintained Rubies This updates our build matrix to test against Ruby versions that are still under maintenance by the Ruby core team. * The mutating tr! and downcase! methods are not chainable; don't chain them * Make it easier to specify a propgator * Update changelog for B3
This commit is contained in:
Родитель
4620b5f750
Коммит
38b6701e7a
|
@ -1,31 +1,32 @@
|
|||
version: 2
|
||||
|
||||
jobs:
|
||||
test:
|
||||
test-ruby-24:
|
||||
docker:
|
||||
- image: circleci/ruby:2
|
||||
- image: circleci/ruby:2.4-stretch
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- gem-cache-v2-{{ .Branch }}-{{ checksum "Gemfile.lock" }}
|
||||
- gem-cache-v2-{{ .Branch }}-
|
||||
- gem-cache-v2-
|
||||
- run:
|
||||
name: "set up environment"
|
||||
command: |
|
||||
echo 'export BUNDLE_PATH="$HOME/project/.bundler_cache"' >> $BASH_ENV
|
||||
source $BASH_ENV
|
||||
- run: bundle
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/project/.bundler_cache
|
||||
key: gem-cache-v2-{{ .Branch }}-{{ checksum "Gemfile.lock" }}
|
||||
- run: make test
|
||||
- run: gem install --no-document bundler && bundle install --jobs=3 --retry=3
|
||||
- run: bundle exec rake
|
||||
test-ruby-25:
|
||||
docker:
|
||||
- image: circleci/ruby:2.5-stretch
|
||||
steps:
|
||||
- checkout
|
||||
- run: gem install --no-document bundler && bundle install --jobs=3 --retry=3
|
||||
- run: bundle exec rake
|
||||
test-ruby-26:
|
||||
docker:
|
||||
- image: circleci/ruby:2.6-stretch
|
||||
steps:
|
||||
- checkout
|
||||
- run: gem install --no-document bundler && bundle install --jobs=3 --retry=3
|
||||
- run: bundle exec rake
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
test:
|
||||
jobs:
|
||||
- test
|
||||
|
||||
- test-ruby-24
|
||||
- test-ruby-25
|
||||
- test-ruby-26
|
||||
|
|
|
@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## v0.16.0
|
||||
### Added
|
||||
- The tracer now supports B3 context propagation. Propagation can be set by using the `propagator` keyword argument to `Tracer#configure`. Valid values are `:lightstep` (default), and `:b3`.
|
||||
|
||||
## v0.15.0
|
||||
### Added
|
||||
- A Changelog
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
machine:
|
||||
ruby:
|
||||
version: 2.2.3
|
|
@ -0,0 +1,25 @@
|
|||
#frozen_string_literal: true
|
||||
|
||||
require 'lightstep/propagation/lightstep_propagator'
|
||||
require 'lightstep/propagation/b3_propagator'
|
||||
|
||||
module LightStep
|
||||
module Propagation
|
||||
PROPAGATOR_MAP = {
|
||||
lightstep: LightStepPropagator,
|
||||
b3: B3Propagator
|
||||
}
|
||||
|
||||
class << self
|
||||
# Constructs a propagator instance from the given propagator name. If the
|
||||
# name is unknown returns the LightStepPropagator as a default
|
||||
#
|
||||
# @param [Symbol, String] propagator_name One of :lightstep or :b3
|
||||
# @return [Propagator]
|
||||
def [](propagator_name)
|
||||
klass = PROPAGATOR_MAP[propagator_name.to_sym] || LightStepPropagator
|
||||
klass.new
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
#frozen_string_literal: true
|
||||
|
||||
module LightStep
|
||||
module Propagation
|
||||
class B3Propagator < LightStepPropagator
|
||||
CARRIER_TRACER_STATE_PREFIX = 'x-b3-'
|
||||
CARRIER_SPAN_ID = 'x-b3-spanid'
|
||||
CARRIER_TRACE_ID = 'x-b3-traceid'
|
||||
CARRIER_SAMPLED = 'x-b3-sampled'
|
||||
|
||||
private
|
||||
|
||||
def trace_id_from_ctx(ctx)
|
||||
ctx.trace_id16
|
||||
end
|
||||
|
||||
def sampled_flag_from_ctx(ctx)
|
||||
ctx.sampled? ? '1' : '0'
|
||||
end
|
||||
|
||||
def sampled_flag_from_carrier(carrier)
|
||||
carrier[self.class::CARRIER_SAMPLED] == '1' ? true : false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,127 @@
|
|||
#frozen_string_literal: true
|
||||
|
||||
module LightStep
|
||||
module Propagation
|
||||
class LightStepPropagator
|
||||
CARRIER_TRACER_STATE_PREFIX = 'ot-tracer-'
|
||||
CARRIER_SPAN_ID = 'ot-tracer-spanid'
|
||||
CARRIER_TRACE_ID = 'ot-tracer-traceid'
|
||||
CARRIER_SAMPLED = 'ot-tracer-sampled'
|
||||
CARRIER_BAGGAGE_PREFIX = 'ot-baggage-'
|
||||
|
||||
# Inject a SpanContext into the given carrier
|
||||
#
|
||||
# @param spancontext [SpanContext]
|
||||
# @param format [OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY]
|
||||
# @param carrier [Carrier] A carrier object of the type dictated by the specified `format`
|
||||
def inject(span_context, format, carrier)
|
||||
case format
|
||||
when OpenTracing::FORMAT_TEXT_MAP
|
||||
inject_to_text_map(span_context, carrier)
|
||||
when OpenTracing::FORMAT_BINARY
|
||||
warn 'Binary inject format not yet implemented'
|
||||
when OpenTracing::FORMAT_RACK
|
||||
inject_to_rack(span_context, carrier)
|
||||
else
|
||||
warn 'Unknown inject format'
|
||||
end
|
||||
end
|
||||
|
||||
# Extract a SpanContext from a carrier
|
||||
# @param format [OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY, OpenTracing::FORMAT_RACK]
|
||||
# @param carrier [Carrier] A carrier object of the type dictated by the specified `format`
|
||||
# @return [SpanContext] the extracted SpanContext or nil if none could be found
|
||||
def extract(format, carrier)
|
||||
case format
|
||||
when OpenTracing::FORMAT_TEXT_MAP
|
||||
extract_from_text_map(carrier)
|
||||
when OpenTracing::FORMAT_BINARY
|
||||
warn 'Binary join format not yet implemented'
|
||||
nil
|
||||
when OpenTracing::FORMAT_RACK
|
||||
extract_from_rack(carrier)
|
||||
else
|
||||
warn 'Unknown join format'
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def inject_to_text_map(span_context, carrier)
|
||||
if trace_id = trace_id_from_ctx(span_context)
|
||||
carrier[self.class::CARRIER_TRACE_ID] = trace_id
|
||||
end
|
||||
carrier[self.class::CARRIER_SPAN_ID] = span_context.id
|
||||
carrier[self.class::CARRIER_SAMPLED] = sampled_flag_from_ctx(span_context)
|
||||
|
||||
span_context.baggage.each do |key, value|
|
||||
carrier[self.class::CARRIER_BAGGAGE_PREFIX + key] = value
|
||||
end
|
||||
end
|
||||
|
||||
def extract_from_text_map(carrier)
|
||||
# If the carrier does not have both the span_id and trace_id key
|
||||
# skip the processing and just return a normal span
|
||||
if !carrier.has_key?(self.class::CARRIER_SPAN_ID) || !carrier.has_key?(self.class::CARRIER_TRACE_ID)
|
||||
return nil
|
||||
end
|
||||
|
||||
baggage = carrier.reduce({}) do |baggage, (key, value)|
|
||||
if key.start_with?(self.class::CARRIER_BAGGAGE_PREFIX)
|
||||
plain_key = key.to_s[self.class::CARRIER_BAGGAGE_PREFIX.length..key.to_s.length]
|
||||
baggage[plain_key] = value
|
||||
end
|
||||
baggage
|
||||
end
|
||||
|
||||
SpanContext.new(
|
||||
id: carrier[self.class::CARRIER_SPAN_ID],
|
||||
trace_id: carrier[self.class::CARRIER_TRACE_ID],
|
||||
sampled: sampled_flag_from_carrier(carrier),
|
||||
baggage: baggage,
|
||||
)
|
||||
end
|
||||
|
||||
def inject_to_rack(span_context, carrier)
|
||||
if trace_id = trace_id_from_ctx(span_context)
|
||||
carrier[self.class::CARRIER_TRACE_ID] = trace_id
|
||||
end
|
||||
carrier[self.class::CARRIER_SPAN_ID] = span_context.id
|
||||
carrier[self.class::CARRIER_SAMPLED] = sampled_flag_from_ctx(span_context)
|
||||
|
||||
span_context.baggage.each do |key, value|
|
||||
if key =~ /[^A-Za-z0-9\-_]/
|
||||
# TODO: log the error internally
|
||||
next
|
||||
end
|
||||
carrier[self.class::CARRIER_BAGGAGE_PREFIX + key] = value
|
||||
end
|
||||
end
|
||||
|
||||
def extract_from_rack(env)
|
||||
extract_from_text_map(env.reduce({}){|memo, (raw_header, value)|
|
||||
header = raw_header.to_s.gsub(/^HTTP_/, '')
|
||||
header.tr!('_', '-')
|
||||
header.downcase!
|
||||
|
||||
memo[header] = value if header.start_with?(self.class::CARRIER_TRACER_STATE_PREFIX,
|
||||
self.class::CARRIER_BAGGAGE_PREFIX)
|
||||
memo
|
||||
})
|
||||
end
|
||||
|
||||
def trace_id_from_ctx(ctx)
|
||||
ctx.trace_id
|
||||
end
|
||||
|
||||
def sampled_flag_from_ctx(_)
|
||||
'true'
|
||||
end
|
||||
|
||||
def sampled_flag_from_carrier(_)
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,12 +1,31 @@
|
|||
#frozen_string_literal: true
|
||||
|
||||
module LightStep
|
||||
# SpanContext holds the data for a span that gets inherited to child spans
|
||||
class SpanContext
|
||||
attr_reader :id, :trace_id, :baggage
|
||||
attr_reader :id, :trace_id, :trace_id16, :sampled, :baggage
|
||||
alias_method :sampled?, :sampled
|
||||
|
||||
def initialize(id:, trace_id:, baggage: {})
|
||||
ZERO_PADDING = '0' * 16
|
||||
|
||||
def initialize(id:, trace_id:, sampled: true, baggage: {})
|
||||
@id = id.freeze
|
||||
@trace_id = trace_id.freeze
|
||||
@trace_id16 = pad_id(trace_id).freeze
|
||||
@trace_id = truncate_id(trace_id).freeze
|
||||
@sampled = sampled
|
||||
@baggage = baggage.freeze
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def truncate_id(id)
|
||||
return id unless id && id.size == 32
|
||||
id[16..-1]
|
||||
end
|
||||
|
||||
def pad_id(id)
|
||||
return id unless id && id.size == 16
|
||||
"#{ZERO_PADDING}#{id}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,6 +5,7 @@ require 'opentracing'
|
|||
|
||||
require 'lightstep/span'
|
||||
require 'lightstep/reporter'
|
||||
require 'lightstep/propagation'
|
||||
require 'lightstep/transport/http_json'
|
||||
require 'lightstep/transport/nil'
|
||||
require 'lightstep/transport/callback'
|
||||
|
@ -14,6 +15,11 @@ module LightStep
|
|||
class Error < LightStep::Error; end
|
||||
class ConfigurationError < LightStep::Tracer::Error; end
|
||||
|
||||
DEFAULT_MAX_LOG_RECORDS = 1000
|
||||
MIN_MAX_LOG_RECORDS = 1
|
||||
DEFAULT_MAX_SPAN_RECORDS = 1000
|
||||
MIN_MAX_SPAN_RECORDS = 1
|
||||
|
||||
attr_reader :access_token, :guid
|
||||
|
||||
# Initialize a new tracer. Either an access_token or a transport must be
|
||||
|
@ -22,10 +28,20 @@ module LightStep
|
|||
# @param access_token [String] The project access token when pushing to LightStep
|
||||
# @param transport [LightStep::Transport] How the data should be transported
|
||||
# @param tags [Hash] Tracer-level tags
|
||||
# @param propagator [Propagator] Symbol one of :lightstep, :b3 indicating the propgator
|
||||
# to use
|
||||
# @return LightStep::Tracer
|
||||
# @raise LightStep::ConfigurationError if the group name or access token is not a valid string.
|
||||
def initialize(component_name:, access_token: nil, transport: nil, tags: {})
|
||||
configure(component_name: component_name, access_token: access_token, transport: transport, tags: tags)
|
||||
def initialize(component_name:,
|
||||
access_token: nil,
|
||||
transport: nil,
|
||||
tags: {},
|
||||
propagator: :lightstep)
|
||||
configure(component_name: component_name,
|
||||
access_token: access_token,
|
||||
transport: transport,
|
||||
tags: tags,
|
||||
propagator: propagator)
|
||||
end
|
||||
|
||||
def max_log_records
|
||||
|
@ -172,22 +188,14 @@ module LightStep
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
# Inject a SpanContext into the given carrier
|
||||
#
|
||||
# @param spancontext [SpanContext]
|
||||
# @param format [OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY]
|
||||
# @param carrier [Carrier] A carrier object of the type dictated by the specified `format`
|
||||
def inject(span_context, format, carrier)
|
||||
case format
|
||||
when OpenTracing::FORMAT_TEXT_MAP
|
||||
inject_to_text_map(span_context, carrier)
|
||||
when OpenTracing::FORMAT_BINARY
|
||||
warn 'Binary inject format not yet implemented'
|
||||
when OpenTracing::FORMAT_RACK
|
||||
inject_to_rack(span_context, carrier)
|
||||
else
|
||||
warn 'Unknown inject format'
|
||||
end
|
||||
@propagator.inject(span_context, format, carrier)
|
||||
end
|
||||
|
||||
# Extract a SpanContext from a carrier
|
||||
|
@ -195,18 +203,7 @@ module LightStep
|
|||
# @param carrier [Carrier] A carrier object of the type dictated by the specified `format`
|
||||
# @return [SpanContext] the extracted SpanContext or nil if none could be found
|
||||
def extract(format, carrier)
|
||||
case format
|
||||
when OpenTracing::FORMAT_TEXT_MAP
|
||||
extract_from_text_map(carrier)
|
||||
when OpenTracing::FORMAT_BINARY
|
||||
warn 'Binary join format not yet implemented'
|
||||
nil
|
||||
when OpenTracing::FORMAT_RACK
|
||||
extract_from_rack(carrier)
|
||||
else
|
||||
warn 'Unknown join format'
|
||||
nil
|
||||
end
|
||||
@propagator.extract(format, carrier)
|
||||
end
|
||||
|
||||
# @return true if the tracer is enabled
|
||||
|
@ -243,7 +240,11 @@ module LightStep
|
|||
|
||||
protected
|
||||
|
||||
def configure(component_name:, access_token: nil, transport: nil, tags: {})
|
||||
def configure(component_name:,
|
||||
access_token: nil,
|
||||
transport: nil, tags: {},
|
||||
propagator: :lightstep)
|
||||
|
||||
raise ConfigurationError, "component_name must be a string" unless component_name.is_a?(String)
|
||||
raise ConfigurationError, "component_name cannot be blank" if component_name.empty?
|
||||
|
||||
|
@ -254,6 +255,8 @@ module LightStep
|
|||
raise ConfigurationError, "you must provide an access token or a transport" if transport.nil?
|
||||
raise ConfigurationError, "#{transport} is not a LightStep transport class" if !(LightStep::Transport::Base === transport)
|
||||
|
||||
@propagator = Propagation[propagator]
|
||||
|
||||
@guid = LightStep.guid
|
||||
|
||||
@reporter = LightStep::Reporter.new(
|
||||
|
@ -264,75 +267,5 @@ module LightStep
|
|||
tags: tags
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
CARRIER_TRACER_STATE_PREFIX = 'ot-tracer-'.freeze
|
||||
CARRIER_BAGGAGE_PREFIX = 'ot-baggage-'.freeze
|
||||
|
||||
CARRIER_SPAN_ID = (CARRIER_TRACER_STATE_PREFIX + 'spanid').freeze
|
||||
CARRIER_TRACE_ID = (CARRIER_TRACER_STATE_PREFIX + 'traceid').freeze
|
||||
CARRIER_SAMPLED = (CARRIER_TRACER_STATE_PREFIX + 'sampled').freeze
|
||||
|
||||
DEFAULT_MAX_LOG_RECORDS = 1000
|
||||
MIN_MAX_LOG_RECORDS = 1
|
||||
DEFAULT_MAX_SPAN_RECORDS = 1000
|
||||
MIN_MAX_SPAN_RECORDS = 1
|
||||
|
||||
def inject_to_text_map(span_context, carrier)
|
||||
carrier[CARRIER_SPAN_ID] = span_context.id
|
||||
carrier[CARRIER_TRACE_ID] = span_context.trace_id unless span_context.trace_id.nil?
|
||||
carrier[CARRIER_SAMPLED] = 'true'
|
||||
|
||||
span_context.baggage.each do |key, value|
|
||||
carrier[CARRIER_BAGGAGE_PREFIX + key] = value
|
||||
end
|
||||
end
|
||||
|
||||
def extract_from_text_map(carrier)
|
||||
# If the carrier does not have both the span_id and trace_id key
|
||||
# skip the processing and just return a normal span
|
||||
if !carrier.has_key?(CARRIER_SPAN_ID) || !carrier.has_key?(CARRIER_TRACE_ID)
|
||||
return nil
|
||||
end
|
||||
|
||||
baggage = carrier.reduce({}) do |baggage, tuple|
|
||||
key, value = tuple
|
||||
if key.start_with?(CARRIER_BAGGAGE_PREFIX)
|
||||
plain_key = key.to_s[CARRIER_BAGGAGE_PREFIX.length..key.to_s.length]
|
||||
baggage[plain_key] = value
|
||||
end
|
||||
baggage
|
||||
end
|
||||
SpanContext.new(
|
||||
id: carrier[CARRIER_SPAN_ID],
|
||||
trace_id: carrier[CARRIER_TRACE_ID],
|
||||
baggage: baggage,
|
||||
)
|
||||
end
|
||||
|
||||
def inject_to_rack(span_context, carrier)
|
||||
carrier[CARRIER_SPAN_ID] = span_context.id
|
||||
carrier[CARRIER_TRACE_ID] = span_context.trace_id unless span_context.trace_id.nil?
|
||||
carrier[CARRIER_SAMPLED] = 'true'
|
||||
|
||||
span_context.baggage.each do |key, value|
|
||||
if key =~ /[^A-Za-z0-9\-_]/
|
||||
# TODO: log the error internally
|
||||
next
|
||||
end
|
||||
carrier[CARRIER_BAGGAGE_PREFIX + key] = value
|
||||
end
|
||||
end
|
||||
|
||||
def extract_from_rack(env)
|
||||
extract_from_text_map(env.reduce({}){|memo, tuple|
|
||||
raw_header, value = tuple
|
||||
header = raw_header.to_s.gsub(/^HTTP_/, '').tr('_', '-').downcase
|
||||
|
||||
memo[header] = value if header.start_with?(CARRIER_TRACER_STATE_PREFIX, CARRIER_BAGGAGE_PREFIX)
|
||||
memo
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
module RackHelpers
|
||||
def to_rack_env(input_hash)
|
||||
input_hash.inject({}) do |memo, (k, v)|
|
||||
memo[to_rack_key(k)] = v
|
||||
memo
|
||||
end
|
||||
end
|
||||
|
||||
def to_rack_key(key)
|
||||
"HTTP_#{key.gsub("-", "_").upcase!}"
|
||||
end
|
||||
end
|
|
@ -0,0 +1,216 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe LightStep::Propagation::B3Propagator, :rack_helpers do
|
||||
let(:propagator) { subject }
|
||||
let(:trace_id_high_bytes) { LightStep.guid }
|
||||
let(:trace_id_low_bytes) { LightStep.guid }
|
||||
let(:trace_id) { [trace_id_high_bytes, trace_id_low_bytes].join }
|
||||
let(:span_id) { LightStep.guid }
|
||||
let(:baggage) do
|
||||
{
|
||||
'footwear' => 'cleats',
|
||||
'umbrella' => 'golf'
|
||||
}
|
||||
end
|
||||
let(:span_context) do
|
||||
LightStep::SpanContext.new(
|
||||
id: span_id,
|
||||
trace_id: trace_id,
|
||||
baggage: baggage
|
||||
)
|
||||
end
|
||||
let(:span_context_trace_id_low_bytes) do
|
||||
LightStep::SpanContext.new(
|
||||
id: span_id,
|
||||
trace_id: trace_id_low_bytes,
|
||||
baggage: baggage
|
||||
)
|
||||
end
|
||||
let(:unsampled_span_context) do
|
||||
LightStep::SpanContext.new(
|
||||
id: span_id,
|
||||
trace_id: trace_id,
|
||||
sampled: true,
|
||||
baggage: baggage
|
||||
)
|
||||
end
|
||||
|
||||
describe '#inject' do
|
||||
it 'handles text carriers' do
|
||||
carrier = {}
|
||||
propagator.inject(span_context, OpenTracing::FORMAT_TEXT_MAP, carrier)
|
||||
|
||||
expect(carrier['x-b3-traceid']).to eq(trace_id)
|
||||
expect(carrier['x-b3-spanid']).to eq(span_id)
|
||||
expect(carrier['x-b3-sampled']).to eq('1')
|
||||
expect(carrier['ot-baggage-footwear']).to eq('cleats')
|
||||
expect(carrier['ot-baggage-umbrella']).to eq('golf')
|
||||
end
|
||||
|
||||
it 'handles rack carriers' do
|
||||
baggage.merge!({
|
||||
'unsafe!@#$%$^&header' => 'value',
|
||||
'CASE-Sensitivity_Underscores'=> 'value'
|
||||
})
|
||||
|
||||
carrier = {}
|
||||
propagator.inject(span_context, OpenTracing::FORMAT_RACK, carrier)
|
||||
|
||||
expect(carrier['x-b3-traceid']).to eq(trace_id)
|
||||
expect(carrier['x-b3-spanid']).to eq(span_id)
|
||||
expect(carrier['x-b3-sampled']).to eq('1')
|
||||
expect(carrier['ot-baggage-footwear']).to eq('cleats')
|
||||
expect(carrier['ot-baggage-umbrella']).to eq('golf')
|
||||
expect(carrier['ot-baggage-unsafeheader']).to be_nil
|
||||
expect(carrier['ot-baggage-CASE-Sensitivity_Underscores']).to eq('value')
|
||||
end
|
||||
|
||||
it 'propagates a 16 byte trace id' do
|
||||
carrier = {}
|
||||
propagator.inject(span_context, OpenTracing::FORMAT_TEXT_MAP, carrier)
|
||||
|
||||
expect(carrier['x-b3-traceid']).to eq(trace_id)
|
||||
expect(carrier['x-b3-traceid'].size).to eq(32)
|
||||
end
|
||||
|
||||
it 'pads 8 byte trace_ids' do
|
||||
carrier = {}
|
||||
|
||||
propagator.inject(span_context_trace_id_low_bytes, OpenTracing::FORMAT_RACK, carrier)
|
||||
expect(carrier['x-b3-traceid']).to eq('0' * 16 << trace_id_low_bytes)
|
||||
expect(carrier['x-b3-spanid']).to eq(span_id)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#extract' do
|
||||
it 'handles text carriers' do
|
||||
carrier = {}
|
||||
propagator.inject(span_context, OpenTracing::FORMAT_TEXT_MAP, carrier)
|
||||
extracted_ctx = propagator.extract(OpenTracing::FORMAT_TEXT_MAP, carrier)
|
||||
|
||||
expect(extracted_ctx.trace_id).to eq(trace_id_low_bytes)
|
||||
expect(extracted_ctx.trace_id16).to eq(trace_id)
|
||||
expect(extracted_ctx.id).to eq(span_id)
|
||||
expect(extracted_ctx.baggage['footwear']).to eq('cleats')
|
||||
expect(extracted_ctx.baggage['umbrella']).to eq('golf')
|
||||
end
|
||||
|
||||
it 'handles rack carriers' do
|
||||
baggage.merge!({
|
||||
'unsafe!@#$%$^&header' => 'value',
|
||||
'CASE-Sensitivity_Underscores'=> 'value'
|
||||
})
|
||||
|
||||
carrier = {}
|
||||
propagator.inject(span_context, OpenTracing::FORMAT_RACK, carrier)
|
||||
extracted_ctx = propagator.extract(OpenTracing::FORMAT_RACK, to_rack_env(carrier))
|
||||
|
||||
expect(extracted_ctx.trace_id).to eq(trace_id_low_bytes)
|
||||
expect(extracted_ctx.trace_id16).to eq(trace_id)
|
||||
expect(extracted_ctx.id).to eq(span_id)
|
||||
expect(extracted_ctx.baggage['footwear']).to eq('cleats')
|
||||
expect(extracted_ctx.baggage['umbrella']).to eq('golf')
|
||||
expect(extracted_ctx.baggage['unsafe!@#$%$^&header']).to be_nil
|
||||
expect(extracted_ctx.baggage['unsafeheader']).to be_nil
|
||||
expect(extracted_ctx.baggage['case-sensitivity-underscores']).to eq('value')
|
||||
end
|
||||
|
||||
it 'returns a span context when carrier has both a span_id and trace_id' do
|
||||
extracted_ctx = propagator.extract(
|
||||
OpenTracing::FORMAT_RACK,
|
||||
{'HTTP_X_B3_TRACEID' => trace_id}
|
||||
)
|
||||
|
||||
expect(extracted_ctx).to be_nil
|
||||
extracted_ctx = propagator.extract(
|
||||
OpenTracing::FORMAT_RACK,
|
||||
{'HTTP_X_B3_SPANID' => span_id}
|
||||
)
|
||||
expect(extracted_ctx).to be_nil
|
||||
|
||||
# We need both a TRACEID and SPANID; this has both so it should work.
|
||||
extracted_ctx = propagator.extract(
|
||||
OpenTracing::FORMAT_RACK,
|
||||
{'HTTP_X_B3_SPANID' => span_id, 'HTTP_X_B3_TRACEID' => trace_id}
|
||||
)
|
||||
expect(extracted_ctx.id).to eq(span_id)
|
||||
expect(extracted_ctx.trace_id16).to eq(trace_id)
|
||||
expect(extracted_ctx.trace_id).to eq(trace_id_low_bytes)
|
||||
end
|
||||
|
||||
it 'handles carriers with string keys' do
|
||||
carrier_with_strings = {
|
||||
'HTTP_X_B3_TRACEID' => trace_id,
|
||||
'HTTP_X_B3_SPANID' => span_id,
|
||||
'HTTP_X_B3_SAMPLED' => '1'
|
||||
}
|
||||
string_ctx = propagator.extract(OpenTracing::FORMAT_RACK, carrier_with_strings)
|
||||
|
||||
expect(string_ctx).not_to be_nil
|
||||
expect(string_ctx.trace_id16).to eq(trace_id)
|
||||
expect(string_ctx.trace_id).to eq(trace_id_low_bytes)
|
||||
expect(string_ctx).to be_sampled
|
||||
expect(string_ctx.id).to eq(span_id)
|
||||
end
|
||||
|
||||
it 'handles carriers symbol keys' do
|
||||
carrier_with_symbols = {
|
||||
HTTP_X_B3_TRACEID: trace_id,
|
||||
HTTP_X_B3_SPANID: span_id,
|
||||
HTTP_X_B3_SAMPLED: '1'
|
||||
}
|
||||
symbol_ctx = propagator.extract(OpenTracing::FORMAT_RACK, carrier_with_symbols)
|
||||
|
||||
expect(symbol_ctx).not_to be_nil
|
||||
expect(symbol_ctx.trace_id16).to eq(trace_id)
|
||||
expect(symbol_ctx.trace_id).to eq(trace_id_low_bytes)
|
||||
expect(symbol_ctx).to be_sampled
|
||||
expect(symbol_ctx.id).to eq(span_id)
|
||||
end
|
||||
|
||||
it 'pads 8 byte trace_ids' do
|
||||
carrier = {
|
||||
'x-b3-traceid' => trace_id_low_bytes,
|
||||
'x-b3-spanid' => span_id,
|
||||
'x-b3-sampled' => '1'
|
||||
}
|
||||
|
||||
extracted_ctx = propagator.extract(OpenTracing::FORMAT_TEXT_MAP, carrier)
|
||||
expect(extracted_ctx.trace_id16).to eq('0' * 16 << trace_id_low_bytes)
|
||||
expect(extracted_ctx.trace_id).to eq(trace_id_low_bytes)
|
||||
end
|
||||
|
||||
it 'interprets a true sampled flag properly' do
|
||||
carrier = {
|
||||
'x-b3-traceid' => trace_id,
|
||||
'x-b3-spanid' => span_id,
|
||||
'x-b3-sampled' => '1'
|
||||
}
|
||||
|
||||
extracted_ctx = propagator.extract(OpenTracing::FORMAT_TEXT_MAP, carrier)
|
||||
expect(extracted_ctx).to be_sampled
|
||||
end
|
||||
|
||||
it 'interprets a false sampled flag properly' do
|
||||
carrier = {
|
||||
'x-b3-traceid' => trace_id,
|
||||
'x-b3-spanid' => span_id,
|
||||
'x-b3-sampled' => '0'
|
||||
}
|
||||
|
||||
extracted_ctx = propagator.extract(OpenTracing::FORMAT_TEXT_MAP, carrier)
|
||||
expect(extracted_ctx).not_to be_sampled
|
||||
end
|
||||
|
||||
it 'maintains 8 and 16 byte trace ids' do
|
||||
carrier = {}
|
||||
propagator.inject(span_context, OpenTracing::FORMAT_TEXT_MAP, carrier)
|
||||
|
||||
extracted_ctx = propagator.extract(OpenTracing::FORMAT_TEXT_MAP, carrier)
|
||||
expect(extracted_ctx.trace_id16).to eq(trace_id)
|
||||
expect(extracted_ctx.trace_id16.size).to eq(32)
|
||||
expect(extracted_ctx.trace_id).to eq(trace_id_low_bytes)
|
||||
expect(extracted_ctx.trace_id.size).to eq(16)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,184 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe LightStep::Propagation::LightStepPropagator, :rack_helpers do
|
||||
let(:propagator) { subject }
|
||||
let(:trace_id) { LightStep.guid }
|
||||
let(:padded_trace_id) { '0' * 16 << trace_id }
|
||||
let(:span_id) { LightStep.guid }
|
||||
let(:baggage) do
|
||||
{
|
||||
'footwear' => 'cleats',
|
||||
'umbrella' => 'golf'
|
||||
}
|
||||
end
|
||||
let(:span_context) do
|
||||
LightStep::SpanContext.new(
|
||||
id: span_id,
|
||||
trace_id: trace_id,
|
||||
baggage: baggage
|
||||
)
|
||||
end
|
||||
|
||||
describe '#inject' do
|
||||
it 'handles text carriers' do
|
||||
carrier = {}
|
||||
propagator.inject(span_context, OpenTracing::FORMAT_TEXT_MAP, carrier)
|
||||
|
||||
expect(carrier['ot-tracer-traceid']).to eq(trace_id)
|
||||
expect(carrier['ot-tracer-spanid']).to eq(span_id)
|
||||
expect(carrier['ot-baggage-footwear']).to eq('cleats')
|
||||
expect(carrier['ot-baggage-umbrella']).to eq('golf')
|
||||
end
|
||||
|
||||
it 'handles rack carriers' do
|
||||
baggage.merge!({
|
||||
'unsafe!@#$%$^&header' => 'value',
|
||||
'CASE-Sensitivity_Underscores'=> 'value'
|
||||
})
|
||||
|
||||
carrier = {}
|
||||
propagator.inject(span_context, OpenTracing::FORMAT_RACK, carrier)
|
||||
|
||||
expect(carrier['ot-tracer-traceid']).to eq(trace_id)
|
||||
expect(carrier['ot-tracer-spanid']).to eq(span_id)
|
||||
expect(carrier['ot-baggage-footwear']).to eq('cleats')
|
||||
expect(carrier['ot-baggage-umbrella']).to eq('golf')
|
||||
expect(carrier['ot-baggage-unsafeheader']).to be_nil
|
||||
expect(carrier['ot-baggage-CASE-Sensitivity_Underscores']).to eq('value')
|
||||
end
|
||||
|
||||
it 'propagates an 8 byte trace id' do
|
||||
carrier = {}
|
||||
propagator.inject(span_context, OpenTracing::FORMAT_TEXT_MAP, carrier)
|
||||
|
||||
expect(carrier['ot-tracer-traceid']).to eq(trace_id)
|
||||
expect(carrier['ot-tracer-traceid'].size).to eq(16)
|
||||
end
|
||||
|
||||
it 'always propagates a true sampled flag' do
|
||||
[true, false].each do |sampled|
|
||||
ctx = LightStep::SpanContext.new(
|
||||
id: span_id,
|
||||
trace_id: trace_id,
|
||||
sampled: sampled,
|
||||
baggage: baggage
|
||||
)
|
||||
carrier = {}
|
||||
propagator.inject(span_context, OpenTracing::FORMAT_TEXT_MAP, carrier)
|
||||
expect(carrier['ot-tracer-sampled']).to eq('true')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#extract' do
|
||||
it 'handles text carriers' do
|
||||
carrier = {}
|
||||
propagator.inject(span_context, OpenTracing::FORMAT_TEXT_MAP, carrier)
|
||||
extracted_ctx = propagator.extract(OpenTracing::FORMAT_TEXT_MAP, carrier)
|
||||
|
||||
expect(extracted_ctx.trace_id).to eq(trace_id)
|
||||
expect(extracted_ctx.trace_id16).to eq(padded_trace_id)
|
||||
expect(extracted_ctx.id).to eq(span_id)
|
||||
expect(extracted_ctx.baggage['footwear']).to eq('cleats')
|
||||
expect(extracted_ctx.baggage['umbrella']).to eq('golf')
|
||||
end
|
||||
|
||||
it 'handles rack carriers' do
|
||||
baggage.merge!({
|
||||
'unsafe!@#$%$^&header' => 'value',
|
||||
'CASE-Sensitivity_Underscores'=> 'value'
|
||||
})
|
||||
|
||||
carrier = {}
|
||||
propagator.inject(span_context, OpenTracing::FORMAT_RACK, carrier)
|
||||
extracted_ctx = propagator.extract(OpenTracing::FORMAT_RACK, to_rack_env(carrier))
|
||||
|
||||
expect(extracted_ctx.trace_id).to eq(trace_id)
|
||||
expect(extracted_ctx.trace_id16).to eq(padded_trace_id)
|
||||
expect(extracted_ctx.id).to eq(span_id)
|
||||
expect(extracted_ctx.baggage['footwear']).to eq('cleats')
|
||||
expect(extracted_ctx.baggage['umbrella']).to eq('golf')
|
||||
expect(extracted_ctx.baggage['unsafe!@#$%$^&header']).to be_nil
|
||||
expect(extracted_ctx.baggage['unsafeheader']).to be_nil
|
||||
expect(extracted_ctx.baggage['case-sensitivity-underscores']).to eq('value')
|
||||
end
|
||||
|
||||
it 'returns a span context when carrier has both a span_id and trace_id' do
|
||||
extracted_ctx = propagator.extract(
|
||||
OpenTracing::FORMAT_RACK,
|
||||
{'HTTP_OT_TRACER_TRACEID' => trace_id}
|
||||
)
|
||||
|
||||
expect(extracted_ctx).to be_nil
|
||||
extracted_ctx = propagator.extract(
|
||||
OpenTracing::FORMAT_RACK,
|
||||
{'HTTP_OT_TRACER_SPANID' => span_id}
|
||||
)
|
||||
expect(extracted_ctx).to be_nil
|
||||
|
||||
# We need both a TRACEID and SPANID; this has both so it should work.
|
||||
extracted_ctx = propagator.extract(
|
||||
OpenTracing::FORMAT_RACK,
|
||||
{'HTTP_OT_TRACER_SPANID' => span_id, 'HTTP_OT_TRACER_TRACEID' => trace_id}
|
||||
)
|
||||
expect(extracted_ctx.id).to eq(span_id)
|
||||
expect(extracted_ctx.trace_id).to eq(trace_id)
|
||||
expect(extracted_ctx.trace_id16).to eq(padded_trace_id)
|
||||
end
|
||||
|
||||
it 'handles carriers with string keys' do
|
||||
carrier = {
|
||||
'HTTP_OT_TRACER_TRACEID' => trace_id,
|
||||
'HTTP_OT_TRACER_SPANID' => span_id,
|
||||
}
|
||||
extracted_ctx = propagator.extract(OpenTracing::FORMAT_RACK, carrier)
|
||||
|
||||
expect(extracted_ctx).not_to be_nil
|
||||
expect(extracted_ctx.trace_id).to eq(trace_id)
|
||||
expect(extracted_ctx.trace_id16).to eq(padded_trace_id)
|
||||
expect(extracted_ctx.id).to eq(span_id)
|
||||
end
|
||||
|
||||
it 'handles carriers with symbol keys' do
|
||||
carrier = {
|
||||
HTTP_OT_TRACER_TRACEID: trace_id,
|
||||
HTTP_OT_TRACER_SPANID: span_id,
|
||||
}
|
||||
extracted_ctx = propagator.extract(OpenTracing::FORMAT_RACK, carrier)
|
||||
|
||||
expect(extracted_ctx).not_to be_nil
|
||||
expect(extracted_ctx.trace_id).to eq(trace_id)
|
||||
expect(extracted_ctx.trace_id16).to eq(padded_trace_id)
|
||||
expect(extracted_ctx.id).to eq(span_id)
|
||||
end
|
||||
|
||||
it 'maintains 8 and 16 byte trace ids' do
|
||||
trace_id16 = [LightStep.guid, trace_id].join
|
||||
|
||||
carrier = {
|
||||
'ot-tracer-traceid' => trace_id16,
|
||||
'ot-tracer-spanid' => span_id,
|
||||
'ot-tracer-sampled' => 'true'
|
||||
}
|
||||
|
||||
extracted_ctx = propagator.extract(OpenTracing::FORMAT_TEXT_MAP, carrier)
|
||||
expect(extracted_ctx.trace_id16).to eq(trace_id16)
|
||||
expect(extracted_ctx.trace_id16.size).to eq(32)
|
||||
expect(extracted_ctx.trace_id).to eq(trace_id)
|
||||
expect(extracted_ctx.trace_id.size).to eq(16)
|
||||
end
|
||||
|
||||
it 'always sets sampled: true on returned context' do
|
||||
['true', 'false'].each do |sampled|
|
||||
carrier = {
|
||||
'ot-tracer-traceid' => trace_id,
|
||||
'ot-tracer-spanid' => span_id,
|
||||
'ot-tracer-sampled' => sampled
|
||||
}
|
||||
|
||||
extracted_ctx = propagator.extract(OpenTracing::FORMAT_TEXT_MAP, carrier)
|
||||
expect(extracted_ctx).to be_sampled
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,25 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe LightStep::Propagation do
|
||||
let(:propagator_map) { LightStep::Propagation::PROPAGATOR_MAP }
|
||||
describe "[]" do
|
||||
it 'returns propagator instance from symbol' do
|
||||
propagator_map.each_pair do |sym, klass|
|
||||
propagator = LightStep::Propagation[sym]
|
||||
expect(propagator).to be_an_instance_of(klass)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns propagator instance from a string' do
|
||||
propagator_map.each_pair do |sym, klass|
|
||||
propagator = LightStep::Propagation[sym.to_s]
|
||||
expect(propagator).to be_an_instance_of(klass)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns lightstep propagator when name is unknown' do
|
||||
propagator = LightStep::Propagation[:this_propagator_is_unknown]
|
||||
expect(propagator).to be_an_instance_of(LightStep::Propagation::LightStepPropagator)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -20,9 +20,11 @@ SimpleCov.start
|
|||
require 'pp'
|
||||
require 'lightstep'
|
||||
require 'timecop'
|
||||
require 'helpers/rack_helpers'
|
||||
|
||||
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
||||
RSpec.configure do |config|
|
||||
config.include RackHelpers, :rack_helpers
|
||||
# rspec-expectations config goes here. You can use an alternate
|
||||
# assertion/expectation library such as wrong or the stdlib/minitest
|
||||
# assertions if you prefer.
|
||||
|
|
Загрузка…
Ссылка в новой задаче