11  Print debugging

Published

2025-01-19

Warning

The contents for this section are under development. Thank you for your patience.

I’ve created the shinypak R package in an effort to make each section accessible and easy to follow. Install shinypak using pak (or remotes):

install.packages('pak')
pak::pak('mjfrigaard/shinypak')
library(shinypak)

List the apps in this chapter:

list_apps(regex = '11')

Launch apps with launch()

launch(app = '11_debug-print')

Download apps with get_app()

get_app(app = '11_debug-print')

In the previous chapter we covered the interactive debugger in Positron with browser() and observe(). In this chapter, we’ll explore another commonly used technique for identifying and resolving issues or errors in your code: print debugging.

11.1 Exposing variables and values

Launch app with the shinypak package:

launch('11_debug-print')

Print debugging uses a simpler approach than the interactive debugger, and it’s well-suited for a variety of development workflows. Print debugging involves inserting a print() (or cat()) statement into the body of a function to output variables, values, or other important information at various points.

%%{init: {'theme': 'neutral', 'themeVariables': { 'fontFamily': 'monospace', "fontSize":"13px"}}}%%
flowchart LR
    Insert(["Insert call<br>to <code>print()</code><br>"])
    Print("Encounter<br><code>print()</code> or<br><code>cat()</code>")
    Insert --> Exec
    Exec("Execute<br>function") --> Print("Encounter<br><code>print()</code>")  
    Print --> Issue{{"<strong><em>Output<br>reveals<br>issue?</em></strong>"}}
    Issue -- "Yes" --> Mod(["Modify<br>function"])
    Issue -- "No" --> Insert

Print debugging with print() function calls


11.2 Reactive printing

To track reactivity, we need to place the print functions in a reactive expression, observer, or event handler. This allows us to see when these values are triggered (and any other values they work with).

%%{init: {'theme': 'neutral', 'themeVariables': { 'fontFamily': 'monospace', "fontSize":"13px"}}}%%
flowchart LR
        Insert(["Insert call<br>to <code>print()</code>"]) 
        Exec("Execute<br>function") 
        Insert --> Exec --> Print("Encounter<br><code>print()</code>") 
        subgraph Reactive["Reactive Context"]
            Print 
            Issue{{"<strong><em>Output<br>reveals<br>issue?</em></strong>"}}
            Print --> Issue
        end
        Issue -- "Yes" --> Mod(["Modify<br>function"])
        Issue -- "No" --> Insert

Reactive print debugging with print() function calls

Using well-placed calls to print() or cat() allows us to display input values, which helps us to understand the inner-workings of our application.

For example, placing the code below in the server function of our variable input module will print the selected variables to the console:

mod_var_input_server <- function(id) {

  moduleServer(id, function(input, output, session) {

    observe({
      cat("Selected x, y, and color:\n")
      print(c('x' = input$x,
              'y' = input$y, 
              'color' = input$z))
    }) |>
      bindEvent(c(input$x, input$y, input$x))
    
    return(
      reactive({
        list(
          "y" = input$y,
          "x" = input$x,
          "z" = input$z,
          "alpha" = input$alpha,
          "size" = input$size,
          "plot_title" = input$plot_title
        )
      })
    )

  })
}
1
Wrap in observe() to provide reactive context for printing
2
Print message with cat()
3
Print reactives with print()
4
Bind this event to print whenever the variable inputs change

After running the function, we can view the output in the Console (or log file).

Variables printed to Console (click to enlarge)

Variables printed to Console (click to enlarge)

Unfortunately, print() and cat() can only provide a snapshot of values or variables at the location and time of the print call. This limits their usefulness in exploring the other parts of the environment or interacting with the running application.

However, we can place verbatimTextOutput() in our module UI function:

show/hide updated mod_var_input_ui()
mod_var_input_ui <- function(id) {
  ns <- NS(id)
  tagList(
    selectInput(
      inputId = ns("y"),
      label = "Y-axis:",
      choices = c(
        "IMDB rating" = "imdb_rating",
        "IMDB number of votes" = "imdb_num_votes",
        "Critics Score" = "critics_score",
        "Audience Score" = "audience_score",
        "Runtime" = "runtime"
      ),
      selected = "audience_score"
    ),
    selectInput(
      inputId = ns("x"),
      label = "X-axis:",
      choices = c(
        "IMDB rating" = "imdb_rating",
        "IMDB number of votes" = "imdb_num_votes",
        "Critics Score" = "critics_score",
        "Audience Score" = "audience_score",
        "Runtime" = "runtime"
      ),
      selected = "imdb_rating"
    ),
    selectInput(
      inputId = ns("z"),
      label = "Color by:",
      choices = c(
        "Title Type" = "title_type",
        "Genre" = "genre",
        "MPAA Rating" = "mpaa_rating",
        "Critics Rating" = "critics_rating",
        "Audience Rating" = "audience_rating"
      ),
      selected = "mpaa_rating"
    ),
    verbatimTextOutput(ns("vars")),
    sliderInput(
      inputId = ns("alpha"),
      label = "Alpha:",
      min = 0, max = 1, step = 0.1,
      value = 0.5
    ),
    sliderInput(
      inputId = ns("size"),
      label = "Size:",
      min = 0, max = 5,
      value = 2
    ),
    textInput(
      inputId = ns("plot_title"),
      label = "Plot title",
      placeholder = "Enter plot title"
    )
  )
}
1
Print the values to the UI

