#[cfg(feature = "foundations-metrics-backend")]
use std::sync::OnceLock;
use crate::ServiceInfo;
use crate::telemetry::settings::MetricsSettings;
#[cfg(not(feature = "foundations-metrics-backend"))]
use super::internal::Registries;
use super::{info_metric, report_info};
#[info_metric(crate_path = "crate")]
struct BuildInfo {
version: &'static str,
}
#[info_metric(crate_path = "crate")]
struct RuntimeInfo {
pid: u32,
}
#[cfg(feature = "foundations-metrics-backend")]
static UNINITIALISED_SERVICE_NAME: &str = "undefined";
#[cfg(feature = "foundations-metrics-backend")]
static SERVICE_NAME: OnceLock<String> = OnceLock::new();
#[cfg(feature = "foundations-metrics-backend")]
pub(super) fn service_name() -> &'static str {
SERVICE_NAME
.get()
.map(String::as_str)
.unwrap_or(UNINITIALISED_SERVICE_NAME)
}
pub(crate) fn init(
service_info: &ServiceInfo,
settings: &MetricsSettings,
) -> crate::BootstrapResult<()> {
#[cfg(feature = "foundations-metrics-backend")]
validate_service_name_format(settings)?;
#[cfg(not(feature = "foundations-metrics-backend"))]
let first_install = Registries::init(service_info, settings);
#[cfg(feature = "foundations-metrics-backend")]
let first_install = {
let _ = settings;
let _ = foundations_metrics::set_collect_error_hook(|args| {
super::report_nonfatal_collect_error(&args);
});
SERVICE_NAME
.set(service_info.name_in_metrics.clone())
.is_ok()
};
if first_install {
report_info(BuildInfo {
version: service_info.version,
});
report_info(RuntimeInfo {
pid: std::process::id(),
});
}
Ok(())
}
#[cfg(feature = "foundations-metrics-backend")]
fn validate_service_name_format(settings: &MetricsSettings) -> crate::BootstrapResult<()> {
use crate::telemetry::settings::ServiceNameFormat;
if let ServiceNameFormat::LabelWithName(label_name) = &settings.service_name_format
&& !foundations_metrics::is_valid_name(label_name)
{
anyhow::bail!(
"metrics.service_name_format label name {label_name:?} cannot be encoded; expected {}",
foundations_metrics::NAME_REQUIREMENT,
);
}
Ok(())
}
#[cfg(all(test, feature = "foundations-metrics-backend", feature = "settings"))]
mod service_name_format_tests {
use super::*;
use crate::telemetry::settings::ServiceNameFormat;
fn validate(format: ServiceNameFormat) -> crate::BootstrapResult<()> {
validate_service_name_format(&MetricsSettings {
service_name_format: format,
..Default::default()
})
}
#[test]
fn usable_label_names_are_accepted() {
for name in ["service", "app_name", "a", "sérvice", "with space"] {
assert!(
validate(ServiceNameFormat::LabelWithName(name.to_owned())).is_ok(),
"{name:?} is encodable and should be accepted"
);
}
}
#[test]
fn unencodable_label_names_are_rejected() {
for name in ["", "\0", "ser\0vice"] {
assert!(
validate(ServiceNameFormat::LabelWithName(name.to_owned())).is_err(),
"{name:?} cannot be encoded and should be rejected"
);
}
}
#[test]
fn metric_prefix_format_is_not_subject_to_the_check() {
assert!(validate(ServiceNameFormat::MetricPrefix).is_ok());
}
}