opentelemetry #distributed-tracing #ruby

利用到分布式跟踪的库

  • [[opentelemetry-instrumentation-http浅析]]
  • [[opentelemetry-instrumentation-rack浅析]]
- [[opentelemetry-instrumentation-active_job浅析]]

配置

可通过环境变量OTEL_PROPAGATORS设置传递格式

搜索该环境变量,可见默认使用tracecontext和baggage方式传递,会组装成Context::Propagation::CompositeTextMapPropagator

# opentelemetry-sdk-1.2.0/lib/opentelemetry/sdk/configurator.rb
def configure_propagation
  propagators = ENV.fetch('OTEL_PROPAGATORS', 'tracecontext,baggage').split(',').uniq.collect do |propagator|
    case propagator
    when 'tracecontext' then OpenTelemetry::Trace::Propagation::TraceContext.text_map_propagator
    when 'baggage' then OpenTelemetry::Baggage::Propagation.text_map_propagator
    when 'b3' then fetch_propagator(propagator, 'OpenTelemetry::Propagator::B3::Single')
    when 'b3multi' then fetch_propagator(propagator, 'OpenTelemetry::Propagator::B3::Multi', 'b3')
    when 'jaeger' then fetch_propagator(propagator, 'OpenTelemetry::Propagator::Jaeger')
    when 'xray' then fetch_propagator(propagator, 'OpenTelemetry::Propagator::XRay')
    when 'ottrace' then fetch_propagator(propagator, 'OpenTelemetry::Propagator::OTTrace')
    when 'none' then NoopTextMapPropagator.new
    else
      OpenTelemetry.logger.warn "The #{propagator} propagator is unknown and cannot be configured"
      NoopTextMapPropagator.new
    end
  end
  OpenTelemetry.propagation = Context::Propagation::CompositeTextMapPropagator.compose_propagators((@propagators || propagators).compact)
end

tracecontext和baggage以外的,需要另外引入gem

# opentelemetry-sdk-1.2.0/lib/opentelemetry/sdk/configurator.rb
def fetch_propagator(name, class_name, gem_suffix = name)
  Kernel.const_get(class_name).text_map_propagator
rescue NameError
  OpenTelemetry.logger.warn "The #{name} propagator cannot be configured - please add opentelemetry-propagator-#{gem_suffix} to your Gemfile"
  nil
end

另外可见优先使用@propagators,而这是可以在SDK.configure中设置的,例如

OpenTelemetry::SDK.configure do |c|
  c.propagators = [
    OpenTelemetry::Trace::Propagation::TraceContext.text_map_propagator,
    OpenTelemetry::Baggage::Propagation.text_map_propagator,
    Sentry::OpenTelemetry::Propagator.new
  ]
end

使用propagator注入traceid和spanid

调用方使用inject方法将context注入到carrier(消息结构的消息头,例如http头,grpc的metadata……)中

接收方从中还原出context

例如OpenTelemetry::Trace:: Propagation::TraceContext::TextMapPropagator,他将traceid和spanid拼接,以TRACEPARENT_KEY标识,塞到carrier中

# opentelemetry-api-1.1.0/lib/opentelemetry/trace/propagation/trace_context/text_map_propagator.rb
def inject(carrier, context: Context.current, setter: Context::Propagation.text_map_setter)
  span_context = Trace.current_span(context).context
  return unless span_context.valid?

  setter.set(carrier, TRACEPARENT_KEY, TraceParent.from_span_context(span_context).to_s)
  setter.set(carrier, TRACESTATE_KEY, span_context.tracestate.to_s) unless span_context.tracestate.empty?
  nil
end

def extract(carrier, context: Context.current, getter: Context::Propagation.text_map_getter)
  trace_parent_value = getter.get(carrier, TRACEPARENT_KEY)
  return context unless trace_parent_value

  tp = TraceParent.from_string(trace_parent_value)
  tracestate = Tracestate.from_string(getter.get(carrier, TRACESTATE_KEY))

  span_context = Trace::SpanContext.new(trace_id: tp.trace_id,
                                        span_id: tp.span_id,
                                        trace_flags: tp.flags,
                                        tracestate: tracestate,
                                        remote: true)
  span = OpenTelemetry::Trace.non_recording_span(span_context)
  OpenTelemetry::Trace.context_with_span(span, parent_context: context)
rescue OpenTelemetry::Error
  context
end

又例如OpenTelemetry::Context::Propagation::CompositeTextMapPropagator,它可以组合多种propagator,使上下游兼容不同的协议

# opentelemetry-api-1.1.0/lib/opentelemetry/context/propagation/composite_text_map_propagator.rb
def inject(carrier, context: Context.current, setter: Context::Propagation.text_map_setter)
  injectors = @injectors || @propagators
  injectors.each do |injector|
    injector.inject(carrier, context: context, setter: setter)
  rescue StandardError => e
    OpenTelemetry.logger.warn "Error in CompositePropagator#inject #{e.message}"
  end
  nil
end

def extract(carrier, context: Context.current, getter: Context::Propagation.text_map_getter)
  extractors = @extractors || @propagators
  extractors.inject(context) do |ctx, extractor|
    extractor.extract(carrier, context: ctx, getter: getter)
  rescue StandardError => e
    OpenTelemetry.logger.warn "Error in CompositePropagator#extract #{e.message}"
    ctx
  end
end

生成trace_id

?

相关模块

如下

 - OpenTelemetry {:kla=>Module}
 ├─ - Baggage {:kla=>Module}
 │  └─ - Propagation {:kla=>Module}
 │     ├─   ContextKeys {:kla=>Module}
 │     └─   TextMapPropagator {:kla=>Class}
 ├─ - Common {:kla=>Module}
 │  └─ - Propagation {:kla=>Module}
 │     ├─   RackEnvGetter {:kla=>Class}
 │     └─   SymbolKeyGetter {:kla=>Class}
 ├─ - Context {:kla=>Class}
 │  └─ - Propagation {:kla=>Module}
 │     ├─   CompositeTextMapPropagator {:kla=>Class}
 │     ├─   NoopTextMapPropagator {:kla=>Class}
 │     ├─   RackEnvGetter {:kla=>Class}
 │     ├─   TextMapGetter {:kla=>Class}
 │     ├─   TextMapPropagator {:kla=>Class}
 │     └─   TextMapSetter {:kla=>Class}
 ├─ - SDK {:kla=>Module}
 │  └─ - Configurator {:kla=>Class}
 │     └─   NoopTextMapPropagator {:kla=>Class}
 └─ - Trace {:kla=>Module}
    └─ - Propagation {:kla=>Module}
       └─ - TraceContext {:kla=>Module}
          ├─   TextMapPropagator {:kla=>Class}
          └─ + TraceParent {:kla=>Class}