Skip to content

Commit 92054c8

Browse files
thomasp85daroczig
andauthored
Add traceback support in log_errors
* Add traceback support in log_errors * add news bullet * avoid formatting traceback calls * Add coverage of adding srcref info to traceback * lint break long line --------- Co-authored-by: Gergely Daroczi (@daroczig) <daroczig@rapporter.net>
1 parent f1d103d commit 92054c8

File tree

6 files changed

+42
-3
lines changed

6 files changed

+42
-3
lines changed

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ improved documentations, modernized tests, performance speedups.
1616
* `log_appender()`, `log_layout()` and `log_formatter()` now check that you are calling them with a function, and return the previously set value (#170, @hadley)
1717
* new function to return number of log indices (#194, @WurmPeter)
1818
* `appender_async` is now using `mirai` instead of a custom background process and queue system (#214, @hadley @shikokuchuo)
19+
* `log_errors()` gains a `traceback` argument that toggles whether the error traceback should be logged along with the message (#86, @thomasp85)
1920

2021
## Fixes
2122

R/hooks.R

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,19 +58,20 @@ log_warnings <- function(muffle = getOption("logger_muffle_warnings", FALSE)) {
5858
}
5959
}
6060

61-
6261
#' Injects a logger call to standard errors
6362
#'
6463
#' This function uses [trace()] to add a [log_error()] function call when
6564
#' [stop()] is called to log the error messages with the `logger` layout
6665
#' and appender.
6766
#' @param muffle if TRUE, the error is not thrown after being logged
67+
#' @param traceback if TRUE the error traceback is logged along with the error
68+
#' message
6869
#' @export
6970
#' @examples \dontrun{
7071
#' log_errors()
7172
#' stop("foobar")
7273
#' }
73-
log_errors <- function(muffle = getOption("logger_muffle_errors", FALSE)) {
74+
log_errors <- function(muffle = getOption("logger_muffle_errors", FALSE), traceback = FALSE) {
7475
if (any(sapply(
7576
globalCallingHandlers()[names(globalCallingHandlers()) == "error"],
7677
attr,
@@ -81,6 +82,21 @@ log_errors <- function(muffle = getOption("logger_muffle_errors", FALSE)) {
8182
globalCallingHandlers(
8283
error = structure(function(m) {
8384
logger::log_level(logger::ERROR, m$message, .topcall = m$call)
85+
if (traceback) {
86+
bt <- .traceback(3L)
87+
logger::log_level(logger::ERROR, "Traceback:", .topcall = m$call)
88+
for (i in seq_along(bt)) {
89+
msg <- paste0(length(bt) - i + 1L, ": ", bt[[i]])
90+
ref <- attr(bt[[i]], "srcref")
91+
file <- attr(ref, "srcfile")
92+
if (inherits(file, "srcfile")) {
93+
file <- basename(file$filename)
94+
line <- paste(unique(c(ref[1L], ref[3L])), collapse = "-")
95+
msg <- paste0(msg, " at ", file, " #", line)
96+
}
97+
logger::log_level(logger::ERROR, skip_formatter(msg), .topcall = m$call)
98+
}
99+
}
84100
if (isTRUE(muffle)) {
85101
invokeRestart("abort")
86102
}

man/log_errors.Rd

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/_snaps/hooks.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@
2727
writeLines(eval_outside("log_errors()", "f<-function(x) {42 * \"foobar\"}; f()"))
2828
Output
2929
ERROR non-numeric argument to binary operator
30+
Code
31+
writeLines(eval_outside("log_errors(traceback = TRUE)",
32+
"source(\"helper.R\", keep.source = TRUE)", "function_that_fails()"))
33+
Output
34+
ERROR I'm failing
35+
ERROR Traceback:
36+
ERROR 2: stop("I'm failing") at helper.R #46
37+
ERROR 1: function_that_fails()
3038

3139
# shiny input initialization is detected
3240

tests/testthat/helper.R

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,8 @@ eval_outside <- function(...) {
4040
suppressWarnings(system2(path, input, stdout = TRUE, stderr = TRUE))
4141
readLines(output)
4242
}
43+
44+
# This function is needed to test traceback logging
45+
function_that_fails <- function() {
46+
stop("I'm failing")
47+
}

tests/testthat/test-hooks.R

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ test_that("log_errors", {
1515
writeLines(eval_outside("log_errors()", "stop(42)"))
1616
writeLines(eval_outside("log_errors()", "foobar"))
1717
writeLines(eval_outside("log_errors()", 'f<-function(x) {42 * "foobar"}; f()'))
18+
writeLines(eval_outside("log_errors(traceback = TRUE)",
19+
'source("helper.R", keep.source = TRUE)',
20+
"function_that_fails()"))
1821
})
1922
})
2023

0 commit comments

Comments
 (0)