Logging

December 18, 2025 · View on GitHub

Simple logging setup for both Rust backend and TypeScript frontend.

Quick Start

Rust (Backend)

log::info!("Application starting up");
log::debug!("Debug info: {}", some_value);
log::warn!("Something unexpected happened");
log::error!("Error occurred: {}", error);

TypeScript (Frontend)

import { logger } from '@/lib/logger'

logger.info('User action completed')
logger.debug('Debug data', { userId: 123, action: 'click' })
logger.warn('Performance warning')
logger.error('Request failed', { error: response.error })

Configuration

Rust Backend

  • Uses tauri-plugin-log with standard Rust log crate
  • Development: Debug level, logs to stdout + webview console
  • Production: Info level, logs to stdout + app log directory
  • Configuration in src-tauri/src/lib.rs

TypeScript Frontend

  • Development: All logs go to browser console
  • Production: Console logging only (keeps it simple)
  • Logger utility at src/lib/logger.ts

Log Levels

LevelWhen to UseDevProd
traceMost verbose debugging
debugDevelopment debugging
infoGeneral information
warnWarning conditions
errorError conditions

Where Logs Appear

Development

  • Rust: Terminal (stdout) + Browser DevTools console (webview)
  • TypeScript: Browser DevTools console

Production

  • Rust: Terminal (stdout) + log file in app log directory
  • TypeScript: Browser DevTools console

Log directory locations vary by platform (e.g., ~/Library/Logs/ on macOS).

Examples

Rust Tauri Commands

#[tauri::command]
async fn save_data(data: MyData) -> Result<(), String> {
    log::info!("Saving data for user: {}", data.user_id);

    match save_to_disk(&data).await {
        Ok(_) => {
            log::info!("Data saved successfully");
            Ok(())
        }
        Err(e) => {
            log::error!("Failed to save data: {}", e);
            Err(format!("Save failed: {}", e))
        }
    }
}

TypeScript React Components

import { logger } from '@/lib/logger'

function MyComponent() {
  const handleClick = () => {
    logger.debug('Button clicked', { component: 'MyComponent' })

    try {
      performAction()
      logger.info('Action completed successfully')
    } catch (error) {
      logger.error('Action failed', { error })
    }
  }

  return <button onClick={handleClick}>Click me</button>
}

Best Practices

  1. Use appropriate log levels - Don't log everything as info
  2. Include context - Add relevant data to help debugging
  3. Log errors with details - Include error messages and context
  4. Keep messages concise - But descriptive enough to be useful
  5. Use structured logging - Include objects/context for complex data

See error-handling.md for patterns on when to log vs show errors to users.

Production Considerations

  • Rust logs go to the app's log directory (platform-specific location)
  • No sensitive data should be logged (passwords, tokens, etc.)
  • The plugin supports log rotation when files reach size limits
  • Frontend logs stay in browser - not sent to backend by default