← Google Cloud pour les devs : gcloud et Cloud Logging

Ta boîte à outils : helpers shell, scripts et raccourcis

≈ 30 minutes · Jalon M4 · Prérequis : Des logs au tableau de bord : métriques, graphiques et alertes

Le gain du jour : taper gerr api-commandes au lieu d’une commande de quatre lignes, et partager ces raccourcis avec toute l’équipe dans le dépôt.

Un helper, c’est quoi ?

Un helper est un petit outil qui emballe une commande longue derrière un nom court. Tu le tapes cent fois par semaine, donc chaque seconde gagnée compte.

Les filtres de la leçon 3 sont puissants, mais longs à retaper. Le but ici n’est pas d’apprendre de nouvelles requêtes. C’est de ranger celles que tu connais déjà là où tu les retrouves en deux secondes.

On construit la boîte dans cet ordre : le shell, gcloud lui-même, un script Python, la console, l’éditeur, puis le partage en équipe.

1. Des fonctions shell

Une fonction shell est une commande que tu définis toi-même, avec des arguments. Elle marche à l’identique dans bash et dans zsh.

# tools/gcloud-helpers.sh : helpers de lecture des logs (lecture seule)
# Usage : export GCP_PROJECT=mon-projet-dev ; source tools/gcloud-helpers.sh

# Erreurs récentes d'un service Cloud Run : gerr api-commandes 30m
gerr() {
  local service="$1" freshness="${2:-1h}"
  [ -n "$service" ] || { echo "usage: gerr SERVICE [FRESHNESS]" >&2; return 1; }
  gcloud logging read \
    "resource.type=\"cloud_run_revision\" AND resource.labels.service_name=\"$service\" AND severity>=ERROR" \
    --project="${GCP_PROJECT:?définis GCP_PROJECT}" \
    --freshness="$freshness" --limit=50 \
    --format='value(timestamp,severity,jsonPayload.message)'
}

# Toute une requête, dans l'ordre chronologique : gtrace 7c1e9a0f...
gtrace() {
  local trace_id="$1"
  [ -n "$trace_id" ] || { echo "usage: gtrace TRACE_ID" >&2; return 1; }
  gcloud logging read \
    "trace=\"projects/${GCP_PROJECT:?définis GCP_PROJECT}/traces/$trace_id\"" \
    --project="$GCP_PROJECT" --freshness=7d --limit=200 --format=json \
  | jq -r 'reverse | .[]
      | "\(.timestamp) \(.severity // "DEFAULT") \(.jsonPayload.message // .textPayload // "")"'
}

# Ce qu'un utilisateur a déclenché : guser u-42 1d
guser() {
  local user_id="$1" freshness="${2:-1d}"
  [ -n "$user_id" ] || { echo "usage: guser USER_ID [FRESHNESS]" >&2; return 1; }
  gcloud logging read "jsonPayload.userId=\"$user_id\"" \
    --project="${GCP_PROJECT:?définis GCP_PROJECT}" \
    --freshness="$freshness" --limit=100 --format=json \
  | jq '.[] | {timestamp, severity, service: .resource.labels.service_name, message: .jsonPayload.message}'
}

Trois détails comptent :

Où ranger ce fichier ? Dans ton ~/.zshrc, il ne sert qu’à toi. Dans le dépôt (tools/gcloud-helpers.sh), il est versionné, relu en code review, et toute l’équipe a la même version. La seconde option gagne presque toujours.

2. Ce que gcloud fait déjà pour toi

Avant d’écrire un helper, vérifie que gcloud ne le fait pas déjà.

Les configurations nommées. Une par environnement, vues en leçon 1. Deux variantes utiles en plus : --configuration=prod vise une configuration pour une seule commande, et la variable CLOUDSDK_ACTIVE_CONFIG_NAME fait de même pour tout un terminal.

Les valeurs par défaut. Chaque configuration garde ses propriétés. Une région fixée une fois t’évite de la retaper :

gcloud config set run/region europe-west1   # utilisée par gcloud run
gcloud config list                          # vérifie ce qui est actif

Le shell interactif. gcloud beta interactive (aussi en alpha) ouvre un shell avec autocomplétion des commandes, des flags et des ressources. Il reste en préversion : pratique pour découvrir une commande, pas pour un script.

Les pages d’aide thématiques. Deux pages valent le détour :

gcloud topic filters    # la syntaxe de --filter (celle de gcloud, pas de Logging)
gcloud topic formats    # tout ce que --format sait faire : value, table, json, csv

Le débogage de gcloud lui-même. Quand une commande échoue sans raison claire :

gcloud logging read 'severity>=ERROR' --limit=1 --verbosity=debug
gcloud logging read 'severity>=ERROR' --limit=1 --log-http

--verbosity accepte debug, info, warning (le défaut), error, critical et none. --log-http écrit chaque requête et réponse HTTP sur stderr. Tu vois alors l’API appelée, le projet visé, et le vrai message d’erreur.

—log-http montre tout

gcloud masque les jetons OAuth dans cette sortie par défaut. Mais les réponses contiennent tes logs, donc parfois des données sensibles. Lis-la dans ton terminal, et ne la colle jamais telle quelle dans un ticket ou un chat. Si tu dois la partager, garde seulement l’URL appelée et le code d’erreur.

3. Un script Python pour résumer

