# frozen_string_literal: true require 'elastic_apm/trace_context/tracestate' require 'elastic_apm/trace_context/traceparent' module ElasticAPM # @api private class TraceContext extend Forwardable class InvalidTraceparentHeader < StandardError; end def initialize( traceparent: nil, tracestate: nil, **legacy_traceparent_attrs ) @traceparent = traceparent || Traceparent.new(**legacy_traceparent_attrs) @tracestate = tracestate end attr_accessor :traceparent, :tracestate def_delegators :traceparent, :version, :trace_id, :id, :parent_id, :ensure_parent_id, :recorded? class << self # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity def parse(legacy_header = nil, env: nil, metadata: nil) unless legacy_header || env || metadata raise ArgumentError, 'TraceContext expects env:, metadata: ' \ 'or single argument header string' end if legacy_header legacy_parse_from_header(legacy_header) elsif env trace_context_from_env(env) elsif metadata trace_context_from_metadata(metadata) end end # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity private def trace_context_from_env(env) return unless ( header = env['HTTP_ELASTIC_APM_TRACEPARENT'] || env['HTTP_TRACEPARENT'] ) parent = TraceContext::Traceparent.parse(header) state = if (header = env['HTTP_TRACESTATE']) TraceContext::Tracestate.parse(header) end new(traceparent: parent, tracestate: state) end def trace_context_from_metadata(metadata) return unless (header = metadata['elastic-apm-traceparent'] || metadata['traceparent']) parent = TraceContext::Traceparent.parse(header) state = if (header = metadata['tracestate']) TraceContext::Tracestate.parse(header) end new(traceparent: parent, tracestate: state) end def legacy_parse_from_header(header) parent = Traceparent.parse(header) new(traceparent: parent) end end def child dup.tap do |tc| tc.traceparent = tc.traceparent.child end end def apply_headers yield 'Traceparent', traceparent.to_header if tracestate yield 'Tracestate', tracestate.to_header end return unless ElasticAPM.agent.config.use_elastic_traceparent_header yield 'Elastic-Apm-Traceparent', traceparent.to_header end end end