Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

logtree logtree website

R-CMD-check Codecov test coverage

Documentation | GitHub

logtree renders nested process execution as a live, colored tree in the console – tree connectors, status glyphs, and elapsed time per step – while keeping nesting depth correct even when a step errors partway through.

Annotated logtree console output

Installation

logtree isn’t on CRAN yet. Install the development version from GitHub:

# install.packages("pak")
pak::pak("IvanSortino/logtree")

Once released to CRAN:

install.packages("logtree")

Quick start

library(logtree)

pipeline <- function() {
  log_step("Pipeline")
  load_config()
}

load_config <- function() {
  log_step("Load config")
  log_info("Reading config.yml")
  log_success("Validated 12 parameters")
}

pipeline()
#> ▶ Pipeline
#> ├─ ▶ Load config
#> │  ├─ ℹ Reading config.yml
#> │  ├─ ✔ Validated 12 parameters
#> │  └─ ✔ Done  0.00s
#> └─ ✔ Done  0.00s

log_step() is meant to be called from inside a function: the step auto-closes when the function that opened it returns – normally, early, or via an uncaught error – so nesting depth never gets stuck out of sync. (At top level, with no function frame to close on, reach for log_open() / log_close() instead.)

Status levels & verbosity

Five leaf levels – log_debug(), log_info(), log_success(), log_warn(), log_error() – plus logtree_threshold() to filter them. log_warn()/ log_error() also elevate the enclosing step’s glyph, even when suppressed by verbosity; step lines always render regardless of threshold.

fetch <- function() {
  log_step("Fetch")
  log_debug("cache miss for key user:42")
  log_info("requesting from API")
  log_warn("rate limit at 80%")
  log_success("fetched 128 rows")
}

with_logging(fetch(), summary = FALSE) # default verbosity ("info"): debug hidden
#> ▶ Fetch
#> ├─ ℹ requesting from API
#> ├─ ⚠ rate limit at 80%
#> ├─ ✔ fetched 128 rows
#> └─ ⚠ Done  0.00s

logtree_threshold("debug")
with_logging(fetch(), summary = FALSE) # verbosity raised: debug shown
#> ▶ Fetch
#> ├─ ⚙ cache miss for key user:42
#> ├─ ℹ requesting from API
#> ├─ ⚠ rate limit at 80%
#> ├─ ✔ fetched 128 rows
#> └─ ⚠ Done  0.00s
logtree_threshold("info")

Error handling

log_error() from code that itself returns normally elevates the enclosing step’s glyph but lets the run continue – pass status = "success" to log_close() once you know recovery actually worked:

connect_db <- function() {
  log_step("Connect primary")
  log_error("primary unreachable")
  log_info("failing over to replica")
  log_success("connected to replica")
  log_close(status = "success") # recovered: override the elevated glyph
}

with_logging(connect_db(), summary = FALSE)
#> ▶ Connect primary
#> ├─ ✖ primary unreachable
#> ├─ ℹ failing over to replica
#> ├─ ✔ connected to replica
#> └─ ✔ Done  0.00s

A step whose code actually throws is different: with_logging() marks every currently-open step failed, logs the condition as a leaf, prints a run summary, then rethrows – it never silently swallows errors.

apply_migration <- function() {
  log_step("Apply migration")
  log_info("adding column users.tier")
  stop("constraint violation on users.email")
}

try(with_logging(apply_migration()), silent = TRUE)
#> ▶ Apply migration
#> ├─ ℹ adding column users.tier
#> ├─ ✖ constraint violation on users.email
#> └─ ✖ Done  0.00s
#> ✖ Run failed in 0.00s

Run summary

When a run ends, logtree_summary() prints a breadcrumb digest of every error, warning, and pinned leaf since the last reset – so you see what went wrong without scrolling back through the tree. filter restricts by status; depth trims each breadcrumb to its N deepest nodes.

logtree_reset()

migrate <- function() {
  log_step("Apply migration")
  log_warn("table lock held 800ms")
  log_error("constraint violation on users.email")
}

release <- function() {
  log_step("Release v2.1")
  migrate()
  log_step("Smoke test")
  log_success("all endpoints 200")
}

with_logging(release(), summary = FALSE)
#> ▶ Release v2.1
#> ├─ ▶ Apply migration
#> │  ├─ ⚠ table lock held 800ms
#> │  ├─ ✖ constraint violation on users.email
#> │  └─ ✖ Done  0.00s
#> ├─ ▶ Smoke test
#> │  ├─ ✔ all endpoints 200
#> │  └─ ✔ Done  0.00s
#> └─ ✔ Done  0.00s
logtree_summary()
#> Summary: 1 error, 1 warning
#> ⚠ Release v2.1 > Apply migration > table lock held 800ms
#> ✖ Release v2.1 > Apply migration > constraint violation on users.email

Grouping

Adjacent log_step() calls that share a group = c(name = value) value collapse under one < name > header instead of stacking as siblings:

