knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.path = "man/figures/README-", out.width = "100%" ) if(file.exists("shiny-events.csv")) unlink("shiny-events.csv") if(file.exists("shiny-events.log")) unlink("shiny-events.log") if(file.exists("example.db")) unlink("example.db") if(file.exists("shinyevents-pipe.txt")) unlink("shinyevents-pipe.txt")
shinyevents
provides a flexible and easy to use framework to record activity occurring withing a Shiny app. The logged information can be accumulated and later be used to help improve the app's performance and user's experience.
The development version of shinyevents
is available on GitHub:
# install.packages("remotes") remotes::install_github("edgararuiz/shinyevents")
shinyevents
attempts to provide a simple way to track different Shiny events by using two functions. The first function is one of four which initialize the tracking:
shiny_events_to_log()
- Saves the event data into a .log
fileshiny_events_to_csv()
- Saves the event data into a .csv
fileshiny_events_to_dbi()
- Saves the event data into a database, via the DBI
packageshiny_events_to_logger()
- Sends the event as an entry to the logger
packageOnce the target for the event tracking is chosen, shinyevents
is initialized by assigning the function's result to a variable:
library(shinyevents) tracker <- shiny_events_to_log()
Once initialized, the second function to use, is one created inside the assigned variable. The function is called: event()
. Since selected variable's name was tracker
, to access it we use tracker$event()
. To it we can pass two values, as arguments, those values are free-form text, so the developer of the app can decide what to record:
tracker$event("example", "readme")
By default, the log file name is shiny-events.log
. At this point, it can be accessed in one of many ways. In this case we'll just use readLines()
.
readLines("shiny-events.log")
A Globally Unique Identifier, or GUID, is created by shiny_events_to_log()
. Every event entry for that session will contain the same GUID. This allows us to know what activity was part of which app's user session.
tracker$event("start_app") tracker$event("slider", "3") tracker$event("stop_app")
readLines("shiny-events.log")
Here is the code for a sample app that tracks several different events:
Feel free to copy and run this code in your R session. There are several comments inside the code to further clarify the purpose of the tracking related activity.
library(shiny) library(shinyevents) ui <- fluidPage( titlePanel("Old Faithful Geyser Data"), sidebarLayout( sidebarPanel(sliderInput("bins", "Bins:", 1, 50, 30)), mainPanel(plotOutput("distPlot")) )) server <- function(input, output, session) { tracker <- shiny_events_to_log() # <- Initializes as log file tracker$event("app_initiated") # <- Tracks start of app session observeEvent( # <- Track input using shiny::observeEvent() input$bins, tracker$event("bin_slider", input$bins), # <- Pass the input's value to the event ignoreInit = TRUE) # <- ignoreInit avoids logging the input's initial value session$onSessionEnded( # <- Track when the app closes using session$onSessionEnded function() tracker$event("close_app")) # <- Combine with a simple event entry output$distPlot <- renderPlot({ tracker$event("plot_started", input$bins) # <- Tracks code start x <- faithful[, 2] bins <- seq(min(x), max(x), length.out = input$bins + 1) hist(x, breaks = bins, col = 'darkgray', border = 'white') tracker$event("plot_rendered", input$bins) # <- Tracks code completion }) } shinyApp(ui, server)
After playing a bit with the slider, the app can be closed. This activity was recorded in a new file, called shiny-events.log
. If you tried the app code above, use the readLines()
code below to see the resulting entries.
readLines("shiny-events.log")
## [1] "2019-09-03 16:22:17 CDT INFO shinyevents ff55ea39-7d8a-4e5f-9f33-18ee804b2218 app_initiated " ## [2] "2019-09-03 16:22:17 CDT INFO shinyevents ff55ea39-7d8a-4e5f-9f33-18ee804b2218 plot_started 30" ## [3] "2019-09-03 16:22:17 CDT INFO shinyevents ff55ea39-7d8a-4e5f-9f33-18ee804b2218 plot_rendered 30" ## [4] "2019-09-03 16:22:20 CDT INFO shinyevents ff55ea39-7d8a-4e5f-9f33-18ee804b2218 bin_slider 19" ## [5] "2019-09-03 16:22:20 CDT INFO shinyevents ff55ea39-7d8a-4e5f-9f33-18ee804b2218 plot_started 19" ## [6] "2019-09-03 16:22:20 CDT INFO shinyevents ff55ea39-7d8a-4e5f-9f33-18ee804b2218 plot_rendered 19" ## [7] "2019-09-03 16:22:22 CDT INFO shinyevents ff55ea39-7d8a-4e5f-9f33-18ee804b2218 plot_started 41" ## [8] "2019-09-03 16:22:22 CDT INFO shinyevents ff55ea39-7d8a-4e5f-9f33-18ee804b2218 plot_rendered 41" ## [9] "2019-09-03 16:22:22 CDT INFO shinyevents ff55ea39-7d8a-4e5f-9f33-18ee804b2218 bin_slider 41" ## [10] "2019-09-03 16:22:24 CDT INFO shinyevents ff55ea39-7d8a-4e5f-9f33-18ee804b2218 close_app "
Some additional information is exposed by the variable assigned to the Shiny event function. These are made available to allow developers to use them in other functions, or to create a custom target for the event tracking. Two of these are:
tracker$app tracker$guid
The entry()
function returns a list
object. The list contains the session information, and the date/time of the entry. This is the base function that the shiny_events_to_log()
, shiny_events_to_csv()
and shiny_events_to_dbi()
use.
tracker$entry()
It is possible to customize the output or add a new target file (beyond CSV, log or Database). To do that, override the event()
function after assigning it to a variable. For example, if a pipe delimited file is required for tracking the Shiny events, we could use the following:
tracker <- shiny_events() tracker$event <- function(activity = "", value = "") { entry <- tracker$entry(activity = activity, value = value) cat( paste(entry$guid, entry$datetime, entry$app, entry$activity, entry$value, "\n", sep = "|"), file = "shinyevents-pipe.txt", append = TRUE ) } tracker$event("example", "readme") readLines("shinyevents-pipe.txt")
Initialize a new CSV log with shiny_events_to_csv()
.
tracker <- shiny_events_to_csv() tracker$event("start_app") tracker$event("slider", "3") tracker$event("stop_app")
To avoid file locks, shiny_events_to_csv()
uses the cat()
function inside its code. It also means that the table will not have headers, so they have to be defined at read time:
read.csv( "shiny-events.csv", stringsAsFactors = FALSE, header = FALSE, col.names = c("guid", "app", "activity", "value", "datetime") )
shiny_events_to_dbi
uses the DBI
package to record events. Here are a few highlights of how it works:
dbWriteTable()
function, this allows it to work on most databases DBI
is able to interact withappend = TRUE
argument is used. This allows the table to be created if it doesn't exists yet, and only to add new records to the table, instead of overriding its content.guid
, app
, datetime
, activity
, value
.library(DBI) library(RSQLite) con <- dbConnect(SQLite(), "example.db")
tracker <- shiny_events_to_dbi(table = "shinyevents", connection = con) tracker$event("start_app") tracker$event("slider", "3") tracker$event("stop_app")
dbGetQuery(con, "SELECT * FROM shinyevents")
dbDisconnect(con)
logger
To combine with the logger
package, simply use shiny_events_to_logger()
. The log_appender()
function should be initialized before running the first event()
function.
library(logger) t <- tempfile() log_appender(appender_file(t)) tracker <- shiny_events_to_logger() tracker$event("start_app") tracker$event("slider", "3") tracker$event("data_frame", "0", "warn") tracker$event("stop_app") readLines(t)
The package includes several app examples. The example pictured below, uses shiny_events_to_dbi()
to record the events in a database, in this case SQLite. It records when there are changes in each of the inputs, as well as when the plot;s code starts and ends. There are entries for when the app starts and is closed.
Run the following code in your R session to access the app:
shiny::runApp( system.file( "samples", "shinydashboard-db", package = "shinyevents" ), display.mode = "normal" )
Simple example - An example using the "Old Faithful Geyser" app, shows the easiest way to include shinyevents
in an app.
r
shiny::runApp(
system.file(
"samples", "simple",
package = "shinyevents"
),
display.mode = "normal"
)
shinydashboard - An example that looks the same as the shinydashboard
example above, but it uses a CSV file to record the events instead of a database.
r
shiny::runApp(
system.file(
"samples", "shinydashboard",
package = "shinyevents"
),
display.mode = "normal"
)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.