Wrap a script or pipeline's top-level call in with_logging() so an
uncaught error leaves a clean, correctly-colored tree instead of dimmed
"interrupted" steps. On error, every currently open step is marked
failed, the error is logged as a leaf line, then rethrown –
with_logging() never silently swallows errors. It also prints a
"Run complete" / "Run failed" summary line with elapsed time.
Arguments
- expr
Code to run. Omitted when
global = TRUE.- summary
Print an end-of-run summary line? Default
TRUE. In global mode only the "Run failed" line is printed (on an uncaught error); there is no frame exit to hang a "Run complete" line on.- global
If
TRUE, do not wrap an expression; instead install a session-persistent global error handler for use at the top level of a script. On an error that reaches top level unhandled while logtree steps are open, it marks those steps failed and logs the error message as a leaf – the same result as the block form, but without wrapping the body. It fires only for genuinely uncaught top-level errors (an innertryCatch()that catches first pre-empts it) and only when steps are open. The handler persists untillogtree_reset(). Must be called from a clean top level – the first line of a script: calling it where condition handlers are active (insidetryCatch(), or a function running under one) errors, by design. Requires R (>= 4.0).- warnings
Route R's own conditions into the tree as leaf lines?
FALSE(the default) leaveswarning()andmessage()exactly as they are.TRUEroutes both; a subset such as"warning"or"message"routes only those.Two consequences to weigh before switching it on:
Routed conditions are muffled. A routed
warning()becomes a leaf and stops there: it no longer reacheswarnings(), the caller's own handlers, or stderr. That is the point – one record of the run rather than two halves – but it means the tree is now the only place that warning appears.A routed warning elevates its step, exactly as
log_warn()does. So wrapping third-party code that warns freely will turn the enclosing steps yellow, which may be more honesty than you wanted.
In global mode the routing applies only while logtree steps are open, so a session-persistent handler cannot swallow warnings from unrelated code.
Details
Note: expr is lazily evaluated, so log_step() calls written inside
the { ... } block close when the function lexically enclosing that
block returns – not necessarily when with_logging() itself returns.
Use with_logging({ ... }) as a function's entire body to keep these
in sync; if other code runs after the call in the same function, steps
opened inside the block stay open until that function returns.
The global = TRUE form is meant for the top level of a script, where
there is no frame to wrap. It is not shown in the examples below because
it installs a session-persistent handler and is only meaningful for an
error that reaches top level:
with_logging(global = TRUE)
log_open("Load data")
stop("EOF") # marks the open step failed + logs "EOF" before R exitswarnings = TRUE additionally routes R's own conditions into the tree:
a warning() raised by wrapped code becomes a log_warn() leaf and a
message() becomes a log_info() leaf, in place rather than on stderr, so
the tree is a complete record of the run rather than half of one. It is
opt-in because routing means muffling – see the argument's own
documentation below for what that costs.
Examples
logtree_reset()
with_logging({
log_step("Step one")
log_success("done")
})
#> ▶ Step one
#> ├─ ✔ done
#> ✔ Run complete in 0.00s
#> └─ ◌ Done 0.00s
# R's own conditions routed into the tree instead of onto stderr.
logtree_reset()
with_logging({
log_step("Load data")
warning("3 rows coerced to NA")
message("using cached manifest")
}, warnings = TRUE)
#> ▶ Load data
#> ├─ ⚠ 3 rows coerced to NA
#> ├─ ℹ using cached manifest
#> ✔ Run complete in 0.01s
#> └─ ⚠ Done 0.01s
