#![forbid(unsafe_code)]
#![warn(missing_docs)]
pub mod backtrace;
mod client;
pub mod db;
pub mod diagnostics;
pub mod envelope;
mod event;
mod hub;
mod integration;
pub mod options;
mod performance;
pub mod propagation;
pub mod protocol;
mod scope;
pub mod scrub;
mod session;
pub mod transport;
pub mod util;
#[cfg(feature = "panic")]
pub mod panic;
pub mod integrations {
#[cfg(feature = "actix")]
pub mod actix;
#[cfg(feature = "axum")]
pub mod axum;
#[cfg(feature = "reqwest-middleware")]
pub mod reqwest;
#[cfg(feature = "sqlx")]
pub mod sqlx;
#[cfg(feature = "tracing")]
pub mod tracing;
}
use std::sync::Arc;
use std::time::Duration;
pub use client::Client;
pub use db::{capture_query as capture_db_query, normalize_query, query_hash, query_type};
pub use diagnostics::Diagnostics;
pub use hub::{last_event_id, Hub, ScopeGuard};
pub use integration::Integration;
pub use options::{ClientOptions, IntoClientOptions, SessionMode, DEFAULT_HOST};
pub use performance::{start_span, start_transaction, Span};
pub use protocol::{
Breadcrumb, ErrorEvent, Frame, Heartbeat, Level, RequestContext, SessionStatus, User,
};
pub use scope::Scope;
pub use session::Session;
#[cfg(feature = "reqwest-middleware")]
pub use integrations::reqwest::{
instrumented_client as instrumented_http_client,
instrumented_client_from as instrumented_http_client_from, AllstakHttpMiddleware,
};
#[cfg(feature = "anyhow")]
pub use crate::anyhow_support::capture_anyhow;
use uuid::Uuid;
pub struct ClientInitGuard {
client: Arc<Client>,
shutdown_timeout: Duration,
end_session_on_drop: bool,
}
impl ClientInitGuard {
pub fn client(&self) -> &Arc<Client> {
&self.client
}
pub fn is_enabled(&self) -> bool {
self.client.is_enabled()
}
pub fn flush(&self) -> bool {
self.client.flush(self.shutdown_timeout)
}
}
impl Drop for ClientInitGuard {
fn drop(&mut self) {
if self.end_session_on_drop {
Hub::main().end_session();
}
self.client.close(self.shutdown_timeout);
}
}
pub fn init(opts: impl IntoClientOptions) -> ClientInitGuard {
let mut options = opts.into_client_options();
if options.api_key.is_empty() {
if let Ok(key) = std::env::var("ALLSTAK_API_KEY") {
options.api_key = key;
} else if let Ok(dsn) = std::env::var("ALLSTAK_DSN") {
options.api_key = dsn;
}
}
if options.default_integrations {
#[cfg(feature = "panic")]
{
let already = options.integrations.iter().any(|i| i.name() == "panic");
if !already {
options
.integrations
.push(Arc::new(panic::PanicIntegration::new()));
}
}
}
let shutdown_timeout = options.shutdown_timeout;
let auto_session =
options.auto_session_tracking && matches!(options.session_mode, SessionMode::Application);
let client = Client::new(options);
hub::bind_main_client(client.clone());
if auto_session {
Hub::main().start_session();
}
ClientInitGuard {
client,
shutdown_timeout,
end_session_on_drop: auto_session,
}
}
pub fn init_from_env() -> ClientInitGuard {
let mut options = ClientOptions::default();
if let Ok(key) = std::env::var("ALLSTAK_API_KEY") {
options.api_key = key;
} else if let Ok(dsn) = std::env::var("ALLSTAK_DSN") {
options.api_key = dsn;
}
if let Ok(release) = std::env::var("ALLSTAK_RELEASE") {
options.release = Some(release);
}
if let Ok(env) = std::env::var("ALLSTAK_ENVIRONMENT") {
options.environment = Some(env);
}
if let Ok(name) = std::env::var("ALLSTAK_SERVER_NAME") {
options.server_name = Some(name);
}
if let Ok(debug) = std::env::var("ALLSTAK_DEBUG") {
options.debug = matches!(debug.to_ascii_lowercase().as_str(), "1" | "true" | "yes");
}
if let Ok(rate) = std::env::var("ALLSTAK_SAMPLE_RATE") {
if let Ok(r) = rate.parse::<f32>() {
options.sample_rate = r;
}
}
if let Ok(pii) = std::env::var("ALLSTAK_SEND_DEFAULT_PII") {
options.send_default_pii =
matches!(pii.to_ascii_lowercase().as_str(), "1" | "true" | "yes");
}
let guard = init(options);
#[cfg(feature = "tracing")]
install_default_tracing_subscriber();
guard
}
#[cfg(feature = "tracing")]
fn install_default_tracing_subscriber() {
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
let registry = tracing_subscriber::registry().with(integrations::tracing::layer());
#[cfg(feature = "sqlx")]
let result = registry.with(integrations::sqlx::layer()).try_init();
#[cfg(not(feature = "sqlx"))]
let result = registry.try_init();
let _ = result;
}
pub fn capture_event(event: ErrorEvent) -> Uuid {
Hub::current().capture_event(event)
}
pub fn capture_error(error: &dyn std::error::Error) -> Uuid {
Hub::current().capture_error(error)
}
pub fn capture_message(message: &str, level: Level) -> Uuid {
Hub::current().capture_message(message, level)
}
pub fn event_from_error(error: &dyn std::error::Error) -> ErrorEvent {
event::event_from_error(error, Hub::current().client().as_deref())
}
pub fn configure_scope<F>(f: F)
where
F: FnOnce(&mut Scope),
{
Hub::current().configure_scope(f);
}
pub fn with_scope<C, F, R>(scope_fn: C, body: F) -> R
where
C: FnOnce(&mut Scope),
F: FnOnce() -> R,
{
Hub::current().with_scope(scope_fn, body)
}
pub fn add_breadcrumb(breadcrumb: Breadcrumb) {
Hub::current().add_breadcrumb(breadcrumb);
}
pub fn set_user(user: Option<User>) {
Hub::current().set_user(user);
}
pub fn start_session() {
Hub::current().start_session();
}
pub fn end_session() {
Hub::current().end_session();
}
pub fn end_session_with_status(status: SessionStatus) {
Hub::current().end_session_with_status(status);
}
pub fn flush(timeout: Duration) -> bool {
Hub::current().flush(timeout)
}
pub fn get_diagnostics() -> Diagnostics {
Hub::current().get_diagnostics()
}
#[cfg(feature = "anyhow")]
mod anyhow_support {
use uuid::Uuid;
use crate::hub::Hub;
use crate::protocol::{ErrorEvent, Level};
pub fn capture_anyhow(error: &anyhow::Error) -> Uuid {
let class = "anyhow::Error".to_string();
let message = error.to_string();
let chain: Vec<String> = error.chain().skip(1).map(|c| c.to_string()).collect();
let mut event = ErrorEvent::new(class, message);
event.level = Some(Level::Error.as_str().to_string());
if !chain.is_empty() {
event.stack_trace = Some(chain);
}
Hub::current().capture_event(event)
}
}