Debugging
There are some options in SPS that will give you more information and help you on
debugging. They are: verbose
and traceback
. You can config
(enable/disable) themin a SPS project’s global.R
file, or use
spsOption("verbose", TRUE)
and spsOption("traceback", TRUE)
to turn on them.
Some setup:
suppressPackageStartupMessages(library(systemPipeShiny))
app_dir <- tempdir()
spsInit(app_path = app_dir, overwrite = TRUE, change_wd = FALSE, open_files = FALSE)
## [SPS-INFO] 2021-04-16 17:46:53 Start to create a new SPS project
## [SPS-INFO] 2021-04-16 17:46:53 Create project under /tmp/RtmpUfKCYR/SPS_20210416
## [SPS-INFO] 2021-04-16 17:46:53 Now copy files
## [SPS-INFO] 2021-04-16 17:46:53 Create SPS database
## [SPS-INFO] 2021-04-16 17:46:53 Created SPS database method container
## [SPS-INFO] 2021-04-16 17:46:53 Creating SPS db...
## [SPS-DANGER] 2021-04-16 17:46:53 Done, Db created at '/tmp/RtmpUfKCYR/SPS_20210416/config/sps.db'. DO NOT share this file with others or upload to open access domains.
## [SPS-INFO] 2021-04-16 17:46:53 Key md5 fc8c85a0e87073328864bd542d740801
## [SPS-INFO] 2021-04-16 17:46:53 SPS project setup done!
app_path <- file.path(app_dir, glue::glue("SPS_{format(Sys.time(), '%Y%m%d')}"))
verbose
In many SPS functions, there is this argument verbose
and usually default is FALSE
.
It means do not print extra message, keep it clean. You can set in spsOption("verbose", TRUE)
or inside global.R
file to turn on. These are called global settings, and you
can use a local setting to overwrite it (func(..., verbose = TRUE)
).
Let’s use SPS main function sps
for example, without the verbose
spsOption("verbose", FALSE)
app <- sps(app_path = app_path)
## Warning:
[SPS-WARNING] 2021-04-16 17:46:53 These plot tabs has no image path:
## 'vs_example'
## It is recommended to add an image. It will be used to generate gallery. Now an empty image is used for these tabs' gallery.
## [SPS-INFO] 2021-04-16 17:46:55 App starts ...
Turn on the verbose:
spsOption("verbose", TRUE)
app <- sps(app_path = app_path)
## [SPS-INFO] 2021-04-16 17:46:55 App has 19 default configs, resolving 19 custom configs
## [SPS-INFO] 2021-04-16 17:46:55 Now check the tab info in tabs.csv
## Warning:
[SPS-WARNING] 2021-04-16 17:46:55 These plot tabs has no image path:
## 'vs_example'
## It is recommended to add an image. It will be used to generate gallery. Now an empty image is used for these tabs' gallery.
## [SPS-INFO] 2021-04-16 17:46:55 tab.csv info check pass
## [SPS-INFO] 2021-04-16 17:46:55 Using default tabs
## [SPS-INFO] 2021-04-16 17:46:55 check guide
## [SPS-INFO] 2021-04-16 17:46:55 Start to generate UI
## [SPS-INFO] 2021-04-16 17:46:55 parse title and logo
## [SPS-INFO] 2021-04-16 17:46:55 resolve default tabs UI
## [SPS-INFO] 2021-04-16 17:46:55 Loading custom tab UI ...
## [SPS-INFO] 2021-04-16 17:46:55 Loading notifications from developer...
## [SPS-INFO] 2021-04-16 17:46:55 Loading guide UI
## [SPS-INFO] 2021-04-16 17:46:55 Create UI header ...
## [SPS-INFO] 2021-04-16 17:46:55 Create UI sidebar menu ...
## [SPS-INFO] 2021-04-16 17:46:55 Create UI tab content ...
## [SPS-INFO] 2021-04-16 17:46:55 Add tab content to body ...
## [SPS-INFO] 2021-04-16 17:46:55 Merge header, menu, body to dashboard ...
## [SPS-INFO] 2021-04-16 17:46:55 Add overlay loading screen, admin panel.
## Merge everything to app container ...
## [SPS-INFO] 2021-04-16 17:46:55 UI created
## [SPS-INFO] 2021-04-16 17:46:55 Start to create server function
## [SPS-INFO] 2021-04-16 17:46:55 Resolve default tabs server
## [SPS-INFO] 2021-04-16 17:46:55 Load custom tabs servers
## [SPS-INFO] 2021-04-16 17:46:55 Server functions created
## [SPS-INFO] 2021-04-16 17:46:55 App starts ...
Exception
There is one exception which is the spsInit
. It is used to create a SPS project
for you, so it assumes you do not have a SPS project yet and therefore do not have
the chance to reach SPS options. So the verbose
global setting will not work here.
You need to turn it on locally with verbose = TRUE
.
Compare messages of this with the initial spsInit
creation on top.
spsInit(verbose = TRUE, app_path = app_path, overwrite = TRUE, change_wd = FALSE, open_files = FALSE)
## [SPS-INFO] 2021-04-16 17:46:55 Start to create a new SPS project
## [SPS-INFO] 2021-04-16 17:46:55 Create project under /tmp/RtmpUfKCYR/SPS_20210416/SPS_20210416
## [SPS-INFO] 2021-04-16 17:46:55 Now copy files
## [SPS-INFO] 2021-04-16 17:46:55 File(s) copied for /tmp/RtmpUfKCYR/SPS_20210416/SPS_20210416/www
## [SPS-INFO] 2021-04-16 17:46:55 File(s) copied for /tmp/RtmpUfKCYR/SPS_20210416/SPS_20210416/config
## [SPS-INFO] 2021-04-16 17:46:55 File(s) copied for /tmp/RtmpUfKCYR/SPS_20210416/SPS_20210416/R
## [SPS-INFO] 2021-04-16 17:46:55 File(s) copied for /tmp/RtmpUfKCYR/SPS_20210416/SPS_20210416/data
## [SPS-INFO] 2021-04-16 17:46:55 File(s) copied for /tmp/RtmpUfKCYR/SPS_20210416/SPS_20210416/results
## [SPS-INFO] 2021-04-16 17:46:55 File(s) copied for /tmp/RtmpUfKCYR/SPS_20210416/SPS_20210416/README.md
## [SPS-INFO] 2021-04-16 17:46:55 File(s) copied for /tmp/RtmpUfKCYR/SPS_20210416/SPS_20210416/deploy.R
## [SPS-INFO] 2021-04-16 17:46:55 File(s) copied for /tmp/RtmpUfKCYR/SPS_20210416/SPS_20210416/server.R
## [SPS-INFO] 2021-04-16 17:46:55 File(s) copied for /tmp/RtmpUfKCYR/SPS_20210416/SPS_20210416/global.R
## [SPS-INFO] 2021-04-16 17:46:55 File(s) copied for /tmp/RtmpUfKCYR/SPS_20210416/SPS_20210416/ui.R
## [SPS-INFO] 2021-04-16 17:46:55 File(s) copied for /tmp/RtmpUfKCYR/SPS_20210416/SPS_20210416/server.R
## [SPS-INFO] 2021-04-16 17:46:55 Create SPS database
## [SPS-INFO] 2021-04-16 17:46:55 Created SPS database method container
## [SPS-INFO] 2021-04-16 17:46:55 Db connected
## [SPS-INFO] 2021-04-16 17:46:55 Default SPS-db found and is working
## [SPS-INFO] 2021-04-16 17:46:55 Db connected
## [SPS-INFO] 2021-04-16 17:46:55 Creating SPS db...
## [SPS-INFO] 2021-04-16 17:46:55 Db write the meta table
## [SPS-INFO] 2021-04-16 17:46:56 Db write the raw table
## [SPS-INFO] 2021-04-16 17:46:56 Key generated and stored in db
## [SPS-INFO] 2021-04-16 17:46:56 Db create admin account
## [SPS-DANGER] 2021-04-16 17:46:56 Done, Db created at '/tmp/RtmpUfKCYR/SPS_20210416/SPS_20210416/config/sps.db'. DO NOT share this file with others or upload to open access domains.
## [SPS-INFO] 2021-04-16 17:46:56 Key md5 7dca8a45de4d7260f91ddb1a02d0a6bd
## [SPS-INFO] 2021-04-16 17:46:56 SPS project setup done!
traceback
When error happens, it will be helpful if we can know where it happened. This option will give you additional information of which function it happened, the system call list and error file and line of code if possible.
This feature is enabled in two functions sps
and shinyCatch
.
sps
: Adding tracebacks if there are some errors sourcing helper functions located in your SPS project under theR
folder.shinyCatch
: Traceback errors of expressions insideshinyCatch
Let’s use shinyCatch
to demo.
Before adding traceback:
spsOption("traceback", FALSE)
shinyCatch({
stop("some error message")
})
## [SPS-ERROR] 2021-04-16 17:46:56 some error message
## NULL
After
spsOption("traceback", TRUE)
shinyCatch({
stop("some error message")
})
## 1. local({
## if (length(a <- commandArgs(TRUE)) != 2)
## stop("The number of arguments passed to Rscript should be 2.")
## x = readRDS(a[1])
## f = x[[1]]
## if (is.character(f))
## f = eval(parse(text = f), envir = globalenv())
## r = do.call(f, x[[2]], envir = globalenv())
## saveRDS(r, a[2])
## })
## 2. eval.parent(substitute(eval(quote(expr), envir)))
## 3. eval(expr, p)
## 4. eval(expr, p)
## 5. eval(quote({
## if (length(a <- commandArgs(TRUE)) != 2) stop("The number of arguments passed to Rscript should be 2.")
## x = readRDS(a[1])
## f = x[[1]]
## if (is.character(f)) f = eval(parse(text = f), envir = globalenv())
## r = do.call(f, x[[2]], envir = globalenv())
## saveRDS(r, a[2])
## }), new.env())
## 6. eval(quote({
## if (length(a <- commandArgs(TRUE)) != 2) stop("The number of arguments passed to Rscript should be 2.")
## x = readRDS(a[1])
## f = x[[1]]
## if (is.character(f)) f = eval(parse(text = f), envir = globalenv())
## r = do.call(f, x[[2]], envir = globalenv())
## saveRDS(r, a[2])
## }), new.env())
## 7. do.call(f, x[[2]], envir = globalenv())
## 8. (function (input, output, to_md = file_ext(output) != "html", quiet = TRUE)
## {
## options(htmltools.dir.version = FALSE)
## setwd(dirname(input))
## input = basename(input)
## if (to_md)
## options(bookdown.output.markdown = TRUE)
## res = rmarkdown::render(input, "blogdown::html_page", output_file = output, envir = globalenv(), quiet = quiet, run_pandoc = !to_md, clean = !to_md)
## x = read_utf8(res)
## if (to_md)
## x = process_markdown(res, x)
## unlink(res)
## x
## })("content/en/sps/adv_features/debug.Rmd", "debug.md~", TRUE, TRUE)
## 9. rmarkdown::render(input, "blogdown::html_page", output_file = output, envir = globalenv(), quiet = quiet, run_pandoc = !to_md, clean = !to_md)
## 10. knitr::knit(knit_input, knit_output, envir = envir, quiet = quiet)
## 11. process_file(text, output)
## 12. withCallingHandlers(if (tangle) process_tangle(group) else process_group(group), error = function(e) {
## setwd(wd)
## cat(res, sep = "\n", file = output %n% "")
## message("Quitting from lines ", paste(current_lines(i), collapse = "-"), " (", knit_concord$get("infile"), ") ")
## })
## 13. process_group(group)
## 14. process_group.block(group)
## 15. call_block(x)
## 16. block_exec(params)
## 17. in_dir(input_dir(), evaluate(code, envir = env, new_device = FALSE, keep_warning = !isFALSE(options$warning), keep_message = !isFALSE(options$message), stop_on_error = if (options$error && options$include) 0 else 2, output_handler = knit_handlers(options$render, options)))
## 18. evaluate(code, envir = env, new_device = FALSE, keep_warning = !isFALSE(options$warning), keep_message = !isFALSE(options$message), stop_on_error = if (options$error && options$include) 0 else 2, output_handler = knit_handlers(options$render, options))
## 19. evaluate::evaluate(...)
## 20. evaluate_call(expr, parsed$src[[i]], envir = envir, enclos = enclos, debug = debug, last = i == length(out), use_try = stop_on_error != 2, keep_warning = keep_warning, keep_message = keep_message, output_handler = output_handler, include_timing = include_timing)
## 21. timing_fn(handle(ev <- withCallingHandlers(withVisible(eval(expr, envir, enclos)), warning = wHandler, error = eHandler, message = mHandler)))
## 22. handle(ev <- withCallingHandlers(withVisible(eval(expr, envir, enclos)), warning = wHandler, error = eHandler, message = mHandler))
## 23. withCallingHandlers(withVisible(eval(expr, envir, enclos)), warning = wHandler, error = eHandler, message = mHandler)
## 24. withVisible(eval(expr, envir, enclos))
## 25. eval(expr, envir, enclos)
## 26. eval(expr, envir, enclos)
## 27. shinyCatch({
## stop("some error message")
## })
## 28. tryCatch(suppressMessages(suppressWarnings(withCallingHandlers(expr, message = function(m) toastr_actions$message(m), warning = function(m) toastr_actions$warning(m), error = function(m) if (trace_back) printTraceback(sys.calls())))), error = function(m) {
## toastr_actions$error(m)
## return(NULL)
## })
## 29. tryCatchList(expr, classes, parentenv, handlers)
## 30. tryCatchOne(expr, names, parentenv, handlers[[1]])
## 31. doTryCatch(return(expr), name, parentenv, handler)
## 32. suppressMessages(suppressWarnings(withCallingHandlers(expr, message = function(m) toastr_actions$message(m), warning = function(m) toastr_actions$warning(m), error = function(m) if (trace_back) printTraceback(sys.calls()))))
## 33. withCallingHandlers(expr, message = function(c) if (inherits(c, classes)) tryInvokeRestart("muffleMessage"))
## 34. suppressWarnings(withCallingHandlers(expr, message = function(m) toastr_actions$message(m), warning = function(m) toastr_actions$warning(m), error = function(m) if (trace_back) printTraceback(sys.calls())))
## 35. withCallingHandlers(expr, warning = function(w) if (inherits(w, classes)) tryInvokeRestart("muffleWarning"))
## 36. withCallingHandlers(expr, message = function(m) toastr_actions$message(m), warning = function(m) toastr_actions$warning(m), error = function(m) if (trace_back) printTraceback(sys.calls()))
## [SPS-ERROR] 2021-04-16 17:46:56 some error message
## NULL
Or use local setting to overwrite the global, even we have spsOption("traceback", TRUE)
,
but traceback is still muted by trace_back = FALSE
.
spsOption("traceback", TRUE)
shinyCatch({
stop("some error message")
}, trace_back = FALSE)
## [SPS-ERROR] 2021-04-16 17:46:56 some error message
## NULL
Traceback with file and line number
Let’s write an R file with functions, source it and then call the function from this file. Try it on your own computer:
temp_file <- tempfile(fileext = ".R")
writeLines(
"myFunc <- function(){
myFunc2()
}
myFunc2 <- function(){
stop('some error message')
}
",
temp_file
)
source(temp_file)
shinyCatch({
myFunc()
})
You can see the error happened in myFunc
line No. 2 and then inside this function
it calls another function myFunc2
which caused the final error. In myFunc2
it is also the line No. 2 caused the issue and error is coming from /tmp/...
file.
other Shiny built-in options
There are some Shiny options can also be helpful on debugging:
# developer mode, use ?devmode to see details
devmode(TRUE)
# inspect reactivity in shiny
options(shiny.reactlog = TRUE)
# similar to SPS's traceback but on the whole app level
options(shiny.fullstacktrace = TRUE)
# open the `browser` debug mode on error
options(shiny.error = browser)
# when a shiny app file saves, reload the app, not working with modular apps like SPS at this moment
options(shiny.autoreload = TRUE)
See Shiny option website{blk} for more details