At present, the Windows and Linux GUI client launch the Tauri application via the `App::run` method. This function never returns again. Instead, whenever we request the Tauri app to exit, Tauri will internally call `std::process::exit`, thus preventing ordinary clean-up from happening. Whilst we somehow managed to work around this particular part, having the app exit the process internally also makes error handling and reporting to the user difficult as there are now two parts in the code where we need to handle errors: - Before we start up the Tauri app - Before we end the Tauri app (i.e. signal to it that we want to exit) It would be much easier to understand, if we could call into Tauri, let it do its thing and upon a requested exit by the user, the called function (i.e. `App::run`) simply returns again. After diving into the inner workings of Tauri, we have achieved just that by adding a new function to `App`: `App::run_return` (https://github.com/tauri-apps/tauri/pull/12668). Using `App::run_return` we can now orchestrate a `gui::run` function that simply returns after Tauri has shutdown. Most importantly, it will also exit upon any fatal errors that we encounter in the controller and thus unify the error handling path into a single one. These errors are now all handled at the call-site of `gui::run`. Building on top of this, we will be able to further simplify the error handling within the GUI client. I am hoping to gradually replace our monolithic `Error` enums with individual errors that we can extract from an `anyhow::Error`. This would make it easier to reason about where certain errors get generated and thus overall improve the UX of the application by displaying better error messages, not failing the entire app in certain cases, etc.
Rust development guide
Firezone uses Rust for all data plane components. This directory contains the Linux and Windows clients, and low-level networking implementations related to STUN/TURN.
We target the last stable release of Rust using rust-toolchain.toml.
If you are using rustup, that is automatically handled for you.
Otherwise, ensure you have the latest stable version of Rust installed.
Reading Client logs
The Client logs are written as JSONL for machine-readability.
To make them more human-friendly, pipe them through jq like this:
cd path/to/logs # e.g. `$HOME/.cache/dev.firezone.client/data/logs` on Linux
cat *.log | jq -r '"\(.time) \(.severity) \(.message)"'
Resulting in, e.g.
2024-04-01T18:25:47.237661392Z INFO started log
2024-04-01T18:25:47.238193266Z INFO GIT_VERSION = 1.0.0-pre.11-35-gcc0d43531
2024-04-01T18:25:48.295243016Z INFO No token / actor_name on disk, starting in signed-out state
2024-04-01T18:25:48.295360641Z INFO null
Benchmarking on Linux
The recommended way for benchmarking any of the Rust components is Linux' perf utility.
For example, to attach to a running application, do:
- Ensure the binary you are profiling is compiled with the
releaseprofile. sudo perf record -g --freq 10000 --pid $(pgrep <your-binary>).- Run the speed test or whatever load-inducing task you want to measure.
sudo perf script > profile.perf- Open profiler.firefox.com and load
profile.perf
Instead of attaching to a process with --pid, you can also specify the path to executable directly.
That is useful if you want to capture perf data for a test or a micro-benchmark.