{"id":322174,"date":"2022-03-11T18:00:00","date_gmt":"2022-03-12T00:00:00","guid":{"rendered":"https:\/\/www.brodrigues.co\/blog\/2022-03-12-purely\/"},"modified":"2022-03-11T18:00:00","modified_gmt":"2022-03-12T00:00:00","slug":"capture-errors-warnings-and-messages","status":"publish","type":"post","link":"https:\/\/www.r-bloggers.com\/2022\/03\/capture-errors-warnings-and-messages\/","title":{"rendered":"Capture errors, warnings and messages"},"content":{"rendered":"<!-- \r\n<div style=\"min-height: 30px;\">\r\n[social4i size=\"small\" align=\"align-left\"]\r\n<\/div>\r\n-->\r\n\r\n<div style=\"border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 12px;\">\r\n[This article was first published on  <strong><a href=\"https:\/\/www.brodrigues.co\/blog\/2022-03-12-purely\/\"> Econometrics and Free Software<\/a><\/strong>, and kindly contributed to <a href=\"https:\/\/www.r-bloggers.com\/\" rel=\"nofollow\">R-bloggers<\/a>].  (You can report issue about the content on this page <a href=\"https:\/\/www.r-bloggers.com\/contact-us\/\">here<\/a>)\r\n<hr>Want to share your content on R-bloggers?<a href=\"https:\/\/www.r-bloggers.com\/add-your-blog\/\" rel=\"nofollow\"> click here<\/a> if you have a blog, or <a href=\"http:\/\/r-posts.com\/\" rel=\"nofollow\"> here<\/a> if you don't.\r\n<\/div>\n<script src=\"https:\/\/www.brodrigues.co\/rmarkdown-libs\/header-attrs\/header-attrs.js\"><\/script>\n\n\n<div style=\"text-align:center;\">\n<p><a href=\"https:\/\/www.youtube.com\/watch?v=vUvut7jOPgs\" rel=\"nofollow\" target=\"_blank\">\n<img src=\"https:\/\/i1.wp.com\/www.brodrigues.co\/img\/pure.jpg?w=578&#038;ssl=1\" title = \"Hell is side effects\" data-recalc-dims=\"1\"><\/a><\/p>\n<\/div>\n<p>In my <a href=\"https:\/\/www.youtube.com\/watch?v=vUvut7jOPgs\" rel=\"nofollow\" target=\"_blank\">last video<\/a> I tried to add a feature to my\n{loud} package (more info <a href=\"https:\/\/b-rodrigues.github.io\/loud\/\" rel=\"nofollow\" target=\"_blank\">here<\/a>) and I succeeded. But in\nsucceeding in realised that I would need to write a bit more code than what I expected. To make\na long story short: it is possible to capture errors using <code>purrr::safely()<\/code>:<\/p>\n<pre>library(purrr)\nsafe_log &lt;- safely(log)\n\na &lt;- safe_log(&quot;10&quot;)\n\nstr(a)\r\n## List of 2\n##  $ result: NULL\n##  $ error :List of 2\n##   ..$ message: chr &quot;non-numeric argument to mathematical function&quot;\n##   ..$ call   : language .Primitive(&quot;log&quot;)(x, base)\n##   ..- attr(*, &quot;class&quot;)= chr [1:3] &quot;simpleError&quot; &quot;error&quot; &quot;condition&quot;<\/pre>\n<p><code>a<\/code> is now a list with elements <code>$result<\/code> and <code>$error<\/code>. If everything goes right, <code>$result<\/code> holds\nthe result of the operation, and if everything goes wrong, <code>$result<\/code> is <code>NULL<\/code> but <code>$error<\/code> now\ncontains the error message. This is especially useful in non-interactive contexts. There is\nanother similar function in <code>{purrr}<\/code> called <code>quietly()<\/code>, which captures warnings and messages:<\/p>\n<pre>quiet_log &lt;- quietly(log)\n\nb &lt;- quiet_log(-10)\n\nstr(b)\r\n## List of 4\n##  $ result  : num NaN\n##  $ output  : chr &quot;&quot;\n##  $ warnings: chr &quot;NaNs produced&quot;\n##  $ messages: chr(0)<\/pre>\n<p>as you can see, providing a negative number to <code>log()<\/code> does not cause an error, but simply a\nwarning. A result of <code>NaN<\/code> is returned (you can try with <code>log(-10)<\/code> in your console). <code>quietly()<\/code>\ncaptures the warning message and returns a list of 4 elements, <code>$result<\/code>, <code>$output<\/code>, <code>$warnings<\/code>\nand <code>$messages<\/code>. The problem here, is that:<\/p>\n<pre>safe_log(-10)\r\n## Warning in .Primitive(&quot;log&quot;)(x, base): NaNs produced\r\n## $result\n## [1] NaN\n## \n## $error\n## NULL<\/pre>\n<p>returns something useless: <code>$result<\/code> is <code>NaN<\/code> (because that\u2019s what <code>log()<\/code> returns for negative\nnumbers) but <code>$error<\/code> is <code>NULL<\/code> since no error was thrown, but only a warning! We have a similar\nproblem with <code>quiet_log()<\/code>:<\/p>\n<pre>quiet_log(&quot;10&quot;)\r\nError in .Primitive(&quot;log&quot;)(x, base) : \n  non-numeric argument to mathematical function<\/pre>\n<p>here, the error message is thrown, but not captured, since <code>quietly()<\/code> does not capture error messages.<\/p>\n<p>So, are we back to square one? Not necessarily, since you could compose both functions:<\/p>\n<pre>pure_log &lt;- quietly(safely(log))\n\na2 &lt;- pure_log(-10)\n\nstr(a2)\r\n## List of 4\n##  $ result  :List of 2\n##   ..$ result: num NaN\n##   ..$ error : NULL\n##  $ output  : chr &quot;&quot;\n##  $ warnings: chr &quot;NaNs produced&quot;\n##  $ messages: chr(0)\r\nb2 &lt;- pure_log(&quot;10&quot;)\n\nstr(b2)\r\n## List of 4\n##  $ result  :List of 2\n##   ..$ result: NULL\n##   ..$ error :List of 2\n##   .. ..$ message: chr &quot;non-numeric argument to mathematical function&quot;\n##   .. ..$ call   : language .Primitive(&quot;log&quot;)(x, base)\n##   .. ..- attr(*, &quot;class&quot;)= chr [1:3] &quot;simpleError&quot; &quot;error&quot; &quot;condition&quot;\n##  $ output  : chr &quot;&quot;\n##  $ warnings: chr(0) \n##  $ messages: chr(0)<\/pre>\n<p>As you can see, in the case of <code>a2<\/code>, the warning was captured, and in the case of <code>b2<\/code> the error\nwas captured. The problem, is that the resulting object is quite complex. It\u2019s a list where\n<code>$result<\/code> is itself a list in case of a warning, or <code>$error<\/code> is a list in case of an error.<\/p>\n<p>I tried to write a function that would decorate a function (as do <code>safely()<\/code> and <code>quietly()<\/code>), which\nin turn would then return a simple list and capture, errors, warnings and messages. I came up with\nthis code, after re-reading <em>Advanced R<\/em>, in particular this\n<a href=\"https:\/\/adv-r.hadley.nz\/conditions.html\" rel=\"nofollow\" target=\"_blank\">chapter<\/a>:<\/p>\n<pre>purely &lt;- function(.f){\n\n  function(..., .log = &quot;Log start...&quot;){\n\n    res &lt;- rlang::try_fetch(\n                    rlang::eval_tidy(.f(...)),\n                    error = function(err) err,\n                    warning = function(warn) warn,\n                    message = function(message) message,\n                    )\n\n    final_result &lt;- list(\n      result = NULL,\n      log = NULL\n    )\n\n    final_result$result &lt;- if(any(c(&quot;error&quot;, &quot;warning&quot;, &quot;message&quot;) %in% class(res))){\n                             NA\n                           } else {\n                             res\n                           }\n\n    final_result$log &lt;- if(any(c(&quot;error&quot;, &quot;warning&quot;, &quot;message&quot;) %in% class(res))){\n                          res$message\n                        } else {\n                          NA\n                        }\n    final_result\n  }\n}\r\nf_m &lt;- function(x){\n  message(&quot;this is a message&quot;)\n  str(x)\n}\n\nf_w &lt;- function(x){\n  warning(&quot;this is a warning&quot;)\n  str(x)\n\n}\n\nf_e &lt;- function(){\n  stop(&quot;This is an error&quot;)\n\n}\n\npure_fm &lt;- purely(f_m)\npure_fw &lt;- purely(f_w)\npure_fe &lt;- purely(f_e)<\/pre>\n<p>Messages get captured:<\/p>\n<pre>pure_fm(10) |&gt;\n  str()\r\n## List of 2\n##  $ result: logi NA\n##  $ log   : chr &quot;this is a message\\n&quot;<\/pre>\n<p>as do warnings:<\/p>\n<pre>pure_fw(10) |&gt;\n  str()\r\n## List of 2\n##  $ result: logi NA\n##  $ log   : chr &quot;this is a warning&quot;<\/pre>\n<p>as do errors:<\/p>\n<pre>pure_fe() |&gt;\n  str()\r\n## List of 2\n##  $ result: logi NA\n##  $ log   : chr &quot;This is an error&quot;<\/pre>\n<p>The structure of the result is always <code>$result<\/code> and <code>$log<\/code>. In case everything goes well\n<code>$result<\/code> holds the result:<\/p>\n<pre>pure_log &lt;- purely(log)\n\npure_log(c(1,10))\r\n## $result\n## [1] 0.000000 2.302585\n## \n## $log\n## [1] NA<\/pre>\n<p>And another example, with a more complex call:<\/p>\n<pre>pure_mean &lt;- purely(mean)\n\npure_mean(c(1,10, NA), na.rm = TRUE)\r\n## $result\n## [1] 5.5\n## \n## $log\n## [1] NA<\/pre>\n<p>But in case something goes wrong, the error message will get captured.<\/p>\n<pre>suppressPackageStartupMessages(library(dplyr))\r\n## {paint} masked print.tbl_df\r\npure_select &lt;- purely(select)<\/pre>\n<p>Let\u2019s try here to select a column that does not exist:<\/p>\n<pre>clean_mtcars &lt;- mtcars %&gt;%\n  pure_select(hp, am, bm) #bm does not exist\n\nstr(clean_mtcars)\r\n## List of 2\n##  $ result: logi NA\n##  $ log   : chr &quot;&quot;<\/pre>\n<p>Compare to what happens with <code>select()<\/code>:<\/p>\n<pre>clean_mtcars2 &lt;- mtcars %&gt;%\n  select(hp, am, bm) #bm does not exist\r\nError in `select()`:\n! Can&#39;t subset columns that don&#39;t exist.\n\u2716 Column `bm` doesn&#39;t exist.\nBacktrace:\n  1. mtcars %&gt;% select(hp, am, bm)\n...\n...<\/pre>\n<p>The code (and thus the pipeline) completely fails! I\u2019ve added this function to my\n<a href=\"https:\/\/b-rodrigues.github.io\/loud\/\" rel=\"nofollow\" target=\"_blank\">{loud}<\/a> package, but the biggest benefit of all this is that the\nmain function of the package, <code>loudly()<\/code> now uses <code>purely()<\/code> under the hood to provide more useful\nlog messages in case of failure:<\/p>\n<pre>suppressPackageStartupMessages(library(loud))\n\nloud_sqrt &lt;- loudly(sqrt)\nloud_mean &lt;- loudly(mean)\nloud_exp &lt;- loudly(exp)\n\n\nresult_pipe &lt;- -1:-10 |&gt;\n  loud_mean() %&gt;=% # This results in a negative number...\n  loud_sqrt() %&gt;=% # which sqrt() does not know how to handle\n  loud_exp()<\/pre>\n<p>If we now inspect <code>result_pipe<\/code>, we find a complete log of what went wrong:<\/p>\n<pre>result_pipe\r\n## $result\n## NULL\n## \n## $log\n## [1] &quot;Log start...&quot;                                                                                                                                                            \n## [2] &quot;\u2714 mean(-1:-10) started at 2022-03-12 22:23:48 and ended at 2022-03-12 22:23:48&quot;                                                                                          \n## [3] &quot;\u2716 CAUTION - ERROR: sqrt(.l$result) started at 2022-03-12 22:23:48 and failed at 2022-03-12 22:23:48 with following message: NaNs produced&quot;                               \n## [4] &quot;\u2716 CAUTION - ERROR: exp(.l$result) started at 2022-03-12 22:23:48 and failed at 2022-03-12 22:23:48 with following message: non-numeric argument to mathematical function&quot;<\/pre>\n<p>If you want to know more about <code>{loud}<\/code>, I suggest you read\n<a href=\"https:\/\/www.brodrigues.co\/blog\/2022-02-18-loudly\/\" rel=\"nofollow\" target=\"_blank\">my previous blog post<\/a> and if you need a more\nrealistic example, take a look at\n<a href=\"https:\/\/b-rodrigues.github.io\/loud\/articles\/real-world-example.html\" rel=\"nofollow\" target=\"_blank\">this<\/a>.<\/p>\n<p>If you try it, please let me know!<\/p>\n<p>Hope you enjoyed! If you found this blog post useful, you might want to follow\nme on <a href=\"https:\/\/www.twitter.com\/brodriguesco\" rel=\"nofollow\" target=\"_blank\">twitter<\/a> for blog post updates and\n<a href=\"https:\/\/www.buymeacoffee.com\/brodriguesco\" rel=\"nofollow\" target=\"_blank\">buy me an espresso<\/a> or <a href=\"https:\/\/www.paypal.me\/brodriguesco\" rel=\"nofollow\" target=\"_blank\">paypal.me<\/a>, or buy my ebook on <a href=\"https:\/\/leanpub.com\/modern_tidyverse\" rel=\"nofollow\" target=\"_blank\">Leanpub<\/a>.\nYou can also watch my videos on <a href=\"https:\/\/www.youtube.com\/c\/BrunoRodrigues1988\/\" rel=\"nofollow\" target=\"_blank\">youtube<\/a>.\nSo much content for you to consoom!<\/p>\n<style>.bmc-button img{width: 27px !important;margin-bottom: 1px !important;box-shadow: none !important;border: none !important;vertical-align: middle !important;}.bmc-button{line-height: 36px !important;height:37px !important;text-decoration: none !important;display:inline-flex !important;color:#ffffff !important;background-color:#272b30 !important;border-radius: 3px !important;border: 1px solid transparent !important;padding: 1px 9px !important;font-size: 22px !important;letter-spacing:0.6px !important;box-shadow: 0px 1px 2px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 1px 2px 2px rgba(190, 190, 190, 0.5) !important;margin: 0 auto !important;font-family:'Cookie', cursive !important;-webkit-box-sizing: border-box !important;box-sizing: border-box !important;-o-transition: 0.3s all linear !important;-webkit-transition: 0.3s all linear !important;-moz-transition: 0.3s all linear !important;-ms-transition: 0.3s all linear !important;transition: 0.3s all linear !important;}.bmc-button:hover, .bmc-button:active, .bmc-button:focus {-webkit-box-shadow: 0px 1px 2px 2px rgba(190, 190, 190, 0.5) !important;text-decoration: none !important;box-shadow: 0px 1px 2px 2px rgba(190, 190, 190, 0.5) !important;opacity: 0.85 !important;color:#82518c !important;}<\/style>\n<p><link href=\"https:\/\/fonts.googleapis.com\/css?family=Cookie\" rel=\"stylesheet\"><a class=\"bmc-button\" href=\"https:\/\/www.buymeacoffee.com\/brodriguesco\" rel=\"nofollow\" target=\"_blank\"><img src=\"https:\/\/www.buymeacoffee.com\/assets\/img\/BMC-btn-logo.svg\" alt=\"Buy me an Espresso\"><span style=\"margin-left:5px\">Buy me an Espresso<\/span><\/a><\/p>\n\n<div style=\"border: 1px solid; background: none repeat scroll 0 0 #EDEDED; margin: 1px; font-size: 13px;\">\r\n<div style=\"text-align: center;\">To <strong>leave a comment<\/strong> for the author, please follow the link and comment on their blog: <strong><a href=\"https:\/\/www.brodrigues.co\/blog\/2022-03-12-purely\/\"> Econometrics and Free Software<\/a><\/strong>.<\/div>\r\n<hr \/>\r\n<a href=\"https:\/\/www.r-bloggers.com\/\" rel=\"nofollow\">R-bloggers.com<\/a> offers <strong><a href=\"https:\/\/feedburner.google.com\/fb\/a\/mailverify?uri=RBloggers\" rel=\"nofollow\">daily e-mail updates<\/a><\/strong> about <a title=\"The R Project for Statistical Computing\" href=\"https:\/\/www.r-project.org\/\" rel=\"nofollow\">R<\/a> news and tutorials about <a title=\"R tutorials\" href=\"https:\/\/www.r-bloggers.com\/how-to-learn-r-2\/\" rel=\"nofollow\">learning R<\/a> and many other topics. <a title=\"Data science jobs\" href=\"https:\/\/www.r-users.com\/\" rel=\"nofollow\">Click here if you're looking to post or find an R\/data-science job<\/a>.\r\n\r\n<hr>Want to share your content on R-bloggers?<a href=\"https:\/\/www.r-bloggers.com\/add-your-blog\/\" rel=\"nofollow\"> click here<\/a> if you have a blog, or <a href=\"http:\/\/r-posts.com\/\" rel=\"nofollow\"> here<\/a> if you don't.\r\n<\/div>","protected":false},"excerpt":{"rendered":"<div style = \"width:60%; display: inline-block; float:left; \">\n<p>In my last video I tried to add a feature to my<br \/>\n{loud} package (more info here) and I succeeded. But in<br \/>\nsucceeding in realised that I would need to write a bit more code than what I expected. To make<br \/>\na long story short: it is possible to capture&#8230;<\/p><\/div>\n<div style = \"width: 40%; display: inline-block; float:right;\"><\/div>\n<div style=\"clear: both;\"><\/div>\n","protected":false},"author":1523,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[4],"tags":[],"aioseo_notices":[],"jetpack-related-posts":[],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/posts\/322174"}],"collection":[{"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/users\/1523"}],"replies":[{"embeddable":true,"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/comments?post=322174"}],"version-history":[{"count":21,"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/posts\/322174\/revisions"}],"predecessor-version":[{"id":377829,"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/posts\/322174\/revisions\/377829"}],"wp:attachment":[{"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/media?parent=322174"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/categories?post=322174"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.r-bloggers.com\/wp-json\/wp\/v2\/tags?post=322174"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}