Live and historical metrics
This commit is contained in:
@@ -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 * *")
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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!())),
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user