Sampling

You are viewing the English version of this page because it has not yet been fully translated. Interested in helping out? See Contributing.

Sampling is a process that restricts the amount of traces that are generated by a system. The Ruby SDK offers several head samplers.

Default behavior

By default, all spans are sampled, and thus, 100% of traces are sampled. If you do not need to manage data volume, don’t bother setting a sampler.

Specifically, the default sampler is a composite of ParentBased and ALWAYS_ON that ensures the root span in a trace is always sampled, and that all child spans respect use their parent’s sampling flag to make a sampling decision. This guarantees that all spans in a trace are sampled by default.

TraceIDRatioBased Sampler

The most common head sampler to use is the TraceIdRatioBased sampler. It deterministically samples a percentage of traces that you pass in as a parameter.

Environment Variables

You can configure a TraceIdRatioBased sampler with environment variables:

export OTEL_TRACES_SAMPLER="traceidratio"
export OTEL_TRACES_SAMPLER_ARG="0.1"

This tells the SDK to sample spans such that only 10% of traces get exported.

Configuration in Code

Although it is possible to configure a TraceIdRatioBased sampler in code, it’s not recommended. Doing so requires you to manually set up a Tracer Provider with all the right configuration options, which is hard to get right compared to just using OpenTelemetry::SDK.configure.

Custom Samplers

You may want to create a custom Sampler that makes a decision based on a span’s name, attribute, or other identifying criteria. Samplers are ideal when the information needed for the sampling decision is available at the start of the span. If the information needed to make the decision is not available until the end of the span, consider using a Span Processor.

Below is an example for a custom Sampler class that excludes all spans with a db.statement value of ;.

This query is common for applications that use Active Record and the PostgreSQL (pg gem) together. Active Record checks for an active database connection every two seconds, which executes a query with just a ; in the statement in pg. This can lead to a lot of unwanted spans.

When the span meets this criteria, it is dropped. When it doesn’t meet this criteria, the sampling decision falls back to a delegate sampler. In this case, the delegate sampler is a TraceIdRatioBased sampler.

class CustomSampler
  def initialize(delegate)
    @delegate = delegate
  end

  def should_sample?(trace_id:, parent_context:, links:, name:, kind:, attributes:)
    if attributes && attributes["db.statement"] == ";"
      OpenTelemetry::SDK::Trace::Samplers::Result.new(
        decision: OpenTelemetry::SDK::Trace::Samplers::Decision::DROP,
        tracestate: OpenTelemetry::Trace.current_span(parent_context).context.tracestate
      )
    else
      @delegate.should_sample?(trace_id: trace_id, parent_context: parent_context,
        links: links, name: name, kind: kind, attributes: attributes)
    end
  end

  def description
    "CustomSampler{#{@delegate.description}}"
  end
end

# Then, after OpenTelemetry::SDK.configure is called, you can assign the sampler
# to the default tracer provider.

OpenTelemetry.tracer_provider.sampler = CustomSampler.new(
  OpenTelemetry::SDK::Trace::Samplers.trace_id_ratio_based(0.5))