Journalisation A : pourquoi print n'est pas du logging, et les niveaux du journal

Objectifs de la leçon
  • Expliquer ce qu’est un journal (log) et pourquoi print n’est pas adapté pour enregistrer ce qui se passe dans un vrai système
  • Connaître les cinq niveaux du logging et quand utiliser chacun
  • Mettre en place un logging de base avec logging.basicConfig et choisir un niveau minimum
  • Ajouter l’heure et le niveau au format du journal

Qu’allons-nous apprendre ?

  • Ce qu’est un journal (Log) et pourquoi les vrais systèmes en écrivent un.
  • Pourquoi print n’est pas du logging.
  • Le module logging de Python.
  • Les cinq niveaux du journal : debug, info, warning, error, critical.

Pourquoi en avons-nous besoin ?

Le serveur que vous écrirez dans les thèmes backend et bases de données tournera sans que personne ne le surveille, peut-être sur un ordinateur dans le cloud, en pleine nuit. Demain matin, un client dira « ma commande a disparu pendant la nuit ». Comment saurez-vous ce qui s’est passé ?

  • print a affiché sur un écran que personne n’a vu, et c’est déjà perdu.
  • Le débogueur n’aide pas, car le bug s’est produit hier, pas maintenant.

Le journal est une « boîte noire » comme dans un avion : un enregistrement de ce qui s’est passé, quand, et avec quelle gravité. Tout système professionnel écrit des journaux, et les développeurs les lisent tous les jours.

Explication à partir des bases

Qu’est-ce qu’un journal ?

Un journal (Log) est une suite d’enregistrements. Chaque enregistrement décrit un événement : quand il s’est produit, quelle est sa gravité, et ce qui s’est passé.

2026-10-06 09:14:02 INFO     User dana logged in
2026-10-06 09:14:30 WARNING  Login attempt with wrong password for avi
2026-10-06 09:15:11 ERROR    Payment failed for order 1042: card declined

Pourquoi print n’est pas du logging

printlogging
HeureAucune. On ne sait pas quand c’est arrivé.Automatique dans chaque enregistrement
GravitéTous les messages se ressemblent5 niveaux. On peut filtrer
Désactiver/activerIl faut supprimer ou ajouter des lignes de codeChanger un seul réglage, sans toucher au code
DestinationSeulement l’écranL’écran, un fichier, un serveur de journaux…
Sur un serveurSe perdConservé et consulté plus tard

Règle : print sert à communiquer avec l’utilisateur (la sortie du programme). logging sert à communiquer avec les développeurs et les équipes d’exploitation (ce qui s’est passé à l’intérieur du programme).

Les cinq niveaux

NiveauNombreQuand l’utiliserExemple
DEBUG10Détails techniques pour le développeur, pendant le développement et le débogage"cart contents: [...]"
INFO20Événements normaux et importants — « tout va bien »"user dana logged in"
WARNING30Quelque chose d’inhabituel, mais le système continue"disk 90% full", "retrying request"
ERROR40Une action a échoué"payment failed for order 1042"
CRITICAL50Tout le système est en danger et risque de ne pas pouvoir continuer"database unreachable - shutting down"

Le niveau minimum — le filtrage

Vous fixez un niveau minimum, et seuls les messages de ce niveau et au-dessus sont affichés :

  • Pendant le développement : DEBUG. Vous voyez tout.
  • Sur un vrai serveur (Production) : INFO ou WARNING. Seulement ce qui compte, sans « bruit ».

La valeur par défaut de Python est WARNING. Donc si vous ne configurez rien, les messages debug et info ne s’affichent pas du tout.

Analogie : le système d’alerte d’un hôpital

DEBUG = l’enregistrement complet de chaque mesure (seul le médecin chercheur le regarde). INFO = « le patient a reçu son déjeuner ». WARNING = « température un peu élevée, continuer à surveiller ». ERROR = « un médicament n’a pas été donné à l’heure ». CRITICAL = « code bleu ! ». L’infirmière de nuit choisit de ne voir que WARNING et au-dessus, sinon les messages la submergeraient.

Syntaxe

import logging

# configure once, at the start of the program
logging.basicConfig(
    level=logging.INFO,                                  # minimum level to show
    format="%(asctime)s %(levelname)s %(message)s",      # how each record looks
)

# write log records
logging.debug("technical details")
logging.info("normal important event")
logging.warning("something unusual")
logging.error("an operation failed")
logging.critical("the whole system is in danger")

Champs courants dans format :

ChampCe qui est affiché
%(asctime)sLa date et l’heure
%(levelname)sLe nom du niveau (INFO, ERROR…)
%(message)sVotre message
%(funcName)sLe nom de la fonction qui a écrit l’enregistrement
%(lineno)dLe numéro de ligne

Premier exemple de code

week-2/log_levels.py :

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(message)s",
)

logging.debug("This is a debug message")
logging.info("Application started")
logging.warning("Config file not found, using defaults")
logging.error("Could not connect to the payment service")
logging.critical("Out of memory - shutting down")

Sortie :

