Lire et filtrer les logs : Logs Explorer et gcloud logging read
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.
- Time range : la fenêtre de temps, ajoutée à ton filtre. Garde-la courte.
- Query pane : là où tu écris le filtre.
- Fields pane : les résultats résumés par champ (sévérité, ressource, champs
jsonPayloadfréquents), avec un nombre approximatif. Un clic ajoute la condition. - Histogramme : un pic d’erreurs se voit tout de suite. Souvent, un déploiement.
- Query results : une entrée dépliée montre son JSON. Un clic sur une valeur l’ajoute au filtre (« Show matching entries ») ou l’exclut.
- Stream logs : affiche les nouvelles entrées en direct, filtrées par ta requête. Faire défiler la liste met le flux en pause.
- Query library : tes requêtes sauvegardées, récentes et suggérées.
- Share link : une URL courte qui contient la requête et la fenêtre de temps en heures absolues. Ton collègue voit exactement ce que tu as vu.
Deux outils pour suivre une requête d’un bout à l’autre :
- L’icône trace sur une entrée, puis « Show all logs for this trace » : le filtre
est complété avec le
tracede cette entrée. - Le menu « Correlate by » : tu choisis un log parent (par exemple les logs de
requêtes), et chaque requête se déplie pour montrer ses logs enfants qui partagent
le même
trace.
Le langage de requête
Une condition a toujours la forme CHAMP OPÉRATEUR VALEUR.
| Opérateur | Sens | Exemple |
|---|---|---|
= | égal | severity=ERROR |
!= | différent | resource.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 regex | logName!~"health" |
Pour combiner les conditions :
- Une condition par ligne =
ANDimplicite. Deux lignes, deux conditions qui doivent être vraies. ORetNOTexistent aussi.NOTpeut s’écrire-.- Mets des parenthèses dès que tu mélanges
ANDetOR. Sans elles, la précédence (NOT, puisOR, puisAND) te surprendra. - Une liste de valeurs s’écrit entre parenthèses :
severity=(ERROR OR CRITICAL).
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 :
| Champ | Ce qu’il filtre |
|---|---|
resource.type | le type de service : cloud_run_revision, k8s_container, gce_instance |
resource.labels.* | le service précis : service_name, revision_name, namespace_name |
logName | le flux : projects/PROJET/logs/run.googleapis.com%2Frequests |
severity | le niveau. severity>=WARNING compare dans l’ordre des niveaux |
httpRequest.status | le code HTTP de la réponse |
jsonPayload.<champ> | tes champs à toi : jsonPayload.orderId="A-1234" |
timestamp | la date, en UTC : timestamp>="2026-11-05T09:30:00Z" |
trace | toutes 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 opérateurs booléens s’écrivent en majuscules.
and,or,noten minuscules sont lus comme des mots à chercher, pas comme des opérateurs. - Les regex sont sensibles à la casse.
=~"error"ne trouve pasERROR. Ajoute(?i):=~"(?i)error". À l’inverse,SEARCHignore toujours la casse. - Mets les chaînes entre guillemets doubles, surtout si elles contiennent des
espaces, des
/ou des-. Un nom de champ avec des caractères spéciaux se met aussi entre guillemets :labels."compute.googleapis.com/resource_id". - La recherche globale est lente. Un texte seul entre guillemets (
"Hello Kitty") cherche une sous-chaîne dans toute l’entrée. Commence toujours parresource.type,logNameou un champ précis, et préfèreSEARCH()à:pour du texte. - Le temps est en UTC.
timestamp>="2026-11-05T10:42:00Z"vise 11 h 42 à Paris en hiver.
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 :
--limit: nombre maximum d’entrées. Mets-le toujours, sinon tout arrive.--freshness: âge maximum des entrées (30m,1h,2d). Défaut :1d. Il ne marche qu’en ordre décroissant et sans condition surtimestampdans le filtre. Pour une fenêtre précise, écris plutôttimestamp>=... AND timestamp<=....--order:desc(défaut, le plus récent d’abord) ouasc. Pour lire une requête dans l’ordre chronologique, prendsasc.--project: le projet à lire. Sans lui, gcloud prend le projet de la configuration active. Vérifie-le avant de conclure « il n’y a rien ».--format:jsonpour un traitement,value(...)pour des colonnes.
# 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
Dans le Logs Explorer, filtre un service avec
resource.typeetresource.labels.service_name, puis ajouteseverity>=WARNINGdepuis le Fields pane.Trouve une entrée avec un
trace, puis affiche tous les logs de cette trace. Essaie aussi le menu « Correlate by ».Relance la même requête dans le terminal avec
gcloud logging read,—limit=20et—format=‘value(timestamp,severity,jsonPayload.message)’.Compte les messages d’erreur de la dernière heure avec
—format=jsonetjq.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.