Event tracking is on by default in the PHP SDK (events.enabled, environment variable RANETRACE_EVENTS_ENABLED). Use it to record things that matter to your business from anywhere in your application.
Using Laravel? See Event Tracking instead.
$ranetrace->trackEvent('button_clicked', [
'button_id' => 'header-cta',
'page' => 'homepage',
]);
The full signature:
$ranetrace->trackEvent(
string $name,
array $properties = [],
int|string|null $userId = null,
bool $validate = true,
);
Properties are free-form. They are sanitized for serialization and secret-scrubbed before they leave your host, so a value under a key like api_token is redacted rather than sent.
An event name must be 3 to 50 characters, start with a letter, and contain only lowercase letters, digits and underscores. In other words, snake_case.
An invalid name is a mistake you can still fix, so it is the one thing in the capture path that fails loudly: trackEvent() throws an InvalidArgumentException before any capture work happens. Everything after that point is caught and dropped instead, because monitoring must never be the reason a checkout breaks.
If you have to preserve a name from an external system that breaks the convention, pass validate: false or use customUnsafe(). Reach for it rarely: unconventional names make the events dashboard harder to read for everyone after you.
events() returns the event tracker, which has typed methods for the events most applications record:
$events = $ranetrace->events();
// Sales
$events->sale(
orderId: 'ORDER-456',
totalAmount: 89.97,
products: [
['id' => 'PROD-123', 'name' => 'Widget', 'price' => 29.99, 'quantity' => 3],
],
currency: 'USD',
);
// Cart additions
$events->productAddedToCart(
productId: 'PROD-123',
productName: 'Widget',
price: 29.99,
quantity: 1,
category: 'tools',
);
// Authentication
$events->userRegistered(userId: $user->id);
$events->userLoggedIn(userId: $user->id);
// Page views, recorded server side
$events->pageView(pageName: 'pricing');
// Any other event, name validated
$events->custom('newsletter_signup', ['source' => 'footer']);
// Same, but skipping name validation
$events->customUnsafe('Legacy.Signup');
Every method takes an array $additionalProperties = [] as its last argument for ad-hoc fields. Those are merged over the properties the method builds, so you can override any of them. The one exception is productAddedToCart()'s category, which is applied after the merge: the named argument is the more specific statement of intent, so it wins.
What each method fills in for you:
| Method | Event name | Properties it sets |
|---|---|---|
sale() |
sale |
order_id, total_amount, currency, products, product_count |
productAddedToCart() |
product_added_to_cart |
product_id, product_name, price, quantity, total_value, category |
userRegistered() |
user_registered |
none, your properties are sent as given |
userLoggedIn() |
user_logged_in |
none, your properties are sent as given |
pageView() |
page_view |
page_name |
custom() |
your name | none |
customUnsafe() |
your name, unvalidated | none |
Every event carries the name, the properties, a timestamp, and the URL of the current request. The URL has its sensitive query parameters redacted, and under CLI there is no URL, so the field is empty.
Two identifiers travel with an event, and both are one-way hashes:
user_agent_hash, an HMAC-SHA256 of the raw user agent. Empty when the request carried none, so "no user agent" stays distinguishable from "some user agent we hashed".session_id_hash, an HMAC-SHA256 of the client IP, the first 100 characters of the user agent, and today's date. Because the date is part of the input, this fingerprint rotates every day, and it cannot be joined across sites.The raw IP address is never part of the payload. Neither is an email: the only user field an event carries is the id.
That id comes from the $userId argument when you pass one, and otherwise from the user_resolver you configured (see PHP error tracking). When there is neither, the event is recorded without a user.
The hashes are salted per install. By default the salt is your API key, which every install already has and which never travels in a payload, so fingerprints are non-reversible out of the box.
Set an explicit salt when you want to rotate fingerprints independently of the key, for example after a key rotation that should not break event grouping:
RANETRACE_FINGERPRINT_SALT=a-long-random-string
Changing the salt changes every hash, so past and future events for the same visitor stop lining up. That is the point when you rotate deliberately, and worth knowing before you change it by accident.
| Environment variable | Config key | Default | What it controls |
|---|---|---|---|
RANETRACE_EVENTS_ENABLED |
events.enabled |
true |
Whether events are captured at all. |
RANETRACE_EVENTS_TIMEOUT |
events.timeout |
10 |
Seconds to wait for the API when a batch of events is sent. |
RANETRACE_FINGERPRINT_SALT |
fingerprint_salt |
your API key | Salt for the user agent and session hashes. |
To turn event tracking off:
RANETRACE_EVENTS_ENABLED=false
Captured events reach the API on the next flush. See PHP installation.