lib/opentelemetry/sdk/trace/samplers/consistent_probability_tracestate.rb in opentelemetry-sdk-experimental-0.1.0 vs lib/opentelemetry/sdk/trace/samplers/consistent_probability_tracestate.rb in opentelemetry-sdk-experimental-0.1.1

- old
+ new

@@ -15,10 +15,35 @@ module ConsistentProbabilityTraceState DECIMAL = /\A\d+\z/.freeze MAX_LIST_LENGTH = 256 # Defined by https://www.w3.org/TR/trace-context/ private_constant(:DECIMAL, :MAX_LIST_LENGTH) + private + + # sanitized_tracestate returns an OpenTelemetry Tracestate object with the + # tracestate sanitized according to the Context invariants defined in the + # tracestate probability sampling spec. + # + # @param span_context [OpenTelemetry::Trace::SpanContext] the parent span context + # @return [OpenTelemetry::Trace::Tracestate] the sanitized tracestate + def sanitized_tracestate(span_context) + sampled = span_context.trace_flags.sampled? + tracestate = span_context.tracestate + parse_ot_vendor_tag(tracestate) do |p, r, rest| + if !r.nil? && r > 62 + p = r = nil + elsif !p.nil? && p > 63 + p = nil + elsif !p.nil? && !r.nil? && !invariant(p, r, sampled) + p = nil + else + return tracestate + end + update_tracestate(tracestate, p, r, rest) + end + end + # parse_ot_vendor_tag parses the 'ot' vendor tag of the tracestate. # It yields the parsed probability fields and the remaining tracestate. # It returns the result of the block. def parse_ot_vendor_tag(tracestate) return yield(nil, nil, nil) if tracestate.empty? @@ -42,19 +67,26 @@ rest = nil if rest.empty? yield(p, r, rest) end def update_tracestate(tracestate, p, r, rest) # rubocop:disable Naming/UncommunicativeMethodParamName - # This could be more efficient and allocate less, however it *should* only be used for root spans and sanitizing invalid tracestate - # in the most common configuration of parent_consistent_probability_based(root: consistent_probability_based(p)). - ps = "p:#{p}" unless p.nil? - rs = "r:#{r}" unless r.nil? - ot = [ps, rs, rest].compact.join(';') - if ot.empty? + if p.nil? && r.nil? && rest.nil? tracestate.delete('ot') + elsif p.nil? && r.nil? + tracestate.set_value('ot', rest) + elsif p.nil? && rest.nil? + tracestate.set_value('ot', "r:#{r}") + elsif r.nil? && rest.nil? + tracestate.set_value('ot', "p:#{p}") + elsif p.nil? + tracestate.set_value('ot', "r:#{r};#{rest}") + elsif r.nil? + tracestate.set_value('ot', "p:#{p};#{rest}") + elsif rest.nil? + tracestate.set_value('ot', "p:#{p};r:#{r}") else - tracestate.set_value('ot', ot) + tracestate.set_value('ot', "p:#{p};r:#{r};#{rest}") end end def new_tracestate(p: nil, r: nil) # rubocop:disable Naming/UncommunicativeMethodParamName if p.nil? && r.nil? @@ -72,9 +104,15 @@ ((p <= r) == sampled) || (sampled && (p == 63)) end def decimal(str) str.to_i if !str.nil? && DECIMAL.match?(str) + end + + def generate_r(trace_id) + x = trace_id[8, 8].unpack1('Q>') | 0x3 + clz = 64 - x.bit_length + clz end end end end end