Crate tauri_plugin_tracing

Crate tauri_plugin_tracing 

Source
Expand description

§Tauri Plugin Tracing

A Tauri plugin that integrates the tracing crate for structured logging in Tauri applications. This plugin bridges logging between the Rust backend and JavaScript frontend, providing call stack information.

§Features

  • colored: Enables colored terminal output using ANSI escape codes
  • specta: Enables TypeScript type generation via the specta crate
  • flamegraph: Enables flamegraph/flamechart profiling support (wall-clock span timing)
  • profiling: Enables CPU profiling via tauri-plugin-profiling

§Usage

By default, this plugin does not set up a global tracing subscriber, following the convention that libraries should not set globals. You compose your own subscriber using WebviewLayer to forward logs to the frontend:

let tracing_builder = Builder::new()
    .with_max_level(LevelFilter::DEBUG)
    .with_target("hyper", LevelFilter::WARN);
let filter = tracing_builder.build_filter();

tauri::Builder::default()
    .plugin(tracing_builder.build())
    .setup(move |app| {
        Registry::default()
            .with(fmt::layer())
            .with(WebviewLayer::new(app.handle().clone()))
            .with(filter)
            .init();
        Ok(())
    });
    // .run(tauri::generate_context!("examples/default-subscriber/src-tauri/tauri.conf.json"))

§Quick Start

For simple applications, use Builder::with_default_subscriber() to let the plugin handle all tracing setup:

tauri::Builder::default()
    .plugin(
        Builder::new()
            .with_max_level(LevelFilter::DEBUG)
            .with_default_subscriber()  // Let plugin set up tracing
            .build(),
    );
    // .run(tauri::generate_context!("examples/default-subscriber/src-tauri/tauri.conf.json"))

§File Logging

For simple file logging, use Builder::with_file_logging():

Builder::new()
    .with_max_level(LevelFilter::DEBUG)
    .with_file_logging()
    .with_default_subscriber()
    .build::<tauri::Wry>();

For custom subscribers, use tracing_appender directly (re-exported by this crate):

let tracing_builder = Builder::new().with_max_level(LevelFilter::DEBUG);
let filter = tracing_builder.build_filter();

tauri::Builder::default()
    .plugin(tracing_builder.build())
    .setup(move |app| {
        let log_dir = app.path().app_log_dir()?;
        let file_appender = tracing_appender::rolling::daily(&log_dir, "app");
        let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
        // Store _guard in Tauri state to keep file logging active

        Registry::default()
            .with(fmt::layer())
            .with(fmt::layer().with_ansi(false).with_writer(non_blocking))
            .with(WebviewLayer::new(app.handle().clone()))
            .with(filter)
            .init();
        Ok(())
    });
    // .run(tauri::generate_context!("examples/default-subscriber/src-tauri/tauri.conf.json"))

Log files rotate daily and are written to:

  • macOS: ~/Library/Logs/{bundle_identifier}/app.YYYY-MM-DD.log
  • Linux: ~/.local/share/{bundle_identifier}/logs/app.YYYY-MM-DD.log
  • Windows: %LOCALAPPDATA%/{bundle_identifier}/logs/app.YYYY-MM-DD.log

§Early Initialization

For maximum control, initialize tracing before creating the Tauri app. This pattern uses tracing_subscriber::registry() with init() and passes a minimal Builder to the plugin:

use tauri_plugin_tracing::{Builder, StripAnsiWriter, tracing_appender};
use tracing::Level;
use tracing_subscriber::filter::Targets;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{fmt, registry};

fn setup_logger() -> Builder {
    let log_dir = std::env::temp_dir().join("my-app");
    let _ = std::fs::create_dir_all(&log_dir);

    let file_appender = tracing_appender::rolling::daily(&log_dir, "app");
    let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
    std::mem::forget(guard); // Keep file logging active for app lifetime

    let targets = Targets::new()
        .with_default(Level::DEBUG)
        .with_target("hyper", Level::WARN)
        .with_target("reqwest", Level::WARN);

    registry()
        .with(fmt::layer().with_ansi(true))
        .with(fmt::layer().with_writer(StripAnsiWriter::new(non_blocking)).with_ansi(false))
        .with(targets)
        .init();

    // Return minimal builder - logging is already configured
    Builder::new()
}

fn main() {
    let builder = setup_logger();
    tauri::Builder::default()
        .plugin(builder.build());
        // .run(tauri::generate_context!("examples/default-subscriber/src-tauri/tauri.conf.json"))
}

This approach is useful when you need logging available before Tauri starts, or when you want full control over the subscriber configuration.

§Flamegraph Profiling

The flamegraph feature enables performance profiling with flamegraph/flamechart visualizations.

§With Default Subscriber

Builder::new()
    .with_max_level(LevelFilter::DEBUG)
    .with_flamegraph()
    .with_default_subscriber()
    .build::<tauri::Wry>();

§With Custom Subscriber

Use [create_flame_layer()] to add flamegraph profiling to a custom subscriber:

use tauri_plugin_tracing::{Builder, WebviewLayer, LevelFilter, create_flame_layer};
use tracing_subscriber::{Registry, layer::SubscriberExt, util::SubscriberInitExt, fmt};

