use crate::Uuid;
use std::time::Instant;
#[derive(Debug, Clone)]
pub struct QueryContext {
model_name: &'static str,
start_time: Instant,
query_id: Uuid,
query: String,
arguments: Vec<String>,
last_insert_id: Option<i64>,
rows_affected: Option<u64>,
success: bool,
cancelled: bool,
}
impl QueryContext {
#[inline]
pub fn new(model_name: &'static str) -> Self {
Self {
model_name,
start_time: Instant::now(),
query_id: Uuid::now_v7(),
query: String::new(),
arguments: Vec::new(),
last_insert_id: None,
rows_affected: None,
success: false,
cancelled: false,
}
}
#[inline]
pub fn set_query(&mut self, query: impl Into<String>) {
self.query = query.into();
}
#[inline]
pub fn add_argument(&mut self, value: impl ToString) {
self.arguments.push(value.to_string());
}
#[inline]
pub fn append_arguments(&mut self, arguments: &mut Vec<String>) {
self.arguments.append(arguments);
}
#[inline]
pub fn set_last_insert_id(&mut self, last_insert_id: i64) {
self.last_insert_id = Some(last_insert_id);
}
#[inline]
pub fn set_query_result(&mut self, rows_affected: impl Into<Option<u64>>, success: bool) {
self.rows_affected = rows_affected.into();
self.success = success;
self.cancelled = false;
}
#[inline]
pub fn cancel(&mut self) {
self.cancelled = true;
}
#[inline]
pub fn model_name(&self) -> &'static str {
self.model_name
}
#[inline]
pub fn start_time(&self) -> Instant {
self.start_time
}
#[inline]
pub fn query_id(&self) -> Uuid {
self.query_id
}
#[inline]
pub fn query(&self) -> &str {
&self.query
}
#[inline]
pub fn arguments(&self) -> &[String] {
&self.arguments
}
#[inline]
pub fn last_insert_id(&self) -> Option<i64> {
self.last_insert_id
}
#[inline]
pub fn rows_affected(&self) -> Option<u64> {
self.rows_affected
}
#[inline]
pub fn is_cancelled(&self) -> bool {
self.cancelled
}
#[inline]
pub fn is_success(&self) -> bool {
self.success
}
#[inline]
pub fn format_arguments(&self) -> Option<String> {
let arguments = self.arguments();
(!arguments.is_empty()).then(|| arguments.join(", "))
}
pub fn record_error(&self, message: impl AsRef<str>) {
fn inner(ctx: &QueryContext, message: &str) {
let model_name = ctx.model_name();
let query_id = ctx.query_id().to_string();
let query = ctx.query();
let arguments = ctx.format_arguments();
if ctx.is_cancelled() {
tracing::warn!(
cancelled = true,
model_name,
query_id,
query,
arguments,
message,
);
} else {
tracing::error!(model_name, query_id, query, arguments, message);
}
}
inner(self, message.as_ref())
}
#[cfg(feature = "metrics")]
#[inline]
pub fn emit_metrics(&self, action: impl Into<crate::SharedString>) {
fn inner(ctx: &QueryContext, action: crate::SharedString) {
metrics::histogram!(
"zino_model_query_duration_seconds",
"model_name" => ctx.model_name(),
"action" => action,
)
.record(ctx.start_time().elapsed().as_secs_f64());
}
inner(self, action.into())
}
}