Debugging

With all the moving pieces in a Tauri application, chances are you will not write perfect bug-free code all the time. Your app might behave weirdly, be very slow, or outright crash.

In this guide, we give you a number of tools and techniques to troubleshoot problems when they arise.

Rust

To effectively debug a program, you need to know what's going on inside. Rust and Tauri provide many tools to make this possible.

Logging

When your first learned Rust, you might have printed logging messages by using the println! macro:

fn main() {
    println!("foobar");
}

However, for more complex projects, Rust provides an elegant logging system that allows log messages from app code and dependencies with different levels, timestamps, and metadata.

To use this system, add the log crate to your Cargo.toml file:

[package]
name = "app"
version = "0.1.0"
edition = "2021"

[dependencies]
log = "0.4"

Now you can use a number of logging macros: error!, warn!, info!, debug! and trace! where error! represents the highest-priority log.

use log::{trace, debug, info, warn, error};

fn main() {
    trace!("A trace-level message");
    debug!("A debug-level message");
    info!("An info-level message");
    warn!("A warn-level message");
    error!("An error-level message");
}

However, you will notice it doesn't actually print anything when you run this! This is because log expects you to bring your own logger. There are many available implementations to choose from, here are some of the most popular ones:

tauri-plugin-log

The Tauri team maintains a logger that is built explicitly for Tauri applications. It is built on top of fern and supports writing logs to many different targets and consuming log messages produced in the WebView.

Add it to your Cargo.toml:

[package]
name = "app"
version = "0.1.0"
edition = "2021"

[dependencies]
log = "0.4"
tauri-plugin-log = { git = "https://github.com/tauri-apps/tauri-plugin-log" }

and import it like any other Tauri plugin:

use tauri_plugin_log::{LogTarget, LoggerBuilder};

fn main() {
    tauri::Builder::default()
        .plugin(
            LoggerBuilder::new()
                .targets([
                    // write to the OS logs folder
                    LogTarget::LogDir,
                    // write to stdout
                    LogTarget::Stdout,
                    // forward logs to the webview
                    LogTarget::Webview,
                ])
                .build(),
        )
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
Listing 2-TODO: Example configuration that emits logs to the WebView, Stdout and to the OS's log folder.

Tracing

Sometimes simple logs are not enough to debug your problem, though, so you might reach for the tracing crate.

In addition to logging-style diagnostics recorded by the log crate, it provides information about temporality and causality. Spans in tracing are events that have a beginning and end time, may be entered and exited by the flow of execution, and may exist within a nested tree of similar spans.


#![allow(unused)]
fn main() {
use tracing::{info, debug, span, Level};

// records an event outside of any span context:
info!("something happened");

let span = span!(Level::INFO, "my_span");
let _guard = span.enter();

// records an event within "my_span".
debug!("something happened inside my_span");
}

GDB

The GNU Project Debugger (GDB) is a very old program written by Richard Stallman in 1986. GDB has support for several languages, such as C/C++, but also modern languages such as Go and Rust.

rust-gdb comes with the Rust installation by default and is a wrapper around GDB that enables pretty-printing rust types in the GDB output.

To debug a Rust binary, build the binary:

cargo build --debug

And then load it into GDB:

rust-gdb target/debug/<app name>

LLDB

LLDB is a debugger built on top of LLVM, the compiler backend used by Rust itself. We can use the rust-lldb tool, which comes with the Rust installation by default. It wraps LLDB to provide pretty-printing rust types.

To debug a Rust binary, build the binary:

cargo build --debug

And then load it into LLDB:

rust-lldb target/debug/<app name>

Panics

When your Rust code runs into a problem that is so severe that it can't recover from it, your program should panic. When a Panic occurs, your program will print a failure message, unwind and clean up the stack, and then quit. A Panic will manifest as a hard crash, so it's crucial to minimize the number of panics.

To determine what caused the Panic, you can re-run your application with the RUST_BACKTRACE environment variable set to 1 to print a more detailed failure message.

JavaScript

To open the WebView dev tools, right-click in the WebView and choose Inspect Element. This opens up the web-inspector similar to the one you're used to from Chrome, Firefox, or Safari.

If you run into problems with your frontend framework, you might reach for framework-specific dev tools. While many of them are distributed as Chromium Extensions, which are not compatible with Tauri, some of them - such as the Vue Devtools - provide standalone versions that work nicely with Tauri.

tauri-plugin-log-api

As an alternative to the ubiquitous console.log debugging, tauri-plugin-log offers a JavaScript API that has a very similar feature set to the Rust version.
You can install it from npm with the following command:

npm

npm install --save-dev tauri-plugin-log-api

yarn

yarn add -D tauri-plugin-log-api

pnpm

pnpm add -D tauri-plugin-log-api

Now you can emit logs using the trace(), debug(), info(), warn() and error() functions and attach the devtools console to the loggers event stream by calling attachConsole():

import {
  attachConsole,
  trace,
  debug,
  info,
  warn,
  error,
} from "tauri-plugin-log-api";

// with LogTarget::Webview enabled this function will print logs to the browser console
const detach = await attachConsole();

trace("A trace-level message");
debug("A debug-level message");
info("An info-level message");
warn("A warn-level message");
error("An error-level message");

// detach the webview console from the log stream
detach();

Please refer to the plugin's documentation for details.

Debugging Production Builds

Not all bugs are found during development; some will be reported by your end-users. Below are some tips to help you debug production builds.

Tauri plugin log

tauri-plugin-log can be used in production code too. When configured with the LogTarget::LogDir it will write logs to the canonical log-file directory of your Operating System. When your application crashes, you can recover logs from those locations, e.g., the Console application can be used to view log files on macOS:

Apple Console App
Listing 2-TODO: Apple's Console App showing showing the Tauri app's log file.