Pour une vue d’ensemble (« quelles erreurs, combien de fois »), un petit script avec google-cloud-logging fait mieux que le shell. Il utilise les ADC de ta machine, comme en leçon 4.

"""Résumé des erreurs récentes, groupées par message (lecture seule)."""

import argparse
from collections import Counter
from datetime import datetime, timedelta, timezone

from google.cloud import logging


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--project", required=True)
    parser.add_argument("--service", required=True)
    parser.add_argument("--hours", type=int, default=1)
    args = parser.parse_args()

    since = datetime.now(timezone.utc) - timedelta(hours=args.hours)
    filtre = "\n".join([
        'resource.type="cloud_run_revision"',
        f'resource.labels.service_name="{args.service}"',
        "severity>=ERROR",
        f'timestamp>="{since:%Y-%m-%dT%H:%M:%SZ}"',
    ])

    client = logging.Client(project=args.project)
    compteur: Counter[str] = Counter()
    for entry in client.list_entries(
        filter_=filtre, order_by=logging.DESCENDING, max_results=1000
    ):
        payload = entry.payload
        if isinstance(payload, dict):
            message = payload.get("message", "(sans message)")
        else:
            message = str(payload)
        compteur[message[:80]] += 1

    print(f"{sum(compteur.values())} erreurs sur {args.hours} h pour {args.service}")
    for message, nb in compteur.most_common(10):
        print(f"{nb:>5}  {message}")


if __name__ == "__main__":
    main()
$ python3 tools/resume_erreurs.py --project=mon-projet-dev --service=api-commandes
57 erreurs sur 1 h pour api-commandes
   50  stock service timeout after 5000ms
    6  PaymentDeclined: card expired
    1  KeyError: 'userId'

Ce qu’il faut savoir sur list_entries :

4. Les raccourcis de la console

Le Logs Explorer a sa propre mémoire, dans la Query library :

Le lien de partage d’une requête est déjà décrit en leçon 3.

Cloud Shell est un terminal dans le navigateur, avec gcloud déjà installé et connecté à ton compte. Ton dossier personnel garde 5 Go entre les sessions, ce qui suffit pour y déposer gcloud-helpers.sh. Le quota par défaut est de 50 heures par semaine, et le dossier est supprimé après 120 jours sans connexion.

5. Les raccourcis de l’éditeur

Cloud Code est l’extension Google pour VS Code et les IDE JetBrains. Dans VS Code, la commande Cloud Code: View Logs ouvre un Log Viewer. Tu choisis le type Cloud Run Logs, le service, une révision, et tu actives Streaming pour voir les lignes arriver. Il couvre aussi les logs Kubernetes. Pratique quand tu codes, mais le Logs Explorer reste plus riche pour une vraie enquête.

jq lit le JSON en terminal. Avec --format=json, gcloud rend une liste. Quatre motifs suffisent pour démarrer :

jq '.[0]'                                            # la première entrée, en entier
jq -r '.[] | .jsonPayload.message // .textPayload'   # le message, JSON ou texte
jq '[.[] | select(.httpRequest.status >= 500)]'      # garder les 5xx
jq 'length'                                          # combien d'entrées

6. Partager avec l’équipe

Un Makefile à la racine du dépôt donne des noms connus de tous. Il réutilise le fichier de helpers au lieu de copier les commandes.

GCP_PROJECT ?= mon-projet-dev
FRESHNESS ?= 1h
HELPERS := . tools/gcloud-helpers.sh

.PHONY: logs-errors logs-trace logs-summary

logs-errors: ## make logs-errors SERVICE=api-commandes
	@GCP_PROJECT=$(GCP_PROJECT) bash -c '$(HELPERS) && gerr "$(SERVICE)" "$(FRESHNESS)"'

logs-trace: ## make logs-trace TRACE=7c1e9a0f...
	@GCP_PROJECT=$(GCP_PROJECT) bash -c '$(HELPERS) && gtrace "$(TRACE)"'

logs-summary: ## make logs-summary SERVICE=api-commandes
	python3 tools/resume_erreurs.py --project=$(GCP_PROJECT) --service=$(SERVICE)

Un nouveau venu tape make logs-errors SERVICE=api-commandes le premier jour, sans rien installer d’autre que gcloud et jq. Si l’équipe préfère just, le principe est le même : une recette par helper, dans un justfile versionné.

Les bonnes pratiques

Vérifie ta compréhension

Tu veux que gtrace affiche une requête dans l'ordre chronologique sur les 7 derniers jours. Pourquoi ne pas simplement ajouter --order=asc à côté de --freshness=7d ?

Le script resume_erreurs.py tourne en CI et lit parfois le mauvais projet. Quelle est la meilleure correction ?

À toi de jouer

  1. Crée tools/gcloud-helpers.sh avec gerr, gtrace et guser, charge-le avec source, puis lance gerr TON_SERVICE 30m.

  2. Fixe ta région par défaut avec gcloud config set run/region, puis parcours gcloud topic formats.

  3. Lance une commande qui échoue avec —log-http et repère l’URL appelée et le code d’erreur.

  4. Exécute resume_erreurs.py sur un service et compare son top 3 avec gerr.

  5. Sauvegarde ta meilleure requête dans la Query library et active Share with project.

  6. Ajoute les cibles logs-errors et logs-summary au Makefile du dépôt, et fais-les relire en code review.

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