Attribute providers
Recorders use attribute providers to turn framework-specific objects (a Symfony request, a Console input, a queued job) into the attributes that Flare records on a span. Going through a provider instead of accepting framework objects directly lets flare-client-php stay framework-agnostic while still shipping ready-to-use helpers for the common stacks.
You hand a provider to a recorder when you start or end a span, and the recorder calls the provider to get the attributes it needs. For the common cases, recorders expose recordStartFrom* and recordEndFrom* helpers that build the matching provider for you. When you work with a framework or object that Flare does not ship a provider for, you write your own.
The base contract is small. Every provider implements AttributesProvider:
namespace Spatie\FlareClient\Contracts;
interface AttributesProvider
{
/** @return array<string, mixed> */
public function toArray(): array;
}
Each recorder accepts a more specific provider contract that adds methods for the data the recorder needs.
| Contract | Used by | Methods beyond toArray() |
|---|---|---|
RequestAttributesProvider |
RequestRecorder |
url(), path(), method() |
ResponseAttributesProvider |
RequestRecorder (on recordEnd) |
toArray() only |
RouteAttributesProvider |
RoutingRecorder, RequestRecorder |
route() |
CommandAttributesProvider |
CommandRecorder |
command(), commandClass() |
JobAttributesProvider |
JobRecorder, QueueRecorder |
jobName(), jobClass() |
UserAttributesProvider (abstract base class) |
RequestRecorder (on recordEnd) |
id(), fullName(), email(), attributes() |
Built-in providers
flare-client-php ships with implementations for plain PHP and for Symfony's HttpFoundation / Console components. They live under Spatie\FlareClient\AttributesProviders\:
| Provider | Builds attributes from |
|---|---|
PhpRequestAttributesProvider |
PHP superglobals ($_SERVER, $_GET, etc.) |
SymfonyRequestAttributesProvider |
A Symfony HttpFoundation\Request |
PhpResponseAttributesProvider |
A defined status code, body size, and header set |
SymfonyResponseAttributesProvider |
A Symfony HttpFoundation\Response |
PhpRouteAttributesProvider |
A route pattern, method, and handler name |
PhpCommandAttributesProvider |
A command name, class, and arguments array |
SymfonyInputCommandAttributesProvider |
A Symfony Console InputInterface |
PhpJobAttributesProvider |
A job name and class |
UserAttributesProvider (abstract) |
The user object you pass to its constructor |
EmptyUserAttributesProvider |
A no-op for cases where there is no user |
GitAttributesProvider |
The git working copy at applicationPath |
Each recorder also exposes recordStartFrom* and recordEndFrom* helpers that build the matching provider from common framework objects. For example:
$flare->request()->recordStartFromSymfonyRequest($request);
$flare->command()->recordStartFromSymfonyInput(
'my:command',
$input,
MyCommand::class
);
$flare->job()->recordStartFromJob(
'SendWelcomeEmail',
SendWelcomeEmail::class
);
If you do not have one of those framework objects, build a provider yourself and pass it to recordStart() or recordEnd().
Writing a custom attributes provider
Implement the contract that matches the recorder you want to feed. As an example, here is a minimal request provider for a hypothetical framework that exposes the request through a MyHttpRequest object:
use Spatie\FlareClient\Contracts\RequestAttributesProvider;
class MyHttpRequestAttributesProvider implements RequestAttributesProvider
{
public function __construct(protected MyHttpRequest $request)
{
}
public function url(): string
{
return $this->request->fullUrl();
}
public function path(): ?string
{
return $this->request->path();
}
public function method(): string
{
return $this->request->method();
}
public function toArray(): array
{
return [
'url.full' => $this->url(),
'url.path' => $this->path(),
'http.request.method' => $this->method(),
'http.request.headers' => $this->request->headers(),
];
}
}
Pass it to the recorder:
$flare->request()->recordStart(new MyHttpRequestAttributesProvider($request));
The same approach applies to every recorder. Implement the contract, return the attribute keys documented in the protocol events, and the recorder will merge them into the span.
Describing the entry point handler
Some recorders also need to know who the entry point handler is (the controller, command class, or job class). When the same provider can describe both the framework object and the handler, implement the optional EntryPointHandlerProvider interface alongside the main contract:
use Spatie\FlareClient\Contracts\EntryPointHandlerProvider;
use Spatie\FlareClient\Contracts\RouteAttributesProvider;
class MyRouteAttributesProvider implements RouteAttributesProvider, EntryPointHandlerProvider
{
public function __construct(protected MyRoute $route)
{
}
public function route(): ?string
{
return $this->route->pattern();
}
public function toArray(): array
{
return ['http.route' => $this->route()];
}
public function entryPointHandlerIdentifier(): ?string
{
return "{$this->route->method()} {$this->route->pattern()}";
}
public function entryPointHandlerName(): ?string
{
return $this->route->controller();
}
public function entryPointHandlerType(): ?string
{
return 'my_framework_controller';
}
}
The recorders detect the interface and call setHandler() on the entry point automatically. See Entry points for the attribute schema and the recommended handler.type values.