#![warn(missing_docs)]
#![warn(rust_2018_idioms)]
pub mod backtrace;
pub mod client;
pub mod env;
pub mod fingerprint;
pub mod panic_hook;
pub mod transport;
pub mod types;
#[cfg(any(feature = "actix", feature = "axum"))]
pub mod integrations;
pub use client::BugwatchClient;
pub use env::{get_env_options, EnvError};
pub use fingerprint::{fingerprint_from_exception, generate_fingerprint};
pub use panic_hook::{install_panic_hook, install_panic_hook_with_abort, PanicGuard};
pub use transport::{ConsoleTransport, HttpTransport, NoopTransport, Transport, TransportError};
pub use types::{
Breadcrumb, BugwatchOptions, ErrorEvent, ExceptionInfo, Level, RequestContext, RuntimeInfo,
SdkInfo, StackFrame, UserContext,
};
use lazy_static::lazy_static;
use parking_lot::RwLock;
use std::sync::Arc;
lazy_static! {
static ref GLOBAL_CLIENT: RwLock<Option<Arc<BugwatchClient>>> = RwLock::new(None);
}
pub fn init(options: BugwatchOptions) -> Arc<BugwatchClient> {
let mut guard = GLOBAL_CLIENT.write();
if let Some(ref existing) = *guard {
return existing.clone();
}
let client = Arc::new(BugwatchClient::new(options));
*guard = Some(client.clone());
client
}
pub fn init_from_env() -> Result<Arc<BugwatchClient>, EnvError> {
let options = get_env_options()?;
Ok(init(options))
}
pub fn get_client() -> Option<Arc<BugwatchClient>> {
GLOBAL_CLIENT.read().clone()
}
pub fn capture_error<E: std::error::Error>(error: &E) -> String {
if let Some(client) = get_client() {
client.capture_error(error)
} else {
tracing::warn!("[Bugwatch] SDK not initialized. Call init() first.");
String::new()
}
}
pub fn capture_message(message: &str, level: Level) -> String {
if let Some(client) = get_client() {
client.capture_message(message, level)
} else {
tracing::warn!("[Bugwatch] SDK not initialized. Call init() first.");
String::new()
}
}
pub fn add_breadcrumb(breadcrumb: Breadcrumb) {
if let Some(client) = get_client() {
client.add_breadcrumb(breadcrumb);
}
}
pub fn set_user(user: Option<UserContext>) {
if let Some(client) = get_client() {
client.set_user(user);
}
}
pub fn set_tag(key: impl Into<String>, value: impl Into<String>) {
if let Some(client) = get_client() {
client.set_tag(key, value);
}
}
pub fn set_extra(key: impl Into<String>, value: impl Into<serde_json::Value>) {
if let Some(client) = get_client() {
client.set_extra(key, value);
}
}
pub fn flush() -> Result<(), TransportError> {
if let Some(client) = get_client() {
client.flush()
} else {
tracing::warn!("[Bugwatch] SDK not initialized. Call init() first.");
Ok(())
}
}
pub fn close() -> Result<(), TransportError> {
if let Some(client) = get_client() {
let result = client.close();
*GLOBAL_CLIENT.write() = None;
result
} else {
tracing::warn!("[Bugwatch] SDK not initialized. Call init() first.");
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_global_client_lifecycle() {
*GLOBAL_CLIENT.write() = None;
let result = capture_message("test", Level::Info);
assert!(result.is_empty());
init(BugwatchOptions::new("test-key"));
assert!(get_client().is_some());
*GLOBAL_CLIENT.write() = None;
}
}