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.

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 :

1
print("Hello, world!")

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 :

1
2
3
import logging

logging.error('This is an error message')

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.

1
python script.py > output.log 2> error.log

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 :

1
nohup python script.py > output.log 2> error.log &

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 flux stderr 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’environnement PYTHONUNBUFFERED.

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 et stderr : vous pouvez rediriger la sortie standard et l’erreur standard d’un programme Python en modifiant les valeurs de sys.stdout et sys.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 module subprocess vous permet de contrôler les flux stdout et stderr 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 dans std::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 :

1
2
freopen("output.txt", "w", stdout);
freopen("error.log", "w", stderr);

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 :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <mutex>
#include <thread>

std::mutex cout_mutex;

void thread_function(int id) {
    std::lock_guard<std::mutex> lock(cout_mutex);
    std::cout << "Thread " << id << " is running\n";
}

int main() {
    std::thread t1(thread_function, 1);
    std::thread t2(thread_function, 2);

    t1.join();
    t2.join();

    return 0;
}

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 manuellement std::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 des streambuf 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.

Buy me a coffee~
Tim AlipayAlipay
Tim PayPalPayPal
Tim WeChat PayWeChat Pay
0%