And an accompanying renderPrint() in the module server function to print the values in the UI:

show/hide updated mod_var_input_server()
mod_var_input_server <- function(id) {

  moduleServer(id, function(input, output, session) {

    observe({
      cat("Selected x, y, and color:\n")
      print(c('x' = input$x,
              'y' = input$y, 
              'color' = input$z))
      
      output$vars <- renderPrint({
        list('x' = input$x, 
             'y' = input$y, 
             'color' = input$z) 
      })
    }) |>
      bindEvent(c(input$x, input$y, input$x))
    
    return(
      reactive({
        list(
          "y" = input$y,
          "x" = input$x,
          "z" = input$z,
          "alpha" = input$alpha,
          "size" = input$size,
          "plot_title" = input$plot_title
        )
      })
    )

  })
}
1
Reactive context
2
Print message with cat()
3
Print to inputs with print()
4
Print inputs to UI
5
Bind this event to print whenever the variable inputs change

Variables printed to UI (click to enlarge)

Variables printed to UI (click to enlarge)

This allows us to view the inputs in the UI as they change in the server.

11.3 Reactive values

A powerful tool for capturing and printing input values in the server is reactiveValuesToList():

reactiveValuesToList(x = , all.names = )
1
I recommend setting all.names to TRUE.

This function can be used inside server-side code (modules or server functions) to collect all the input values. When placed in the app server function, the function will return the inputs from both modules.

Below is an example of using reactiveValuesToList() inside the our app server function:

movies_server <- function(input, output, session) {

      output$vals <- renderPrint({
        app_vals <- reactiveValuesToList(x = input, all.names = TRUE)
        str(app_vals)
      })
      
      selected_vars <- mod_var_input_server("vars")

      mod_scatter_display_server("plot", var_inputs = selected_vars)
      
}

We’ll also add the following to the bslib = TRUE section of movies_ui() to print the output of the reactive list to the UI:

movies_ui <- function(bslib = FALSE) {
  addResourcePath(
    prefix = 'www',
    directoryPath = system.file('www/', package = 'sap'))
  if (isFALSE(bslib)) {
    tagList(
        bslib::page_fillable(
          h1("Movie Reviews"),
          bslib::layout_sidebar(
            sidebar =
              bslib::sidebar(
                title = tags$h4("Sidebar inputs"),
                img(
                  src = "www/shiny.png",
                  height = 60,
                  width = 55,
                  style = "margin:10px 10px"
                ),
                mod_var_input_ui("vars")
              ),
            bslib::card(
              full_screen = TRUE,
              bslib::card_header(
                tags$h4("Scatter Plot")
              ),
              bslib::card_body(fillable = TRUE,
                mod_scatter_display_ui("plot")
              ),
              bslib::card_footer(
                tags$blockquote(
                  tags$em(
                    tags$p(
                      "The data for this application comes from the ",
                      tags$a("Building web applications with Shiny",
                        href = "https://rstudio-education.github.io/shiny-course/"
                      ),
                      "tutorial"
                    )
                  )
                )
              )
            )
          )
        )
      )
  } else {
    tagList(
      bslib::page_fillable(
        title = "Movie Reviews (bslib)",
        theme = bslib::bs_theme(
          bg = "#101010",
          fg = "#F6F5F5",
          primary = "#EE6F57",
          secondary = "#32E0C4",
          success = "#FF4B5C",
          base_font = sass::font_google("Ubuntu"),
          heading_font = sass::font_google("Ubuntu")
        ),
        bslib::layout_sidebar(
          sidebar = bslib::sidebar(
            mod_var_input_ui("vars")
          ),
          bslib::card(
            full_screen = TRUE,
                bslib::card_header(
                  tags$img(
                  src = "www/bootstrap.png",
                  height = 80,
                  width = 100,
                  style = "margin:10px 10px"
                )
              ),
             bslib::card_body(
              verbatimTextOutput(outputId = "vals"),
              mod_scatter_display_ui("plot")
            )
          )
        )
      )
    )
  }
} 
1
Printed reactive value outputs

After loading the changes to our package, we can view the application with the captured and printed reactive values:

Click to enlarge

Click to enlarge

Note the difference between the two printed values: the output in the sidebar has been printed from within the module server function, so the input names are contained within that namespace (i.e., x, y, etc.). However, the list returned from reactiveValuesToList() has been placed in the app server function, so we can see the module id appended to the input name (i.e., vars-y, vars-x, etc.).

Recap

Recap: Print Debugging

The advantages of print debugging are 1) it’s easy to insert a few lines of code and run the program, 2) it works in any programming environment, even if sophisticated debugging tools aren’t available, and 3) it doesn’t require special configurations or debugging tools.

However, the downside is we can’t pause execution or inspect the app state in real-time. We only can see what we explicitly print, and if we miss a key variable or condition, we have to to insert, remove, or adjust print functions, which can be time-consuming. Excessive print functions can also clutter the code and output especially in large or complex application.