Arnis Telemetry Service: How It Sends Generation Logs and Events

The telemetry service in arnis is an opt-in, privacy-focused subsystem that asynchronously reports generation clicks, structured log entries, and crash diagnostics to a remote endpoint while strictly enforcing user consent and isolating all network activity in background threads to prevent UI blocking.

The arnis repository by louis-e implements a lightweight telemetry module designed to gather diagnostic insights without compromising user privacy or application stability. This service handles three distinct event types—generation clicks, log entries, and crash reports—transmitting them to https://arnismc.com/telemetry/report_telemetry.php only when explicit user consent is granted via an atomic boolean flag.

Architecture and Event Types

The telemetry implementation resides in src/telemetry.rs and operates as a non-intrusive bridge between the application and analytics infrastructure. All transmissions share common architectural principles: strict consent validation, platform detection, compile-time version injection, and thread-isolated execution.

Telemetry Event Categories

The service categorizes data into three JSON payloads:

  • Generation clicks: Simple markers indicating user interaction with the world generation feature, sent via send_generation_click (lines 90‑123)
  • Log entries: Structured diagnostic messages with severity levels, truncated to 1000 characters, transmitted through send_log (lines 45‑92)
  • Crash reports: Panic information captured via custom hooks, truncated to 500 characters, handled by send_crash_report (lines 66‑88)

Core Implementation Details

At the heart of the privacy controls sits a static atomic boolean initialized to false:

static TELEMETRY_CONSENT: AtomicBool = AtomicBool::new(false);

The public API set_telemetry_consent (lines 14‑16) allows the GUI to toggle this flag based on user preferences, while get_telemetry_consent (lines 18‑21) provides internal access for all telemetry paths. No data transmits unless this function returns true.

Platform Detection and Versioning

Two helper functions enrich every payload with environmental context. The get_platform function (lines 23‑31) maps std::env::consts::OS to standardized strings (windows, linux, macos), while get_app_version (lines 33‑36) injects the crate version at compile time via env!("CARGO_PKG_VERSION").

JSON Payload Definitions

Serde-annotated structs in src/telemetry.rs define the wire format. The LogEntry struct (lines 54‑63) includes optional platform and version fields:

#[derive(Serialize)]
struct LogEntry<'a> {
    r#type: &'a str,
    log_level: &'a str,
    log_message: &'a str,
    #[serde(skip_serializing_if = "Option::is_none")]
    platform: Option<&'a str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    app_version: Option<&'a str>,
}

The CrashReport struct (lines 38‑45) and GenerationClick struct (lines 47‑52) provide specialized schemas for their respective event types.

Transmitting Telemetry Events

Tracking Generation Clicks

The send_generation_click function executes when users initiate world generation in the GUI. It validates consent and release build status, then spawns a background thread wrapped in catch_unwind:

pub fn send_generation_click() {
    if !get_telemetry_consent() { return; }
    if cfg!(debug_assertions) { return; }   // Disable in debug builds
    
    std::thread::spawn(|| {
        let _ = std::panic::catch_unwind(AssertUnwindSafe(|| {
            let client = Client::new();
            let payload = GenerationClick { r#type: "generation_click" };
            let _ = client.post(TELEMETRY_URL)
                         .header("Content-Type", "application/json")
                         .json(&payload)
                         .send();
        }));
    });
}

Sending Log Entries

The send_log function constructs a LogEntry containing the severity level, truncated message (limited to 1000 characters), platform, and version. It dispatches via the same background thread pattern, using std::thread::spawn and AssertUnwindSafe to guarantee that network failures cannot panic the main application.

Crash Reporting via Panic Hooks

The install_panic_hook function (lines 94‑149) registers a custom handler that captures thread panics. After logging locally via the error! macro, it extracts error details and invokes send_crash_report only when telemetry consent is granted and running in release mode. The implementation uses catch_unwind to ensure that the crash reporting itself cannot trigger secondary panics.

GUI Integration Points

The telemetry system connects to the user interface in src/gui.rs at three critical junctions:

  • Startup: install_panic_hook() initializes at line 76 to capture unexpected crashes throughout the application lifecycle
  • Settings: Line 716 calls set_telemetry_consent to persist user preferences from the settings checkbox
  • Generation trigger: Line 719 invokes send_generation_click() immediately after the user requests world generation

Additional modules like src/world_editor/ and src/progress.rs emit log telemetry throughout the processing pipeline via send_log calls.

Summary

  • The telemetry service requires explicit opt-in via an AtomicBool consent flag that defaults to disabled in src/telemetry.rs (lines 10‑12)
  • Three event types—generation clicks, logs, and crashes—transmit to https://arnismc.com/telemetry/report_telemetry.php only in release builds
  • All network operations execute in isolated background threads with std::panic::catch_unwind protection to prevent UI freezing
  • Payloads include platform information from std::env::consts::OS and compile-time version strings for environmental context
  • Debug builds automatically disable transmission via cfg!(debug_assertions) checks to prevent analytics pollution during development

Frequently Asked Questions

How does arnis ensure telemetry respects user privacy?

The implementation uses a global AtomicBool initialized to false (lines 10‑12) with no public setter except set_telemetry_consent (lines 14‑16), which the GUI calls only when users explicitly enable telemetry in settings. Every transmission function checks get_telemetry_consent() (lines 18‑21) before executing any network code, ensuring silent operation when consent is withheld.

Why do debug builds not send telemetry?

The code explicitly checks cfg!(debug_assertions) at the entry points of send_generation_click, send_log, and the panic hook. This compile-time configuration prevents debug builds from transmitting data, ensuring that development testing does not pollute production analytics or expose unstable features.

What prevents telemetry from crashing the application if the network fails?

All transmission logic runs inside std::thread::spawn with std::panic::catch_unwind wrappers using AssertUnwindSafe. This design isolates network failures and potential panics within background threads, guaranteeing that telemetry code cannot block the main UI thread or propagate errors back to the user workflow.

How are long error messages handled in crash reports?

The panic hook implementation (lines 94‑149) truncates error messages to 500 characters before JSON serialization, while standard log entries truncate at 1000 characters in send_log. This prevents oversized payloads while preserving essential diagnostic information for debugging.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →