Problem/Motivation
The PA module currently only tracks page views via the client-side JS SDK. There are scenarios where the JS SDK never executes — server-side redirects (301/302), API endpoints, cron jobs — and events for these are lost entirely.
The PA Collection API supports server-side HTTP event submission, but there is no reusable service in the module to leverage it.
Proposed resolution
Add a new ServerSideEventSender service that POSTs events to the PA Collection API using the site ID and collect domain already configured in pianoanalytics.settings.
The service:
- POSTs JSON to
https://{collect_domain}/event?s={site}&idclient={visitor_id} with Content-Type: text/plain (per PA Collection API spec)
- Auto-enriches event properties with
device_timestamp_utc and event_collection_platform: api
- Forwards browser context headers (User-Agent, Referer, X-Forwarded-For) so PA can perform device and geo analysis on server-side events
- Provides
resolveVisitorId(Request) to extract the visitor ID from PA cookies (_pcid, pa_vid, atuserid) with fallback to a sha256 hash of the client IP
- Provides
extractBrowserHeaders(Request) to extract the relevant headers from a Symfony request
- Uses a 2-second timeout and catches all exceptions — callers are never broken by PA failures
- Errors are logged to the
pianoanalytics logger channel
New files
pianoanalytics.services.yml — declares the pianoanalytics logger channel and the autowired ServerSideEventSender service
src/ServerSideEventSender.php — the service class
tests/src/Kernel/ServerSideEventSenderTest.php — 6 kernel tests covering request construction, Content-Type, auto-enriched properties, browser header forwarding, missing config guard, HTTP error handling, cookie-based visitor ID resolution, and header extraction
API usage example
$sender = \Drupal::service(ServerSideEventSender::class);
$visitor_id = ServerSideEventSender::resolveVisitorId($request);
$headers = ServerSideEventSender::extractBrowserHeaders($request);
$sender->sendEvent('page.display', [
'page' => '/my-page',
'page_chapter1' => 'section',
], $visitor_id, $headers);
Remaining tasks
Comments
Comment #3
mably commentedComment #5
mably commented