Journalisation A : pourquoi print n'est pas du logging, et les niveaux du journal
- 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.basicConfiget 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
printn’est pas du logging. - Le module
loggingde 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é ?
printa 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 declinedPourquoi print n’est pas du logging
print | logging | |
|---|---|---|
| Heure | Aucune. On ne sait pas quand c’est arrivé. | Automatique dans chaque enregistrement |
| Gravité | Tous les messages se ressemblent | 5 niveaux. On peut filtrer |
| Désactiver/activer | Il faut supprimer ou ajouter des lignes de code | Changer un seul réglage, sans toucher au code |
| Destination | Seulement l’écran | L’écran, un fichier, un serveur de journaux… |
| Sur un serveur | Se perd | Conservé 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
| Niveau | Nombre | Quand l’utiliser | Exemple |
|---|---|---|---|
DEBUG | 10 | Détails techniques pour le développeur, pendant le développement et le débogage | "cart contents: [...]" |
INFO | 20 | Événements normaux et importants — « tout va bien » | "user dana logged in" |
WARNING | 30 | Quelque chose d’inhabituel, mais le système continue | "disk 90% full", "retrying request" |
ERROR | 40 | Une action a échoué | "payment failed for order 1042" |
CRITICAL | 50 | Tout 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) :
INFOouWARNING. 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.
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 :
| Champ | Ce qui est affiché |
|---|---|
%(asctime)s | La date et l’heure |
%(levelname)s | Le nom du niveau (INFO, ERROR…) |
%(message)s | Votre message |
%(funcName)s | Le nom de la fonction qui a écrit l’enregistrement |
%(lineno)d | Le 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 downAnalyse du code ligne par ligne
| Ligne | Explication |
|---|---|
import logging | Le 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.INFO | N’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 sortie | Les 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 12Analyse :
%(levelname)-8s— le-8complè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)—%sest 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—insur 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 shownLe 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 errorUn 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 ?
- Un utilisateur s’est connecté avec succès.
- La valeur d’une variable dans une boucle, pendant la recherche d’un bug.
- Un utilisateur a tapé un mauvais mot de passe.
- Le paiement a échoué.
- Il n’y a pas de connexion à la base de données, et le serveur ne peut pas fonctionner du tout.
Afficher la solution
- INFO — un événement normal qu’il est important d’enregistrer.
- DEBUG — des détails techniques qui ne sont utiles que pendant le développement.
- WARNING — quelque chose d’inhabituel, mais le système continue de fonctionner.
- ERROR — une action a échoué.
- 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 17level=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 - amountAfficher 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- Que représente chaque partie de la ligne, et quelle partie du réglage
formatla crée ? - Pourquoi est-il important que la ligne contienne une heure ?
Afficher la solution
- 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.
- 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.
- Un programme qui demande son nom à l’utilisateur et le salue.
- Un serveur qui tourne toute la nuit, et le matin vous voulez savoir quelles commandes ont échoué.
- 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
- print — c’est la sortie que le programme montre à l’utilisateur.
- logging (ERROR) — une information pour le développeur, avec heure et niveau, que l’on peut enregistrer et rechercher.
- 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.
basicConfigest appelé une seule fois, en premier. logging.info("x=%s", x)est la forme recommandée. Et ne journalisez jamais de mots de passe.