2026-10-06 09:30:12,481 INFO Application started
2026-10-06 09:30:12,481 WARNING Config file not found, using defaults
2026-10-06 09:30:12,482 ERROR Could not connect to the payment service
2026-10-06 09:30:12,482 CRITICAL Out of memory - shutting down

Analyse du code ligne par ligne

LigneExplication
import loggingLe module intégré de Python pour les journaux. Aucune installation nécessaire.
logging.basicConfig(...)Une configuration unique au début du programme : quel est le niveau minimum et à quoi ressemble chaque enregistrement.
level=logging.INFON’afficher qu’à partir d’INFO.
format="..."Le modèle de l’enregistrement : heure, niveau, message.
logging.debug(...)Non affiché ! DEBUG (10) est inférieur à INFO (20), il a donc été filtré.
logging.info(...) à logging.critical(...)Affichés, car ils sont au niveau INFO ou au-dessus.
,481 dans la sortieLes millisecondes. Utile quand de nombreux événements se produisent dans la même seconde.

Essayez : passez à level=logging.DEBUG et relancez. Maintenant 5 lignes apparaissent. Avec level=logging.ERROR, seules 2 apparaissent. Vous avez changé une seule ligne, sans toucher aux messages eux-mêmes. C’est la force des niveaux.

Un autre exemple

Des journaux dans un vrai programme, combinés avec les Exceptions des leçons précédentes. week-2/orders.py :

import logging

logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s %(levelname)-8s %(funcName)s: %(message)s",
)

STOCK = {"bread": 5, "milk": 0, "eggs": 12}

def place_order(product, quantity):
    logging.debug("place_order called with product=%s quantity=%s", product, quantity)

    if product not in STOCK:
        logging.error("Unknown product requested: %s", product)
        return False

    available = STOCK[product]
    if available == 0:
        logging.warning("Product out of stock: %s", product)
        return False

    if quantity > available:
        logging.warning("Not enough %s: requested %s, available %s", product, quantity, available)
        return False

    STOCK[product] = available - quantity
    logging.info("Order placed: %s x%s (left in stock: %s)", product, quantity, STOCK[product])
    return True

place_order("bread", 2)
place_order("milk", 1)
place_order("cake", 1)
place_order("eggs", 20)

Sortie :

2026-10-06 09:41:03,115 DEBUG    place_order: place_order called with product=bread quantity=2
2026-10-06 09:41:03,115 INFO     place_order: Order placed: bread x2 (left in stock: 3)
2026-10-06 09:41:03,116 DEBUG    place_order: place_order called with product=milk quantity=1
2026-10-06 09:41:03,116 WARNING  place_order: Product out of stock: milk
2026-10-06 09:41:03,116 DEBUG    place_order: place_order called with product=cake quantity=1
2026-10-06 09:41:03,116 ERROR    place_order: Unknown product requested: cake
2026-10-06 09:41:03,117 DEBUG    place_order: place_order called with product=eggs quantity=20
2026-10-06 09:41:03,117 WARNING  place_order: Not enough eggs: requested 20, available 12

Analyse :

  • %(levelname)-8s — le -8 complète le nom du niveau jusqu’à 8 caractères, pour que les messages soient alignés en colonne. Facile à lire.
  • %(funcName)s — vous voyez de quelle fonction vient chaque enregistrement.
  • logging.info("... %s ...", product, quantity) — %s est un emplacement pour une valeur, et les valeurs sont passées comme arguments supplémentaires. C’est la façon recommandée en logging : le texte n’est construit que si le message sera vraiment affiché, donc les messages DEBUG filtrés ne coûtent presque rien.
  • Le choix des niveaux : « commandé » est INFO (un événement normal et important). « En rupture de stock » est WARNING (l’activité continue, mais il vaut la peine de le savoir). « Le produit n’existe pas » est ERROR (quelque chose ne va pas dans la requête, peut-être un bug côté client). Les détails de l’appel sont en DEBUG.
  • if product not in STOCK — in sur un dict vérifie si la clé existe.

Passez maintenant à level=logging.WARNING : le journal n’affichera que les 3 problèmes. C’est à cela qu’il ressemblera sur un vrai serveur, où seules les anomalies sont intéressantes.

Erreur courante

Erreur 1 : appeler basicConfig après qu’un message a déjà été écrit, ou deux fois.

import logging
logging.info("starting")                     # uses the default config!
logging.basicConfig(level=logging.INFO)      # ignored - already configured
logging.info("now?")                         # still not shown

Le premier appel à logging.info met automatiquement en place la configuration par défaut (WARNING), et un basicConfig tardif est silencieusement ignoré. basicConfig toujours en premier, et une seule fois, juste après l’import.

Erreur 2 : « je ne vois pas mes info ». La raison est presque toujours la valeur par défaut WARNING. La correction : level=logging.INFO (ou DEBUG).

Erreur 3 : le mauvais niveau.

logging.error("User dana logged in")       # not an error!
logging.info("Payment failed!")            # this IS an error

Un mauvais niveau rend le filtrage inutile : celui qui ne regarde que les ERROR manquera un paiement échoué, et sera inondé de connexions ordinaires.

