#' Temporarily set OpenTelemetry collection level #' #' @description #' `withOtelCollect()` temporarily sets the OpenTelemetry collection level for #' the duration of evaluating `expr`. `localOtelCollect()` sets the collection #' level for the remainder of the current function scope. #' #' These functions are useful for temporarily controlling telemetry collection #' during reactive expression creation. Only the following levels are allowed: #' * `"none"` - No telemetry data collected #' * `"reactivity"` - Collect reactive execution spans (includes session and #' reactive update events) #' * `"all"` - All available telemetry (currently equivalent to `"reactivity"`) #' #' Note that `"session"` and `"reactive_update"` levels are not permitted as #' these are runtime-specific levels that should only be set permanently via #' `options(shiny.otel.collect = ...)` or the `SHINY_OTEL_COLLECT` environment #' variable, not temporarily during reactive expression creation. #' #' @section Intended Usage: #' #' These functions are designed to perform sweeping changes to telemetry #' collection, such as enabling or disabling OpenTelemetry for an entire module #' or section of code where reactive expressions are being **created**: #' #' ```r #' # Enable telemetry for an entire module #' withOtelCollect("all", { #' my_result <- my_module("my_id") #' }) #' #' # Disable telemetry for expensive development-only reactives #' withOtelCollect("none", { #' debug_reactive <- reactive({ expensive_debug_computation() }) #' }) #' ``` #' #' @section Pipe Usage (Not Recommended): #' #' While using `withOtelCollect()` as a pipe-able method, it is not recommended due to the use case where the reactive object is created before the `withOtelCollect()` call. In such cases, the reactive object will not inherit the intended OpenTelemetry settings. #' #' Therefore, to avoid this hard-to-debug situation, we recommend that you only create your reactive objects within the `withOtelCollect()` call or after setting the local collection level with `localOtelCollect()`. #' #' ```r #' # Technically works, but not recommended #' x <- reactive({ ... }) %>% withOtelCollect(collect = "all") #' x <- reactive({ ... }) |> withOtelCollect(collect = "all") #' # Equivalent to: #' x <- withOtelCollect("all", reactive({ ... })) #' #' # Does NOT work as intended #' x <- reactive({ ... }) #' # `x` was created outside of `withOtelCollect()`, #' # therefore no OTel settings are applied #' x_no_otel <- withOtelCollect("all", x) #' #' # Best practice: Create the reactive object within `expr=` #' withOtelCollect("all", { #' x_with_otel <- reactive({ ... }) #' y_with_otel <- reactive({ ... }) #' }) #' ``` #' #' @param collect Character string specifying the OpenTelemetry collection level. #' Must be one of `"none"`, `"reactivity"`, or `"all"`. #' @param expr Expression to evaluate with the specified collection level #' (for `withOtelCollect()`). #' @param envir Environment where the collection level should be set #' (for `localOtelCollect()`). Defaults to the parent frame. #' #' @return #' * `withOtelCollect()` returns the value of `expr`. #' * `localOtelCollect()` is called for its side effect and returns the previous #' `collect` value invisibly. #' #' @seealso See the `shiny.otel.collect` option within [`shinyOptions`]. Setting #' this value will globally control OpenTelemetry collection levels. #' #' @examples #' \dontrun{ #' # Temporarily disable telemetry collection #' withOtelCollect("none", { #' # Code here won't generate telemetry #' reactive({ input$x + 1 }) #' }) #' #' # Collect reactivity telemetry but not other events #' withOtelCollect("reactivity", { #' # Reactive execution will be traced #' observe({ print(input$x) }) #' }) #' #' # Use local variant in a function #' my_function <- function() { #' localOtelCollect("none") #' # Rest of function executes without telemetry #' reactive({ input$y * 2 }) #' } #' } #' #' @rdname withOtelCollect #' @export withOtelCollect <- function(collect, expr) { collect <- as_otel_collect_with(collect) withr::with_options( list(shiny.otel.collect = collect), expr ) } #' @rdname withOtelCollect #' @export localOtelCollect <- function(collect, envir = parent.frame()) { collect <- as_otel_collect_with(collect) old <- withr::local_options( list(shiny.otel.collect = collect), .local_envir = envir ) invisible(old) } # Helper function to validate collect levels for with/local functions # Only allows "none", "reactivity", and "all" - not "session" or "reactive_update" as_otel_collect_with <- function(collect) { if (!is.character(collect)) { stop("`collect` must be a character vector.") } allowed_levels <- c("none", "reactivity", "all") collect <- match.arg(collect, allowed_levels, several.ok = FALSE) return(collect) }