Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
127 commits
Select commit Hold shift + click to select a range
36b830c
Add otel promise remote
schloerke Aug 4, 2025
b8a9184
WIP: Add OpenTelemetry integration for reactives
schloerke Aug 5, 2025
0faf1b7
Remove check errors
schloerke Aug 5, 2025
7b8c9c1
Merge branch 'main' into feat/otel
schloerke Aug 5, 2025
542681c
Add otel packages
schloerke Aug 5, 2025
21c1ce1
Merge branch 'main' into feat/otel
schloerke Aug 5, 2025
bf963a1
Add r-lib/otelsdk to Remotes in DESCRIPTION
schloerke Aug 5, 2025
bd6d1ab
Add Open Telemetry section to pkgdown config
schloerke Aug 5, 2025
7f38738
connect sync items. Need to prove promise calculations
schloerke Aug 5, 2025
eca760b
Update shiny-package.R
schloerke Aug 5, 2025
2ca085e
Commit current state
schloerke Aug 5, 2025
c88bef4
Merge branch 'main' into feat/otel
schloerke Aug 18, 2025
0903eee
Init otel.R
schloerke Aug 18, 2025
f004e54
Nerf all impl in bindOtel to use new otel methods
schloerke Aug 18, 2025
bb66440
Log with safe and dynamically use existing reactive locks when available
schloerke Aug 18, 2025
c584a00
Use new otel methods
schloerke Aug 18, 2025
ac14795
Disable render function observer from otel
schloerke Aug 18, 2025
8c4d9d7
Set the otel span before running calling handlers for render function
schloerke Aug 18, 2025
751ce6e
`devtools::document()` (GitHub Actions)
schloerke Aug 18, 2025
f8c2fda
Pair to the correct react lock span
schloerke Aug 18, 2025
1858220
Merge branch 'feat/otel' of https://github.com/rstudio/shiny into fea…
schloerke Aug 18, 2025
abf7b5c
Require domain be given to with exsting ospan method
schloerke Aug 18, 2025
bdbfd0b
Update bind-otel.R
schloerke Aug 18, 2025
3de7b7b
tweaks
schloerke Aug 18, 2025
1c91cd9
Wrap execute flush with context's domain for ospan activation
schloerke Aug 18, 2025
686fcf2
`devtools::document()` (GitHub Actions)
schloerke Aug 18, 2025
aacf00c
Update demo app to use promises.
schloerke Aug 18, 2025
35eeb09
Reactivate ospan promise domains in reverse order so that the most re…
schloerke Aug 18, 2025
1267d3e
Fix promise resolve to be a regular promise to get the eval in the co…
schloerke Aug 18, 2025
c3e90a2
Better debugging
schloerke Aug 18, 2025
cae54cb
Add debug message
schloerke Aug 19, 2025
0583213
Try to add a span link. Leave as comment for now
schloerke Aug 19, 2025
e181c0a
clean up codes
schloerke Aug 19, 2025
3126080
`rm` does not inherit when removing... so only check for existence wi…
schloerke Aug 19, 2025
e7c8f97
use single value within shiny for `otel is tracing` to avoid consiste…
schloerke Aug 19, 2025
3b8abd9
Update bind-otel.R
schloerke Aug 19, 2025
d924ea8
Consistent label format
schloerke Aug 20, 2025
8b4d49b
Comments
schloerke Aug 20, 2025
c202b53
Latest otel integrations. Use wrapper otel methods for consistent imp…
schloerke Aug 25, 2025
222ead0
Disable languageserver styler changes
schloerke Aug 25, 2025
f839cf5
Update otel log to not interpolate anything
schloerke Aug 25, 2025
97b9973
Use `"Reactive update"` . Need to remove "lock" from name
schloerke Aug 25, 2025
a4a9f7f
Update shiny.R
schloerke Aug 26, 2025
4910fdb
Unify otel label display within `otel-label.R`
schloerke Aug 26, 2025
7c77156
`reactive_lock` -> `reactive_update`
schloerke Aug 26, 2025
f8e87be
Bump dev version v1.11.1.9001
schloerke Aug 26, 2025
6fd9a9c
`withOtelShiny()` -> `withOtel()`
schloerke Aug 26, 2025
85809a5
Apply suggestions from code review
schloerke Aug 26, 2025
df6994b
Code feedback
schloerke Aug 26, 2025
b32ff78
Have `otel_is_tracing` allow to be dynamic
schloerke Aug 26, 2025
2f12f0f
Fix new bind otel. Add docs. Move existing ospan methods closer to impl
schloerke Aug 27, 2025
923a81a
Update docs for option
schloerke Aug 27, 2025
877f5c7
From ospan capture from within Context flush to `run()`
schloerke Aug 27, 2025
ff45870
Don't make the option for the dev app bleed out
schloerke Aug 27, 2025
653ef9f
Update stacks given new otel
schloerke Aug 27, 2025
a969167
Make stack testing more robust
schloerke Aug 27, 2025
e1c0310
Update pkgdown.yml
schloerke Aug 27, 2025
3df8447
Update bind-otel.R
schloerke Aug 28, 2025
5fa79aa
`npm run build` (GitHub Actions)
schloerke Aug 28, 2025
f308e53
Sync package version (GitHub Actions)
schloerke Aug 28, 2025
c35b254
Nerf bind options to just `"all"` and `"none"`
schloerke Aug 28, 2025
0d1037f
`reactive-update` -> `reactive_update`
schloerke Aug 28, 2025
4977e52
Merge branch 'feat/otel' of https://github.com/rstudio/shiny into fea…
schloerke Aug 29, 2025
b23fd71
Update labels and srcrefs for all otel bound objects
schloerke Aug 29, 2025
ec5e4e4
Start transition to otel session start/end event. Use `session.id` at…
schloerke Aug 29, 2025
6c8cbc2
Remove unnecessary domain parameters
schloerke Aug 29, 2025
83ab5ac
refactor(otel): split span management into focused modules
schloerke Aug 31, 2025
982aad4
Require new promises package
schloerke Sep 1, 2025
e0ba77e
refactor(otel): migrate to promises package otel functions
schloerke Sep 3, 2025
24dd639
Update reactives.R
schloerke Sep 3, 2025
3580742
Update bind-otel.R
schloerke Sep 3, 2025
07649ad
If a srcref is `NULL`, return `NULL`
schloerke Sep 3, 2025
cc56f4a
Update stacks snaps
schloerke Sep 3, 2025
7ebd19c
rerun stack traces with testthat from cran, not github
schloerke Sep 3, 2025
346bf38
Fix doc issue
schloerke Sep 3, 2025
9117f55
Remove message about using new syntax in code
schloerke Sep 3, 2025
39bdcaf
`devtools::document()` (GitHub Actions)
schloerke Sep 3, 2025
c284c2d
Merge branch 'feat/otel' of https://github.com/rstudio/shiny into fea…
schloerke Sep 3, 2025
273c4c6
Do not namespace reactive val label
schloerke Sep 3, 2025
39d6059
Allow for eventReactive to find the variable name for it's label
schloerke Sep 3, 2025
7980201
Do not auto otel within eventReactive or observeEvent methods until r…
schloerke Sep 3, 2025
17e6261
Update bind-otel.R
schloerke Sep 3, 2025
23ffe44
debug: Add OTEL config options to with_options in barret
schloerke Sep 5, 2025
2fa71cf
Similar to promises, do not have a custom tracer. Instead, capture it…
schloerke Sep 5, 2025
175c5b9
tmp: Use .Renviron instead of local options
schloerke Sep 5, 2025
6aaaf43
Wrap the implementation of `runApp()` in a ospan promise domain
schloerke Sep 5, 2025
0d44c2b
Merge branch 'main' into feat/otel
schloerke Sep 5, 2025
5e8e2ce
`devtools::document()` (GitHub Actions)
schloerke Sep 5, 2025
92584b2
Merge branch 'main' into feat/otel
schloerke Sep 5, 2025
ca46b26
Do not export `withOtel()`
schloerke Sep 9, 2025
f7bc93c
Use fast get_tracer() from httr2
schloerke Sep 9, 2025
6797cbe
Delete withOtel.Rd
schloerke Sep 9, 2025
12a8e44
Make anon label short
schloerke Sep 10, 2025
52c8033
Make the default otel level "all"; Reduce internal options
schloerke Sep 10, 2025
05bedd8
Move helper function outside of R folder into tests folder; Add encom…
schloerke Sep 10, 2025
3828152
Use new `promises::local_ospan_promise_domain()`
schloerke Sep 10, 2025
bdd7569
Clearer fatal / error log title
schloerke Sep 11, 2025
c3f414b
Update helper-barret.R
schloerke Sep 11, 2025
7f275bd
Updates from otelsdk and promises
schloerke Sep 25, 2025
c16d85b
Speed enhancement for `otel_is_tracing_enabled()`
schloerke Sep 25, 2025
bbe8166
Update `otel_log_safe()` -> `otel_log()` with cached logger
schloerke Sep 25, 2025
d7be0e7
Improve default labeling for reactiveVal and reactiveValues
schloerke Sep 26, 2025
537f034
Use flags on reactive objects, rather than wrapping and re-writing th…
schloerke Oct 6, 2025
2b0d2c8
Use safety net of tryCatch
schloerke Oct 6, 2025
745adc0
Do not export internal s3 method: bindOtel()
schloerke Oct 6, 2025
ed6b424
Use a reactiveVal instead of reactiveValues for internal calculations…
schloerke Oct 6, 2025
1f0d2c0
Better anonymous labels for otel
schloerke Oct 6, 2025
f4d6c94
Updates
schloerke Oct 6, 2025
8d8d89f
Update .Renviron
schloerke Oct 8, 2025
3933121
Fix bad subset
schloerke Oct 8, 2025
d1d60ba
FIx always false check
schloerke Oct 8, 2025
54c7fbd
Refactor Session start and session end ospans / logs
schloerke Oct 8, 2025
b632e4b
Refactor otel labels to support both event and cache
schloerke Oct 8, 2025
ac31109
Update shiny.R
schloerke Oct 8, 2025
6000499
Sanitize fatal/unhandled errors (unless disabled) before sending to O…
schloerke Oct 8, 2025
9cf0900
Better labels for debounce and throttle
schloerke Oct 8, 2025
d5d6173
Add logging for reactives that are not made the traditional way (inpu…
schloerke Oct 8, 2025
792d6f4
More otel attrs and better label for eventReactive
schloerke Oct 8, 2025
b889057
Update helper-barret.R
schloerke Oct 8, 2025
645a88f
Spelling and remove outdated pkgdown section
schloerke Oct 8, 2025
875c997
Update NEWS.md
schloerke Oct 8, 2025
da7e334
Add ExtendedTask Support
schloerke Oct 9, 2025
5bbbc39
Add extended task label functions with domain support
schloerke Oct 10, 2025
c07557b
Nerf `session.start` / `session.end` events. Move start attrs into `s…
schloerke Oct 10, 2025
54ca7c8
Update NEWS.md
schloerke Oct 10, 2025
49cc4ec
Merge branch 'main' into feat/otel
schloerke Oct 10, 2025
a854f08
Merge branch 'main' into feat/otel
schloerke Oct 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .Renviron
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please delete

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
OTEL_TRACES_EXPORTER=${LOGFIRE_OTEL_TRACES_EXPORTER}
OTEL_EXPORTER_OTLP_ENDPOINT=${LOGFIRE_OTEL_EXPORTER_OTLP_ENDPOINT}
OTEL_EXPORTER_OTLP_HEADERS=${LOGFIRE_OTEL_EXPORTER_OTLP_HEADERS}
OTEL_LOGS_EXPORTER=${LOGFIRE_OTEL_LOGS_EXPORTER}
OTEL_LOG_LEVEL=${LOGFIRE_OTEL_LOG_LEVEL}
OTEL_METRICS_EXPORTER=${LOGFIRE_OTEL_METRICS_EXPORTER}
OTEL_ENV=dev
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"[r]": {
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
"editor.formatOnSave": false,
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
Expand Down
15 changes: 13 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Type: Package
Package: shiny
Title: Web Application Framework for R
Version: 1.11.1.9000
Version: 1.11.1.9001
Authors@R: c(
person("Winston", "Chang", , "[email protected]", role = "aut",
comment = c(ORCID = "0000-0002-1576-2126")),
Expand Down Expand Up @@ -94,7 +94,8 @@ Imports:
later (>= 1.0.0),
lifecycle (>= 0.2.0),
mime (>= 0.3),
promises (>= 1.3.2),
otel,
promises (>= 1.3.3.9006),
R6 (>= 2.0),
rlang (>= 0.4.10),
sourcetools,
Expand All @@ -114,6 +115,7 @@ Suggests:
magrittr,
markdown,
mirai,
otelsdk (>= 0.2.0),
ragg,
reactlog (>= 1.0.0),
rmarkdown,
Expand All @@ -122,6 +124,8 @@ Suggests:
testthat (>= 3.2.1),
watcher,
yaml
Remotes:
rstudio/promises
Config/Needs/check: shinytest2
Config/testthat/edition: 3
Encoding: UTF-8
Expand All @@ -133,6 +137,7 @@ Collate:
'app_template.R'
'bind-cache.R'
'bind-event.R'
'bind-otel.R'
'bookmark-state-local.R'
'bookmark-state.R'
'bootstrap-deprecated.R'
Expand Down Expand Up @@ -185,6 +190,12 @@ Collate:
'modal.R'
'modules.R'
'notifications.R'
'otel-attr-srcref.R'
'otel-label.R'
'otel-reactive-update.R'
'otel-session.R'
'otel-with.R'
'otel.R'
'priorityqueue.R'
'progress.R'
'react.R'
Expand Down
3 changes: 3 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -388,9 +388,12 @@ importFrom(promises,"%...!%")
importFrom(promises,"%...>%")
importFrom(promises,as.promise)
importFrom(promises,is.promising)
importFrom(promises,local_ospan_promise_domain)
importFrom(promises,promise)
importFrom(promises,promise_reject)
importFrom(promises,promise_resolve)
importFrom(promises,with_ospan_async)
importFrom(promises,with_ospan_promise_domain)
importFrom(rlang,"%||%")
importFrom(rlang,"fn_body<-")
importFrom(rlang,"fn_fmls<-")
Expand Down
24 changes: 23 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
# shiny (development version)

## OpenTelemetry support (#4269)

* Added support for [OpenTelemetry](https://opentelemetry.io/) tracing via [`{otel}`](https://otel.r-lib.org/index.html). By default, if `otel::is_tracing_enabled()` returns `TRUE`, then `{shiny}` will record all OpenTelemetery spans. To disable adjust the default `{shiny}` behavior, set the option `shiny.otel.bind` to `"none"` (or environment variable to `SHINY_OTEL_BIND=none`). See [`{otelsdk}`'s Collecting Telemetry Data](https://otelsdk.r-lib.org/reference/collecting.html) for more details on configuring OpenTelemetry.

* Spans are recorded for:
* `session_start`: Wraps the calling of the `server()` function. Also contains HTTP request within the attributes.
* `session_end`: Wraps the calling of the `onSessionEnded()` handlers.
* `reactive_update`: Signals the start of when Shiny knows something is to be calculated. This span ends when there are no more reactive updates (promises or synchronous) to be calculated.
* `reactive`, `observe`, `output`: Captures the calculation (including any async promise chains) of a reactive expression (`reactive()`), an observer (`observe()`), or an output render function (`render*()`).
* `ExtendedTask`: Captures the calculation (including any async promise chains) of an `ExtendedTask`.

* OpenTelemetry Logs are recorded for:
* `Set reactiveVal <name>` - When a `reactiveVal()` is set
* `Set reactiveValues <name>$<key>` - When a `reactiveValues()` element is set
* Fatal or unhandled errors - When an error occurs that causes the session to end, or when an unhandled error occurs in a reactive context. Contains the error within the attributes. To unsantize the error message being collected, set `options(shiny.otel.sanitize.errors = FALSE)`.
* `Set ExtendedTask <name> <value>` - When an `ExtendedTask`'s respective reactive value (e.g., `status`, `value`, and `error`) is set.
* `<ExtendedTask name> add to queue` - When an `ExtendedTask` is added to the task queue.

* All logs and spans contain the `session.id` attribute.

## New features

* The `icon` argument of `updateActionButton()`/`updateActionLink()` nows allows values other than `shiny::icon()` (e.g., `fontawesome::fa()`, `bsicons::bs_icon()`, etc). (#4249)
Expand All @@ -10,9 +30,11 @@

* Fixed an issue where `updateSelectizeInput(options = list(plugins="remove_button"))` could lead to multiple remove buttons. (#4275)

* The default label for `reactiveValues()` will not attempt to retrieve the assigned name if the srcref is available. If a value can not easily be produced, the default label (`reactiveValuesXXXX`) will be used instead. (#4269)
Copy link
Member

@jcheng5 jcheng5 Oct 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for reactivePoll, debounce, throttle


## Changes

* The return value of `actionButton()`/`actionLink()` changed slightly: `label` and `icon` are wrapped in an additional HTML container element. This allows for: 1. `updateActionButton()`/`updateActionLink()` to distinguish between the `label` and `icon` when making updates and 2. spacing between `label` and `icon` to be more easily customized via CSS.
* The return value of `actionButton()`/`actionLink()` changed slightly: `label` and `icon` are wrapped in an additional HTML container element. This allows for: 1. `updateActionButton()`/`updateActionLink()` to distinguish between the `label` and `icon` when making updates and 2. spacing between `label` and `icon` to be more easily customized via CSS.

# shiny 1.11.1

Expand Down
6 changes: 3 additions & 3 deletions R/bind-cache.R
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ bindCache.default <- function(x, ...) {
bindCache.reactiveExpr <- function(x, ..., cache = "app") {
check_dots_unnamed()

label <- exprToLabel(substitute(key), "cachedReactive")
label <- exprToLabel(substitute(x), "cachedReactive")
domain <- reactive_get_domain(x)

# Convert the ... to a function that returns their evaluated values.
Expand All @@ -494,8 +494,8 @@ bindCache.reactiveExpr <- function(x, ..., cache = "app") {
rm(x)
# Hacky workaround for issue with `%>%` preventing GC:
# https://github.com/tidyverse/magrittr/issues/229
if (exists(".GenericCallEnv") && exists(".", envir = .GenericCallEnv)) {
rm(list = ".", envir = .GenericCallEnv)
if (exists(".GenericCallEnv") && exists(".", envir = .GenericCallEnv, inherits = FALSE)) {
rm(list = ".", envir = .GenericCallEnv, inherits = FALSE)
}


Expand Down
40 changes: 27 additions & 13 deletions R/bind-event.R
Original file line number Diff line number Diff line change
Expand Up @@ -199,28 +199,39 @@ bindEvent.reactiveExpr <- function(x, ..., ignoreNULL = TRUE, ignoreInit = FALSE
label <- label %||%
sprintf('bindEvent(%s, %s)', attr(x, "observable", exact = TRUE)$.label, quos_to_label(qs))

x_classes <- class(x)

# Don't hold on to the reference for x, so that it can be GC'd
rm(x)

initialized <- FALSE

res <- reactive(label = label, domain = domain, ..stacktraceon = FALSE, {
hybrid_chain(
eventFunc(),
function(value) {
if (ignoreInit && !initialized) {
initialized <<- TRUE
req(FALSE)
}
without_otel_bind({
res <- reactive(label = label, domain = domain, ..stacktraceon = FALSE, {
hybrid_chain(
{
eventFunc()
},
function(value) {
if (ignoreInit && !initialized) {
initialized <<- TRUE
req(FALSE)
}

req(!ignoreNULL || !isNullEvent(value))
req(!ignoreNULL || !isNullEvent(value))

isolate(valueFunc())
}
)
isolate(valueFunc())
}
)
})
})

class(res) <- c("reactive.event", class(res))
class(res) <- c("reactive.event", x_classes)

if (has_otel_bind("reactivity")) {
res <- bind_otel_reactive_expr(res)
}

res
}

Expand Down Expand Up @@ -302,6 +313,9 @@ bindEvent.Observer <- function(x, ..., ignoreNULL = TRUE, ignoreInit = FALSE,
)

class(x) <- c("Observer.event", class(x))
if (has_otel_bind("reactivity")) {
x <- bind_otel_observe(x)
}
invisible(x)
}

Expand Down
186 changes: 186 additions & 0 deletions R/bind-otel.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# - OpenTelemetry -----------------------------------
# * Integration locations:
# * √ Server:
# * Start reactive_update when reactive busy count > 0
# * End reactive_update when reactive busy count == 0
# * √ Reactives: val, values, expr, render fn, observe
# * Combinations:
# * √ debounce() / throttle()
# * bindCache()
# * √ bindEvent()
# * X - bindProgress()
# * Special functions:
# * ExtendedTask()
# * Extended task links to submission reactive
# * Reactive update that gets result links to the extended task
# * √ observeEvent()
# * √ eventReactive()
# * TODO: Not recording updates within the span!!
# * Maybe enhance all `withReactiveDomain()` calls?
# * Global options:
# * √ shiny.otel.bind:
# * "all", "none" - all or nothing
# * Current non-public options:
# * "session" - Adds session start/end events
# * "reactive_update" - Spans for any reactive update. (Includes `"session"` features).
# * "reactivity" - Spans for all reactive things. (Includes `"reactive_update"` features).
# * Private methods:
# * bind_otel_*() - Methods that binds the reactive object to OpenTelemetry spans
# * Note: When adding otel to an object, prepend a class of `FOO.otel`. Then add a dispatch method for `bindOtel.FOO.otel()` that declares the object already has been bound.
# * without_otel_bind(expr) - Will not bind any reactives created within `expr` to OpenTelemetry spans.

# - TODO: -----------------------------------
# * Span status for success/failure (render function and regular reactive exprs?)
# * Error handling is not an "exception" for fatal logs
# * Connect `user.id` to be their user name: https://opentelemetry.io/docs/specs/semconv/registry/attributes/user/
# * Tests with otel recording



# - Questions -----------------------------------
# Add error handling for every otel. Use withCallingHandlers similar to https://github.com/r-lib/mirai/pull/395/files#diff-9e809582679952a93b9f34755bb38207471945eb36cedb9e2aa755125449f531R214-R215

# TODO: Extended Tasks are linked from parent span. Maybe use an envvar for span context? It is allowed for child process can end much later than the parent process. Take inspiration from callr PR (copying of the span context to the child process).

# ------------------------------------------

# # Approach
# Use flags on the reactive object to indicate whether to record OpenTelemetry spans.
#
# Cadence:
# * `$.isRecordingOtel` - Whether to record OpenTelemetry spans for this reactive object
# * `$.otelLabel` - The label to use for the OpenTelemetry span
# * `$.otelAttrs` - Additional attributes to add to the OpenTelemetry span


#' Add OpenTelemetry for reactivity to an object
#'
#' @description
#'
#' `bindOtel()` adds OpenTelemetry for [reactive()] expressions and `render*`
#' functions (like [renderText()], [renderTable()], ...).
#'
#' Wrapper to creating an active reactive OpenTelemetry span that closes when
#' the reactive expression is done computing. Typically this is when the
#' reactive expression finishes (synchronous) or when the returned promise is
#' done computing (asynchronous).

#' @section Async with OpenTelemetry:
#'
#' With a reactive expression, the key and/or value expression can be
#' _asynchronous_. In other words, they can be promises --- not regular R
#' promises, but rather objects provided by the
#' \href{https://rstudio.github.io/promises/}{\pkg{promises}} package, which
#' are similar to promises in JavaScript. (See [promises::promise()] for more
#' information.) You can also use [mirai::mirai()] or [future::future()]
#' objects to run code in a separate process or even on a remote machine.
#'
#' When reactive expressions are being calculated in parallel (by having
#' another reactive promise compute in the main process), the currently active
#' OpenTelemetry span will be dynamically swapped out according to the
#' currently active reactive expression. This means that as long as a promise
#' was `then()`ed or `catch()`ed with an active OpenTelemetry span, the span
#' will be correctly propagated to the next step (and subsequently other
#' steps) in the promise chain.
#'
#' While the common case is for a reactive expression to be created
#' synchronously, troubles arise when the reactive expression is created
#' asynchronously. The span **must** be created before the reactive expression
#' is executed, it **must** be active for the duration of the expression, and
#' it **must** not be closed until the reactive expression is done executing.
#' This is not easily achieved with a single function call, so we provide a
#' way to create a reactive expression that is bound to an OpenTelemetry
#' span.
#'
#' @param x The object to add caching to.
#' @param ... Future parameter expansion.
#' @seealso [bindCache()] and [bindEvent()] for other ways to bind to your reactives.
#' @noRd
NULL

bind_otel_reactive_val <- function(x) {

impl <- attr(x, ".impl", exact = TRUE)
# Set flag for otel logging when setting the value
impl$.isRecordingOtel <- TRUE

class(x) <- c("reactiveVal.otel", class(x))

x
}

bind_otel_reactive_values <- function(x) {

impl <- .subset2(x, "impl")
# Set flag for otel logging when setting values
impl$.isRecordingOtel <- TRUE

class(x) <- c("reactivevalues.otel", class(x))

x
}

bind_otel_reactive_expr <- function(x) {

domain <- reactive_get_domain(x)

impl <- attr(x, "observable", exact = TRUE)
impl$.isRecordingOtel <- TRUE
# Covers both reactive and reactive.event
impl$.otelLabel <- ospan_label_reactive(x, domain = impl$.domain)

class(x) <- c("reactiveExpr.otel", class(x))

x
}

bind_otel_observe <- function(x) {
x$.isRecordingOtel <- TRUE
x$.otelLabel <- ospan_label_observer(x, domain = x$.domain)

class(x) <- c("Observer.otel", class(x))
invisible(x)
}



bind_otel_shiny_render_function <- function(x) {
if (inherits(x, "shiny.render.function.otel")) {
# Already bound
return(x)
}
Comment on lines +148 to +151
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't ever happen


valueFunc <- x
span_label <- NULL
ospan_attrs <- attr(x, "otelAttrs")

renderFunc <- function(...) {
# Dynamically determine the span label given the current reactive domain
if (is.null(span_label)) {
span_label <<-
ospan_label_render_function(x, domain = getDefaultReactiveDomain())
}

with_shiny_ospan_async(
span_label,
{
valueFunc(...)
},
attributes = ospan_attrs
)
}

renderFunc <- addAttributes(renderFunc, renderFunctionAttributes(valueFunc))
class(renderFunc) <- c("shiny.render.function.otel", class(valueFunc))
renderFunc
}



# bindOtel.function <- function(x, ...) {
# cli::cli_abort(paste0(
# "Don't know how to add OpenTelemetry recording to a plain function. ",
# "If this is a {.code render*()} function for Shiny, it may need to be updated. ",
# "Please see {.help shiny::bindOtel} for more information."
# ))
# }
Loading
Loading