Erreur 4 : journaliser des informations sensibles. logging.info("login: user=dana password=1234") — jamais de mots de passe, de numéros de carte bancaire ni de jetons (tokens). Les journaux sont lus par beaucoup de monde et conservés longtemps.

Exercices

Les exercices n’utilisent que ce qui a été vu dans cette leçon et les précédentes. Essayez d’abord seul, et n’ouvrez la solution qu’ensuite.

Exercice 1: Quel niveau ?

Pour chaque événement — à quel niveau de journal l’enregistreriez-vous : DEBUG, INFO, WARNING, ERROR ou CRITICAL ?

  1. Un utilisateur s’est connecté avec succès.
  2. La valeur d’une variable dans une boucle, pendant la recherche d’un bug.
  3. Un utilisateur a tapé un mauvais mot de passe.
  4. Le paiement a échoué.
  5. Il n’y a pas de connexion à la base de données, et le serveur ne peut pas fonctionner du tout.
Afficher la solution
  1. INFO — un événement normal qu’il est important d’enregistrer.
  2. DEBUG — des détails techniques qui ne sont utiles que pendant le développement.
  3. WARNING — quelque chose d’inhabituel, mais le système continue de fonctionner.
  4. ERROR — une action a échoué.
  5. CRITICAL — tout le système ne peut pas fonctionner.

Exercice 2: Le niveau minimum

import logging

logging.basicConfig(level=logging.WARNING)

logging.debug("checking item 3")
logging.info("order 17 created")
logging.warning("stock is low: 2 left")
logging.error("payment failed for order 17")

Lesquels des messages apparaîtront, et pourquoi ? Que faut-il changer pour les voir tous ?

Afficher la solution

Seuls WARNING et ERROR apparaîtront :

WARNING:root:stock is low: 2 left
ERROR:root:payment failed for order 17

level=logging.WARNING est un niveau minimum : seuls les messages de ce niveau et au-dessus sont affichés. Pour les voir tous, passez à level=logging.DEBUG. Sans toucher au reste du code — c’est tout l’avantage.

Exercice 3: De print à logging

Remplacez chaque print par le niveau de journal adapté, et définissez un format avec l’heure, le niveau et le message :

def withdraw(balance, amount):
    print("withdraw called with", amount)
    if amount > balance:
        print("not enough money, balance is", balance)
        return balance
    print("withdrew", amount)
    return balance - amount
Afficher la solution
import logging

logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s %(levelname)-8s %(message)s",
)


def withdraw(balance, amount):
    logging.debug("withdraw called with %s", amount)
    if amount > balance:
        logging.error("not enough money, balance is %s", balance)
        return balance
    logging.info("withdrew %s", amount)
    return balance - amount


withdraw(100, 30)
withdraw(100, 500)
  • L’entrée dans la fonction — DEBUG, un retrait réussi — INFO, un échec — ERROR.
  • Le format ajoute l’heure et le niveau à chaque ligne, par exemple : 2026-11-01 10:15:02 ERROR not enough money, balance is 100.

Exercice 4: Lire une ligne de journal

2026-11-01 09:14:30 WARNING  Wrong password for avi
  1. Que représente chaque partie de la ligne, et quelle partie du réglage format la crée ?
  2. Pourquoi est-il important que la ligne contienne une heure ?
Afficher la solution
  1. Les parties :
    • 2026-11-01 09:14:30 — l’heure, créée par %(asctime)s.
    • WARNING — le niveau, créé par %(levelname)s.
    • Wrong password for avi — le message, créé par %(message)s.
  2. Quand quelque chose se passe sur le serveur à trois heures du matin, l’heure permet de reconstituer ce qui s’est passé et dans quel ordre, et de le recouper avec d’autres événements.

Exercice 5: print ou logging ?

Pour chaque cas — print ou logging ? Expliquez.

  1. Un programme qui demande son nom à l’utilisateur et le salue.
  2. Un serveur qui tourne toute la nuit, et le matin vous voulez savoir quelles commandes ont échoué.
  3. Une vérification temporaire de la valeur d’une variable pendant le développement, que vous voulez désactiver sans la supprimer.
Afficher la solution
  1. print — c’est la sortie que le programme montre à l’utilisateur.
  2. logging (ERROR) — une information pour le développeur, avec heure et niveau, que l’on peut enregistrer et rechercher.
  3. logging (DEBUG) — on peut désactiver tous les messages DEBUG en une ligne (en relevant le niveau minimum), sans supprimer de code.

La règle : print pour l’utilisateur, logging pour le développeur.

Résumé

  • Journal = un enregistrement d’événements : quand, quelle gravité, et ce qui s’est passé. La « boîte noire » du logiciel.
  • print = la sortie pour l’utilisateur. logging = un enregistrement pour les développeurs : heure, niveau, filtrage et destinations.
  • 5 niveaux : DEBUG < INFO < WARNING < ERROR < CRITICAL. Seuls les messages du niveau minimum et au-dessus sont affichés.
  • La valeur par défaut est WARNING. basicConfig est appelé une seule fois, en premier.
  • logging.info("x=%s", x) est la forme recommandée. Et ne journalisez jamais de mots de passe.

Documentation officielle