Watch
0
0
Fork
You've already forked hyperhive
0

drop the remaining HYPERHIVE_OTEL_METRIC_INTERVAL_MS readers

Nothing sets the variable once otel.metricIntervalMs is gone, so the
turn-metrics, jobq-metrics and vcs-metrics exporters export at a fixed
60s, as hive-c0re's container-resource exporter already does. The
observability doc and the removal warning now say so.
This commit is contained in:
atlas 2026-10-16 17:48:24 +02:00 • committed by mara
commit f8960dd501
5 changed files with 22 additions and 53 deletions

View file

@ -409,8 +409,8 @@ window used by agent** panel (health tab) divides one by the other.
Resource attributes (`service.name`, `agent`, `swarm`) come from the Resource attributes (`service.name`, `agent`, `swarm`) come from the
same container-wide `OTEL_RESOURCE_ATTRIBUTES` as everything else in this same container-wide `OTEL_RESOURCE_ATTRIBUTES` as everything else in this
section, and the swarm's collector adds `hive` — nothing extra to configure. section, and the swarm's collector adds `hive` — nothing extra to configure.
Cadence follows `HYPERHIVE_OTEL_METRIC_INTERVAL_MS` (default 60s, the same The export cadence is a fixed 60s, the same
cadence as `hive-c0re`'s container-resource export above). That only controls how often as `hive-c0re`'s container-resource export above. That only controls how often
the harness flushes the batched points to the collector, not how often it the harness flushes the batched points to the collector, not how often it
records them (every turn, always). records them (every turn, always).
@ -486,8 +486,8 @@ store either a commit or a push anywhere as a row to count.
A push with zero commits (a branch delete, or a force-push that doesn't add A push with zero commits (a branch delete, or a force-push that doesn't add
new commits) still increments `push.count`; `commit.count` only advances new commits) still increments `push.count`; `commit.count` only advances
when the delivery actually carries commits. Same enable signal (`OTEL_EXPORTER_OTLP_ENDPOINT`), cadence variable when the delivery actually carries commits. Same enable signal (`OTEL_EXPORTER_OTLP_ENDPOINT`), fixed 60s cadence
(`HYPERHIVE_OTEL_METRIC_INTERVAL_MS`) and `HYPERHIVE_OTEL_EXTRA_RESOURCE_ATTRIBUTES` and `HYPERHIVE_OTEL_EXTRA_RESOURCE_ATTRIBUTES`
resource-attribute channel as `swarm-controller`'s other OTEL exporter (its resource-attribute channel as `swarm-controller`'s other OTEL exporter (its
`hive-jobq-metrics`-backed job-graph rollup, undocumented here — see that `hive-jobq-metrics`-backed job-graph rollup, undocumented here — see that
crate's own doc comment) — `swarm-controller` sets `service.name = crate's own doc comment) — `swarm-controller` sets `service.name =

View file

@ -39,10 +39,9 @@ use opentelemetry_sdk::metrics::{PeriodicReader, SdkMeterProvider};
use crate::turn_stats::TurnStatRow; use crate::turn_stats::TurnStatRow;
/// Default export cadence when `HYPERHIVE_OTEL_METRIC_INTERVAL_MS` is unset. /// Export cadence. Matches `hive-c0re`'s container-metrics exporter so the
/// Matches `hive-c0re`'s container-metrics default so the two exporters /// two batch on the same rhythm.
/// batch on the same rhythm by default. const EXPORT_INTERVAL: Duration = Duration::from_mins(1);
const DEFAULT_INTERVAL: Duration = Duration::from_mins(1);
struct Instruments { struct Instruments {
/// Kept alive for the process lifetime — the `PeriodicReader` only /// Kept alive for the process lifetime — the `PeriodicReader` only
@ -177,7 +176,7 @@ fn build() -> Option<Instruments> {
tracing::debug!("otel turn-metrics: no endpoint configured, exporter disabled"); tracing::debug!("otel turn-metrics: no endpoint configured, exporter disabled");
return None; return None;
} }
let interval = interval(); let interval = EXPORT_INTERVAL;
match build_provider(interval) { match build_provider(interval) {
Ok(provider) => { Ok(provider) => {
tracing::info!(?interval, "otel turn-metrics: exporter enabled"); tracing::info!(?interval, "otel turn-metrics: exporter enabled");
@ -254,12 +253,3 @@ fn build_provider(interval: Duration) -> anyhow::Result<SdkMeterProvider> {
pub fn enabled() -> bool { pub fn enabled() -> bool {
std::env::var("OTEL_EXPORTER_OTLP_ENDPOINT").is_ok_and(|s| !s.trim().is_empty()) std::env::var("OTEL_EXPORTER_OTLP_ENDPOINT").is_ok_and(|s| !s.trim().is_empty())
} }
/// Export cadence from `HYPERHIVE_OTEL_METRIC_INTERVAL_MS`, else the default.
fn interval() -> Duration {
std::env::var("HYPERHIVE_OTEL_METRIC_INTERVAL_MS")
.ok()
.and_then(|s| s.trim().parse::<u64>().ok())
.filter(|ms| *ms > 0)
.map_or(DEFAULT_INTERVAL, Duration::from_millis)
}

View file

@ -55,11 +55,10 @@ impl HttpClient for BoxedHttpClient {
} }
} }
/// Default export cadence when `HYPERHIVE_OTEL_METRIC_INTERVAL_MS` is unset. /// Export cadence, and the snapshot refresher's. Matches
/// Matches `hive-c0re::stats::otel_metrics`'s default — no reason for the /// `hive-c0re::stats::otel_metrics` — no reason for the exporters across
/// exporters across this tree to disagree on how fresh "current" means by /// this tree to disagree on how fresh "current" means.
/// default. const EXPORT_INTERVAL: Duration = Duration::from_mins(1);
const DEFAULT_INTERVAL: Duration = Duration::from_mins(1);
/// Spawn a jobq-rollup OTEL exporter for `jobq` if OTEL is configured /// Spawn a jobq-rollup OTEL exporter for `jobq` if OTEL is configured
/// (`OTEL_EXPORTER_OTLP_ENDPOINT` non-empty) — `None` otherwise, same /// (`OTEL_EXPORTER_OTLP_ENDPOINT` non-empty) — `None` otherwise, same
@ -97,7 +96,7 @@ where
{ {
let endpoint = endpoint()?; let endpoint = endpoint()?;
let snapshot: Arc<Mutex<Vec<StateCount>>> = Arc::new(Mutex::new(Vec::new())); let snapshot: Arc<Mutex<Vec<StateCount>>> = Arc::new(Mutex::new(Vec::new()));
let interval = interval(); let interval = EXPORT_INTERVAL;
let refresh = snapshot.clone(); let refresh = snapshot.clone();
tokio::spawn(async move { tokio::spawn(async move {
@ -225,18 +224,6 @@ fn endpoint() -> Option<String> {
.filter(|s| !s.is_empty()) .filter(|s| !s.is_empty())
} }
/// Export cadence from `HYPERHIVE_OTEL_METRIC_INTERVAL_MS`, else the
/// default. Same variable `hive-c0re::stats::otel_metrics` reads — one
/// cadence knob for every OTEL exporter in the tree, not a new one per
/// module.
fn interval() -> Duration {
std::env::var("HYPERHIVE_OTEL_METRIC_INTERVAL_MS")
.ok()
.and_then(|s| s.trim().parse::<u64>().ok())
.filter(|ms| *ms > 0)
.map_or(DEFAULT_INTERVAL, Duration::from_millis)
}
/// Extra resource attributes from `HYPERHIVE_OTEL_EXTRA_RESOURCE_ATTRIBUTES` /// Extra resource attributes from `HYPERHIVE_OTEL_EXTRA_RESOURCE_ATTRIBUTES`
/// (`key=value,key=value`), same env `hive-c0re::stats::otel_metrics` reads. /// (`key=value,key=value`), same env `hive-c0re::stats::otel_metrics` reads.
fn resource_attributes() -> Vec<(String, String)> { fn resource_attributes() -> Vec<(String, String)> {

View file

@ -6,8 +6,9 @@
# (hive-agent/prompts/system.md) names the operator's pronouns itself, the # (hive-agent/prompts/system.md) names the operator's pronouns itself, the
# harness (hive-agent/src/harness_state.rs) holds the per-model context # harness (hive-agent/src/harness_state.rs) holds the per-model context
# windows it uses until a turn reports the model's own, every OTEL exporter # windows it uses until a turn reports the model's own, every OTEL exporter
# on the hive runs at its SDK's default cadence, and an agent's own config # exports every 60 seconds (claude at its SDK default, the rest by a fixed
# sets `services.hyperhive.agent.otel.debug`. # constant), and an agent's own config sets
# `services.hyperhive.agent.otel.debug`.
# #
# Self-contained and deletable, like ./swarm-peers-removed.nix: one file to # Self-contained and deletable, like ./swarm-peers-removed.nix: one file to
# remove when the deprecation window closes. # remove when the deprecation window closes.
@ -52,8 +53,8 @@ in
1000000, opus 1000000 tokens). 1000000, opus 1000000 tokens).
'' ''
++ removed "otel.metricIntervalMs" cfg.otel.metricIntervalMs '' ++ removed "otel.metricIntervalMs" cfg.otel.metricIntervalMs ''
claude in every agent and hive-c0re's container-resource exporter both claude exports at its SDK default of 60 seconds, and every hyperhive
export metrics at the SDK default of 60 seconds. exporter at a fixed 60 seconds.
'' ''
++ removed "otel.debug" cfg.otel.debug '' ++ removed "otel.debug" cfg.otel.debug ''
to see one agent's OTEL SDK diagnostics, set to see one agent's OTEL SDK diagnostics, set

View file

@ -13,7 +13,7 @@
//! exporter): blocking OTLP client on the `PeriodicReader`'s own background //! exporter): blocking OTLP client on the `PeriodicReader`'s own background
//! thread, `service.name` + `HYPERHIVE_OTEL_EXTRA_RESOURCE_ATTRIBUTES` for //! thread, `service.name` + `HYPERHIVE_OTEL_EXTRA_RESOURCE_ATTRIBUTES` for
//! the resource, `OTEL_EXPORTER_OTLP_ENDPOINT` as the sole enable signal, //! the resource, `OTEL_EXPORTER_OTLP_ENDPOINT` as the sole enable signal,
//! `HYPERHIVE_OTEL_METRIC_INTERVAL_MS` for cadence. Recording is //! a fixed 60 s export cadence. Recording is
//! synchronous (`counter.add`, called at the event site) rather than //! synchronous (`counter.add`, called at the event site) rather than
//! observable/polled — see `hive-agent::otel_turn_metrics`'s doc comment //! observable/polled — see `hive-agent::otel_turn_metrics`'s doc comment
//! for why an occurrence, unlike a live gauge, has nothing to poll between //! for why an occurrence, unlike a live gauge, has nothing to poll between
@ -29,9 +29,8 @@ use opentelemetry_otlp::{MetricExporter, Protocol, WithExportConfig, WithHttpCon
use opentelemetry_sdk::Resource; use opentelemetry_sdk::Resource;
use opentelemetry_sdk::metrics::{PeriodicReader, SdkMeterProvider}; use opentelemetry_sdk::metrics::{PeriodicReader, SdkMeterProvider};
/// Default export cadence when `HYPERHIVE_OTEL_METRIC_INTERVAL_MS` is /// Export cadence. Matches every other OTEL exporter in this tree.
/// unset. Matches every other OTEL exporter in this tree. const EXPORT_INTERVAL: Duration = Duration::from_mins(1);
const DEFAULT_INTERVAL: Duration = Duration::from_mins(1);
/// One push delivery, as parsed off the webhook body — just the two fields /// One push delivery, as parsed off the webhook body — just the two fields
/// this module needs, not a full mirror of Forgejo's push-event schema. /// this module needs, not a full mirror of Forgejo's push-event schema.
@ -86,7 +85,7 @@ pub(super) fn record_push(activity: &PushActivity) {
fn build() -> Option<Instruments> { fn build() -> Option<Instruments> {
let endpoint = endpoint()?; let endpoint = endpoint()?;
let interval = interval(); let interval = EXPORT_INTERVAL;
match build_provider(interval) { match build_provider(interval) {
Ok(provider) => { Ok(provider) => {
tracing::info!(%endpoint, ?interval, "otel vcs-metrics: exporter enabled"); tracing::info!(%endpoint, ?interval, "otel vcs-metrics: exporter enabled");
@ -198,14 +197,6 @@ fn endpoint() -> Option<String> {
.filter(|s| !s.is_empty()) .filter(|s| !s.is_empty())
} }
fn interval() -> Duration {
std::env::var("HYPERHIVE_OTEL_METRIC_INTERVAL_MS")
.ok()
.and_then(|s| s.trim().parse::<u64>().ok())
.filter(|ms| *ms > 0)
.map_or(DEFAULT_INTERVAL, Duration::from_millis)
}
fn resource_attributes() -> Vec<(String, String)> { fn resource_attributes() -> Vec<(String, String)> {
std::env::var("HYPERHIVE_OTEL_EXTRA_RESOURCE_ATTRIBUTES") std::env::var("HYPERHIVE_OTEL_EXTRA_RESOURCE_ATTRIBUTES")
.ok() .ok()