← Google Cloud pour les devs : gcloud et Cloud Logging

Cloud Logging : le parcours d'une entrée de log

≈ 30 minutes · Jalon M2 · Prérequis : Google Cloud et la CLI gcloud

Le gain du jour : savoir lire une entrée de log champ par champ, comprendre où elle va après avoir été écrite, et régler toi-même combien de temps on la garde et combien elle coûte.

Cloud Logging, c’est quoi ?

Cloud Logging est le service de Google Cloud qui reçoit, range et conserve les logs. Un log est une ligne horodatée qui raconte un événement : « requête reçue », « VM créée », « accès refusé ». Chaque ligne stockée s’appelle une entrée de log (log entry).

Tu n’as presque rien à installer. Sur Cloud Run, Cloud Run functions, GKE ou App Engine, tout ce que ton code écrit sur stdout et stderr part tout seul vers Cloud Logging. Un simple print() en Python suffit pour produire une entrée.

D’où viennent les logs

Trois familles alimentent ton projet :

FamilleQui l’écritExemple
Logs applicatifsTon code, via stdout / stderr ou une bibliothèque clienterun.googleapis.com/stderr sur Cloud Run
Logs de plateformeLes services Google, sur leur propre fonctionnementRequêtes HTTP reçues par Cloud Run, logs Cloud SQL
Audit logsCloud Audit Logs, pour savoir « qui a fait quoi, où, quand »Quelqu’un a modifié un rôle IAM

Les audit logs se déclinent en quatre types. Retiens surtout lesquels sont actifs par défaut :

TypeCe qu’il enregistrePeut-on le couper ?
Admin ActivityLes modifications de configuration (créer une VM, changer un rôle)Non, toujours écrit
System EventLes actions de Google lui-même sur tes ressourcesNon, toujours écrit
Data AccessLes lectures de configuration et les accès aux donnéesDésactivé par défaut (sauf BigQuery)
Policy DeniedLes accès refusés par une politique de sécuritéNon désactivable, mais on peut l’exclure du stockage

L’anatomie d’une entrée : le LogEntry

Toutes les entrées ont la même enveloppe, le type LogEntry. Voici une entrée écrite par un service Cloud Run :

{
  "logName": "projects/mon-projet/logs/run.googleapis.com%2Fstderr",
  "resource": {
    "type": "cloud_run_revision",
    "labels": { "service_name": "api-panier", "location": "europe-west1" }
  },
  "timestamp": "2026-11-05T09:42:17.318Z",
  "receiveTimestamp": "2026-11-05T09:42:17.502Z",
  "severity": "ERROR",
  "insertId": "6729f1a90004d2b1",
  "labels": { "instanceId": "00f4b2…" },
  "httpRequest": { "requestMethod": "POST", "status": 500 },
  "trace": "projects/mon-projet/traces/4bf92f3577b34da6a3ce929d0e0e4736",
  "spanId": "a3ce929d0e0e4736",
  "jsonPayload": { "message": "quantity must be >= 0", "userId": "u-042" }
}

Le contenu lui-même est dans un seul de ces trois champs :

ChampContenuQuand tu le vois
textPayloadUne chaîne de texte brutUn print("hello") classique
jsonPayloadUn objet JSON libreTon code écrit une ligne JSON sur stdout
protoPayloadUn message protobuf typé (@type)Les audit logs (AuditLog)

Un log en jsonPayload est dit structuré. On peut filtrer sur un champ précis (jsonPayload.userId="u-042") et même l’indexer. Un textPayload se cherche, mais ne s’indexe pas.

Les niveaux de severity, du plus faible au plus grave, avec leur valeur numérique : DEFAULT (0), DEBUG (100), INFO (200), NOTICE (300), WARNING (400), ERROR (500), CRITICAL (600), ALERT (700), EMERGENCY (800). Une entrée sans niveau reçoit DEFAULT.

Le trajet : du code au bucket

Une entrée ne va pas directement dans un stockage. Elle passe d’abord par le Log Router. On construit le chemin pas à pas.

D’abord, une source écrit une entrée, qui arrive à l’API Cloud Logging :

 🐍 Ton code (stdout)  ─┐
 ☁️ Services Google    ─┼──▶  📥 API Cloud Logging
 🛡 Cloud Audit Logs   ─┘

Ensuite, le Log Router compare l’entrée aux filtres de chaque sink du projet :

 Sources  ──▶  📥 API  ──▶  🔀 Log Router
                               │
                               ├─ sink _Required
                               ├─ sink _Default
                               └─ sink(s) que tu crées

