# Sampling

LLMS index: [llms.txt](/llms.txt)

---

[Sampling](/docs/concepts/sampling/) is a process that restricts the amount of
traces that are generated by a system. The Ruby SDK offers several
[head samplers](/docs/concepts/sampling#head-sampling).

## 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.

[ParentBased]:
  https://www.rubydoc.info/gems/opentelemetry-sdk/OpenTelemetry/SDK/Trace/Samplers/ParentBased
[ALWAYS_ON]:
  https://www.rubydoc.info/gems/opentelemetry-sdk/OpenTelemetry/SDK/Trace/Samplers

## 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.

[TraceIdRatioBased]:
  https://www.rubydoc.info/gems/opentelemetry-sdk/OpenTelemetry/SDK/Trace/Samplers/TraceIdRatioBased

### Environment Variables

You can configure a `TraceIdRatioBased` sampler with environment variables:

```shell
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][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.

[Sampler]:
  https://www.rubydoc.info/gems/opentelemetry-sdk/OpenTelemetry/SDK/Trace/Samplers
[span-processor]:
  https://www.rubydoc.info/gems/opentelemetry-sdk/OpenTelemetry/SDK/Trace/SpanProcessor

```ruby
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))
```