fn main() {
    let tracing_builder = Builder::new().with_max_level(LevelFilter::DEBUG);
    let filter = tracing_builder.build_filter();

    tauri::Builder::default()
        .plugin(tracing_builder.build())
        .setup(move |app| {
            let flame_layer = create_flame_layer(app.handle())?;

            Registry::default()
                .with(flame_layer) // Must be first - typed for Registry
                .with(fmt::layer())
                .with(WebviewLayer::new(app.handle().clone()))
                .with(filter)
                .init();
            Ok(())
        })
        .run(tauri::generate_context!("examples/default-subscriber/src-tauri/tauri.conf.json"))
        .expect("error while running tauri application");
}

§Early Initialization with Flamegraph

Use [create_flame_layer_with_path()] and [FlameExt] to initialize tracing before Tauri starts while still enabling frontend flamegraph generation:

use tauri_plugin_tracing::{Builder, create_flame_layer_with_path, FlameExt};
use tracing_subscriber::{registry, layer::SubscriberExt, util::SubscriberInitExt, fmt};

fn main() {
    let log_dir = std::env::temp_dir().join("my-app");
    std::fs::create_dir_all(&log_dir).unwrap();

    // Create flame layer before Tauri starts
    let (flame_layer, flame_guard) = create_flame_layer_with_path(
        &log_dir.join("profile.folded")
    ).unwrap();

    // Initialize tracing early
    registry()
        .with(flame_layer) // Must be first - typed for Registry
        .with(fmt::layer())
        .init();

    // Now start Tauri and register the guard
    tauri::Builder::default()
        .plugin(Builder::new().build())
        .setup(move |app| {
            // Register the guard so JS can generate flamegraphs
            app.handle().register_flamegraph(flame_guard)?;
            Ok(())
        })
        .run(tauri::generate_context!("examples/default-subscriber/src-tauri/tauri.conf.json"))
        .expect("error while running tauri application");
}

Then generate visualizations from JavaScript:

import { generateFlamegraph, generateFlamechart } from '@fltsci/tauri-plugin-tracing';

// Generate a flamegraph (collapses identical stack frames)
const flamegraphPath = await generateFlamegraph();

// Generate a flamechart (preserves event ordering)
const flamechartPath = await generateFlamechart();

§CPU Profiling

The profiling feature enables sampling-based CPU profiling via tauri-plugin-profiling. Unlike flamegraph profiling (which measures wall-clock time including I/O waits), CPU profiling measures actual CPU cycles spent executing code.

Use [TracedProfilingExt] for automatic span creation and logging:

use tauri::Manager;
use tauri_plugin_tracing::{Builder, LevelFilter, TracedProfilingExt, init_profiling};

tauri::Builder::default()
    .plugin(Builder::new().with_max_level(LevelFilter::DEBUG).build())
    .plugin(init_profiling())
    .setup(|app| {
        // Start CPU profiling with automatic span + logging
        app.start_cpu_profile_traced()?;

        // ... do CPU-intensive work ...

        // Stop - automatically logs results (samples, duration, path)
        let result = app.stop_cpu_profile_traced()?;
        Ok(())
    })
    .run(tauri::generate_context!("examples/default-subscriber/src-tauri/tauri.conf.json"))
    .expect("error while running tauri application");

Or use the base ProfilingExt trait directly without tracing integration.

From JavaScript (import from the profiling package directly):

import { startCpuProfile, stopCpuProfile } from '@fltsci/tauri-plugin-profiling';

await startCpuProfile({ frequency: 100 });
// ... do work ...
const result = await stopCpuProfile();
console.log('CPU flamegraph:', result.flamegraphPath);

§JavaScript API

import { trace, debug, info, warn, error } from '@fltsci/tauri-plugin-tracing';

info('Application started');
debug('Debug information', { key: 'value' });
error('Something went wrong');

Re-exports§

pub use tracing;
pub use tracing_appender;
pub use tracing_subscriber;

Structs§

Builder
Builder for configuring and creating the tracing plugin.
CallStack
A parsed JavaScript call stack.
CallStackLine
A single line from a JavaScript call stack.
FormatOptions
Configuration options for log output formatting.
LevelFilter
Re-export of tracing_subscriber::filter::LevelFilter for configuring log levels. A filter comparable to a verbosity Level.
LogMessage
A log message consisting of one or more string parts.
MaxFileSize
Maximum file size for log rotation.
RecordPayload
Payload for a log record, used when emitting events to the webview.
StripAnsiWriter
A writer wrapper that strips ANSI escape codes from all output.
StripAnsiWriterGuard
A writer handle returned by the MakeWriter implementation.
WebviewLayer
A tracing layer that emits log events to the webview via Tauri events.

Enums§

Error
Errors that can occur when using the tracing plugin.
LogFormat
Log output format style.
LogLevel
An enum representing the available verbosity levels of the logger.
Rotation
Time-based rotation period for log files.
RotationStrategy
Retention policy for rotated log files.
Target
Specifies a log output destination.
TimezoneStrategy
Timezone strategy for log timestamps.

Functions§

log

Type Aliases§

BoxedLayer
A boxed tracing layer that can be added to the default subscriber.
FilterFn
A boxed filter function for metadata-based log filtering.
Result
A specialized Result type for tracing plugin operations.