À propos de la journalisation et de la configuration de l’enregistreur

Aperçu

Le sous-système de journalisation dans ROS 2 vise à fournir des messages de journalisation à diverses cibles, notamment :

  • À la console (le cas échéant)

  • Pour enregistrer des fichiers sur le disque (si le stockage local est disponible)

  • Vers le sujet /rosout sur le réseau ROS 2

Par défaut, les messages de journalisation des nœuds ROS 2 seront envoyés à la console (sur stderr), aux fichiers de journalisation sur le disque et à la rubrique /rosout sur le réseau ROS 2. Toutes les cibles peuvent être activées ou désactivées individuellement sur une base par nœud.

Le reste de ce document passera en revue certaines des idées derrière le sous-système de journalisation.

Degré de gravité

Les messages de log ont un niveau de sévérité qui leur est associé : DEBUG, INFO, WARN, ERROR ou FATAL, dans l’ordre croissant.

Un enregistreur traitera uniquement les messages de journal dont la gravité est égale ou supérieure à un niveau spécifié choisi pour l’enregistreur.

Chaque nœud est associé à un enregistreur qui inclut automatiquement le nom et l’espace de noms du nœud. Si le nom du nœud est remappé en externe sur autre chose que ce qui est défini dans le code source, il sera reflété dans le nom de l’enregistreur. Des enregistreurs non-nœuds peuvent également être créés en utilisant un nom spécifique.

Les noms des enregistreurs représentent une hiérarchie. Si le niveau d’un enregistreur nommé « abc.def » n’est pas défini, il s’en remettra au niveau de son parent nommé « abc », et si ce niveau est également non défini, le niveau d’enregistrement par défaut sera utilisé. Lorsque le niveau de l’enregistreur « abc » est modifié, tous ses descendants (par exemple « abc.def », « abc.ghi.jkl ») verront leur niveau impacté à moins que leur niveau n’ait été explicitement défini.

Apis

Ce sont les API que les utilisateurs finaux de l’infrastructure de journalisation ROS 2 doivent utiliser, réparties par bibliothèque cliente.

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL} - affiche le message de style printf donné chaque fois que cette ligne est atteinte

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_ONCE - affiche le message de style printf donné uniquement la première fois que cette ligne est atteinte

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_EXPRESSION - affiche le message de style printf donné uniquement si l’expression donnée est vraie

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_FUNCTION - affiche le message de style printf donné uniquement si la fonction donnée renvoie true

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_SKIPFIRST - produit le message de style printf donné tous sauf la première fois que cette ligne est atteinte

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_THROTTLE - produit le message de style printf donné pas plus que le taux donné en millisecondes entières

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_SKIPFIRST_THROTTLE - produit le message de style printf donné pas plus que le taux donné en millisecondes entières, mais ignore le premier

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM - affiche le message de style flux C++ donné chaque fois que cette ligne est atteinte

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_ONCE - affiche le message de style flux C++ donné uniquement la première fois que cette ligne est atteinte

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_EXPRESSION - affiche le message de style flux C++ donné uniquement si l’expression donnée est vraie

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_FUNCTION - affiche le message de style flux C++ donné uniquement si la fonction donnée renvoie true

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_SKIPFIRST - produit le message de style flux C++ donné tous sauf la première fois que cette ligne est atteinte

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_THROTTLE - produit le message de style flux C++ donné pas plus que le taux donné en millisecondes entières

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_SKIPFIRST_THROTTLE - produit le message de style flux C++ donné pas plus que le taux donné en millisecondes entières, mais ignore le premier

Chacune des API ci-dessus prend un objet rclcpp::Logger comme premier argument. Cela peut être extrait de l’API du nœud en appelant node->get_logger() (recommandé), ou en construisant un objet autonome rclcpp::Logger.

  • rutils_logging_set_logger_level - Définit le niveau de journalisation pour un nom de journalisation particulier au niveau de gravité donné

  • rutils_logging_get_logger_effective_level - Étant donné un nom d’enregistreur, renvoie le niveau de l’enregistreur (qui peut être désactivé)

Configuration

