Stderr Et Stdout : Comprendre Les Journaux Et La Sortie
Les concepts de sortie standard (stdout
) et d’erreur standard (stderr
), bien que simples, jouent un rôle central dans l’enregistrement des journaux, la gestion des erreurs et la gestion des flux de données. Cet article explorera les différences et les applications de stdout
et stderr
, en particulier comment les utiliser efficacement dans un environnement Python.
1 Sortie standard (stdout
) et erreur standard (stderr
)
Dans la plupart des systèmes d’exploitation, la sortie standard et l’erreur standard sont les deux principaux flux de sortie d’un processus. Ils fournissent un mécanisme permettant au processus d’envoyer des informations et des messages d’erreur au terminal ou à un fichier. Bien que ces deux flux puissent être physiquement identiques (par exemple, affichés dans la même interface terminal), ils sont logiquement utilisés à des fins différentes :
- Sortie standard (
stdout
) : généralement utilisée pour afficher les résultats de l’exécution du programme ou des informations de fonctionnement normales. - Erreur standard (
stderr
) : spécifiquement utilisée pour afficher des messages d’erreur ou des avertissements, même lorsque la sortie standard est redirigée, ces informations doivent généralement être vues ou enregistrées.
2 print
et logging
en Python
En Python, la fonction print
envoie par défaut les informations à stdout
, tandis que le module logging
envoie par défaut les messages de journal à stderr
. L’objectif est de distinguer la sortie normale du programme des sorties de journal (y compris les erreurs et les informations de débogage), permettant ainsi aux développeurs de gérer et de filtrer plus facilement les informations de sortie.
2.1 Utilisation de print
print
est la fonction de sortie la plus basique en Python, utilisée pour envoyer des informations au flux de sortie standard. Elle est simple à utiliser et convient pour un débogage rapide ou pour afficher des informations à l’utilisateur. Par exemple :
|
|
2.2 Utilisation de logging
Le module logging
fournit un cadre flexible pour ajouter des messages de journal dans une application. Contrairement à print
, logging
prend en charge différents niveaux de journalisation (DEBUG, INFO, WARNING, ERROR, CRITICAL), permettant aux développeurs d’ajuster le niveau de détail et l’emplacement de la sortie des journaux selon leurs besoins. Par exemple :
|
|
3 tqdm et stderr
Dans les programmes complexes ou de longue durée, l’utilisation d’une barre de progression est un moyen efficace de montrer à l’utilisateur l’avancement du processus. La bibliothèque tqdm
de Python est un outil largement utilisé pour ajouter des barres de progression dans la ligne de commande. tqdm
envoie par défaut les informations de progression à stderr
pour éviter d’interférer avec la sortie normale du programme (stdout
).
3.1 Séparation de stdout
et stderr
Dans certains cas, il est utile de séparer la sortie normale des messages d’erreur ou de journal, par exemple en les redirigeant vers des fichiers ou des terminaux différents. En ligne de commande, vous pouvez utiliser les opérateurs de redirection >
et 2>
pour y parvenir. Dans le code Python, vous pouvez configurer le module logging
ou utiliser des objets de fichier spécifiques pour un contrôle plus granulaire.
|
|
Grâce à la redirection en ligne de commande, à la fonction print
de Python et même au module logging
, vous pouvez contrôler et séparer de manière flexible ces deux types de sortie, rendant ainsi la gestion des erreurs, l’enregistrement des journaux et l’interaction avec l’utilisateur plus clairs et ordonnés.
4 Utilisation de nohup
pour gérer stdout
et stderr
Lors du déploiement de processus en arrière-plan de longue durée, la commande nohup
devient un outil important. nohup
, ou “no hang up”, permet à une commande de continuer à s’exécuter après la déconnexion de l’utilisateur, ce qui est particulièrement utile pour lancer des tâches à distance. Une caractéristique clé de nohup
est sa capacité à gérer stdout
et stderr
.
Par défaut, l’utilisation de nohup
pour exécuter une commande redirige stdout
et stderr
vers le fichier nohup.out
, sauf indication contraire. Cela signifie que les sorties normales et les messages d’erreur seront capturés dans le même fichier, ce qui est pratique pour un examen ultérieur. Cependant, dans certains cas, il peut être plus utile de séparer ces deux types de sortie.
4.1 Séparation de stdout
et stderr
avec nohup
Pour séparer les sorties stdout
et stderr
lors de l’utilisation de nohup
, vous pouvez combiner les opérateurs de redirection. Par exemple :
|
|
Cette commande redirige stdout
vers output.log
, stderr
vers error.log
, et exécute le programme en arrière-plan avec &
. Ainsi, même si le terminal ou la session SSH est fermé, le programme continuera de s’exécuter et ses sorties seront correctement enregistrées.
5 Comportement de mise en mémoire tampon en Python
stdout
et stderr
ont des comportements différents en matière de mise en mémoire tampon. Par défaut, stdout
est mis en mémoire tampon par ligne lorsqu’il est connecté à un terminal, ce qui signifie qu’il met en mémoire tampon les données jusqu’à ce qu’un saut de ligne soit reçu ou que le tampon soit plein ; en mode non interactif, stdout
est mis en mémoire tampon par bloc (comme un fichier). En revanche, stderr
est toujours mis en mémoire tampon par ligne (avant la version 3.9 de Python, il était mis en mémoire tampon par bloc en mode non interactif). Les informations suivantes proviennent de la documentation officielle sys — Paramètres et fonctions liés au système — Documentation Python 3.12.2
Lorsque interactif, le flux
stdout
est mis en mémoire tampon par ligne. Sinon, il est mis en mémoire tampon par bloc comme les fichiers texte réguliers. Le fluxstderr
est mis en mémoire tampon par ligne dans les deux cas. Vous pouvez rendre les deux flux non mis en mémoire tampon en passant l’option de ligne de commande-u
ou en définissant la variable d’environnementPYTHONUNBUFFERED
.Changé dans la version 3.9:
stderr
non interactif est maintenant mis en mémoire tampon par ligne au lieu d’être entièrement mis en mémoire tampon.
Plus la granularité de la mise en mémoire tampon est petite, plus la sortie est rapide, mais le coût d’E/S est plus élevé. Avant Python 3.9, stdout
et stderr
avaient la même granularité de mise en mémoire tampon, ce qui n’était pas très raisonnable ; après la version 3.9, stderr
a une granularité de mise en mémoire tampon plus petite, ce qui signifie que chaque opération d’écriture est plus rapide que stdout
. Cette différence rend stderr
adapté aux informations d’erreur et de journal, garantissant que même en cas de plantage ou de sortie anormale du programme, ces informations ont une priorité plus élevée que la sortie standard.
En C++, l’erreur standard n’est pas mise en mémoire tampon (voir ci-dessous), ce qui est plus agressif, mais je pense personnellement que c’est plus raisonnable.
Heureusement, en Python, vous pouvez désactiver ce comportement de mise en mémoire tampon en utilisant python -u
ou en définissant la variable d’environnement PYTHONUNBUFFERED
, ou en utilisant directement sys.stdout.flush()
pour contrôler le moment de la sortie.
6 Comportement en environnement concurrent en Python
Lors de l’utilisation de stdout
et stderr
dans un environnement multithread ou multiprocessus, les sorties peuvent être entremêlées ou désordonnées, car les sorties provenant de différents threads ou processus peuvent interférer les unes avec les autres lors de l’écriture sur le terminal ou le fichier. Une solution à ce problème est de créer des fichiers de sortie distincts pour chaque thread ou processus, ou d’utiliser des verrous de thread (thread locks) ou des mécanismes de synchronisation de processus (comme multiprocessing.Lock
) pour synchroniser l’accès à stdout
ou stderr
.
7 Contrôle de stdout
et stderr
en Python
Dans des applications complexes, vous pouvez avoir besoin de contrôler plus flexiblement la destination des flux de sortie. Python offre plusieurs moyens pour y parvenir :
- Redirection de
stdout
etstderr
: vous pouvez rediriger la sortie standard et l’erreur standard d’un programme Python en modifiant les valeurs desys.stdout
etsys.stderr
. Cela est particulièrement utile pour capturer et analyser les sorties, ou pour rediriger les sorties vers des interfaces graphiques ou d’autres dispositifs de sortie non standards. - Utilisation du module
subprocess
: lors de l’exécution de commandes ou de scripts externes, le modulesubprocess
vous permet de contrôler les fluxstdout
etstderr
de la commande, y compris les rediriger vers des variables internes du programme Python, ou les séparer ou les combiner. - Utilisation avancée du module de journalisation : le module
logging
de Python prend en charge l’envoi des journaux vers plusieurs destinations, y compris des fichiers, la sortie standard, le réseau, etc. En configurant différents gestionnaires de journaux (handlers), vous pouvez mettre en œuvre des solutions de gestion de journaux complexes, telles que la séparation des journaux en fonction du niveau ou du contenu du message vers différentes sorties.
7.1 Conseils
- Gérer les sorties avec précaution : lors de la conception de logiciels, distinguer clairement la sortie destinée à l’interaction avec l’utilisateur (
stdout
) de celle destinée au rapport d’erreurs ou à l’enregistrement des journaux (stderr
). Cela aide à améliorer la convivialité et la maintenabilité du programme. - Optimiser les performances : tenir compte de l’impact des opérations de sortie sur les performances, en particulier dans les scénarios de journalisation ou de sortie de données à haute fréquence. Une utilisation raisonnable de la mise en mémoire tampon et du traitement par lots peut réduire l’impact sur les performances.
- Considérations de sécurité : avant de sortir des informations sensibles, effectuer un filtrage et une désensibilisation appropriés pour éviter de divulguer des données sensibles par le biais des journaux.
En comprenant en profondeur et en appliquant de manière flexible stdout
et stderr
, vous pouvez construire des applications Python plus robustes et plus faciles à gérer, traiter efficacement les journaux et les sorties, et améliorer l’expérience utilisateur et la stabilité de l’application.
8 Comportement de mise en mémoire tampon en C++
En C++, stdout
(généralement associé à std::cout
) et stderr
(associé à std::cerr
) ont des stratégies de mise en mémoire tampon différentes :
std::cout
est par défaut mis en mémoire tampon par ligne, ce qui signifie que lorsqu’il est connecté à un terminal, la sortie est rafraîchie à chaque saut de ligne ou lorsque le tampon est plein.std::cerr
est par défaut non mis en mémoire tampon, donc chaque donnée écrite dansstd::cerr
est immédiatement sortie, ce qui est très utile pour signaler des messages d’erreur, car cela réduit le risque que des informations d’erreur ne soient pas sorties en cas de plantage du programme.
9 Redirection de stdout
et stderr
Dans un programme C++, vous pouvez rediriger stdout
et stderr
de plusieurs manières. Une méthode courante consiste à utiliser la fonction freopen
pour rediriger la sortie standard ou l’erreur standard vers un fichier pendant l’exécution du programme :
|
|
Cette méthode peut être utilisée pour rediriger la sortie vers un fichier, facilitant ainsi l’analyse et le débogage ultérieurs.
10 Utilisation en environnement multithread en C++
Dans un programme C++ multithread, l’utilisation de std::cout
et std::cerr
peut entraîner des conditions de concurrence, entraînant une sortie désordonnée. Pour éviter cela, il est recommandé d’utiliser des verrous mutex (comme std::mutex
) pour synchroniser l’accès à ces flux :
|
|
11 Contrôle de la sortie en C++
La bibliothèque standard C++ fournit std::streambuf
, qui peut être utilisé pour un contrôle plus fin de std::cout
et std::cerr
, y compris la redirection et le comportement de mise en mémoire tampon personnalisé. En héritant de std::streambuf
et en réécrivant les fonctions membres correspondantes, vous pouvez créer des stratégies de mise en mémoire tampon personnalisées ou rediriger la sortie vers des composants GUI, des connexions réseau, etc.
11.1 Conseils
- Utiliser la mise en mémoire tampon de manière appropriée : choisir une stratégie de mise en mémoire tampon appropriée en fonction du scénario d’application. Pour les informations d’erreur nécessitant un retour immédiat, utiliser
std::cerr
ou rafraîchir manuellementstd::cout
. - Éviter d’utiliser directement la sortie standard dans un environnement multithread : utiliser des verrous mutex ou d’autres mécanismes de synchronisation pour garantir la cohérence et l’ordre de la sortie.
- Utiliser la redirection et des
streambuf
personnalisés : pour traiter les sorties de manière plus flexible, envisager d’utiliser la redirection ou desstreambuf
personnalisés pour répondre à des besoins de sortie spécifiques, tels que l’enregistrement des journaux, la transmission sur le réseau, etc.
En maîtrisant ces techniques avancées, vous pouvez gérer et contrôler efficacement la sortie des programmes C++ tout en garantissant la robustesse et la flexibilité du programme.