Skip to main content

Events

The package fires Laravel events at every interesting point in a hit's life. All of them live in MatomoAnalytics\Events, all are final readonly, and all can be switched off in one place:

'events' => true, // false fires none of them

Switching them off is worth doing only if you fire a very high volume of hits and profiled the dispatcher as a cost. The load simulator does exactly that while measuring.

The events​

EventCarriesFires when
VisitorExcludedstring $reasonThe gate declined a hit.
TrackingQueuedarray $payloadsHits were handed off for delivery.
TrackingSentint $count, int $statusMatomo accepted a delivery.
TrackingFailedThrowable $exceptionA delivery failed permanently, in any mode.
HitsDeadLetteredint $count, int $attemptsA batch was moved to the dead-letter store.
DataSubjectForgottenint $visits, array $deletedA GDPR erasure deleted visits.

VisitorExcluded​

use Illuminate\Support\Facades\Event;
use MatomoAnalytics\Events\VisitorExcluded;

Event::listen(fn (VisitorExcluded $event) => logger()->info("Excluded: {$event->reason}"));

reason is one of the gate's reason strings — disabled, not_configured, environment, dnt, opted_out, bot, prefetch, authenticated, ability, ip, route, gate. See the tracking gate for what each means.

This is the most useful event in practice. Counting the reasons in a local or staging environment answers "why is my traffic not showing up?" without any instrumentation inside the package. See troubleshooting.

TrackingQueued​

Carries the raw hit payloads that were handed off. It fires once per request, in every mode, with the whole request's payloads — the dispatch sits ahead of the branch that chooses between the queued job and the buffer, so the mode does not change how often you hear about it.

It described a per-hit event in batch mode until 0.24.0, and it no longer does. A listener written as a hit counter therefore counts requests: read count($event->payloads) rather than incrementing by one.

Use it to mirror what you track into another system. Note that a queued hit is not yet a delivered hit; TrackingSent is the confirmation.

TrackingSent​

Carries the number of hits delivered and Matomo's HTTP status. It fires from all three delivery paths — the inline send in sync mode, the queued job, and a batch flush — so a listener sees every successful delivery regardless of mode.

TrackingFailed​

Carries the exception. It means "this will not be attempted again", not "an attempt failed" — a send in sync mode that failed, a queued delivery Matomo rejected or that has exhausted its retries, or a batch the flusher has dead-lettered as poison or after batch.max_attempts.

It fires in every delivery mode. A released batch stays silent, because it is attempted again on the next flush.

Individual failed attempts are not events; they go through the throttled alerting instead, which is what keeps a transient timeout from paging anyone.

HitsDeadLettered​

Carries the hit count and the number of delivery attempts made. Listen for it if you want to know the moment tracking data starts accumulating in the dead-letter store, rather than finding out the next time somebody runs matomo:replay --list.

The attempt count distinguishes the two causes: a low number means Matomo rejected the batch permanently, a number at batch.max_attempts means transient failures persisted.

DataSubjectForgotten​

Carries the number of visits erased and the per-area deletion counts. This is the audit signal for "right to be forgotten" requests — see GDPR requests.

It carries counts, not the erased data. It is an audit trail, not a backup.

Registering listeners​

Laravel discovers listeners automatically in a standard application. For an explicit registration:

// in a service provider's boot() method
use Illuminate\Support\Facades\Event;
use MatomoAnalytics\Events\HitsDeadLettered;

Event::listen(function (HitsDeadLettered $event): void {
logger()->warning("Matomo dead-lettered {$event->count} hits after {$event->attempts} attempts.");
});

Keep listeners cheap, or queue them. TrackingQueued and TrackingSent fire on the delivery path; a slow synchronous listener there gives back the latency the package works to avoid.

Telescope and Horizon already see this​

Nothing needs installing or registering for either, because this package uses the framework's own paths rather than going around them.

Telescope records every Matomo hit through its stock watchers: the delivery goes out on Laravel's HTTP client, so the client-request watcher captures the URL and the payload, and the batch/queue modes dispatch an ordinary queued job, which the job watcher records. A package-specific watcher would be a second view of the same rows.

Horizon groups the queued batches under the tag matomo, and under matomo:site-<id> where a site id is configured. That is one tag per configured site on purpose — anything per-batch would mint a new tag on every dispatch, and Horizon indexes tags.

Pulse is the one that would need something from this package: a recorder over TrackingSent/TrackingFailed and a card to draw it. That card is a Livewire component, so it would pull Livewire into a package that deliberately depends on focused illuminate/* components and not on the framework — which is why it is a deliberate open question rather than an oversight. The events above are public, so an application that wants the card today can write a recorder against them in a few lines.