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:
- Simple minimal loggers:
- Complex configurable frameworks:
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:
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: