← Google Cloud pour les devs : gcloud et Cloud Logging

Lire et filtrer les logs : Logs Explorer et gcloud logging read

≈ 35 minutes · Jalon M2 · Prérequis : Cloud Logging : le parcours d'une entrée de log

Le gain du jour : écrire des filtres Cloud Logging précis, les lancer depuis la console ou le terminal, et passer d’une erreur 500 à la ligne de log qui l’explique en quatre requêtes.

Un filtre, c’est quoi ?

Un filtre (ou requête) est une expression booléenne évaluée sur chaque entrée de log. Si l’expression est vraie, l’entrée s’affiche. Sinon, elle est ignorée.

Chaque entrée est un objet LogEntry avec des champs fixes (timestamp, severity, resource, logName, httpRequest, trace) et un contenu libre (jsonPayload ou textPayload). Filtrer, c’est poser des conditions sur ces champs.

Le même langage sert dans le Logs Explorer, dans gcloud logging read, et plus tard dans les sinks.

Si tu veux la version QA de ce sujet (trier front ou back, enrichir un bug report), elle est dans Explorer les logs cloud pour aider l’équipe. Ici, on va plus loin côté développeur.

Le Logs Explorer en un coup d’œil

L’écran a une barre d’outils en haut, le filtre dessous, les champs à gauche, et l’histogramme au-dessus des résultats.

Deux outils pour suivre une requête d’un bout à l’autre :

Le langage de requête

Une condition a toujours la forme CHAMP OPÉRATEUR VALEUR.

OpérateurSensExemple
=égalseverity=ERROR
!=différentresource.type!="k8s_container"
> >= < <=ordre (nombres, dates, sévérités)httpRequest.status>=500
:« contient » (sous-chaîne)jsonPayload.message:"timeout"
=~correspond à une regex (RE2)jsonPayload.path=~"^/v1/orders"
!~ne correspond pas à la regexlogName!~"health"

Pour combiner les conditions :

resource.type="cloud_run_revision"
resource.labels.service_name="api-commandes"
(httpRequest.status>=500 OR severity>=ERROR)
NOT httpRequest.requestUrl:"/healthz"

Les champs que tu utiliseras tout le temps :

ChampCe qu’il filtre
resource.typele type de service : cloud_run_revision, k8s_container, gce_instance
resource.labels.*le service précis : service_name, revision_name, namespace_name
logNamele flux : projects/PROJET/logs/run.googleapis.com%2Frequests
severityle niveau. severity>=WARNING compare dans l’ordre des niveaux
httpRequest.statusle code HTTP de la réponse
jsonPayload.<champ>tes champs à toi : jsonPayload.orderId="A-1234"
timestampla date, en UTC : timestamp>="2026-11-05T09:30:00Z"
tracetoutes les entrées d’une même requête

Pour chercher du texte sans connaître le champ, utilise SEARCH() :

SEARCH("PaymentDeclined")
SEARCH(jsonPayload.message, "stock insuffisant")
SEARCH("`connection reset`")

SEARCH découpe le texte en mots (des tokens), ignore la casse et cherche des mots entiers : SEARCH("world") ne trouve pas worldwide. Sans champ précisé, tous les mots doivent apparaître, dans n’importe quel ordre. Des backticks imposent la phrase exacte.

Les pièges qui font perdre une heure

Lire depuis le terminal : gcloud logging read

Le même filtre fonctionne dans le terminal, pour un script ou une CI.

gcloud logging read 'resource.type="cloud_run_revision" AND severity>=ERROR' \
  --project=mon-projet-dev \
  --freshness=1h \
  --limit=20

Les flags utiles :

# Trois colonnes, lisibles dans le terminal
gcloud logging read 'severity>=ERROR' --freshness=1h --limit=10 \
  --format='value(timestamp,severity,jsonPayload.message)'

# JSON + jq : compter les erreurs par message
gcloud logging read 'severity>=ERROR' --freshness=1h --limit=500 --format=json \
  | jq -r '.[].jsonPayload.message' | sort | uniq -c | sort -rn | head

