Skip to content

ga4 ​

The ga4 plugin reports Google Analytics 4 figures for every account and property one Google login can see. Per property it sends users active right now, users, sessions and page views for today, yesterday and the last 7 days, and 30 days of daily figures. It answers "is traffic still arriving", and it pages when Google stops answering.

Set it up ​

Google Analytics has no service-account path for this plugin. It runs on a user refresh token with the analytics.readonly scope, minted once by hand.

  1. In Google Cloud, create an OAuth client of type Desktop app and download its JSON.

  2. Enable the Google Analytics Admin API and the Google Analytics Data API in that project.

  3. Set the OAuth consent screen to In production, or Internal. A client left in Testing hands out refresh tokens that expire after 7 days.

  4. From the client directory, run the token helper.

    bash
    node etc/google-auth.mjs --source=ga4 <client_secret.json> [ssh-host] [remote-config-path]

    It opens Google's consent page, catches the redirect on a loopback port and trades the code for a refresh token. Before it saves anything, it checks that the token can see at least one Analytics account. With no ssh-host it writes the ga4 block into ./client.config.json. With one, it writes into that host's config over ssh and sends the secrets on stdin. Set BROWSER_APP to pick the browser that holds the right Google login.

  5. Add ga4 to the agent's plugins array and restart the agent.

The Google account you consent with must have access to the Analytics properties.

json
{
  "plugins": ["ga4"],
  "ga4": {
    "name": "Google Analytics",
    "uniqueId": "ga4",
    "refreshInterval": 900000,
    "clientId": "<CLIENT_ID>.apps.googleusercontent.com",
    "clientSecret": "<CLIENT_SECRET>",
    "refreshToken": "<REFRESH_TOKEN>",
    "accounts": ["<GA_ACCOUNT_ID>"]
  }
}
OptionDefaultWhat it does
clientIdrequiredOAuth client id.
clientSecretrequiredOAuth client secret.
refreshTokenrequiredRefresh token from google-auth.mjs.
accountsevery accountGA account ids to collect, as strings. Accounts outside the list are never queried.
refreshInterval900000Milliseconds between reports.
timeout20000Milliseconds per HTTP request.

Set accounts unless the login sees only this customer's accounts. A Google login often sees accounts that belong to other customers, and everything collected lands in the view of the system this agent reports to.

client.config.json.example has no ga4 block. google-auth.mjs creates one.

What it reports ​

One report per interval, sent immediately on start.

  • name, always Google Analytics.
  • properties, the number of properties reported.
  • accounts[], each with id, name and properties[].
  • Per property, id, name, asOf, which is today's date in the property's time zone, and activeNow, the users active in the last 30 minutes. activeNow is null when the realtime call fails.
  • Per property, today, yesterday and last7, each with users, sessions and views. The 7-day users figure comes from GA itself, so one person on three days counts once.
  • Per property, daily with a start date and 30-entry users, sessions and views arrays. Days with no traffic are filled with 0.
  • A property that fails carries error in place of figures. The others still report.
  • When the whole poll fails, the report is { error }, plus authExpired: true if Google answered invalid_grant.

Only ordinary GA4 properties are listed. The plugin filters out other property types.

The server does not write a history row per report. The type is in SELF_HISTORIED, because each report already carries 30 days.

When it alerts ​

ConditionSeverityReason text
authExpirederrorGoogle Analytics: authorization expired, mint a new refresh token
errorerrorGoogle Analytics: <error>
clientId, clientSecret or refreshToken missingerrorGoogle Analytics: ga4 plugin needs clientId, clientSecret and refreshToken
One or more properties carry errorwarningGoogle Analytics: <n> properties failed to report (<first name>: <error>)

A new problem does not notify on the first bad report. The server waits for a second consecutive bad report, which is 15 minutes later at the default interval. A report that comes back clean resolves the alert.

There is no traffic threshold. A property that drops to zero users does not alert.

On the dashboard ​

The card is titled Google Analytics.

ReadingSource
PROPERTIESNumber of properties
ACTIVE NOWSum of activeNow across properties
USERS TODAYSum of today.users
SESSIONS 7DSum of last7.sessions

Below that, one row per property with today's users, or error in amber. While the report carries a top-level error, the figures blank out rather than showing zeroes. The card links to Google Analytics.

The card goes OFFLINE after 1800 seconds without a report, which is two missed polls at the default interval.

Gotchas ​

  • A failed realtime call is silent. activeNow becomes null, and ACTIVE NOW counts it as 0.
  • Changing accounts is a privacy decision, not a display one. Hiding properties and nicknames live in the portal.
  • Refresh tokens from a consent screen in Testing die after 7 days, and the card turns red with authorization expired. Run google-auth.mjs again with the app set to In production.
  • If Google returns no refresh token, remove the app's access at myaccount.google.com/permissions and run the helper again.