Send the request's User-Agent on backend Snowplow events
What does this MR do and why?
Server-side Snowplow events are tracked with a bare tracker Subject, so every event fired inside a web or API request reaches the collector with the Ruby tracker's own user agent. Frontend events carry the browser's User-Agent in the same field (useragent), so only requests made through the API by other clients (the GitLab mobile app, glab, editor extensions, scripts) are indistinguishable from each other and from the Web UI in the warehouse.
This MR:
- captures the request's User-Agent in
Gitlab::RequestContext, next to the client IP the Rack middleware already records; - passes a per-event
SnowplowTracker::Subjectcarrying it fromGitlab::Tracking::Destinations::Snowplow#event, the single path bothGitlab::Tracking.eventand Internal Events take. The tracker (snowplow-tracker0.8.0) merges an event-level subject over its default one, so the request's User-Agent becomes the event'sua(raw) /useragent(processed) field, exactly as browser-emitted events populate it.
Outside a request (Sidekiq) there is no user agent, so the tracker's default subject stands and worker-tracked events are unchanged. No schema or context changes: useragent is a standard Snowplow atomic field that frontend events already fill, and the UA-parser enrichment applies to it as it does for browsers.
Motivation: the GitLab mobile app now sends User-Agent: GitLabMobile/<version> (<OS> <OS version>; build <n>) on every request. api_json.log records it (ua), but the Snowplow events those requests trigger did not, so the app's usage cannot be split out per version and OS in Snowflake. With this change, any API client that identifies itself gets the same attribution the browser gets.
Self-managed and Dedicated instances on 18.0+ forward structured events; after this change those include the API client's User-Agent in the same field they already forward for browser events.
References
- Internal event tracking: https://docs.gitlab.com/development/internal_analytics/internal_event_instrumentation/
- Snowplow Ruby tracker event-level subject: https://snowplow.github.io/snowplow-ruby-tracker/SnowplowTracker/Tracker.html#track_struct_event-instance_method
How to set up and validate locally
- Run GDK with Snowplow Micro (
gdk start snowplow-micro) or the Internal Events monitor:bundle exec rails runner scripts/internal_events/monitor.rb g_project_management_users_creating_issues - Trigger a server-side event from a request with a distinctive User-Agent, for example:
curl -H "PRIVATE-TOKEN: $TOKEN" -H "User-Agent: GitLabMobile/1.2.0 (iOS 26.0.1; build 45)" \ -X POST "http://gdk.test:3000/api/v4/projects/<id>/issues?title=UA+test" - In Snowplow Micro's UI the event's
useragentisGitLabMobile/1.2.0 (iOS 26.0.1; build 45); before this change it is the Ruby tracker'sruby-trackerUA. - Trigger an event from a Sidekiq job (for example merge a merge request) and confirm its
useragentis unchanged.
Specs:
bin/rspec spec/lib/gitlab/request_context_spec.rb spec/lib/gitlab/middleware/request_context_spec.rb spec/lib/gitlab/tracking/destinations/snowplow_spec.rbMR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.