Mets le filtre entre apostrophes : il contient des " et des > que le shell interpréterait.

Pour suivre les logs en direct dans le terminal, il existe gcloud beta logging tail (passé d’alpha à beta en 2021). Il demande un peu d’installation : le paquet Python grpcio, et la variable CLOUDSDK_PYTHON_SITEPACKAGES=1 pour que gcloud le trouve.

gcloud beta logging tail 'resource.type="cloud_run_revision" AND severity>=WARNING'

Sinon, le bouton Stream logs du Logs Explorer fait le même travail.

Compter plutôt que lire : Observability Analytics

Lire des lignes, c’est bien pour une requête. Pour savoir « combien d’erreurs par heure depuis le déploiement », il faut compter. C’est le rôle de Observability Analytics (anciennement Log Analytics) : tu écris du SQL sur tes logs.

Il faut d’abord mettre à niveau le bucket de logs (bouton « Upgrade » dans Logs Storage, ou gcloud logging buckets update BUCKET --location=LOCATION --enable-analytics). Attention : c’est irréversible, et seules les nouvelles entrées deviennent analysables. C’est une décision d’équipe, pas un clic en passant.

Ensuite, chaque vue de log devient une table :

SELECT severity, COUNT(*) AS nb
FROM `mon-projet-dev.global._Default._AllLogs`
WHERE severity IS NOT NULL
GROUP BY severity
ORDER BY nb DESC

La recette : d’un 500 à la cause en 4 requêtes

Un client signale « erreur 500 en validant ma commande, vers 10 h 15 ». Fenêtre de temps : 10 h 00 à 10 h 30 UTC (après conversion). On resserre étape par étape.

1. Trouver les requêtes en échec.

resource.type="cloud_run_revision"
resource.labels.service_name="api-commandes"
httpRequest.status>=500

L’histogramme montre un pic. Tu déplies une entrée : POST /v1/orders, statut 500.

2. Récupérer la trace de cette requête. Icône trace, puis « Show all logs for this trace ». Le filtre devient :

trace="projects/mon-projet-dev/traces/7c1e9a0f42d84b6f9e3a51c2d0b8f611"

Tu vois toute l’histoire de la requête, dans l’ordre, même si elle a traversé plusieurs services.

3. Isoler l’erreur applicative dans cette trace.

trace="projects/mon-projet-dev/traces/7c1e9a0f42d84b6f9e3a51c2d0b8f611"
severity>=ERROR

La ligne apparaît : jsonPayload.message: "stock service timeout after 5000ms", écrite par api-stock.

4. Mesurer l’étendue. Est-ce un cas isolé ou un incident ?

resource.labels.service_name="api-stock"
SEARCH(jsonPayload.message, "timeout")

Cinquante occurrences, toutes après 10 h 02, juste après le dernier déploiement de api-stock. Tu tiens ta cause. Le lien partagé de la requête 3 va dans le ticket.

Vérifie ta compréhension

Tu tapes ce filtre : severity>=ERROR and resource.type="cloud_run_revision". Les résultats semblent faux. Pourquoi ?

Tu lances gcloud logging read avec --freshness=1h et un filtre qui contient déjà timestamp>="2026-11-05T09:00:00Z". Que se passe-t-il ?

À toi de jouer

  1. Dans le Logs Explorer, filtre un service avec resource.type et resource.labels.service_name, puis ajoute severity>=WARNING depuis le Fields pane.

  2. Trouve une entrée avec un trace, puis affiche tous les logs de cette trace. Essaie aussi le menu « Correlate by ».

  3. Relance la même requête dans le terminal avec gcloud logging read, —limit=20 et —format=‘value(timestamp,severity,jsonPayload.message)’.

  4. Compte les messages d’erreur de la dernière heure avec —format=json et jq.

  5. Sauvegarde ta requête la plus utile dans la Query library et partage son lien à un collègue.

🗂 Fiche de référence associée : Google Cloud et Cloud Logging — antisèche.