Étant donné que rclcpp et rclpy utilisent la même infrastructure de journalisation sous-jacente, les options de configuration sont les mêmes.

Variables d’environnement

Les variables d’environnement suivantes contrôlent certains aspects des enregistreurs ROS 2. Pour chacun des paramètres d’environnement, notez qu’il s’agit d’un paramètre à l’échelle du processus, il s’applique donc à tous les nœuds de ce processus.

  • ROS_LOG_DIR - Contrôle le répertoire de journalisation utilisé pour écrire les messages de journalisation sur le disque (s’il est activé). S’il n’est pas vide, utilisez le répertoire exact spécifié dans cette variable. Si vide, utilisez le contenu de la variable d’environnement ROS_HOME pour construire un chemin de la forme $ROS_HOME/.log. Dans tous les cas, le caractère ~ est étendu au répertoire HOME de l’utilisateur.

  • ROS_HOME - Contrôle le répertoire personnel utilisé pour divers fichiers ROS, y compris les fichiers de journalisation et de configuration. Dans le contexte de la journalisation, cette variable est utilisée pour construire un chemin vers un répertoire pour les fichiers journaux. Si non vide, utilisez le contenu de cette variable pour le chemin ROS_HOME. Dans tous les cas, le caractère ~ est étendu au répertoire HOME des utilisateurs.

  • RCUTILS_LOGGING_USE_STDOUT - Contrôle vers quel flux les messages de sortie vont. Si ce n’est pas défini ou 0, utilisez stderr. Si c’est 1, utilisez stdout.

  • RCUTILS_LOGGING_BUFFERED_STREAM - Contrôle si le flux de journalisation (tel que configuré dans RCUTILS_LOGGING_USE_STDOUT) doit être mis en mémoire tampon ou non. S’il n’est pas défini, utilisez la valeur par défaut du flux (généralement en mémoire tampon pour stdout et sans mémoire tampon pour stderr). S’il s’agit de 0, forcez le flux à ne pas être tamponné. Si c’est 1, force le flux à être mis en mémoire tampon.

  • RCUTILS_COLORIZED_OUTPUT - Contrôle si les couleurs sont utilisées lors de la sortie des messages. S’il n’est pas défini, détermine automatiquement en fonction de la plate-forme et si la console est un ATS. Si 0, forcer la désactivation en utilisant des couleurs pour la sortie. Si 1, forcez l’activation en utilisant des couleurs pour la sortie.

  • RCUTILS_CONSOLE_OUTPUT_FORMAT - Contrôle les champs qui sont sortis pour chaque message de journal. Les champs disponibles sont :

    • {severity} - Le niveau de gravité.

    • {name} - Le nom du logger (peut être vide).

    • {message} - Le message du journal (peut être vide).

    • {function_name} - Le nom de la fonction à partir de laquelle elle a été appelée (peut être vide).

    • {file_name} - Le nom du fichier à partir duquel il a été appelé (peut être vide).

    • {time} - Le temps en secondes depuis l’époque.

    • {time_as_nanoseconds} - Le temps en nanosecondes depuis l’époque.

    • {line_number} - Le numéro de ligne à partir duquel il a été appelé (peut être vide).

    Si aucun format n’est donné, une valeur par défaut de [{severity}] [{time}] [{name}] : {message} est utilisée.

Création de nœud

