use parking_lot::{MappedMutexGuard, MutexGuard};
use super::{out::GLOBAL_LOG, GlobalLog};
use crate::prelude::*;
/// Record an exception to the currently active span, making sure the record location is added to the stacktrace.
/// Matches oltp spec so it shows up correctly as an exception in observers
///
///
/// Arguments:
/// - `message`: Information about the exception e.g. `Internal Error leading to 500 http response`.
/// - `stacktrace`: All of the location information for the exception, (maybe also the exception itself if e.g. from `Report`).
#[track_caller]
pub fn record_exception(message: impl Into, stacktrace: impl Into) {
let caller = std::panic::Location::caller();
record_exception_custom_caller(caller, message, stacktrace);
}
/// Same as [`record_exception`] except you pass a custom caller.
pub fn record_exception_custom_caller(
caller: &std::panic::Location<'_>,
message: impl Into,
stacktrace: impl Into,
) {
let mut stacktrace = stacktrace.into();
stacktrace = if stacktrace.trim().is_empty() {
format!("╰╴at {}", caller)
} else {
format!("{}\n╰╴at {}", stacktrace, caller)
};
super::exceptions::record_exception_inner(message, stacktrace, "Err");
}
#[cfg(any(feature = "opentelemetry-grpc", feature = "opentelemetry-http"))]
/// Returns a new [`opentelemetry::metrics::Meter`] with the provided name and default configuration.
///
/// A [opentelemetry::metrics::Meter] should be scoped at most to a single application or crate. The
/// name needs to be unique so it does not collide with other names used by
/// an application, nor other applications.
///
/// If the name is empty, then an implementation defined default name will
/// be used instead.
pub fn meter(name: impl Into>) -> opentelemetry::metrics::Meter {
opentelemetry::global::meter(name)
}
#[cfg(any(feature = "opentelemetry-grpc", feature = "opentelemetry-http"))]
/// Returns the default [`opentelemetry::metrics::Meter`] for the app, labelled "default".
pub fn global_meter() -> &'static opentelemetry::metrics::Meter {
use std::sync::LazyLock;
static GLOBAL_METER: LazyLock =
LazyLock::new(|| opentelemetry::global::meter("default"));
&GLOBAL_METER
}
#[cfg(any(feature = "opentelemetry-grpc", feature = "opentelemetry-http"))]
/// Connect this program's span to the trace that is represented by the provided HTTP headers.
/// E.g. connect an axum handler's trace/span to the nginx trace/span.
pub fn set_span_parent_from_http_headers(
span: &tracing::Span,
headers: &http::HeaderMap,
) -> RResult<(), AnyErr> {
get_global()?.set_span_parent_from_http_headers(span, headers)
}
#[cfg(any(feature = "opentelemetry-grpc", feature = "opentelemetry-http"))]
/// Set the response headers from the current span context. So downstream services can continue the current trace.
pub fn set_response_headers_from_ctx(response: &mut http::Response) -> RResult<(), AnyErr> {
get_global()?.set_response_headers_from_ctx(response)
}
/// NOTE ALL LOGGING WILL CEASE AFTER A FLUSH!
/// Force through logs, traces and metrics, useful in e.g. testing.
///
/// Note there doesn't seem to be an underlying interface to force through metrics.
pub fn flush_and_consume() -> RResult<(), AnyErr> {
let glob = GLOBAL_LOG.lock().take();
if let Some(glob) = glob {
glob.flush_and_consume()
} else {
Err(anyerr!("GlobalLog not registered or already consumed!"))
}
}
/// Shutdown the logger, traces and metrics, should be called when the program is about to exit.
pub fn shutdown() -> RResult<(), AnyErr> {
get_global()?.shutdown()
}
fn get_global<'a>() -> RResult, AnyErr> {
if GLOBAL_LOG.lock().is_none() {
return Err(anyerr!("GlobalLog not registered or already consumed!"));
}
Ok(MutexGuard::map(GLOBAL_LOG.lock(), |x| x.as_mut().unwrap()))
}