///|
/// Errors raised when a registry cannot produce one internally consistent
/// snapshot. Gather is fail-closed: it never returns a partial exposition.
pub(all) suberror GatherError {
CollectorFailed(token~ : Int, message~ : String)
UndeclaredFamily(String)
FamilyMetadataMismatch(name~ : String, field~ : String)
InvalidFamilySamples(String)
InvalidLabels(String)
DuplicateSample(String)
InvalidHistogram(String)
} derive(Debug, Eq)
///|
fn labels_for_snapshot(labels : Array[@metric.Label]) -> Array[@metric.Label] {
canonical_labels(labels)
}
///|
fn compare_labels(
left : Array[@metric.Label],
right : Array[@metric.Label],
) -> Int {
let shared_length = if left.length() < right.length() {
left.length()
} else {
right.length()
}
for index in 0.. Unit raise GatherError {
let canonical = canonical_labels(labels)
let expected_names = descriptor.expected_label_names()
if canonical.length() != expected_names.length() {
raise InvalidLabels(descriptor.name)
}
for index in 0.. 0 && canonical[index - 1].name == canonical[index].name {
raise InvalidLabels(descriptor.name)
}
}
for constant in descriptor.const_labels {
let matched = canonical.search_by(label => label.name == constant.name)
match matched {
Some(index) =>
if canonical[index].value != constant.value {
raise InvalidLabels(descriptor.name)
}
None => raise InvalidLabels(descriptor.name)
}
}
}
///|
fn validate_histogram(
sample : @metric.HistogramSnapshot,
family_name : String,
) -> Unit raise GatherError {
if sample.boundaries.length() != sample.cumulative_counts.length() {
raise InvalidHistogram(family_name)
}
for index in 0.. 0 && sample.boundaries[index] <= sample.boundaries[index - 1] {
raise InvalidHistogram(family_name)
}
if sample.cumulative_counts[index] < 0L {
raise InvalidHistogram(family_name)
}
if index > 0 &&
sample.cumulative_counts[index] < sample.cumulative_counts[index - 1] {
raise InvalidHistogram(family_name)
}
}
if sample.count < 0L ||
(
sample.cumulative_counts.length() > 0 &&
sample.cumulative_counts[sample.cumulative_counts.length() - 1] >
sample.count
) {
raise InvalidHistogram(family_name)
}
}
///|
fn normalize_scalar_samples(
samples : Array[ScalarSampleSnapshot],
descriptor : DescriptorInfo,
) -> Array[ScalarSampleSnapshot] raise GatherError {
let result : Array[ScalarSampleSnapshot] = []
for sample in samples {
validate_labels(sample.labels, descriptor)
result.push(scalar_sample(labels_for_snapshot(sample.labels), sample.value))
}
result.sort_by((left, right) => compare_labels(left.labels, right.labels))
for index in 1.. Array[HistogramSampleSnapshot] raise GatherError {
let result : Array[HistogramSampleSnapshot] = []
for sample in samples {
validate_labels(sample.labels, descriptor)
validate_histogram(sample.value, descriptor.name)
result.push(
histogram_sample(labels_for_snapshot(sample.labels), sample.value),
)
}
result.sort_by((left, right) => compare_labels(left.labels, right.labels))
for index in 1.. DescriptorInfo? {
for descriptor in descriptors {
if descriptor.name == name {
return Some(descriptor)
}
}
None
}
///|
fn normalize_family(
family : FamilySnapshot,
descriptors : Array[DescriptorInfo],
) -> FamilySnapshot raise GatherError {
let descriptor = match descriptor_for_family(descriptors, family.name) {
Some(descriptor) => descriptor
None => raise UndeclaredFamily(family.name)
}
if family.help != descriptor.help {
raise FamilyMetadataMismatch(name=family.name, field="help")
}
if family.unit != descriptor.unit {
raise FamilyMetadataMismatch(name=family.name, field="unit")
}
if family.family_type != descriptor.family_type() {
raise FamilyMetadataMismatch(name=family.name, field="type")
}
match family.family_type {
CounterFamily | GaugeFamily => {
if !family.histogram_samples.is_empty() {
raise InvalidFamilySamples(family.name)
}
FamilySnapshot::scalar(
family.name,
family.help,
family.family_type,
normalize_scalar_samples(family.scalar_samples, descriptor),
family.unit,
)
}
HistogramFamily => {
if !family.scalar_samples.is_empty() {
raise InvalidFamilySamples(family.name)
}
FamilySnapshot::histogram(
family.name,
family.help,
normalize_histogram_samples(family.histogram_samples, descriptor),
family.unit,
)
}
}
}
///|
fn normalize_entry_families(
entry : CollectorEntry,
families : Array[FamilySnapshot],
) -> Array[FamilySnapshot] raise GatherError {
let descriptors = entry.descriptors()
let normalized : Array[FamilySnapshot] = []
for family in families {
for previous in normalized {
if previous.name == family.name {
raise InvalidFamilySamples(family.name)
}
}
normalized.push(normalize_family(family, descriptors))
}
normalized
}