Lors de l’initialisation d’un nœud ROS 2, il est possible de contrôler certains aspects du comportement via les options de nœud. Comme il s’agit d’options par nœud, elles peuvent être définies différemment pour différents nœuds, même lorsque les nœuds sont composés en un seul processus.

  • log_levels - Le niveau de journalisation à utiliser pour un composant dans ce nœud particulier. Cela peut être défini avec ce qui suit : ros2 run demo_nodes_cpp talker --ros-args --log-level talker:=DEBUG

  • external_log_config_file - Le fichier externe à utiliser pour configurer l’enregistreur principal. S’il est NULL, la configuration par défaut sera utilisée. Notez que le format de ce fichier est spécifique au backend (et n’est actuellement pas implémenté pour le logger backend par défaut de spdlog). Cela peut être défini avec ce qui suit : ros2 run demo_nodes_cpp talker --ros-args --log-config-file log-config.txt

  • log_stdout_disabled - S’il faut désactiver l’écriture des messages du journal sur la console. Cela peut être fait avec ce qui suit : ros2 run demo_nodes_cpp talker --ros-args --disable-stdout-logs

  • log_rosout_disabled - S’il faut désactiver l’écriture des messages de journalisation vers /rosout. Cela peut considérablement économiser sur la bande passante du réseau, mais les observateurs externes ne pourront pas surveiller la journalisation. Cela peut être fait avec ce qui suit : ros2 run demo_nodes_cpp talker --ros-args --disable-rosout-logs

  • log_ext_lib_disabled - S’il faut complètement désactiver l’utilisation d’un enregistreur externe. Cela peut être plus rapide dans certains cas, mais signifie que les journaux ne seront pas écrits sur le disque. Cela peut être fait avec ce qui suit : ros2 run demo_nodes_cpp talker --ros-args --disable-external-lib-logs

Conception du sous-système de journalisation

L’image ci-dessous montre les cinq éléments principaux du sous-système de journalisation et leur interaction.

Architecture de journalisation ROS 2

rutils

rutils a une implémentation de journalisation qui peut formater les messages de journalisation selon un certain format (voir Configuration ci-dessus), et afficher ces messages de journalisation sur une console. rutils implémente une solution de journalisation complète, mais permet aux composants de niveau supérieur de s’insérer dans l’infrastructure de journalisation dans un modèle d’injection de dépendances. Cela deviendra plus évident lorsque nous parlerons de la couche rcl ci-dessous.

Notez qu’il s’agit d’une implémentation de journalisation par processus, donc tout ce qui est configuré à ce niveau affectera l’ensemble du processus, pas seulement les nœuds individuels.

rcl_logging_spdlog

rcl_logging_spdlog implémente l’API rcl_logging_interface, et fournit ainsi des services de journalisation externes à la couche rcl. En particulier, l’implémentation rcl_logging_spdlog prend des messages de journal formatés et les écrit dans des fichiers journaux sur le disque en utilisant la bibliothèque spdlog, généralement dans ~/.ros/log (bien que cela soit configurable ; voir Configuration ci-dessus).

RCL

Le sous-système de journalisation dans rcl utilise rcutils et rcl_logging_spdlog pour fournir la majeure partie des services de journalisation ROS 2. Lorsque les messages de log arrivent, rcl décide où les envoyer. Il existe 3 endroits principaux où les messages de journalisation peuvent être livrés ; un nœud individuel peut avoir n’importe quelle combinaison d’entre eux activée :

  • Vers la console via la couche rutils

  • Vers disque via la couche rcl_logging_spdlog

  • Vers le sujet /rosout sur le réseau ROS 2 via la couche RMW

RCLCP

Il s’agit de l’API principale de ROS 2 C++ qui se trouve au-dessus de l’API rcl. Dans le contexte de la journalisation, rclcpp fournit les macros de journalisation RCLCPP_ ; voir APIs ci-dessus pour une liste complète. Lorsqu’une des macros RCLCPP_ s’exécute, elle vérifie le niveau de gravité actuel du nœud par rapport au niveau de gravité de la macro. Si le niveau de gravité de la macro est supérieur ou égal au niveau de gravité du nœud, le message sera formaté et envoyé à tous les emplacements actuellement configurés. Notez que rclcpp utilise un mutex global pour les appels de journalisation, de sorte que tous les appels de journalisation dans le même processus finissent par être monothread.

rclpy

Il s’agit de l’API Python principale de ROS 2 qui se trouve au-dessus de l’API rcl. Dans le contexte de la journalisation, rclpy fournit les fonctions de style logger.debug ; voir APIs ci-dessus pour une liste complète. Lorsqu’une des fonctions logger.debug s’exécute, elle vérifie le niveau de gravité actuel du nœud par rapport au niveau de gravité de la macro. Si le niveau de gravité de la macro est supérieur ou égal au niveau de gravité du nœud, le message sera formaté et envoyé à tous les emplacements actuellement configurés.

Utilisation de la journalisation