La spécification de l'option globale de R pour la gestion des erreurs non catastrophiques a fonctionné pour moi, ainsi qu'un flux de travail personnalisé pour conserver les informations sur l'erreur et examiner ces informations après l'échec. J'utilise actuellement la version 3.4.1 de R. Ci-dessous, j'ai inclus une description du flux de travail qui a fonctionné pour moi, ainsi qu'une partie du code que j'ai utilisé pour définir l'option globale de gestion des erreurs dans R.
Tel que je l'ai configuré, le traitement des erreurs crée également un fichier RData contenant tous les objets de la mémoire de travail au moment de l'erreur. Ce fichier peut être relu dans R en utilisant load()
et ensuite les différents environnements tels qu'ils existaient au moment de l'erreur peuvent être inspectés de manière interactive en utilisant debugger(errorDump)
.
Je noterai que j'ai pu obtenir les numéros de ligne dans le fichier traceback()
de toutes les fonctions personnalisées de la pile, mais seulement si j'utilise la fonction keep.source=TRUE
lors de l'appel source()
pour toutes les fonctions personnalisées utilisées dans mon script. Sans cette option, le paramétrage de l'option de gestion des erreurs globales comme ci-dessous a envoyé la sortie complète de la commande traceback()
dans un journal d'erreurs nommé error.log
mais les numéros de ligne n'étaient pas disponibles.
Voici les étapes générales que j'ai suivies dans mon flux de travail et comment j'ai pu accéder au vidage de la mémoire et au journal des erreurs après un échec R non interactif.
-
J'ai mis ce qui suit en haut du script principal que j'appelais depuis la ligne de commande. Cela définit l'option de gestion globale des erreurs pour la session R. Mon script principal était appelé myMainScript.R
. Les différentes lignes du code sont suivies de commentaires décrivant ce qu'elles font. Fondamentalement, avec cette option, lorsque R rencontre une erreur qui déclenche la commande stop()
il créera un fichier de vidage RData (*.rda) de la mémoire de travail pour tous les environnements actifs du répertoire. ~/myUsername/directoryForDump
et écrira également un journal d'erreurs nommé error.log
avec quelques informations utiles dans le même répertoire. Vous pouvez modifier cet extrait pour ajouter d'autres traitements en cas d'erreur (par exemple, ajouter un horodatage au fichier de vidage et aux noms de fichiers du journal des erreurs, etc.)
options(error = quote({
setwd('~/myUsername/directoryForDump'); # Set working directory where you want the dump to go, since dump.frames() doesn't seem to accept absolute file paths.
dump.frames("errorDump", to.file=TRUE, include.GlobalEnv=TRUE); # First dump to file; this dump is not accessible by the R session.
sink(file="error.log"); # Specify sink file to redirect all output.
dump.frames(); # Dump again to be able to retrieve error message and write to error log; this dump is accessible by the R session since not dumped to file.
cat(attr(last.dump,"error.message")); # Print error message to file, along with simplified stack trace.
cat('\nTraceback:');
cat('\n');
traceback(2); # Print full traceback of function calls with all parameters. The 2 passed to traceback omits the outermost two function calls.
sink();
q()}))
-
Assurez-vous qu'à partir du script principal et de tout appel de fonction ultérieur, chaque fois qu'une fonction est sourcée, l'option keep.source=TRUE
est utilisé. Ainsi, pour créer une source pour une fonction, on utilise source('~/path/to/myFunction.R', keep.source=TRUE)
. Ceci est nécessaire pour le traceback()
pour contenir les numéros de ligne. Il semble que vous puissiez également définir cette option de manière globale en utilisant options( keep.source=TRUE )
mais je ne l'ai pas testé pour voir si cela fonctionne. Si vous n'avez pas besoin de numéros de ligne, vous pouvez omettre cette option.
-
Depuis le terminal (en dehors de R), appelez le script principal en mode batch en utilisant Rscript myMainScript.R
. Cela démarre une nouvelle session R non interactive et exécute le script. myMainScript.R
. L'extrait de code donné à l'étape 1 qui a été placé en haut de l'écran myMainScript.R
définit l'option de gestion des erreurs pour la session R non interactive.
-
Rencontrer une erreur quelque part dans l'exécution de myMainScript.R
. Cela peut être dans le script principal lui-même, ou imbriqué plusieurs fonctions en profondeur. Lorsque l'erreur est rencontrée, le traitement sera effectué comme spécifié à l'étape 1, et la session R se terminera.
-
Un fichier de vidage RData nommé errorDump.rda
et un journal d'erreurs nommé error.log
sont créés dans le répertoire spécifié par '~/myUsername/directoryForDump'
dans le paramètre de l'option de traitement des erreurs globales.
-
À votre guise, vous pourrez inspecter error.log
pour consulter les informations relatives à l'erreur, y compris le message d'erreur proprement dit et la trace complète de la pile ayant conduit à l'erreur. Voici un exemple de journal généré en cas d'erreur ; notez les chiffres après le symbole #
sont les numéros de ligne de l'erreur à différents endroits de la pile d'appels :
Error in callNonExistFunc() : could not find function "callNonExistFunc"
Calls: test_multi_commodity_flow_cmd -> getExtendedConfigDF -> extendConfigDF
Traceback:
3: extendConfigDF(info_df, data_dir = user_dir, dlevel = dlevel) at test_multi_commodity_flow.R#304
2: getExtendedConfigDF(config_file_path, out_dir, dlevel) at test_multi_commodity_flow.R#352
1: test_multi_commodity_flow_cmd(config_file_path = config_file_path,
spot_file_path = spot_file_path, forward_file_path = forward_file_path,
data_dir = "../", user_dir = "Output", sim_type = "spot",
sim_scheme = "shape", sim_gran = "hourly", sim_adjust = "raw",
nsim = 5, start_date = "2017-07-01", end_date = "2017-12-31",
compute_averages = opt$compute_averages, compute_shapes = opt$compute_shapes,
overwrite = opt$overwrite, nmonths = opt$nmonths, forward_regime = opt$fregime,
ltfv_ratio = opt$ltfv_ratio, method = opt$method, dlevel = 0)
-
A votre guise, vous pouvez charger errorDump.rda
dans une session R interactive en utilisant load('~/path/to/errorDump.rda')
. Une fois chargé, appelez debugger(errorDump)
pour parcourir tous les objets R en mémoire dans l'un des environnements actifs. Voir l'aide de R sur debugger()
pour plus d'informations.
Ce flux de travail est extrêmement utile lorsque vous exécutez R dans un type d'environnement de production où des sessions R non interactives sont lancées à la ligne de commande et où vous souhaitez conserver des informations sur les erreurs inattendues. La possibilité de vider la mémoire dans un fichier que vous pouvez utiliser pour inspecter la mémoire de travail au moment de l'erreur, ainsi que le fait de disposer des numéros de ligne de l'erreur dans la pile d'appels, facilitent le débogage rapide post-mortem de ce qui a causé l'erreur.
42 votes
Des mises à jour ? Quatre ans plus tard, il semble que le problème persiste, malgré l'adoption de R par le grand public.
0 votes
J'ai aussi un très long R script avec beaucoup de petites sorties, je veux imprimer (underscore)(underscore)LINE/FILE(underscore)(underscore) (numéros de ligne et nom du script) comme cela en C, au lieu de coder en dur les numéros de ligne dans la source.
0 votes
Je ne sais pas si R interne a vraiment une notion de "numéros de ligne". Cependant, il a une notion de tâches complètes, c'est-à-dire de tâches de haut niveau. On pourrait, par exemple, facilement définir un gestionnaire de tâches pour indiquer quelle tâche de niveau supérieur a échoué. Bien sûr, ce n'est pas un grand réconfort pour ceux qui ont de grandes chaînes ou de grandes déclarations conditionnelles.