> ## Documentation Index
> Fetch the complete documentation index at: https://docs.monocle.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Job Sampling

> Reduce trace volume for high-throughput jobs while keeping failed executions

Job sampling lets you reduce trace volume for noisy background jobs without disabling job monitoring entirely. It is useful when a small number of jobs run very often or create very large traces.

<Warning>
  Job sampling is currently available only through the `@monocle.sh/adonisjs-agent`.
</Warning>

With the Monocle AdonisJS agent, sampling is configured in `config/monocle.ts` with the `Sample` helper

```typescript title="config/monocle.ts" theme={"theme":"vesper"}
import { defineConfig, Sample } from "@monocle.sh/adonisjs-agent";

export default defineConfig({
  // ...
  sampling(ctx) {
    if (ctx.kind !== "job") return Sample.default();

    if (ctx.name === "SendNewsletterEmail") {
      return Sample.rate(0.01).keepErrors();
    }

    return Sample.default();
  },
});
```

This keeps roughly 1% of successful `SendNewsletterEmail` executions, but still exports failed executions.

## Sampling rules

### Keep default behavior

Use `Sample.default()` when you do not want to override sampling for an execution.

```typescript theme={"theme":"vesper"}
sampling(ctx) {
  return Sample.default();
}
```

### Always keep

Use `Sample.always()` when a specific job should always be exported.

```typescript theme={"theme":"vesper"}
sampling(ctx) {
  if (ctx.kind === "job" && ctx.name === "BillingReport") {
    return Sample.always();
  }

  return Sample.default();
}
```

### Never keep

Use `Sample.never()` for jobs you do not want to export.

```typescript theme={"theme":"vesper"}
sampling(ctx) {
  if (ctx.kind === "job" && ctx.name === "HeartbeatJob") {
    return Sample.never();
  }

  return Sample.default();
}
```

### Sample by rate

Use `Sample.rate(rate)` to keep a percentage of executions. The rate is a number between `0` and `1`.

```typescript theme={"theme":"vesper"}
sampling(ctx) {
  if (ctx.kind === "job" && ctx.name === "SyncContacts") {
    return Sample.rate(0.1); // keep 10%
  }

  return Sample.default();
}
```

### Keep failed executions

Use `.keepErrors()` when successful executions should be sampled, but failed executions should still be exported.

```typescript theme={"theme":"vesper"}
sampling(ctx) {
  if (ctx.kind === "job" && ctx.name === "SendNewsletterEmail") {
    return Sample.rate(0).keepErrors();
  }

  return Sample.default();
}
```

`Sample.rate(0).keepErrors()` drops successful executions and keeps failed executions. This is the most aggressive setting for high-volume jobs where you mainly care about failures.

## Limit buffered spans

When using `keepErrors()`, the agent buffers spans locally until the job finishes so it can decide whether to export the trace. You can cap the number of buffered spans with `maxSpans`.

```typescript theme={"theme":"vesper"}
sampling(ctx) {
  if (ctx.kind === "job" && ctx.name === "SendNewsletterEmail") {
    return Sample.rate(0.01).keepErrors({ maxSpans: 500 });
  }

  return Sample.default();
}
```

If a trace exceeds the limit, Monocle keeps the root span and marks it with truncation attributes:

| Attribute                        | Description                                          |
| -------------------------------- | ---------------------------------------------------- |
| `monocle.sampling.truncated`     | `true` when spans were dropped from the local buffer |
| `monocle.sampling.dropped_spans` | Number of spans dropped from the local buffer        |

## Sampling context

For job executions, the `sampling(ctx)` callback receives:

| Field         | Description                                             |
| ------------- | ------------------------------------------------------- |
| `ctx.kind`    | Always `"job"` for job executions                       |
| `ctx.system`  | Queue system, for example `"bullmq"` or `"boringqueue"` |
| `ctx.name`    | Job name                                                |
| `ctx.queue`   | Queue name, when available                              |
| `ctx.jobId`   | Job ID, when available                                  |
| `ctx.attempt` | Retry attempt, when available                           |

Example:

```typescript theme={"theme":"vesper"}
sampling(ctx) {
  if (ctx.kind !== "job") return Sample.default();

  if (ctx.system === "bullmq" && ctx.queue === "critical") {
    return Sample.always();
  }

  if (ctx.name.includes("Import")) {
    return Sample.rate(0.01).keepErrors();
  }

  return Sample.default();
}
```

## Supported job libraries

### BullMQ

BullMQ sampling is applied before the worker body runs. When a job is dropped with `Sample.never()` or an unsampled rate rule without `keepErrors()`, Monocle suppresses tracing for the worker execution, so child spans are not created.

See the [BullMQ instrumentation guide](/instrumentations/bullmq) for setup and span details.

### @adonisjs/queue

`@adonisjs/queue` job executions are detected from their root job spans. Sampling works with `Sample.rate()`, `Sample.never()`, and `keepErrors()`.

See the [@boringnode/queue instrumentation guide](/instrumentations/boringnode-queue) for span details.

## How keepErrors works

`keepErrors()` is application-side tail sampling. The agent records spans locally for the job execution, buffers them until the root job span ends, and then exports the trace if the execution was sampled or ended with an error.

Because the decision happens after spans are recorded, Monocle configures the default OpenTelemetry head sampling ratio to `1` when `sampling` is enabled. If you provide a custom OpenTelemetry `sampler`, traces dropped by that sampler cannot be recovered by `keepErrors()`.
