* 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:
Matthew Wear 2019-10-24 14:27:25 -07:00 коммит произвёл GitHub
Родитель 4620b5f750
Коммит 38b6701e7a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 692 добавлений и 121 удалений

Просмотреть файл

@ -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.