Live and historical metrics

This commit is contained in:
mdecimus
2024-08-27 17:54:34 +02:00
parent 18a24f7220
commit 62f55ad62b
15 changed files with 346 additions and 40 deletions

View File

@@ -14,7 +14,7 @@ use jmap_proto::types::collection::Collection;
use store::{BitmapKey, Store, Stores};
use utils::config::{cron::SimpleCron, utils::ParseValue, Config};
use super::{license::LicenseValidator, Enterprise, MetricsStore, TraceStore, Undelete};
use super::{license::LicenseValidator, Enterprise, MetricStore, TraceStore, Undelete};
impl Enterprise {
pub async fn parse(config: &mut Config, stores: &Stores, data: &Store) -> Option<Self> {
@@ -85,10 +85,10 @@ impl Enterprise {
.and_then(|name| stores.stores.get(name))
.cloned()
{
MetricsStore {
MetricStore {
retention: config
.property_or_default::<Option<Duration>>("metrics.history.retention", "30d")
.unwrap_or(Some(Duration::from_secs(30 * 24 * 60 * 60))),
.property_or_default::<Option<Duration>>("metrics.history.retention", "90d")
.unwrap_or(Some(Duration::from_secs(90 * 24 * 60 * 60))),
store,
interval: config
.property_or_default::<SimpleCron>("metrics.history.interval", "0 * *")

View File

@@ -26,7 +26,7 @@ pub struct Enterprise {
pub license: LicenseKey,
pub undelete: Option<Undelete>,
pub trace_store: Option<TraceStore>,
pub metrics_store: Option<MetricsStore>,
pub metrics_store: Option<MetricStore>,
}
#[derive(Clone)]
@@ -41,7 +41,7 @@ pub struct TraceStore {
}
#[derive(Clone)]
pub struct MetricsStore {
pub struct MetricStore {
pub retention: Option<Duration>,
pub store: Store,
pub interval: SimpleCron,

View File

@@ -21,6 +21,7 @@ use config::{
};
use directory::{core::secret::verify_secret_hash, Directory, Principal, QueryBy, Type};
use expr::if_block::IfBlock;
use jmap_proto::types::collection::Collection;
use listener::{
blocked::{AllowedIps, BlockedIps},
tls::TlsManager,
@@ -29,10 +30,11 @@ use mail_send::Credentials;
use sieve::Sieve;
use store::{
write::{QueueClass, ValueClass},
IterateParams, LookupStore, ValueKey,
write::{DirectoryClass, QueueClass, ValueClass},
BitmapKey, IterateParams, LookupStore, ValueKey,
};
use tokio::sync::{mpsc, oneshot};
use trc::AddContext;
use utils::BlobHash;
pub mod addresses;
@@ -308,7 +310,7 @@ impl Core {
}
}
pub async fn message_queue_size(&self) -> trc::Result<u64> {
pub async fn total_queued_messages(&self) -> trc::Result<u64> {
let mut total = 0;
self.storage
.data
@@ -327,6 +329,39 @@ impl Core {
.await
.map(|_| total)
}
pub async fn total_accounts(&self) -> trc::Result<u64> {
self.storage
.data
.get_bitmap(BitmapKey::document_ids(u32::MAX, Collection::Principal))
.await
.caused_by(trc::location!())
.map(|bitmap| bitmap.map_or(0, |b| b.len()))
}
pub async fn total_domains(&self) -> trc::Result<u64> {
let mut total = 0;
self.storage
.data
.iterate(
IterateParams::new(
ValueKey::from(ValueClass::Directory(DirectoryClass::Domain(vec![]))),
ValueKey::from(ValueClass::Directory(DirectoryClass::Domain(vec![
u8::MAX;
10
]))),
)
.no_values()
.ascending(),
|_, _| {
total += 1;
Ok(true)
},
)
.await
.caused_by(trc::location!())
.map(|_| total)
}
}
trait CredentialsUsername {

View File

@@ -29,6 +29,7 @@ pub trait MetricsStore: Sync + Send {
fn write_metrics(
&self,
core: Arc<Core>,
timestamp: u64,
history: SharedMetricHistory,
) -> impl Future<Output = trc::Result<()>> + Send;
fn query_metrics(
@@ -51,14 +52,19 @@ struct HistogramHistory {
count: u64,
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "type")]
#[serde(rename_all = "snake_case")]
#[serde(rename_all = "camelCase")]
pub enum Metric<CI, MI, T> {
Counter {
id: CI,
timestamp: T,
value: u32,
value: u64,
},
Gauge {
id: MI,
timestamp: T,
value: u64,
},
Histogram {
id: MI,
@@ -72,16 +78,17 @@ pub type SharedMetricHistory = Arc<Mutex<MetricsHistory>>;
const TYPE_COUNTER: u64 = 0x00;
const TYPE_HISTOGRAM: u64 = 0x01;
const TYPE_GAUGE: u64 = 0x02;
impl MetricsStore for Store {
async fn write_metrics(
&self,
core: Arc<Core>,
timestamp: u64,
history_: SharedMetricHistory,
) -> trc::Result<()> {
let mut batch = BatchBuilder::new();
{
let timestamp = now();
let node_id = core.network.node_id;
let mut history = history_.lock();
for event in [
@@ -91,9 +98,14 @@ impl MetricsStore for Store {
EventType::ManageSieve(ManageSieveEvent::ConnectionStart),
EventType::Http(HttpEvent::ConnectionStart),
EventType::Delivery(DeliveryEvent::AttemptStart),
EventType::Delivery(DeliveryEvent::Completed),
EventType::Queue(QueueEvent::QueueMessage),
EventType::Queue(QueueEvent::QueueMessageAuthenticated),
EventType::Queue(QueueEvent::QueueDsn),
EventType::Queue(QueueEvent::QueueReport),
EventType::MessageIngest(MessageIngestEvent::Ham),
EventType::MessageIngest(MessageIngestEvent::Spam),
EventType::Auth(AuthEvent::Failed),
EventType::Auth(AuthEvent::Banned),
EventType::Network(NetworkEvent::DropBlocked),
EventType::IncomingReport(IncomingReportEvent::DmarcReport),
EventType::IncomingReport(IncomingReportEvent::DmarcReportWithWarnings),
@@ -118,6 +130,23 @@ impl MetricsStore for Store {
}
}
for gauge in Collector::collect_gauges(true) {
let gauge_id = gauge.id();
if matches!(gauge_id, MetricType::QueueCount | MetricType::ServerMemory) {
let value = gauge.get();
if value > 0 {
batch.set(
ValueClass::Telemetry(TelemetryClass::Metric {
timestamp,
metric_id: (gauge_id.code() << 2) | TYPE_GAUGE,
node_id,
}),
KeySerializer::new(U32_LEN).write_leb128(value).finalize(),
);
}
}
}
for histogram in Collector::collect_histograms(true) {
let histogram_id = histogram.id();
if matches!(
@@ -191,7 +220,7 @@ impl MetricsStore for Store {
let id = EventType::from_code(metric_type >> 2).ok_or_else(|| {
trc::Error::corrupted_key(key, None, trc::location!())
})?;
let (value, _) = value.read_leb128::<u32>().ok_or_else(|| {
let (value, _) = value.read_leb128::<u64>().ok_or_else(|| {
trc::Error::corrupted_key(key, value.into(), trc::location!())
})?;
metrics.push(Metric::Counter {
@@ -220,6 +249,19 @@ impl MetricsStore for Store {
sum,
});
}
TYPE_GAUGE => {
let id = MetricType::from_code(metric_type >> 2).ok_or_else(|| {
trc::Error::corrupted_key(key, None, trc::location!())
})?;
let (value, _) = value.read_leb128::<u64>().ok_or_else(|| {
trc::Error::corrupted_key(key, value.into(), trc::location!())
})?;
metrics.push(Metric::Gauge {
id,
timestamp,
value,
});
}
_ => return Err(trc::Error::corrupted_key(key, None, trc::location!())),
}