check <- function(item, label) {
  log_step(label, group = stats::setNames(item, paste0("Item ", item)))
  log_info(paste0(label, " running"))
  log_success(paste0(label, " ok"))
}

process_item <- function(item) {
  check(item, "validate schema")
  check(item, "check bounds")
}

run_pipeline <- function() {
  log_step("Pipeline run")
  for (i in 1:2) process_item(i)
}

with_logging(run_pipeline(), summary = FALSE)
#> ▶ Pipeline run
#> ├─ ▣ Item 1
#> │  ├─ ▶ validate schema
#> │  │  ├─ ℹ validate schema running
#> │  │  ├─ ✔ validate schema ok
#> │  │  └─ ✔ Done  0.00s
#> │  ├─ ▶ check bounds
#> │  │  ├─ ℹ check bounds running
#> │  │  ├─ ✔ check bounds ok
#> │  │  └─ ✔ Done  0.00s
#> │  └─ ✔ Done  0.00s
#> ├─ ▣ Item 2
#> │  ├─ ▶ validate schema
#> │  │  ├─ ℹ validate schema running
#> │  │  ├─ ✔ validate schema ok
#> │  │  └─ ✔ Done  0.00s
#> │  ├─ ▶ check bounds
#> │  │  ├─ ℹ check bounds running
#> │  │  ├─ ✔ check bounds ok
#> │  │  └─ ✔ Done  0.00s
#> │  └─ ✔ Done  0.00s
#> └─ ✔ Done  0.00s

Themes

logtree_theme() swaps the whole glyph/color preset ("unicode", "ascii", "emoji") or merges per-glyph overrides onto the active one:

demo_build <- function() {
  with_logging({
    log_step("Build")
    log_info("compiling")
    log_warn("3 deprecation warnings")
    log_success("build ok")
  }, summary = FALSE)
}

logtree_theme("ascii")
demo_build()
#> > Build
#> |- i compiling
#> |- ! 3 deprecation warnings
#> |- + build ok
#> |- ! Done  0.00s

logtree_theme("emoji")
demo_build()
#> 🔹 Build
#> ├─ 💡 compiling
#> ├─ ⚠️ 3 deprecation warnings
#> ├─ ✅ build ok
#> └─ ⚠️ Done  0.00s

logtree_theme("unicode")
demo_build()
#> ▶ Build
#> ├─ ℹ compiling
#> ├─ ⚠ 3 deprecation warnings
#> ├─ ✔ build ok
#> └─ ⚠ Done  0.00s

Or override individual slots with overrides – a list keyed by slot, each holding only the fields to change (everything else is kept from the active theme):

logtree_theme("unicode", overrides = list(
  success = list(glyph = "*", color = c("green", "bold")),
  group   = list(bracket = TRUE)
))
demo_build()
#> ▶ Build
#> ├─ ℹ compiling
#> ├─ ⚠ 3 deprecation warnings
#> ├─ * build ok
#> └─ ⚠ Done  0.00s
logtree_theme("unicode")

Pass compact to tighten the tree’s per-level indentation – "medium" drops the trailing gap after each connector, "tight" also slims the connectors down to a single character:

logtree_theme("unicode", compact = "medium")
demo_build()
#> ▶ Build
#> ├─ℹ compiling
#> ├─⚠ 3 deprecation warnings
#> ├─✔ build ok
#> └─⚠ Done  0.00s
logtree_theme("unicode")

The full list of overridable slots (step, info, success, group, branch, corner, …) and their fields (glyph, width, color, bracket) is in the Themes section of the vignette and ?logtree_theme.

More

  • Output sinkslogtree_sink_file(path, format = c("text", "json")) mirrors console output to a plain-text or NDJSON file; every registered sink runs alongside the console sink.
  • logger integrationlogtree_logger() routes the logger package through logtree in one call, so logger::log_info() and friends render as logtree leaves.
  • Manual step controllog_open()/log_close() open and close steps by hand (with an explicit parent) instead of relying on frame exit, useful at top level or across script blocks. Opening a step at the same depth as an open one (e.g. sharing a parent) retires the earlier sibling automatically, so you can stream siblings without an explicit log_close() on each.
  • Re-running top-level lines – at top level (e.g. re-running selected lines in RStudio/Positron while iterating), log_step()/log_open() key each step on its label and call site, so re-running the same line re-anchors to that node instead of nesting deeper on every run. Pass an explicit key to opt out, or to keep two same-label steps open at once distinct.

See vignette("logtree") and the documentation site for full details on error handling semantics, manual step control, and the design philosophy behind the tree renderer.

About

❗ This is a read-only mirror of the CRAN R package repository. logtree — Tree-Style Console Logger for Nested Processes. Homepage: https://github.com/IvanSortino/logtreehttps://ivansortino.github.io/logtree/ Report bugs for this package: https://github.com/IvanSortino/logtree/issues

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages