Skip to content

Commit

Permalink
Merge pull request #189 from Appsilon/187-fix-log_all_inputs
Browse files Browse the repository at this point in the history
Fixes problem when `log_all_inputs` is called manually
  • Loading branch information
averissimo authored Oct 9, 2024
2 parents a1341b1 + 6ad01eb commit 06b89f1
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 71 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Type: Package
Package: shiny.telemetry
Title: 'Shiny' App Usage Telemetry
Version: 0.3.0
Version: 0.3.0.9000
Authors@R: c(
person("André", "Veríssimo", , "[email protected]", role = c("aut", "cre")),
person("Kamil", "Żyła", , "[email protected]", role = "aut"),
Expand Down
11 changes: 11 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
# shiny.telemetry (development)

### New Features

- Added `log_errors` method that allows users to track errors in their Shiny apps outside `start_session` (#189).

### Bug Fixes

- Fixed problem with `log_all_inputs` call that crashed telemetry (#187).
- Fixed error appearing in analytics app (#188).

# shiny.telemetry 0.3.0

### New Features
Expand Down
10 changes: 8 additions & 2 deletions R/prepare-admin-panel.R
Original file line number Diff line number Diff line change
Expand Up @@ -917,7 +917,13 @@ prepare_admin_panel_components <- function(

selected_session_data <- shiny::reactive({
shiny::validate(shiny::need(selected_session(), label = "selected_session"))
selected_log_data() %>%
selected_data <- selected_log_data()

if (!"message" %in% names(selected_data)) {
selected_data$message <- NA
}

selected_data %>%
dplyr::filter(
.data$type %in% c("login", "logout", "input", "navigation", "error"),
session == selected_session()
Expand All @@ -927,7 +933,7 @@ prepare_admin_panel_components <- function(
content = dplyr::case_when(
type %in% c("login", "logout") ~ type,
type == "input" ~ sprintf("Input: %s <br /> Value: %s", id, value),
type == "navigation" ~ sprintf("Navigated: %s", id),
type == "navigation" ~ sprintf("Navigated (%s): %s", id, value),
type == "error" ~ sprintf("Error: %s", message),
),
style = "text-align: left;",
Expand Down
145 changes: 78 additions & 67 deletions R/telemetry.R
Original file line number Diff line number Diff line change
Expand Up @@ -211,71 +211,7 @@ Telemetry <- R6::R6Class( # nolint object_name.
}

if (isTRUE(track_errors)) {
if ("onUnhandledError" %in% ls(getNamespace("shiny"))) {
# onUnhandledError handler is only available in shiny >= 1.8.1
shiny::onUnhandledError(function(error) {
self$log_error(
output_id = "global",
message = conditionMessage(error)
)
})
} else {
# In shiny < 1.8.1, we fallback to using the `shiny.error` option and
# if that is already set, we track the `shiny:error` javascript event.
lifecycle::deprecate_warn(
when = as.character(utils::packageVersion("shiny")),
what = "Telemetry$start_session(track_errors = \"is not fully enabled \")",
details = c(
paste(
"Update the shiny package to version `1.8.1` or higher to",
"enable logging of all errors.",
sep = " "
),
paste(
"Until then, shiny.telemetry can only reliably detect errors",
"triggered by the `shiny:error` javacript event.",
sep = " "
)
),
env = getNamespace("shiny")
)

if (is.null(getOption("shiny.error"))) {
options(
"shiny.error" = function(.envir = parent.frame()) {
# make sure id is a string without spaces
output_id <- paste(
gsub(
" |\t|\r",
"_",
as.character(.envir$e$call %||% "global")
),
collapse = "__"
)

self$log_error(
output_id = output_id,
message = .envir$e$message %||% "Unknown error.",
session = session
)
}
)

# Restore previous option
shiny::onSessionEnded(
fun = function() options("shiny.error" = NULL),
session = session
)
} else {
shiny::observeEvent(input[[private$.track_error_id]], {
self$log_error(
output_id = input[[private$.track_error_id]]$output_id,
message = input[[private$.track_error_id]]$message,
session = session
)
})
}
}
self$log_errors(session)
}
NULL
},
Expand Down Expand Up @@ -504,6 +440,7 @@ Telemetry <- R6::R6Class( # nolint object_name.
navigation_inputs = c(),
excluded_inputs_regex = excluded_inputs_regex,
include_input_ids = include_input_ids,
track_errors = TRUE,
session = session
)
},
Expand Down Expand Up @@ -606,7 +543,7 @@ Telemetry <- R6::R6Class( # nolint object_name.
)
},
#' @description
#' Log an error event
#' Log a manual error event
#'
#' @param output_id string that refers to the output element where the error occurred.
#' @param message string that describes the error.
Expand All @@ -631,6 +568,80 @@ Telemetry <- R6::R6Class( # nolint object_name.
details = list(output_id = output_id, message = message),
session = session
)
},

#' @description
#' Track errors as they occur in observe and reactive
#'
#' @param session `ShinySession` object or NULL to identify the current Shiny session.
#'
#' @return Nothing. This method is called for side effects.
log_errors = function(session = shiny::getDefaultReactiveDomain()) {
if ("onUnhandledError" %in% ls(getNamespace("shiny"))) {
# onUnhandledError handler is only available in shiny >= 1.8.1
shiny::onUnhandledError(function(error) {
self$log_error(
output_id = "global",
message = conditionMessage(error)
)
})
} else {
# In shiny < 1.8.1, we fallback to using the `shiny.error` option and
# if that is already set, we track the `shiny:error` javascript event.
lifecycle::deprecate_warn(
when = as.character(utils::packageVersion("shiny")),
what = "Telemetry$start_session(track_errors = \"is not fully enabled \")",
details = c(
paste(
"Update the shiny package to version `1.8.1` or higher to",
"enable logging of all errors.",
sep = " "
),
paste(
"Until then, shiny.telemetry can only reliably detect errors",
"triggered by the `shiny:error` javacript event.",
sep = " "
)
),
env = getNamespace("shiny")
)

if (is.null(getOption("shiny.error"))) {
options(
"shiny.error" = function(.envir = parent.frame()) {
# make sure id is a string without spaces
output_id <- paste(
gsub(
" |\t|\r",
"_",
as.character(.envir$e$call %||% "global")
),
collapse = "__"
)

self$log_error(
output_id = output_id,
message = .envir$e$message %||% "Unknown error.",
session = session
)
}
)

# Restore previous option
shiny::onSessionEnded(
fun = function() options("shiny.error" = NULL),
session = session
)
} else {
shiny::observeEvent(session$input[[private$.track_error_id]], {
self$log_error(
output_id = session$input[[private$.track_error_id]]$output_id,
message = session$input[[private$.track_error_id]]$message,
session = session
)
})
}
}
}
),
active = list(
Expand Down Expand Up @@ -682,7 +693,7 @@ Telemetry <- R6::R6Class( # nolint object_name.
track_values,
excluded_inputs,
navigation_inputs,
track_errors,
track_errors = TRUE,
excluded_inputs_regex = NULL,
include_input_ids = NULL,
session
Expand Down
23 changes: 22 additions & 1 deletion man/Telemetry.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 06b89f1

Please sign in to comment.