Skip to content

Commit aa5ff45

Browse files
g-talbotclaude
andcommitted
feat(31): wire invariant recorder to DogStatsD metrics
Emit cloudprem.pomsky.invariant.checked and .violated counters with invariant label via the metrics crate / DogStatsD exporter at process startup, completing Layer 4 of the verification stack. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f2c0f93 commit aa5ff45

File tree

6 files changed

+263
-28
lines changed

6 files changed

+263
-28
lines changed

quickwit/Cargo.lock

Lines changed: 159 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

quickwit/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ libz-sys = "1.1"
156156
lru = "0.16"
157157
matches = "0.1"
158158
md5 = "0.8"
159+
metrics = "0.24"
160+
metrics-exporter-dogstatsd = "0.9"
159161
mime_guess = "2.0"
160162
mini-moka = "0.10.3"
161163
mockall = "0.14"

quickwit/quickwit-cli/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,14 @@ tracing = { workspace = true }
5656
tracing-opentelemetry = { workspace = true }
5757
tracing-subscriber = { workspace = true }
5858

59+
metrics = { workspace = true }
60+
metrics-exporter-dogstatsd = { workspace = true }
61+
5962
quickwit-actors = { workspace = true }
6063
quickwit-cluster = { workspace = true }
6164
quickwit-common = { workspace = true }
6265
quickwit-config = { workspace = true }
66+
quickwit-dst = { workspace = true }
6367
quickwit-index-management = { workspace = true }
6468
quickwit-indexing = { workspace = true }
6569
quickwit-ingest = { workspace = true }

quickwit/quickwit-cli/src/logger.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,65 @@ pub fn setup_logging_and_tracing(
161161
))
162162
}
163163

164+
#[cfg(not(any(test, feature = "testsuite")))]
165+
pub fn setup_dogstatsd_exporter(build_info: &BuildInfo) -> anyhow::Result<()> {
166+
// Reading both `CLOUDPREM_*` and `CP_*` env vars for backward compatibility. The former is
167+
// deprecated and can be removed after 2026-04-01.
168+
let host: String = quickwit_common::get_from_env_opt("CLOUDPREM_DOGSTATSD_SERVER_HOST", false)
169+
.unwrap_or_else(|| {
170+
quickwit_common::get_from_env(
171+
"CP_DOGSTATSD_SERVER_HOST",
172+
"127.0.0.1".to_string(),
173+
false,
174+
)
175+
});
176+
let port: u16 = quickwit_common::get_from_env_opt("CLOUDPREM_DOGSTATSD_SERVER_PORT", false)
177+
.unwrap_or_else(|| quickwit_common::get_from_env("CP_DOGSTATSD_SERVER_PORT", 8125, false));
178+
let addr = format!("{host}:{port}");
179+
180+
let mut global_labels = vec![::metrics::Label::new("version", build_info.version.clone())];
181+
let keys = [
182+
("IMAGE_NAME", "image_name"),
183+
("IMAGE_TAG", "image_tag"),
184+
("KUBERNETES_COMPONENT", "kube_component"),
185+
("KUBERNETES_NAMESPACE", "kube_namespace"),
186+
("KUBERNETES_POD_NAME", "kube_pod_name"),
187+
("QW_CLUSTER_ID", "cloudprem_cluster_id"),
188+
("QW_NODE_ID", "cloudprem_node_id"),
189+
];
190+
for (env_var_key, label_key) in keys {
191+
if let Some(label_val) = quickwit_common::get_from_env_opt::<String>(env_var_key, false) {
192+
global_labels.push(::metrics::Label::new(label_key, label_val));
193+
}
194+
}
195+
metrics_exporter_dogstatsd::DogStatsDBuilder::default()
196+
.set_global_prefix("cloudprem")
197+
.with_global_labels(global_labels)
198+
.with_remote_address(addr)
199+
.context("failed to parse DogStatsD server address")?
200+
.install()
201+
.context("failed to register DogStatsD exporter")?;
202+
Ok(())
203+
}
204+
205+
/// Register the invariant recorder that emits DogStatsD counters.
206+
///
207+
/// Must be called after [`setup_dogstatsd_exporter`] so the `metrics` crate
208+
/// has a registered recorder.
209+
#[cfg(not(any(test, feature = "testsuite")))]
210+
pub fn setup_invariant_recorder() {
211+
quickwit_dst::invariants::set_invariant_recorder(invariant_recorder);
212+
}
213+
214+
#[cfg(not(any(test, feature = "testsuite")))]
215+
fn invariant_recorder(id: quickwit_dst::invariants::InvariantId, passed: bool) {
216+
let name = id.as_str();
217+
metrics::counter!("pomsky.invariant.checked", "invariant" => name).increment(1);
218+
if !passed {
219+
metrics::counter!("pomsky.invariant.violated", "invariant" => name).increment(1);
220+
}
221+
}
222+
164223
/// We do not rely on the RFC3339 implementation, because it has a nanosecond precision.
165224
/// See discussion here: https://github.com/time-rs/time/discussions/418
166225
fn time_formatter() -> UtcTime<Vec<BorrowedFormatItem<'static>>> {

quickwit/quickwit-cli/src/main.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ async fn main_impl() -> anyhow::Result<()> {
101101
let (env_filter_reload_fn, tracer_provider_opt) =
102102
setup_logging_and_tracing(command.default_log_level(), ansi_colors, build_info)?;
103103

104+
#[cfg(not(any(test, feature = "testsuite")))]
105+
quickwit_cli::logger::setup_dogstatsd_exporter(build_info)?;
106+
107+
#[cfg(not(any(test, feature = "testsuite")))]
108+
quickwit_cli::logger::setup_invariant_recorder();
109+
104110
let return_code: i32 = if let Err(command_error) = command.execute(env_filter_reload_fn).await {
105111
error!(error=%command_error, "command failed");
106112
eprintln!(

0 commit comments

Comments
 (0)