Enfin, chaque sink dont le filtre correspond envoie une copie vers sa destination :

 Sources  ──▶  📥 API  ──▶  🔀 Log Router
                               │
                               ├─ sink _Required ──▶ 🪣 bucket _Required (400 j, fixe)
                               ├─ sink _Default  ──▶ 🪣 bucket _Default  (30 j, réglable)
                               └─ sink "erreurs" ──▶ 📊 BigQuery / 🗄 Cloud Storage /
                                                     📨 Pub/Sub / 🪣 autre bucket /
                                                     🏢 autre projet

Un sink a trois éléments : une destination, un filtre d’inclusion (vide = tout prendre) et zéro ou plusieurs filtres d’exclusion (jusqu’à 50 par sink). Une entrée qui correspond à plusieurs sinks est copiée dans chacune de leurs destinations.

DestinationPour quoi faire
Log bucketGarder et chercher dans le Logs Explorer
BigQueryAnalyser en SQL sur de longues périodes
Cloud StorageArchiver pas cher, pour la conformité
Pub/SubEnvoyer à un autre outil (SIEM, Splunk, un service maison)
Un autre projet Google CloudCentraliser : les sinks de ce projet re-routent les entrées

Les deux buckets créés pour toi

Chaque projet reçoit deux sinks et deux buckets du même nom :

# Voir les buckets et leur rétention (colonne RETENTION_DAYS)
gcloud logging buckets list

# Garder les logs du bucket _Default pendant un an
gcloud logging buckets update _Default --location=global --retention-days=365

# Lister les logs qui contiennent des entrées dans le projet
gcloud logging logs list

Allonger la rétention ne s’applique qu’à l’avenir : une entrée déjà supprimée ne revient pas.

Créer un sink

Exemple : garder les erreurs dans un bucket dédié. On crée le bucket, puis le sink :

gcloud logging buckets create erreurs --location=global

gcloud logging sinks create sink-erreurs \
  logging.googleapis.com/projects/mon-projet/locations/global/buckets/erreurs \
  --log-filter='severity>=ERROR' \
  --description="Copie des erreurs dans le bucket erreurs"

Les options utiles : --log-filter (inclusion), --exclusion (répétable), et --description. Si la réponse contient un champ writerIdentity, ce compte de service doit recevoir le droit d’écrire dans la destination (par exemple roles/bigquery.dataEditor pour BigQuery, roles/pubsub.publisher pour Pub/Sub). gcloud ne le fait pas pour toi.

Le coût se joue au stockage, pas à l’écriture

Tu paies au volume de logs stocké dans les log buckets, avec une franchise mensuelle par projet (50 GiB historiquement) et un surcoût au-delà de 30 jours de rétention. Le prix exact par GiB : à vérifier sur la page tarifs de Google Cloud Observability.

Le levier principal est l’exclusion : une entrée exclue de tous les sinks n’est pas stockée. Candidats classiques : les health checks, les logs DEBUG en production, les Data Access audit logs d’un projet de dev. Les logs du bucket _Required, eux, ne peuvent pas être exclus. La page Logs Storage de la console montre le volume du mois et une projection.

Métriques et alertes basées sur les logs

Deux usages vont au-delà de la lecture :

Qui a le droit de faire quoi

RôleCe qu’il permet
roles/logging.viewerLire les logs, sauf les Data Access audit logs du bucket _Default
roles/logging.privateLogViewerTout ce que fait viewer, plus les Data Access audit logs
roles/logging.configWriterCréer et modifier sinks, buckets, vues, métriques basées sur les logs
roles/logging.adminToutes les fonctions de Cloud Logging

Donne viewer par défaut. Les Data Access logs peuvent révéler qui lit quelles données : privateLogViewer se réserve à ceux qui en ont besoin.

Vérifie ta compréhension

Ton équipe veut garder les logs de son API pendant 90 jours au lieu de 30. Quelle est la bonne action ?

Une entrée a un timestamp à 09:42:17 et un receiveTimestamp à 09:47:03. Que peux-tu en conclure ?

À toi de jouer

  1. Lance gcloud logging buckets list sur ton projet et note la rétention de _Required et de _Default.

  2. Lance gcloud logging logs list et repère un log applicatif, un log de plateforme et un audit log (cloudaudit.googleapis.com%2Factivity).

  3. Dans le Logs Explorer, déplie une entrée et identifie son resource.type, sa severity et lequel des trois champs *Payload elle utilise.

  4. Écris (sans l’appliquer) le filtre d’exclusion qui retirerait les requêtes de health check de ton service.

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