errsight 0.1.1

Rust client for ErrSight error tracking — captures panics, errors, and log/tracing events and ships them to the ErrSight API from a background thread.
Documentation
//! `anyhow` integration (feature `anyhow`).
//!
//! [`capture_anyhow`] records an `anyhow::Error` with its full cause chain and,
//! crucially, the backtrace captured *where the error originated* rather than
//! at the capture call site — provided the error carries one (run with
//! `RUST_BACKTRACE=1`, and `anyhow` ≥ 1.0.66 on a backtrace-capable toolchain).

use crate::event::{Cause, Event};
use crate::level::Level;

/// Capture an `anyhow::Error` at `error` level.
///
/// Records:
/// - `message`           — the error's `Display`
/// - `exception_causes`  — every link in `err.chain()` after the first
/// - `backtrace`         — the error's own captured backtrace, parsed into
///   structured `exception_frames` when available
pub fn capture_anyhow(err: &anyhow::Error) -> Option<String> {
    capture_anyhow_at_level(err, Level::Error)
}

/// Capture an `anyhow::Error` at an explicit level.
pub fn capture_anyhow_at_level(err: &anyhow::Error, level: Level) -> Option<String> {
    crate::with_client(|client| {
        let cfg = client.config();
        if !cfg.enabled() || level < cfg.min_level {
            return None;
        }

        let mut event = Event::new(level, err.to_string());

        // The first chain link is the top-level error itself (already the
        // message); subsequent links are the causes.
        let causes: Vec<Cause> = err
            .chain()
            .skip(1)
            .take(5)
            .map(|c| Cause {
                class: None,
                message: c.to_string(),
            })
            .collect();
        if !causes.is_empty() {
            if let Ok(v) = serde_json::to_value(&causes) {
                event.set_metadata("exception_causes", v);
            }
        }

        // anyhow's backtrace is only meaningful when capture was enabled.
        // Its Display is empty/"disabled" otherwise; parse_std_backtrace then
        // simply yields no frames.
        let rendered = format!("{}", err.backtrace());
        if !rendered.is_empty() && !rendered.starts_with("disabled") {
            let frames = crate::backtrace::parse_std_backtrace(&rendered, cfg);
            if !frames.is_empty() {
                event.backtrace = Some(crate::backtrace::frames_to_string(&frames));
                if let Ok(v) = serde_json::to_value(&frames) {
                    event.set_metadata("exception_frames", v);
                }
            } else {
                // Couldn't structure it — keep the raw string so the trace
                // isn't lost entirely.
                event.backtrace = Some(rendered);
            }
        }

        crate::finalize_and_capture(client, event)
    })
    .flatten()
}