Google Cloud has a signal the other clouds do not, and most teams under-use it. Error Reporting groups exceptions by stack trace and tells you which release each group first appeared in — which is deploy correlation, already computed, for free. On any other platform you assemble that yourself from logs and deploy timestamps, and the result is the assumed causal link we keep having to cap confidence on.

Getting alerts in

Cloud Monitoring's alerting policies post a webhook with an incident object. Simple shape, two things to know about it.

FieldBecomesNote
incident_idexternal idStable across open and close, which is what makes the resolve match reliably.
policy_namealert nameSo name your policies as you would name alerts. "Orders API error rate" beats "Policy 4".
resource.labelslabelsWhere service_name, cluster_name, location live. This is what match rules bind to.
state / ended_atresolvedclosed, or any non-null ended_at.
started_atstartedEpoch seconds, not a string — a parser that assumes ISO-8601 silently loses the timestamp.

Severity is usually absent

Cloud Monitoring only carries a severity when the policy explicitly sets one, and most do not. Unset defaults to warning here. If you route by severity, set it on your policies — otherwise every GCP alert arrives at the same level and your routing does nothing.

Error Reporting, and why it is the interesting one

Compare what each signal can establish about "did the release cause this?"

graph TB subgraph L["From logs + deploy timestamps"] L1["Errors rose at 02:14"] --> L2["A release went out at 01:34"] L2 --> L3["Assumed
two things in one window.
Confidence capped at 0.75."] end subgraph E["From Error Reporting"] E1["Group abc123: 4,201 events,
first seen 02:11"] --> E2["affectedServices:
orders-api v2.4.1 only"] E2 --> E3["Established
this signature does not exist
in the previous version."] end style L3 fill:#fff4e2,stroke:#f4a03a style E3 fill:#e8f8f4,stroke:#22c1a4,stroke-width:2px

That is the same lift a change-management system gives you, obtained from the runtime instead of from a process. The gcp.errors step returns grouped exceptions with counts, affected user counts, first- and last-seen times and the service versions each group appears in — and "first seen 02:11, present only in v2.4.1" is not a correlation, it is an observation about which build the signature exists in.

The three read steps

StepWhat it answers
gcp.metricsA Cloud Monitoring time series by filter, with an aligner. The GCP equivalent of a range query.
gcp.logsCloud Logging entries matching a filter, newest first. Severity, timestamp and payload.
gcp.errorsError Reporting groups. Put this early — it frequently answers the incident before the log step runs.

A Cloud Run runbook that uses all three, in the order that makes the fourth step's job easiest:

1  gcp.metrics   metric.type="run.googleapis.com/request_count"
                 resource.labels.service_name="{{service_name}}"   → request_rate
2  gcp.errors    service="{{service_name}}", window 60m       → error_groups
3  gcp.logs      severity>=ERROR
                 resource.type="cloud_run_revision"              → error_logs
4  ai.classify   context_vars: request_rate, error_groups, error_logs
5  ai.verify     conclusion_var: classification

Authentication, three ways

In order of preference, and the first two need no secret at all:

  1. The metadata server. Running on GCE, GKE or Cloud Run, the workload's own service account is used. Nothing to configure, nothing to rotate.
  2. An explicit token via GCP_ACCESS_TOKEN, for short-lived or federated setups.
  3. A service-account key at GOOGLE_APPLICATION_CREDENTIALS, signed into a JWT and exchanged for an access token — the standard OAuth 2.0 JWT bearer flow, done directly rather than through google-auth.

Roles needed, all read-only: roles/monitoring.viewer, roles/logging.viewer, roles/errorreporting.viewer.

One thing we could test live, and did

GCP publishes its incident feed publicly, so the cloud.health step for GCP is the one part of this connector verified against the real thing rather than against documentation. It answers, correctly, "GCP reports nothing open. Whatever this is, it is ours." Everything else here is built to the published APIs and tested through the product, but not against a live project.

Getting it wired

  1. A notification channel of type webhook pointed at /api/webhooks/gcp?token=…, attached to the alerting policies you want triaged.
  2. Name the policies properly and set a severity on each. Both become the thing your match rules bind to.
  3. Set GCP_PROJECT (or let it come from the service-account key), and grant the three viewer roles.
  4. Put gcp.errors before gcp.logs in your first runbook. It is the step most likely to make the rest unnecessary.

Next in this series

InsightResolve on Oracle Cloud — Notifications, request signing, and a status feed that turns out not to be a status page at all.

Keep reading