Skip to content

Commit da7458f

Browse files
committed
Generate units according to OTel required spec
This commit adds a generator for UCUM units and replaces the purely random strings we were using previously. We use an explicitly enumeraged table which won't work forever but probably will work for many years until we learn otherwise. Note that UCUM specifies a number of units do not produce. REF SMPTNG-659 Signed-off-by: Brian L. Troutwine <brian.troutwine@datadoghq.com>
1 parent eae62f5 commit da7458f

File tree

3 files changed

+57
-6
lines changed

3 files changed

+57
-6
lines changed

lading_payload/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ pub enum Error {
8181
/// See [`weighted::Error`]
8282
#[error(transparent)]
8383
Weights(#[from] weighted::Error),
84+
/// See [`unit::Error`]
85+
#[error(transparent)]
86+
Unit(#[from] opentelemetry_metric::unit::Error),
8487
}
8588

8689
/// To serialize into bytes

lading_payload/src/opentelemetry_metric.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
// * value: enum { u64, f64 } -- the value
3939
// * flags: uu32 -- I'm not sure what to make of this yet
4040

41+
pub(crate) mod unit;
42+
4143
use std::io::Write;
4244
use std::rc::Rc;
4345

@@ -55,6 +57,7 @@ use rand::{
5557
seq::IndexedRandom,
5658
};
5759
use serde::{Deserialize, Serialize as SerdeSerialize};
60+
use unit::UnitGenerator;
5861

5962
/// Configure the OpenTelemetry metric payload.
6063
#[derive(Debug, Default, Deserialize, SerdeSerialize, Clone, PartialEq, Copy)]
@@ -247,6 +250,7 @@ impl<'a> Generator<'a> for ScopeGenerator {
247250
pub(crate) struct MetricGenerator {
248251
metric_weights: WeightedIndex<u16>,
249252
attributes_per_metric: ConfRange<u32>,
253+
unit_generator: UnitGenerator,
250254
str_pool: Rc<strings::Pool>,
251255
}
252256

@@ -259,6 +263,7 @@ impl MetricGenerator {
259263
Ok(Self {
260264
str_pool: Rc::clone(str_pool),
261265
metric_weights: WeightedIndex::new(member_choices)?,
266+
unit_generator: UnitGenerator::new(),
262267
attributes_per_metric: config.contexts.attributes_per_metric,
263268
})
264269
}
@@ -289,12 +294,12 @@ impl<'a> Generator<'a> for MetricGenerator {
289294
.of_size_range(rng, 1_u8..16)
290295
.ok_or(Error::StringGenerate)?;
291296

292-
// TODO this is not correct and must be fixed to accord with http://unitsofmeasure.org/ucum.html.
293-
let unit = self
294-
.str_pool
295-
.of_size_range(rng, 1_u8..16)
296-
.ok_or(Error::StringGenerate)?;
297-
297+
// Units are optional, "" is the None unit.
298+
let unit = if rng.random_bool(0.1) {
299+
self.unit_generator.generate(rng)?
300+
} else {
301+
""
302+
};
298303
let count = self.attributes_per_metric.sample(rng);
299304
let metadata = generate_attributes(&self.str_pool, rng, count)?;
300305

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//! Code to generate units according to
2+
//! <http://unitsofmeasure.org/ucum.html>. This may be generally
3+
//! useful in the project and is not specifically tied to the OpenTelemetry metrics
4+
//! implementation.
5+
6+
// The spec defines a fairly elaborate grammar. It's unclear that we need _all_
7+
// that for our purposes, so for now we rely on a simple table method. I imagine
8+
// we can just keep stuffing the table for a while as seems desirable.
9+
10+
const UNITS: &[&str] = &[
11+
"bit", "Kbit", "Mbit", "Gbit", // data size, bits, decimal
12+
"Kibit", "Mibit", "Gibit", // data size, bits, binary
13+
"By", "KBy", "MBy", "GBy", // data size, bytes, decimal
14+
"KiBy", "MiBy", "GiBy", // data size, bytes, binary
15+
"By/s", "KiBy/s", "bit/s", "Mbit/s", // transfer rate
16+
"s", "ms", "us", "ns", // time
17+
"W", "kW", "kWh", // power
18+
];
19+
20+
/// Errors related to generation
21+
#[derive(thiserror::Error, Debug, Clone, Copy)]
22+
pub enum Error {}
23+
24+
#[derive(Debug, Clone, Copy)]
25+
pub(crate) struct UnitGenerator {}
26+
27+
impl UnitGenerator {
28+
pub(crate) fn new() -> Self {
29+
Self {}
30+
}
31+
}
32+
33+
impl crate::Generator<'_> for UnitGenerator {
34+
type Output = &'static str;
35+
type Error = Error;
36+
37+
fn generate<R>(&self, rng: &mut R) -> Result<Self::Output, Self::Error>
38+
where
39+
R: rand::Rng + ?Sized,
40+
{
41+
Ok(UNITS[rng.random_range(0..UNITS.len())])
42+
}
43+
}

0 commit comments

Comments
 (0)