use opentelemetry::metrics::{Counter, Histogram, Meter, UpDownCounter};
use std::time::Duration;
#[derive(Clone)]
pub struct AwaMetrics {
pub jobs_inserted: Counter<u64>,
pub jobs_completed: Counter<u64>,
pub jobs_failed: Counter<u64>,
pub jobs_retried: Counter<u64>,
pub jobs_cancelled: Counter<u64>,
pub jobs_claimed: Counter<u64>,
pub claim_batches: Counter<u64>,
pub claim_batch_size: Histogram<u64>,
pub claim_duration_seconds: Histogram<f64>,
pub job_duration_seconds: Histogram<f64>,
pub completion_flushes: Counter<u64>,
pub completion_flush_batch_size: Histogram<u64>,
pub completion_flush_duration_seconds: Histogram<f64>,
pub promotion_batches: Counter<u64>,
pub promotion_batch_size: Histogram<u64>,
pub promotion_duration_seconds: Histogram<f64>,
pub jobs_in_flight: UpDownCounter<i64>,
pub heartbeat_batches: Counter<u64>,
pub maintenance_rescues: Counter<u64>,
pub jobs_waiting_external: Counter<u64>,
}
impl AwaMetrics {
pub fn new(meter: &Meter) -> Self {
Self {
jobs_inserted: meter
.u64_counter("awa.jobs.inserted")
.with_description("Total jobs inserted")
.build(),
jobs_completed: meter
.u64_counter("awa.jobs.completed")
.with_description("Total jobs completed successfully")
.build(),
jobs_failed: meter
.u64_counter("awa.jobs.failed")
.with_description("Total jobs that failed terminally")
.build(),
jobs_retried: meter
.u64_counter("awa.jobs.retried")
.with_description("Total jobs marked retryable")
.build(),
jobs_cancelled: meter
.u64_counter("awa.jobs.cancelled")
.with_description("Total jobs cancelled")
.build(),
jobs_claimed: meter
.u64_counter("awa.jobs.claimed")
.with_description("Total jobs claimed for execution")
.build(),
claim_batches: meter
.u64_counter("awa.dispatch.claim_batches")
.with_description("Total dispatcher claim queries executed")
.build(),
claim_batch_size: meter
.u64_histogram("awa.dispatch.claim_batch_size")
.with_description("Dispatcher claim batch size in jobs")
.build(),
claim_duration_seconds: meter
.f64_histogram("awa.dispatch.claim_duration")
.with_description("Dispatcher claim query duration in seconds")
.with_unit("s")
.build(),
job_duration_seconds: meter
.f64_histogram("awa.jobs.duration")
.with_description("Job execution duration in seconds")
.with_unit("s")
.build(),
completion_flushes: meter
.u64_counter("awa.completion.flushes")
.with_description("Total completion batch flushes")
.build(),
completion_flush_batch_size: meter
.u64_histogram("awa.completion.flush_batch_size")
.with_description("Completion batch flush size in jobs")
.build(),
completion_flush_duration_seconds: meter
.f64_histogram("awa.completion.flush_duration")
.with_description("Completion batch flush duration in seconds")
.with_unit("s")
.build(),
promotion_batches: meter
.u64_counter("awa.maintenance.promote_batches")
.with_description("Total scheduled/retryable promotion batches")
.build(),
promotion_batch_size: meter
.u64_histogram("awa.maintenance.promote_batch_size")
.with_description("Promotion batch size in jobs")
.build(),
promotion_duration_seconds: meter
.f64_histogram("awa.maintenance.promote_duration")
.with_description("Promotion batch duration in seconds")
.with_unit("s")
.build(),
jobs_in_flight: meter
.i64_up_down_counter("awa.jobs.in_flight")
.with_description("Current number of in-flight jobs")
.build(),
heartbeat_batches: meter
.u64_counter("awa.heartbeat.batches")
.with_description("Total heartbeat batch updates sent")
.build(),
maintenance_rescues: meter
.u64_counter("awa.maintenance.rescues")
.with_description("Total jobs rescued by maintenance (stale heartbeat + deadline)")
.build(),
jobs_waiting_external: meter
.u64_counter("awa.jobs.waiting_external")
.with_description("Total jobs parked for external callback")
.build(),
}
}
pub fn from_global() -> Self {
let meter = opentelemetry::global::meter("awa");
Self::new(&meter)
}
pub fn record_job_completed(&self, kind: &str, queue: &str, duration: Duration) {
let attrs = [
opentelemetry::KeyValue::new("awa.job.kind", kind.to_string()),
opentelemetry::KeyValue::new("awa.job.queue", queue.to_string()),
];
self.jobs_completed.add(1, &attrs);
self.job_duration_seconds
.record(duration.as_secs_f64(), &attrs);
}
pub fn record_job_failed(&self, kind: &str, queue: &str, terminal: bool) {
let attrs = [
opentelemetry::KeyValue::new("awa.job.kind", kind.to_string()),
opentelemetry::KeyValue::new("awa.job.queue", queue.to_string()),
opentelemetry::KeyValue::new("awa.job.terminal", terminal),
];
self.jobs_failed.add(1, &attrs);
}
pub fn record_job_retried(&self, kind: &str, queue: &str) {
let attrs = [
opentelemetry::KeyValue::new("awa.job.kind", kind.to_string()),
opentelemetry::KeyValue::new("awa.job.queue", queue.to_string()),
];
self.jobs_retried.add(1, &attrs);
}
pub fn record_job_claimed(&self, queue: &str, batch_size: u64) {
let attrs = [opentelemetry::KeyValue::new(
"awa.job.queue",
queue.to_string(),
)];
self.jobs_claimed.add(batch_size, &attrs);
}
pub fn record_claim_batch(&self, queue: &str, batch_size: u64, duration: Duration) {
let attrs = [opentelemetry::KeyValue::new(
"awa.job.queue",
queue.to_string(),
)];
self.claim_batches.add(1, &attrs);
self.claim_batch_size.record(batch_size, &attrs);
self.claim_duration_seconds
.record(duration.as_secs_f64(), &attrs);
}
pub fn record_completion_flush(&self, shard: usize, batch_size: u64, duration: Duration) {
let attrs = [opentelemetry::KeyValue::new(
"awa.completion.shard",
shard as i64,
)];
self.completion_flushes.add(1, &attrs);
self.completion_flush_batch_size.record(batch_size, &attrs);
self.completion_flush_duration_seconds
.record(duration.as_secs_f64(), &attrs);
}
pub fn record_promotion_batch(&self, state: &str, batch_size: u64, duration: Duration) {
let attrs = [opentelemetry::KeyValue::new(
"awa.job.state",
state.to_string(),
)];
self.promotion_batches.add(1, &attrs);
self.promotion_batch_size.record(batch_size, &attrs);
self.promotion_duration_seconds
.record(duration.as_secs_f64(), &attrs);
}
pub fn record_in_flight_change(&self, queue: &str, delta: i64) {
let attrs = [opentelemetry::KeyValue::new(
"awa.job.queue",
queue.to_string(),
)];
self.jobs_in_flight.add(delta, &attrs);
}
}
impl Default for AwaMetrics {
fn default() -> Self {
Self::from_global()
}
}