Flare by Spatie
    • Error Tracking
    • Performance Monitoring
    • Logs Coming soon
  • Pricing
  • Docs
  • Insights
  • Changelog
  • Back to Flare ⌘↵ Shortcut: Command or Control Enter
  • Sign in
  • Try Flare for free
  • Error Tracking
  • Performance Monitoring
  • Logs Coming soon
  • Pricing
  • Docs
  • Insights
  • Changelog
    • Back to Flare ⌘↵ Shortcut: Command or Control Enter
    • Try Flare for free
    • Sign in
Flare Flare Laravel Laravel PHP PHP JavaScript JavaScript React React Vue Vue Protocol Protocol
  • General
  • Installation
  • Integrating into a framework
  • Attribute providers
  • Application lifecycle
  • Censoring collected data
  • Ignoring collected data
  • Flare daemon
  • Errors
  • Introduction
  • Customise error report
  • Customising error grouping
  • Linking to errors
  • Logs
  • Introduction
  • Levels
  • With errors
  • Performance
  • Introduction
  • Sampling
  • Limits
  • Modify spans and events
  • Data Collection
  • Application info
  • Cache events
  • Console commands
  • Custom context
  • Database transactions
  • Dumps
  • Errors when tracing
  • Exception context
  • External http requests
  • Filesystem operations
  • Git information
  • Glows
  • Identifying users
  • Jobs and queues
  • Queries
  • Redis commands
  • Requests
  • Routing
  • Server info
  • Spans
  • Stacktrace arguments
  • Views
  • Older Packages
  • Flare Client PHP V2
  • Flare Client PHP V1

Sampling

Sending every trace your application produces gets expensive fast and rarely tells you anything new. Sampling lets you keep a representative slice of traffic and drop the rest. When a trace is dropped, no spans are collected or sent for it.

A sampled trace is not free. Recording spans, capturing query bindings, gathering request and response data, and serialising the trace before it is shipped all add overhead to the request, command, or job that produced it. We work hard to keep this overhead as small as possible, and dropped traces stop almost all of the work before it starts, but on hot paths the difference between a sampled and a dropped trace is still measurable. Picking a sample rate is therefore a trade-off between coverage and runtime cost.

Setting a sample rate

The simplest setup is a single rate that applies to every trace. The default is 10%. Change it with sampleRate():

$config->sampleRate(0.5); // 50% of all traces will be sent to Flare
$config->sampleRate(1.0); // 100%

For full-on or full-off, use the convenience helpers:

$config->alwaysSampleTraces();
$config->neverSampleTraces();

Sampling per kind of work

When you want more control than a single uniform rate, Flare can apply different rates to different kinds of work. Health checks can be sampled rarely, checkout flows aggressively, and noisy queue jobs can follow their own logic. Configure it with sampleTracesDynamic() and a list of rules:

use Spatie\FlareClient\Sampling\SamplingRule;

$config->sampleTracesDynamic(0.1, [
    SamplingRule::forPath('/health-check', 0.0),
    SamplingRule::forPath('/admin/*', 1.0),
    SamplingRule::forRoute('checkout.*', 1.0),
    SamplingRule::forCommand('horizon:work', 0.0),
    SamplingRule::forJob('App\\Jobs\\ProcessPayment', 1.0),
]);

The first argument is the base rate, used when no rule matches. Rules are evaluated in order, and the first one that matches wins.

Rule types

Method Matches against When the entry point type is
SamplingRule::forUrl($pattern, $rate) The full URL (including scheme and host) web
SamplingRule::forPath($pattern, $rate) The URL path web
SamplingRule::forRoute($pattern, $rate) The matched route name or pattern (without HTTP method prefix) web
SamplingRule::forCommand($pattern, $rate) The command name cli
SamplingRule::forJob($pattern, $rate) The job name queue
SamplingRule::using($closure) Anything you want, evaluated after the handler is resolved All
SamplingRule::usingEarly($closure) Anything you want, evaluated before the handler is resolved All

forUrl, forPath, and forJob can run as soon as the trace starts. forRoute, forCommand, and using need the framework to resolve a handler first. See Deferred sampling below.

Pattern syntax

Patterns are literal strings with * as a wildcard. Any other character is matched literally. There is no support for ?, character classes, or full regex.

SamplingRule::forPath('/api/*', 0.1);          // matches /api/users, /api/orders/123
SamplingRule::forJob('App\\Jobs\\*Email', 1.0); // matches SendWelcomeEmail, SendInvoiceEmail
SamplingRule::forCommand('horizon:*', 0.0);    // matches horizon:work, horizon:status

Closure rules

When a pattern is not expressive enough, use a closure:

SamplingRule::using(function (EntryPoint $entryPoint) {
    if ($entryPoint->handlerName === LongRunningReportJob::class) {
        return 1.0;
    }

    return null; // No opinion: fall through to the next rule
});

The closure receives the EntryPoint and returns either a rate (0.0 to 1.0) or null to defer the decision to the next rule.

SamplingRule::using() is evaluated after the framework has resolved the handler. Use SamplingRule::usingEarly() when you need to decide based only on the URL or job name and do not want to wait for routing.

Deferred sampling

Some rules cannot run when the trace starts. A web framework only knows the matched route after routing middleware runs, and a queue worker may only know the job class after it pulls the payload off the queue.

When a rule needs information that has not been resolved yet, the sampler defers its decision. The trace is sampled tentatively (so spans are still collected), and the sampler is re-evaluated as soon as the framework resolves the handler. The recorders trigger that re-evaluation automatically:

  • The routing recorder calls re-evaluation in recordRoutingEnd, so forRoute rules can fire as soon as the route is known.
  • The command recorder re-evaluates in recordStart, once the command class is set.
  • The job recorder re-evaluates in recordStart (subtask mode only), once the job class is set.

If the sampler decides to drop the trace at re-evaluation time, the spans collected so far are discarded.

Custom samplers

You can replace the sampler entirely. Implement the Sampler interface:

use Spatie\FlareClient\EntryPoint\EntryPoint;
use Spatie\FlareClient\Sampling\Sampler;

class AlwaysSampler implements Sampler
{
    public function __construct(protected array $config) {}

    public function shouldSample(EntryPoint $entryPoint): bool
    {
        return true;
    }
}

Register the sampler with sampler(). The optional config array is passed to the constructor:

$config->sampler(AlwaysSampler::class, ['some_option' => true]);

If your sampler should support deferred sampling (so it can change its mind once a handler is resolved), implement DeferrableSampler instead. It adds three methods: isPending() to report whether the sampler is waiting for more information, reevaluate(EntryPoint $entryPoint) to make a final decision, and reset() to clear the pending state. The DynamicSampler documented above is the reference implementation.

Introduction Limits
  • On this page
  • Setting a sample rate
  • Sampling per kind of work
  • Deferred sampling
  • Custom samplers

Catch errors and fix slowdowns with Flare, the full-stack application monitoring platform for Laravel, PHP & JavaScript.

  • Platform
  • Error Tracking
  • Performance Monitoring
  • Pricing
  • Support
  • Resources
  • Insights
  • Newsletter
  • Changelog
  • Documentation
  • Affiliate program
  • uptime status badge Service status
  • Terms of use
  • DPA
  • Privacy & cookie Policy
Made in by
Flare