<p><div><div><div><div data-show-faces="false" data-height="21" data-width="100" data-layout="button_count" data-send="true" data-href="https://www.r-bloggers.com/effortless-but-powerful-exception-logging-in-r-loggit-1-0-0-released-on-cran/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="http://anotherblogaboutr.blogspot.com/2018/02/effortless-but-powerful-exception_20.html"> Another Blog About R</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<div color>Frustrated with the lack of pain-free logging in R, a colleague inspired me to write my first public R package: loggit. What follows is <a rel="nofollow" href="https://github.com/ryapric/loggit" target="_blank">the README that you can find on Github</a>, and verison 1.0.0 of <a rel="nofollow" href="https://cran.r-project.org/web/packages/loggit/index.html" target="_blank">loggit is now available on CRAN</a>. Please feel free to submit requests, <a rel="nofollow" href="https://github.com/ryapric/loggit/issues" target="_blank">bug reports</a>, etc.!</div>
<div color><a name="more"></a></div>
<div color>This package is designed for anyone who has written their own package or analysis functions that already use basic exception handling by way of base R’s <code>message()</code>, <code>warning()</code>, and <code>stop()</code>. The loggit package <em>masks</em> base R’s versions of these functions, but slots in a logging function (<code>loggit()</code>) just before the exception handler executes. Since these functions maintain identical functionality to their base equivalents, there is no need for any change in your existing code, if you have already written handlers. Just install &amp; load/import <code>loggit</code>, tell R where to store your log file on startup, and instantly have your exceptions recorded.</div>
<div color>The included <code>loggit()</code> function is also very flexible on its own, and allows for any number of custom fields to be added to your logs, in addition to maintaining the aforementioned functionality.</div>
<div color>This package was inspired by a colleague’s suggestion of how we could log our ETL workflow results in R, as well as the <a rel="nofollow" href="https://github.com/mjkallen/rlogging" target="_blank">rlogging</a> package. When we looked at how <code>rlogging</code> was doing this, we felt that we could expand upon it and make it more flexible, system-portable, and with even less user effort. As opposed to other packages like <a rel="nofollow" href="http://cran.r-project.org/package=log4r" target="_blank">log4r</a>, <a rel="nofollow" href="http://cran.r-project.org/package=futile.logger" target="_blank">futile.logger</a> and <a rel="nofollow" href="http://cran.r-project.org/package=logging" target="_blank">logging</a>, which may allow for more powerful logs in some cases, we wanted to make great logging “out of sight, out of mind”; at least until your boss asks for those ETL logs!</div>
<p><br color></p>
<h3 color><a rel="nofollow" href="https://github.com/ryapric/loggit#features" target="_blank"></a>Features</h3>
<div color>R has a selection of built-in functions for handling different <em>exceptions</em>, or special cases where diagnostic messages are provided, and/or function execution is halted because of an error. However, R itself provides nothing to record this diagnostic post-hoc; useRs are left with what is printed to the console as their only means of analyzing the what-went-wrong of their code. There are some slightly hacky ways of capturing this console output, such as <code>sink</code>ing to a text file, repetitively <code>cat</code>ing identical exception messages that are passed to existing handler calls, etc. But there are two main issues with these approaches:</div>
<ol color>
<li>
<div>The console output is not at all easy to parse, so that a user can quickly identify the causes of failure without manually scanning through it</div>
</li>
<li>
<div>Even if the user tries to structure a text file output, they would likely have to ensure consistency in that output across all their work, and there is still the issue of parsing that text file into a familiar, usable format</div>
</li>
</ol>
<div color>Enter: <a rel="nofollow" href="https://www.json.org/" target="_blank">JSON</a></div>
<div color>JSON is a lightweight, portable (standardized) data format that is easy to read and write by both humans and machines. An excerpt from the introduction of the JSON link above:</div>
<div color>“JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of the JavaScript Programming Language, Standard ECMA-262 3rd Edition – December 1999. JSON is a text format that is completely language independent but uses conventions that are familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript, Perl, Python, and many others. These properties make JSON an ideal data-interchange language.”</div>
<div color>Basically, you can think of JSON objects like you would think of <code>list</code>s in R: a set of key-value pairs, with the option to nest lists within other lists. This makes JSON not only as powerful as R’s lists, but it allows this nested-list functionality to be translated to other software, and let them handle them just as easily! As such, JSON is the default log file output type in the <code>loggit</code> package. You can manipulate &amp; review JSON objects in R by way of the <a rel="nofollow" href="https://cran.r-project.org/package=jsonlite" target="_blank">jsonlite</a> package.</div>
<p><br color></p>
<h3 color><a rel="nofollow" href="https://github.com/ryapric/loggit#how-to-use" target="_blank"></a>How to Use</h3>
<div color>Chances are that if you’ve read this far, you’re familiar with R’s exception handlers. If so, then there are only two other things that you need to know in order to use <code>loggit</code>:</div>
<ol color>
<li>
<div>As per CRAN policies, <em><span>a package cannot write</span></em> to a user’s “home filespace” without approval. Therefore, you need to set the log file before any logs are written to disk, using <code>setLogFile(logfile)</code> (I recommend in your working directory, and naming it “loggit.json”). If you are using loggit in your own package, you can wrap this in a call to <code>.onLoad()</code>, so that logging is set on package load. If not, then make the set call as soon as possible (e.g. at the top of your script(s), right after your calls to <code>library()</code>); otherwise, no logs will be written to persistent storage!</div>
</li>
<li>
<div>The logs are output to your log file each time an exception is raised, or when <code>loggit()</code> is called, and you can review it there. The fields supplied by default are:</div>
</li>
</ol>
<ul color>
<li><code>timestamp</code>: when the exception was raised</li>
<li><code>log_lvl</code>: the “level” of the exception (INFO, WARN, ERROR, or custom)</li>
<li><code>log_msg</code>: the diagnostic message generated by R</li>
<li><code>log_detail</code>: additional exception details you can pass to the exception handlers, though not necessary to supply (will be filled with “” if not supplied).</li>
</ul>
<div><em><span><span>That’s it!</span></span></em></div>
<p><br color></p>
<div color>However, if you want more control, or care a bit more about the details:</div>
<div color>While this package automatically masks the base handlers, the <code>loggit</code> function that they call is also exported for use, if so desired. This allows you to log arbitrary things as much or as little as you see fit. The <code>loggit</code> function is called as: <code>loggit(log_lvl, log_msg, log_detail, ...)</code>. The argument you may not recognize is <code>...</code>, which is supplied as <em>named </em>vectors (each of length one) providing any additional field names you would like to log:</div>
<pre><code>loggit(&quot;INFO&quot;, &quot;This is a message&quot;, but_maybe = &quot;you want more fields?&quot;, sure = &quot;why not?&quot;, like = 2, or = 10, what = &quot;ever&quot;)<br></code></pre>
<div color>Since <code>loggit()</code> calls <a rel="nofollow" href="http://dplyr.tidyverse.org/reference/bind.html" target="_blank">dplyr::bind_rows()</a> behind the scenes, you can add as many fields of any type as you want without any append issues or errors. Note, however, that this means that any future calls to <code>loggit()</code> for the same custom fields, require that those fields are spelled the same as originally provided. If not, then the misspelling will cause <em>another</em> field to be created, and (the <code>data frame</code> version of) your log file will look like this:</div>
<pre><code>> loggit(&quot;INFO&quot;, &quot;Result 1&quot;, scientist = &quot;Ryan&quot;)<br>> loggit(&quot;INFO&quot;, &quot;Result 2&quot;, scietist = &quot;Thomas&quot;)<br><br>            timestamp log_lvl  log_msg  log_detail scientist scietist<br>1 2018-02-13 18:01:32    INFO Result 1          &quot;&quot;      Ryan     </code></pre>