From a74830396a62d822d15fc365e31354ba98157a62 Mon Sep 17 00:00:00 2001 From: Yohei Kitamura Date: Fri, 25 Oct 2019 11:24:37 -0400 Subject: [PATCH] Add support for writing B3 single header (#166) * Add support for writing B3 single header * downcase 'b3' * Update CHANGELOG.md --- CHANGELOG.md | 7 + README.md | 1 + lib/zipkin-tracer.rb | 1 + lib/zipkin-tracer/config.rb | 9 +- lib/zipkin-tracer/excon/zipkin-tracer.rb | 17 +-- lib/zipkin-tracer/faraday/zipkin-tracer.rb | 16 +- lib/zipkin-tracer/trace.rb | 2 +- lib/zipkin-tracer/version.rb | 2 +- lib/zipkin-tracer/zipkin_b3_header_helper.rb | 30 ++++ .../zipkin_b3_single_header_format.rb | 14 +- spec/lib/excon/zipkin-tracer_spec.rb | 3 +- spec/lib/middleware_shared_examples.rb | 98 ++++++++---- spec/lib/zipkin_b3_header_helper_spec.rb | 71 +++++++++ .../zipkin_b3_single_header_format_spec.rb | 143 ++++++++++++------ 14 files changed, 310 insertions(+), 104 deletions(-) create mode 100644 lib/zipkin-tracer/zipkin_b3_header_helper.rb create mode 100644 spec/lib/zipkin_b3_header_helper_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 45bfc08..f8b0388 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 0.41.0 +* Add support for writing B3 single header. +* Omit ParentSpanId header for root spans. + +# 0.40.1 +* Fix to pass `async` option to the HTTP sender. + # 0.40.0 * Add support for reading B3 single header. diff --git a/README.md b/README.md index e4d55f6..a90b450 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ use ZipkinTracer::RackHandler, config * `:trace_id_128bit` - When set to true, high 8-bytes will be prepended to trace_id. The upper 4-bytes are epoch seconds and the lower 4-bytes are random. This makes it convertible to Amazon X-Ray trace ID format v1. (See http://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-request-tracing.html) * `:async` - By default senders will flush traces asynchronously. Set to `false` to make that process synchronous. Only supported by the HTTP, RabbitMQ, and SQS senders. * `:logger` - The default logger for Rails apps is `Rails.logger`, else it is `STDOUT`. Use this option to pass a custom logger. +* `:write_b3_single_format` - When set to true, only writes a single b3 header for outbound propagation. #### Sender specific * `:json_api_host` - Hostname with protocol of a zipkin api instance (e.g. `https://zipkin.example.com`) to use the HTTP sender diff --git a/lib/zipkin-tracer.rb b/lib/zipkin-tracer.rb index 49e07d0..4659119 100644 --- a/lib/zipkin-tracer.rb +++ b/lib/zipkin-tracer.rb @@ -6,6 +6,7 @@ require 'zipkin-tracer/trace_generator' require 'zipkin-tracer/trace_wrapper' require 'zipkin-tracer/zipkin_b3_single_header_format' +require 'zipkin-tracer/zipkin_b3_header_helper' begin require 'faraday' diff --git a/lib/zipkin-tracer/config.rb b/lib/zipkin-tracer/config.rb index 1a55ed6..96e2e7d 100644 --- a/lib/zipkin-tracer/config.rb +++ b/lib/zipkin-tracer/config.rb @@ -8,7 +8,7 @@ class Config attr_reader :service_name, :sample_rate, :sampled_as_boolean, :trace_id_128bit, :async, :logger, :json_api_host, :zookeeper, :kafka_producer, :kafka_topic, :sqs_queue_name, :sqs_region, :log_tracing, :annotate_plugin, :filter_plugin, :whitelist_plugin, :rabbit_mq_connection, :rabbit_mq_exchange, - :rabbit_mq_routing_key + :rabbit_mq_routing_key, :write_b3_single_format def initialize(app, config_hash) config = config_hash || Application.config(app) @@ -53,9 +53,13 @@ def initialize(app, config_hash) # This makes it convertible to Amazon X-Ray trace ID format v1. # (See http://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-request-tracing.html) @trace_id_128bit = config[:trace_id_128bit].nil? ? DEFAULTS[:trace_id_128bit] : config[:trace_id_128bit] + # When set to true, only writes a single b3 header for outbound propagation. + @write_b3_single_format = + config[:write_b3_single_format].nil? ? DEFAULTS[:write_b3_single_format] : config[:write_b3_single_format] Trace.sample_rate = @sample_rate Trace.trace_id_128bit = @trace_id_128bit + Trace.write_b3_single_format = @write_b3_single_format Trace.default_endpoint = Trace::Endpoint.local_endpoint( domain_service_name(@service_name) @@ -91,7 +95,8 @@ def domain_service_name(default_name) DEFAULTS = { sample_rate: 0.1, sampled_as_boolean: true, - trace_id_128bit: false + trace_id_128bit: false, + write_b3_single_format: false } def present?(str) diff --git a/lib/zipkin-tracer/excon/zipkin-tracer.rb b/lib/zipkin-tracer/excon/zipkin-tracer.rb index d0e1eb9..9aed4c1 100644 --- a/lib/zipkin-tracer/excon/zipkin-tracer.rb +++ b/lib/zipkin-tracer/excon/zipkin-tracer.rb @@ -3,6 +3,8 @@ module ZipkinTracer class ExconHandler < Excon::Middleware::Base + include B3HeaderHelper + def initialize(_) super end @@ -15,10 +17,7 @@ def request_call(datum) trace_id = TraceGenerator.new.next_trace_id TraceContainer.with_trace_id(trace_id) do - b3_headers.each do |method, header| - datum[:headers][header] = trace_id.send(method).to_s - end - + set_b3_header(datum[:headers], trace_id) trace!(datum, trace_id) if Trace.tracer && trace_id.sampled? end @@ -36,16 +35,6 @@ def response_call(datum) private - def b3_headers - { - trace_id: 'X-B3-TraceId', - parent_id: 'X-B3-ParentSpanId', - span_id: 'X-B3-SpanId', - sampled: 'X-B3-Sampled', - flags: 'X-B3-Flags' - } - end - def remote_endpoint(url, service_name) Trace::Endpoint.remote_endpoint(url, service_name) # The endpoint we are calling. end diff --git a/lib/zipkin-tracer/faraday/zipkin-tracer.rb b/lib/zipkin-tracer/faraday/zipkin-tracer.rb index 96469ad..e85cd05 100644 --- a/lib/zipkin-tracer/faraday/zipkin-tracer.rb +++ b/lib/zipkin-tracer/faraday/zipkin-tracer.rb @@ -4,6 +4,8 @@ module ZipkinTracer # Faraday middleware. It will add CR/CS annotations to outgoing connections done by Faraday class FaradayHandler < ::Faraday::Middleware + include B3HeaderHelper + def initialize(app, service_name = nil) @app = app @service_name = service_name @@ -12,9 +14,7 @@ def initialize(app, service_name = nil) def call(env) trace_id = TraceGenerator.new.next_trace_id TraceContainer.with_trace_id(trace_id) do - b3_headers.each do |method, header| - env[:request_headers][header] = trace_id.send(method).to_s - end + set_b3_header(env[:request_headers], trace_id) if Trace.tracer && trace_id.sampled? trace!(env, trace_id) else @@ -25,16 +25,6 @@ def call(env) private - def b3_headers - { - trace_id: 'X-B3-TraceId', - parent_id: 'X-B3-ParentSpanId', - span_id: 'X-B3-SpanId', - sampled: 'X-B3-Sampled', - flags: 'X-B3-Flags' - } - end - def trace!(env, trace_id) response = nil # handle either a URI object (passed by Faraday v0.8.x in testing), or something string-izable diff --git a/lib/zipkin-tracer/trace.rb b/lib/zipkin-tracer/trace.rb index cffbb67..95a869d 100644 --- a/lib/zipkin-tracer/trace.rb +++ b/lib/zipkin-tracer/trace.rb @@ -8,7 +8,7 @@ module Trace # Most of these are set by the config class and then used around. # TODO: Move this out of the Trace module , take out that extend self and be happier extend self - attr_accessor :trace_id_128bit + attr_accessor :trace_id_128bit, :write_b3_single_format # This method is deprecated, please use TraceGenerator.current # Note that this method will always return a trace, it will diff --git a/lib/zipkin-tracer/version.rb b/lib/zipkin-tracer/version.rb index 4fabb3e..04af288 100644 --- a/lib/zipkin-tracer/version.rb +++ b/lib/zipkin-tracer/version.rb @@ -1,3 +1,3 @@ module ZipkinTracer - VERSION = '0.40.1'.freeze + VERSION = '0.41.0'.freeze end diff --git a/lib/zipkin-tracer/zipkin_b3_header_helper.rb b/lib/zipkin-tracer/zipkin_b3_header_helper.rb new file mode 100644 index 0000000..2f2855f --- /dev/null +++ b/lib/zipkin-tracer/zipkin_b3_header_helper.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module ZipkinTracer + module B3HeaderHelper + private + + B3_SINGLE_HEADER = 'b3' + + def set_b3_header(headers, trace_id) + if Trace.write_b3_single_format + headers[B3_SINGLE_HEADER] = B3SingleHeaderFormat.create_header(trace_id) + else + b3_headers.each do |method, header| + header_value = trace_id.send(method).to_s + headers[header] = header_value unless header_value.empty? + end + end + end + + def b3_headers + { + trace_id: 'X-B3-TraceId', + parent_id: 'X-B3-ParentSpanId', + span_id: 'X-B3-SpanId', + sampled: 'X-B3-Sampled', + flags: 'X-B3-Flags' + } + end + end +end diff --git a/lib/zipkin-tracer/zipkin_b3_single_header_format.rb b/lib/zipkin-tracer/zipkin_b3_single_header_format.rb index 499f707..811c540 100644 --- a/lib/zipkin-tracer/zipkin_b3_single_header_format.rb +++ b/lib/zipkin-tracer/zipkin_b3_single_header_format.rb @@ -5,6 +5,10 @@ module ZipkinTracer # b3: {x-b3-traceid}-{x-b3-spanid}-{if x-b3-flags 'd' else x-b3-sampled}-{x-b3-parentspanid} # For details, see: https://github.com/openzipkin/b3-propagation class B3SingleHeaderFormat + SAMPLED = '1' + NOT_SAMPLED = '0' + DEBUG = 'd' + def self.parse_from_header(b3_single_header) if b3_single_header.size == 1 flag = b3_single_header @@ -16,13 +20,19 @@ def self.parse_from_header(b3_single_header) def self.parse_sampled(flag) case flag - when '1', '0' + when SAMPLED, NOT_SAMPLED flag end end def self.parse_flags(flag) - flag == 'd' ? Trace::Flags::DEBUG : Trace::Flags::EMPTY + flag == DEBUG ? Trace::Flags::DEBUG : Trace::Flags::EMPTY + end + + def self.create_header(trace_id) + flag = trace_id.debug? ? DEBUG : (trace_id.sampled? ? SAMPLED : NOT_SAMPLED) + parent_id_with_hyphen = "-#{trace_id.parent_id}" unless trace_id.parent_id.nil? + "#{trace_id.trace_id}-#{trace_id.span_id}-#{flag}#{parent_id_with_hyphen}" end end end diff --git a/spec/lib/excon/zipkin-tracer_spec.rb b/spec/lib/excon/zipkin-tracer_spec.rb index e525205..beda294 100644 --- a/spec/lib/excon/zipkin-tracer_spec.rb +++ b/spec/lib/excon/zipkin-tracer_spec.rb @@ -26,7 +26,8 @@ def process(body, url, headers = {}) 'X-B3-ParentSpanId' => req.headers['X-B3-Parentspanid'], 'X-B3-SpanId' => req.headers['X-B3-Spanid'], 'X-B3-Sampled' => req.headers['X-B3-Sampled'], - 'X-B3-Flags' => req.headers['X-B3-Flags'] + 'X-B3-Flags' => req.headers['X-B3-Flags'], + 'b3' => req.headers['B3'] } }).to have_been_made diff --git a/spec/lib/middleware_shared_examples.rb b/spec/lib/middleware_shared_examples.rb index e684912..7b86bc2 100644 --- a/spec/lib/middleware_shared_examples.rb +++ b/spec/lib/middleware_shared_examples.rb @@ -42,6 +42,8 @@ let(:raw_url) { "https://#{hostname}#{url_path}" } let(:tracer) { Trace.tracer } let(:trace_id) { ::Trace::TraceId.new(1, 2, 3, true, ::Trace::Flags::DEBUG) } + let(:write_b3_single_format) { false } + before { allow(Trace).to receive(:write_b3_single_format).and_return(write_b3_single_format) } # helper to check host component of annotation def expect_host(host, host_ip, service_name) @@ -91,18 +93,33 @@ def expect_tracing end end - it 'sets the X-B3 request headers with a new spanID' do - request_headers = nil - ZipkinTracer::TraceContainer.with_trace_id(trace_id) do - request_headers = process('', url) + context 'write_b3_single_format is false' do + it 'sets the X-B3 request headers with a new spanID' do + request_headers = nil + ZipkinTracer::TraceContainer.with_trace_id(trace_id) do + request_headers = process('', url) + end + + expect(request_headers['X-B3-TraceId']).to eq('0000000000000001') + expect(request_headers['X-B3-ParentSpanId']).to eq('0000000000000003') + expect(request_headers['X-B3-SpanId']).not_to eq('0000000000000002') + expect(request_headers['X-B3-SpanId']).to match(HEX_REGEX) + expect(request_headers['X-B3-Sampled']).to eq('true') + expect(request_headers['X-B3-Flags']).to eq('1') end + end + + context 'write_b3_single_format is true' do + let(:write_b3_single_format) { true } + + it 'sets the B3 single request header with a new spanID' do + request_headers = nil + ZipkinTracer::TraceContainer.with_trace_id(trace_id) do + request_headers = process('', url) + end - expect(request_headers['X-B3-TraceId']).to eq('0000000000000001') - expect(request_headers['X-B3-ParentSpanId']).to eq('0000000000000003') - expect(request_headers['X-B3-SpanId']).not_to eq('0000000000000003') - expect(request_headers['X-B3-SpanId']).to match(HEX_REGEX) - expect(request_headers['X-B3-Sampled']).to eq('true') - expect(request_headers['X-B3-Flags']).to eq('1') + expect(request_headers['b3']).to match(/\A0000000000000001-\h{16}-d-0000000000000003\z/) + end end it 'the original spanID is restored after the calling the middleware' do @@ -122,32 +139,59 @@ def expect_tracing end end - it 'generates a new ID, and sets the X-B3 request headers' do - request_headers = process('', url) + context 'write_b3_single_format is false' do + it 'generates a new ID, and sets the X-B3 request headers' do + request_headers = process('', url) - expect(request_headers['X-B3-TraceId']).to match(HEX_REGEX) - expect(request_headers['X-B3-ParentSpanId']).to match(HEX_REGEX) - expect(request_headers['X-B3-SpanId']).to match(HEX_REGEX) - expect(request_headers['X-B3-Sampled']).to match(/(true|false)/) - expect(request_headers['X-B3-Flags']).to match(/(1|0)/) + expect(request_headers['X-B3-TraceId']).to match(HEX_REGEX) + expect(request_headers['X-B3-ParentSpanId']).to match(HEX_REGEX) + expect(request_headers['X-B3-SpanId']).to match(HEX_REGEX) + expect(request_headers['X-B3-Sampled']).to match(/(true|false)/) + expect(request_headers['X-B3-Flags']).to match(/(1|0)/) + end + end + + context 'write_b3_single_format is true' do + let(:write_b3_single_format) { true } + + it 'generates a new ID, and sets the B3 single request header' do + request_headers = process('', url) + + expect(request_headers['b3']).to match(/\A\h{16}-\h{16}-[01d]-\h{16}\z/) + end end end context 'Trace has not been sampled' do let(:trace_id) { ::Trace::TraceId.new(1, 2, 3, false, 0) } - it 'sets the X-B3 request headers with a new spanID' do - request_headers = nil - ZipkinTracer::TraceContainer.with_trace_id(trace_id) do - request_headers = process('', url) + context 'write_b3_single_format is false' do + it 'sets the X-B3 request headers with a new spanID' do + request_headers = nil + ZipkinTracer::TraceContainer.with_trace_id(trace_id) do + request_headers = process('', url) + end + + expect(request_headers['X-B3-TraceId']).to eq('0000000000000001') + expect(request_headers['X-B3-ParentSpanId']).to eq('0000000000000003') + expect(request_headers['X-B3-SpanId']).not_to eq('0000000000000003') + expect(request_headers['X-B3-SpanId']).to match(HEX_REGEX) + expect(request_headers['X-B3-Sampled']).to eq('false') + expect(request_headers['X-B3-Flags']).to eq('0') end + end + + context 'write_b3_single_format is true' do + let(:write_b3_single_format) { true } - expect(request_headers['X-B3-TraceId']).to eq('0000000000000001') - expect(request_headers['X-B3-ParentSpanId']).to eq('0000000000000003') - expect(request_headers['X-B3-SpanId']).not_to eq('0000000000000003') - expect(request_headers['X-B3-SpanId']).to match(HEX_REGEX) - expect(request_headers['X-B3-Sampled']).to eq('false') - expect(request_headers['X-B3-Flags']).to eq('0') + it 'sets the B3 single request header with a new spanID' do + request_headers = nil + ZipkinTracer::TraceContainer.with_trace_id(trace_id) do + request_headers = process('', url) + end + + expect(request_headers['b3']).to match(/\A0000000000000001-\h{16}-0-0000000000000003\z/) + end end it 'the original spanID is restored after the calling the middleware' do diff --git a/spec/lib/zipkin_b3_header_helper_spec.rb b/spec/lib/zipkin_b3_header_helper_spec.rb new file mode 100644 index 0000000..bd86c65 --- /dev/null +++ b/spec/lib/zipkin_b3_header_helper_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper' + +describe ZipkinTracer::B3HeaderHelper do + class MockHandler + include ZipkinTracer::B3HeaderHelper + + def call(headers, trace_id) + set_b3_header(headers, trace_id) + end + end + + let(:parent_id) { 2 } + let(:trace_id) { ::Trace::TraceId.new(1, parent_id, 3, true, ::Trace::Flags::DEBUG) } + let(:write_b3_single_format) { false } + before { allow(Trace).to receive(:write_b3_single_format).and_return(write_b3_single_format) } + + context 'child span' do + context 'write_b3_single_format is false' do + it 'has ParentSpanId header' do + request_headers = {} + MockHandler.new.call(request_headers, trace_id) + + expect(request_headers['X-B3-TraceId']).to eq('0000000000000001') + expect(request_headers['X-B3-ParentSpanId']).to eq('0000000000000002') + expect(request_headers['X-B3-SpanId']).to eq('0000000000000003') + expect(request_headers['X-B3-Sampled']).to eq('true') + expect(request_headers['X-B3-Flags']).to eq('1') + end + end + + context 'write_b3_single_format is true' do + let(:write_b3_single_format) { true } + + it 'has parent id part' do + request_headers = {} + MockHandler.new.call(request_headers, trace_id) + + expect(request_headers['b3']).to eq('0000000000000001-0000000000000003-d-0000000000000002') + end + end + end + + context 'root span' do + let(:parent_id) { nil } + + context 'write_b3_single_format is false' do + it 'omits ParentSpanId header' do + request_headers = {} + MockHandler.new.call(request_headers, trace_id) + + expect(request_headers).not_to have_key('X-B3-ParentSpanId') + + expect(request_headers['X-B3-TraceId']).to eq('0000000000000001') + expect(request_headers['X-B3-SpanId']).to eq('0000000000000003') + expect(request_headers['X-B3-Sampled']).to eq('true') + expect(request_headers['X-B3-Flags']).to eq('1') + end + end + + context 'write_b3_single_format is true' do + let(:write_b3_single_format) { true } + + it 'does not have parent id part' do + request_headers = {} + MockHandler.new.call(request_headers, trace_id) + + expect(request_headers['b3']).to eq('0000000000000001-0000000000000003-d') + end + end + end +end diff --git a/spec/lib/zipkin_b3_single_header_format_spec.rb b/spec/lib/zipkin_b3_single_header_format_spec.rb index 8a08ae6..d94de83 100644 --- a/spec/lib/zipkin_b3_single_header_format_spec.rb +++ b/spec/lib/zipkin_b3_single_header_format_spec.rb @@ -1,73 +1,130 @@ require 'spec_helper' describe ZipkinTracer::B3SingleHeaderFormat do - let(:b3_single_header_format) { described_class.parse_from_header(b3_single_header) } + describe 'parse_from_header' do + let(:b3_single_header_format) { described_class.parse_from_header(b3_single_header) } - context 'child span' do - let(:b3_single_header) { '80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1-05e3ac9a4f6e3b90' } + context 'child span' do + let(:b3_single_header) { '80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1-05e3ac9a4f6e3b90' } - it 'has all fields' do - expect(b3_single_header_format.to_a).to eq( - ['80f198ee56343ba864fe8b2a57d3eff7', 'e457b5a2e4d86bd1', '05e3ac9a4f6e3b90', '1', 0] - ) + it 'has all fields' do + expect(b3_single_header_format.to_a).to eq( + ['80f198ee56343ba864fe8b2a57d3eff7', 'e457b5a2e4d86bd1', '05e3ac9a4f6e3b90', '1', 0] + ) + end end - end - context 'sampled root span' do - let(:b3_single_header) { '80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1' } + context 'sampled root span' do + let(:b3_single_header) { '80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-1' } - it 'does not have parent_span_id' do - expect(b3_single_header_format.to_a).to eq(['80f198ee56343ba864fe8b2a57d3eff7', 'e457b5a2e4d86bd1', nil, '1', 0]) + it 'does not have parent_span_id' do + expect(b3_single_header_format.to_a).to eq(['80f198ee56343ba864fe8b2a57d3eff7', 'e457b5a2e4d86bd1', nil, '1', 0]) + end end - end - context 'not yet sampled root span' do - let(:b3_single_header) { '80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1' } + context 'not yet sampled root span' do + let(:b3_single_header) { '80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1' } - it 'does not have parent_span_id and sampled' do - expect(b3_single_header_format.to_a).to eq(['80f198ee56343ba864fe8b2a57d3eff7', 'e457b5a2e4d86bd1', nil, nil, 0]) + it 'does not have parent_span_id and sampled' do + expect(b3_single_header_format.to_a).to eq(['80f198ee56343ba864fe8b2a57d3eff7', 'e457b5a2e4d86bd1', nil, nil, 0]) + end end - end - context 'debug RPC child span' do - let(:b3_single_header) { '80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-d-05e3ac9a4f6e3b90' } + context 'debug RPC child span' do + let(:b3_single_header) { '80f198ee56343ba864fe8b2a57d3eff7-e457b5a2e4d86bd1-d-05e3ac9a4f6e3b90' } - it 'has debug flag' do - expect(b3_single_header_format.to_a).to eq( - ['80f198ee56343ba864fe8b2a57d3eff7', 'e457b5a2e4d86bd1', '05e3ac9a4f6e3b90', nil, 1] - ) + it 'has debug flag' do + expect(b3_single_header_format.to_a).to eq( + ['80f198ee56343ba864fe8b2a57d3eff7', 'e457b5a2e4d86bd1', '05e3ac9a4f6e3b90', nil, 1] + ) + end end - end - context 'do not sample flag only' do - let(:b3_single_header) { '0' } + context 'do not sample flag only' do + let(:b3_single_header) { '0' } - it 'has do not sample flag only' do - expect(b3_single_header_format.to_a).to eq([nil, nil, nil, '0', 0]) + it 'has do not sample flag only' do + expect(b3_single_header_format.to_a).to eq([nil, nil, nil, '0', 0]) + end end - end - context 'sampled flag only' do - let(:b3_single_header) { '1' } + context 'sampled flag only' do + let(:b3_single_header) { '1' } - it 'has sampled flag only' do - expect(b3_single_header_format.to_a).to eq([nil, nil, nil, '1', 0]) + it 'has sampled flag only' do + expect(b3_single_header_format.to_a).to eq([nil, nil, nil, '1', 0]) + end end - end - context 'debug flag only' do - let(:b3_single_header) { 'd' } + context 'debug flag only' do + let(:b3_single_header) { 'd' } - it 'has debug flag only' do - expect(b3_single_header_format.to_a).to eq([nil, nil, nil, nil, 1]) + it 'has debug flag only' do + expect(b3_single_header_format.to_a).to eq([nil, nil, nil, nil, 1]) + end + end + + context 'unknown flag only' do + let(:b3_single_header) { 'u' } + + it 'has nothing' do + expect(b3_single_header_format.to_a).to eq([nil, nil, nil, nil, 0]) + end end end - context 'unknown flag only' do - let(:b3_single_header) { 'u' } + describe 'create_header' do + let(:sampled) { '0' } + let(:flags) { Trace::Flags::EMPTY } + let(:trace_id) { ::Trace::TraceId.new(1, parent_id, 3, sampled, flags) } + let(:b3_single_header) { described_class.create_header(trace_id) } + + context 'child span' do + let(:parent_id) { 2 } + + it 'has all fields' do + expect(b3_single_header).to eq('0000000000000001-0000000000000003-0-0000000000000002') + end + + context 'sampled child span' do + let(:sampled) { '1' } + + it 'has sampled flag' do + expect(b3_single_header).to eq('0000000000000001-0000000000000003-1-0000000000000002') + end + end + + context 'debug child span' do + let(:flags) { Trace::Flags::DEBUG } + + it 'has debug flag' do + expect(b3_single_header).to eq('0000000000000001-0000000000000003-d-0000000000000002') + end + end + end + + context 'root span' do + let(:parent_id) { nil } + + it 'does not have parent id part' do + expect(b3_single_header).to eq('0000000000000001-0000000000000003-0') + end + + context 'sampled root span' do + let(:sampled) { '1' } + + it 'has sampled flag' do + expect(b3_single_header).to eq('0000000000000001-0000000000000003-1') + end + end + + context 'debug root span' do + let(:flags) { Trace::Flags::DEBUG } - it 'has nothing' do - expect(b3_single_header_format.to_a).to eq([nil, nil, nil, nil, 0]) + it 'has debug flag' do + expect(b3_single_header).to eq('0000000000000001-0000000000000003-d') + end + end end end end