///|
pub(all) struct HandlerMetrics {
handler : String
handled : Int
ignored : Int
failed : Int
} derive(Eq, Debug)
///|
pub(all) struct TopicMetrics {
topic : String
published : Int
delivered : Int
failed : Int
} derive(Eq, Debug)
///|
/// Immutable metrics snapshot built from dispatch reports.
pub(all) struct BusMetrics {
published : Int
successful_events : Int
failed_events : Int
stopped_events : Int
handler_deliveries : Int
handler_ignored : Int
handler_failures : Int
handlers : Array[HandlerMetrics]
topics : Array[TopicMetrics]
} derive(Eq, Debug)
///|
pub fn BusMetrics::new() -> BusMetrics {
{
published: 0,
successful_events: 0,
failed_events: 0,
stopped_events: 0,
handler_deliveries: 0,
handler_ignored: 0,
handler_failures: 0,
handlers: [],
topics: [],
}
}
///|
fn update_handler_metrics(
items : Array[HandlerMetrics],
report : HandlerReport,
) -> Array[HandlerMetrics] {
let result : Array[HandlerMetrics] = []
let mut found = false
for item in items {
if item.handler == report.handler {
found = true
match report.status {
Handled => result.push({ ..item, handled: item.handled + 1 })
Ignored => result.push({ ..item, ignored: item.ignored + 1 })
Failed(_) => result.push({ ..item, failed: item.failed + 1 })
}
} else {
result.push(item)
}
}
if !found {
match report.status {
Handled =>
result.push({
handler: report.handler,
handled: 1,
ignored: 0,
failed: 0,
})
Ignored =>
result.push({
handler: report.handler,
handled: 0,
ignored: 1,
failed: 0,
})
Failed(_) =>
result.push({
handler: report.handler,
handled: 0,
ignored: 0,
failed: 1,
})
}
}
result
}
///|
fn update_topic_metrics(
items : Array[TopicMetrics],
report : DispatchReport,
) -> Array[TopicMetrics] {
let result : Array[TopicMetrics] = []
let mut found = false
for item in items {
if item.topic == report.event.topic {
found = true
result.push({
..item,
published: item.published + 1,
delivered: item.delivered + report.delivered,
failed: item.failed + report.failed,
})
} else {
result.push(item)
}
}
if !found {
result.push({
topic: report.event.topic,
published: 1,
delivered: report.delivered,
failed: report.failed,
})
}
result
}
///|
pub fn BusMetrics::record(
self : BusMetrics,
report : DispatchReport,
) -> BusMetrics {
let mut handlers = self.handlers
for item in report.reports {
handlers = update_handler_metrics(handlers, item)
}
{
published: self.published + 1,
successful_events: self.successful_events +
(if report.is_ok() { 1 } else { 0 }),
failed_events: self.failed_events + (if report.is_ok() { 0 } else { 1 }),
stopped_events: self.stopped_events + (if report.stopped { 1 } else { 0 }),
handler_deliveries: self.handler_deliveries + report.delivered,
handler_ignored: self.handler_ignored + report.ignored,
handler_failures: self.handler_failures + report.failed,
handlers,
topics: update_topic_metrics(self.topics, report),
}
}
///|
pub fn BusMetrics::record_many(
self : BusMetrics,
reports : Array[DispatchReport],
) -> BusMetrics {
let mut metrics = self
for report in reports {
metrics = metrics.record(report)
}
metrics
}
///|
pub fn BusMetrics::handler(self : BusMetrics, name : String) -> HandlerMetrics? {
for item in self.handlers {
if item.handler == name {
return Some(item)
}
}
None
}
///|
pub fn BusMetrics::topic(self : BusMetrics, name : String) -> TopicMetrics? {
for item in self.topics {
if item.topic == name {
return Some(item)
}
}
None
}
///|
pub fn BusMetrics::success_percent(self : BusMetrics) -> Int {
if self.published == 0 {
100
} else {
self.successful_events * 100 / self.published
}
}
///|
pub fn BusMetrics::delivery_percent(self : BusMetrics) -> Int {
let attempts = self.handler_deliveries +
self.handler_ignored +
self.handler_failures
if attempts == 0 {
100
} else {
self.handler_deliveries * 100 / attempts
}
}
///|
fn merge_handler_metric(
items : Array[HandlerMetrics],
added : HandlerMetrics,
) -> Array[HandlerMetrics] {
let result : Array[HandlerMetrics] = []
let mut found = false
for item in items {
if item.handler == added.handler {
found = true
result.push({
handler: item.handler,
handled: item.handled + added.handled,
ignored: item.ignored + added.ignored,
failed: item.failed + added.failed,
})
} else {
result.push(item)
}
}
if !found {
result.push(added)
}
result
}
///|
fn merge_topic_metric(
items : Array[TopicMetrics],
added : TopicMetrics,
) -> Array[TopicMetrics] {
let result : Array[TopicMetrics] = []
let mut found = false
for item in items {
if item.topic == added.topic {
found = true
result.push({
topic: item.topic,
published: item.published + added.published,
delivered: item.delivered + added.delivered,
failed: item.failed + added.failed,
})
} else {
result.push(item)
}
}
if !found {
result.push(added)
}
result
}
///|
pub fn BusMetrics::merge(self : BusMetrics, other : BusMetrics) -> BusMetrics {
let mut handlers = self.handlers
for item in other.handlers {
handlers = merge_handler_metric(handlers, item)
}
let mut topics = self.topics
for item in other.topics {
topics = merge_topic_metric(topics, item)
}
{
published: self.published + other.published,
successful_events: self.successful_events + other.successful_events,
failed_events: self.failed_events + other.failed_events,
stopped_events: self.stopped_events + other.stopped_events,
handler_deliveries: self.handler_deliveries + other.handler_deliveries,
handler_ignored: self.handler_ignored + other.handler_ignored,
handler_failures: self.handler_failures + other.handler_failures,
handlers,
topics,
}
}
///|
pub fn BusMetrics::failing_handlers(self : BusMetrics) -> Array[HandlerMetrics] {
let result : Array[HandlerMetrics] = []
for item in self.handlers {
if item.failed > 0 {
result.push(item)
}
}
result
}
///|
pub fn BusMetrics::summary(self : BusMetrics) -> String {
"published=" +
self.published.to_string() +
",success=" +
self.successful_events.to_string() +
",failed=" +
self.failed_events.to_string() +
",delivered=" +
self.handler_deliveries.to_string()
}
///|
pub fn metrics_from_reports(reports : Array[DispatchReport]) -> BusMetrics {
BusMetrics::new().record_many(reports)
}