Nothing
#' Injects a logger call to standard messages
#'
#' This function sets a hook to trigger [log_info()] when `message` is called
#' to log the informative messages with the global `logger` layout and appender.
#' @export
#' @examples \dontrun{
#' log_messages()
#' message("hi there")
#' }
log_messages <- function() {
if (any(sapply(
globalCallingHandlers()[names(globalCallingHandlers()) == "message"],
attr,
which = "implements"
) == "log_messages")) {
warning("Ignoring this call to log_messages as it was registered previously.")
} else {
globalCallingHandlers(
message = structure(function(m) {
logger::log_level(logger::INFO, m$message, .topcall = m$call)
}, implements = "log_messages")
)
}
}
#' Injects a logger call to standard warnings
#'
#' This function sets a hook to trigger [log_warn()] when `warning` is called
#' to log the warning messages with the global `logger` layout and appender.
#' @param muffle if TRUE, the warning is not shown after being logged
#' @export
#' @examples \dontrun{
#' log_warnings()
#' for (i in 1:5) {
#' Sys.sleep(runif(1))
#' warning(i)
#' }
#' }
log_warnings <- function(muffle = getOption("logger_muffle_warnings", FALSE)) {
if (any(sapply(
globalCallingHandlers()[names(globalCallingHandlers()) == "warning"],
attr,
which = "implements"
) == "log_warnings")) {
warning("Ignoring this call to log_warnings as it was registered previously.")
} else {
globalCallingHandlers(
warning = structure(function(m) {
logger::log_level(logger::WARN, m$message, .topcall = m$call)
if (isTRUE(muffle)) {
invokeRestart("muffleWarning")
}
}, implements = "log_warnings")
)
}
}
#' Injects a logger call to standard errors
#'
#' This function sets a hook to trigger [log_error()] when [stop()] is called
#' to log the error messages with the global `logger` layout and appender.
#' @param muffle if TRUE, the error is not thrown after being logged
#' @param traceback if TRUE the error traceback is logged along with the error
#' message
#' @export
#' @examples \dontrun{
#' log_errors()
#' stop("foobar")
#' }
log_errors <- function(muffle = getOption("logger_muffle_errors", FALSE), traceback = FALSE) {
if (any(sapply(
globalCallingHandlers()[names(globalCallingHandlers()) == "error"],
attr,
which = "implements"
) == "log_errors")) {
warning("Ignoring this call to log_errors as it was registered previously.")
} else {
globalCallingHandlers(
error = structure(function(m) {
logger::log_level(logger::ERROR, m$message, .topcall = m$call)
if (traceback) {
bt <- .traceback(3L)
logger::log_level(logger::ERROR, "Traceback:", .topcall = m$call)
for (i in seq_along(bt)) {
msg <- paste0(length(bt) - i + 1L, ": ", bt[[i]])
ref <- attr(bt[[i]], "srcref")
file <- attr(ref, "srcfile")
if (inherits(file, "srcfile")) {
file <- basename(file$filename)
line <- paste(unique(c(ref[1L], ref[3L])), collapse = "-")
msg <- paste0(msg, " at ", file, " #", line)
}
logger::log_level(logger::ERROR, skip_formatter(msg), .topcall = m$call)
}
}
if (isTRUE(muffle)) {
invokeRestart("abort")
}
}, implements = "log_errors")
)
}
}
#' Auto logging input changes in Shiny app
#'
#' This is to be called in the `server` section of the Shiny app.
#' @export
#' @param input passed from Shiny's `server`
#' @param level log level
#' @param excluded_inputs character vector of input names to exclude from logging
#' @param namespace the name of the namespace
#' @importFrom utils assignInMyNamespace assignInNamespace
#' @examples \dontrun{
#' library(shiny)
#'
#' ui <- bootstrapPage(
#' numericInput("mean", "mean", 0),
#' numericInput("sd", "sd", 1),
#' textInput("title", "title", "title"),
#' textInput("foo", "This is not used at all, still gets logged", "foo"),
#' passwordInput("password", "Password not to be logged", "secret"),
#' plotOutput("plot")
#' )
#'
#' server <- function(input, output) {
#' logger::log_shiny_input_changes(input, excluded_inputs = "password")
#'
#' output$plot <- renderPlot({
#' hist(rnorm(1e3, input$mean, input$sd), main = input$title)
#' })
#' }
#'
#' shinyApp(ui = ui, server = server)
#' }
log_shiny_input_changes <- function(input,
level = INFO,
namespace = NA_character_,
excluded_inputs = character()) {
fail_on_missing_package("shiny")
fail_on_missing_package("jsonlite")
session <- shiny::getDefaultReactiveDomain()
ns <- ifelse(!is.null(session), session$ns(character(0)), "")
if (!(shiny::isRunning() || inherits(session, "MockShinySession") || inherits(session, "session_proxy"))) {
stop("No Shiny app running, it makes no sense to call this function outside of a Shiny app")
}
input_values <- shiny::isolate(shiny::reactiveValuesToList(input))
assignInMyNamespace("shiny_input_values", input_values)
log_level(level, skip_formatter(trimws(paste(
ns,
"Default Shiny inputs initialized:",
as.character(jsonlite::toJSON(input_values, auto_unbox = TRUE))
))), namespace = namespace)
shiny::observe({
old_input_values <- shiny_input_values
new_input_values <- shiny::reactiveValuesToList(input)
names <- unique(c(names(old_input_values), names(new_input_values)))
names <- setdiff(names, excluded_inputs)
for (name in names) {
old <- old_input_values[name]
new <- new_input_values[name]
if (!identical(old, new)) {
message <- trimws("{ns} Shiny input change detected in {name}: {old} -> {new}")
log_level(level, message, namespace = namespace)
}
}
assignInNamespace("shiny_input_values", new_input_values, ns = "logger")
})
}
shiny_input_values <- NULL
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.