Skip to content

Commit

Permalink
Merge pull request #47 from ttscience/sentry
Browse files Browse the repository at this point in the history
add sentry support
  • Loading branch information
JagGlo authored Feb 13, 2024
2 parents 3a6a050 + 8ee0f66 commit 9337ed5
Show file tree
Hide file tree
Showing 12 changed files with 244 additions and 11 deletions.
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ Suggests:
jsonlite,
purrr,
knitr,
rmarkdown
rmarkdown,
sentryR
RdMacros: mathjaxr
Config/testthat/edition: 3
Encoding: UTF-8
Expand Down
68 changes: 68 additions & 0 deletions R/run-api.R
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@
#'
#' @export
run_unbiased <- function() {
tryCatch(
{
rlang::global_entrace()
},
error = function(e) {
message("Error setting up global_entrace, it is expected in testing environment: ", e$message)
}
)
setup_sentry()
host <- Sys.getenv("UNBIASED_HOST", "0.0.0.0")
port <- as.integer(Sys.getenv("UNBIASED_PORT", "3838"))
assign("db_connection_pool",
Expand All @@ -38,3 +47,62 @@ run_unbiased <- function() {
plumber::pr_run(host = host, port = port)
}
}

# hack to make sure we can mock the globalCallingHandlers
# this method needs to be present in the package environment for mocking to work
# linter disabled intentionally since this is internal method and cannot be renamed
globalCallingHandlers <- NULL # nolint

#' setup_sentry function
#'
#' This function is used to configure Sentry, a service for real-time error tracking.
#' It uses the sentryR package to set up Sentry based on environment variables.
#'
#' @param None
#'
#' @return None. If the SENTRY_DSN environment variable is not set, the function will
#' return a message and stop execution.
#'
#' @examples
#' setup_sentry()
#'
#' @details
#' The function first checks if the SENTRY_DSN environment variable is set. If not, it
#' returns a message and stops execution.
#' If SENTRY_DSN is set, it uses the sentryR::configure_sentry function to set up Sentry with
#' the following parameters:
#' - dsn: The Data Source Name (DSN) is retrieved from the SENTRY_DSN environment variable.
#' - app_name: The application name is set to "unbiased".
#' - app_version: The application version is retrieved from the GITHUB_SHA environment variable.
#' If not set, it defaults to "unspecified".
#' - environment: The environment is retrieved from the SENTRY_ENVIRONMENT environment variable.
#' If not set, it defaults to "development".
#' - release: The release is retrieved from the SENTRY_RELEASE environment variable.
#' If not set, it defaults to "unspecified".
#'
#' @seealso \url{https://docs.sentry.io/}
setup_sentry <- function() {
sentry_dsn <- Sys.getenv("SENTRY_DSN")
if (sentry_dsn == "") {
message("SENTRY_DSN not set, skipping Sentry setup")
return()
}

sentryR::configure_sentry(
dsn = sentry_dsn,
app_name = "unbiased",
app_version = Sys.getenv("GITHUB_SHA", "unspecified"),
environment = Sys.getenv("SENTRY_ENVIRONMENT", "development"),
release = Sys.getenv("SENTRY_RELEASE", "unspecified")
)

globalCallingHandlers(
error = global_calling_handler
)
}

global_calling_handler <- function(error) {
error$function_calls <- sys.calls()
sentryR::capture_exception(error)
signalCondition(error)
}
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,11 @@ To calculate code coverage, you will need to install the `covr` package. Once in
- `covr::report()`: This method runs all tests and generates a detailed coverage report in HTML format.
- `covr::package_coverage()`: This method provides a simpler, text-based code coverage report.

Alternatively, you can use the provided `run_tests_with_coverage.sh` script to run Unbiased tests with code coverage.
Alternatively, you can use the provided `run_tests_with_coverage.sh` script to run Unbiased tests with code coverage.

# Configuring Sentry
The Unbiased server offers robust error reporting capabilities through the integration of the Sentry service. To activate Sentry, simply set the `SENTRY_DSN` environment variable. Additionally, you have the flexibility to customize the setup further by configuring the following environment variables:

* `SENTRY_ENVIRONMENT` This is used to set the environment (e.g., "production", "staging", "development"). If not set, the environment defaults to "development".

* `SENTRY_RELEASE` This is used to set the release in Sentry. If not set, the release defaults to "unspecified".
4 changes: 2 additions & 2 deletions inst/plumber/unbiased_api/meta.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
#* @tag other
#* @get /sha
#* @serializer unboxedJSON
function(res) {
sentryR::with_captured_calls(function(req, res) {
sha <- Sys.getenv("GITHUB_SHA", unset = "NULL")
if (sha == "NULL") {
res$status <- 404
return(c(error = "SHA not found"))
} else {
return(sha)
}
}
})
6 changes: 4 additions & 2 deletions inst/plumber/unbiased_api/plumber.R
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
#*
#* @plumber
function(api) {
meta <- plumber::pr("meta.R")
study <- plumber::pr("study.R")
meta <- plumber::pr("meta.R") |>
plumber::pr_set_error(sentryR::sentry_error_handler)
study <- plumber::pr("study.R") |>
plumber::pr_set_error(sentryR::sentry_error_handler)

api |>
plumber::pr_mount("/meta", meta) |>
Expand Down
10 changes: 6 additions & 4 deletions inst/plumber/unbiased_api/study.R
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
#* @post /minimisation_pocock
#* @serializer unboxedJSON
#*
function(identifier, name, method, arms, covariates, p, req, res) {
sentryR::with_captured_calls(function(
identifier, name, method, arms, covariates, p, req, res
) {
return(
unbiased:::api__minimization_pocock(
identifier, name, method, arms, covariates, p, req, res
)
)
}
})

#* Randomize one patient
#*
Expand All @@ -37,8 +39,8 @@ function(identifier, name, method, arms, covariates, p, req, res) {
#* @serializer unboxedJSON
#*

function(study_id, current_state, req, res) {
sentryR::with_captured_calls(function(study_id, current_state, req, res) {
return(
unbiased:::api__randomize_patient(study_id, current_state, req, res)
)
}
})
42 changes: 42 additions & 0 deletions man/setup_sentry.Rd

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

15 changes: 15 additions & 0 deletions renv.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2156,6 +2156,21 @@
],
"Hash": "c19df082ba346b0ffa6f833e92de34d1"
},
"sentryR": {
"Package": "sentryR",
"Version": "1.1.2",
"Source": "Repository",
"Repository": "RSPM",
"Requirements": [
"httr",
"jsonlite",
"stats",
"stringr",
"tibble",
"uuid"
],
"Hash": "f37e91d605fbf665d7b5467ded4e539e"
},
"sessioninfo": {
"Package": "sessioninfo",
"Version": "1.2.2",
Expand Down
Empty file modified start_unbiased_api.sh
100644 → 100755
Empty file.
4 changes: 4 additions & 0 deletions tests/testthat/setup-testing-environment.R
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ setup_test_db_connection_pool <- function(envir = parent.frame()) {
)
}

# Make sure to disable Sentry during testing
withr::local_envvar(
SENTRY_DSN = NULL
)

# We will always run the API on the localhost
# and on a random port
Expand Down
91 changes: 91 additions & 0 deletions tests/testthat/test-run-api.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
testthat::test_that("uses correct environment variables when setting up sentry", {
withr::local_envvar(
c(
SENTRY_DSN = "https://sentry.io/123",
GITHUB_SHA = "abc",
SENTRY_ENVIRONMENT = "production",
SENTRY_RELEASE = "1.0.0"
)
)

testthat::local_mocked_bindings(
configure_sentry = function(dsn,
app_name,
app_version,
environment,
release) {
testthat::expect_equal(dsn, "https://sentry.io/123")
testthat::expect_equal(app_name, "unbiased")
testthat::expect_equal(app_version, "abc")
testthat::expect_equal(environment, "production")
testthat::expect_equal(release, "1.0.0")
},
.package = "sentryR",
)

global_calling_handlers_called <- FALSE

# mock globalCallingHandlers
testthat::local_mocked_bindings(
globalCallingHandlers = function(error) {
global_calling_handlers_called <<- TRUE
testthat::expect_equal(
unbiased:::global_calling_handler,
error
)
},
)

unbiased:::setup_sentry()

testthat::expect_true(global_calling_handlers_called)
})

testthat::test_that("skips sentry setup if SENTRY_DSN is not set", {
withr::local_envvar(
c(
SENTRY_DSN = ""
)
)

testthat::local_mocked_bindings(
configure_sentry = function(dsn,
app_name,
app_version,
environment,
release) {
# should not be called, so we fail the test
testthat::expect_true(FALSE)
},
.package = "sentryR",
)

was_called <- FALSE

# mock globalCallingHandlers
testthat::local_mocked_bindings(
globalCallingHandlers = function(error) {
was_called <<- TRUE
},
)

testthat::expect_message(unbiased:::setup_sentry(), "SENTRY_DSN not set, skipping Sentry setup")
testthat::expect_false(was_called)
})

testthat::test_that("global_calling_handler captures exception and signals condition", {
error <- simpleError("test error")

capture_exception_called <- FALSE

testthat::local_mocked_bindings(
capture_exception = function(error) {
capture_exception_called <<- TRUE
testthat::expect_equal(error, error)
},
.package = "sentryR",
)

testthat::expect_error(unbiased:::global_calling_handler(error))
testthat::expect_true(capture_exception_called)
})
3 changes: 2 additions & 1 deletion vignettes/articles/minimization_randomization_comparison.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ def <- simstudy::defData(def, varname = "hba1c", formula = "0.888", dist = "bina
def <- simstudy::defData(def, varname = "tpo2", formula = "0.354", dist = "binary")
# correlation with diabetes type
def <- simstudy::defData(
def, varname = "age", formula = "(diabetes_type == 0) * (-0.95)", link = "logit", dist = "binary"
def,
varname = "age", formula = "(diabetes_type == 0) * (-0.95)", link = "logit", dist = "binary"
)
# <= 2 - 0.302
def <- simstudy::defData(def, varname = "wound_size", formula = "0.302", dist = "binary")
Expand Down

0 comments on commit 9337ed5

Please sign in to comment.