Skip to content

Commit 372291e

Browse files
committed
chore: lazily add a comma if needed before writing family labels
1 parent f89b6f4 commit 372291e

File tree

3 files changed

+32
-10
lines changed

3 files changed

+32
-10
lines changed

examples/custom-metric.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use prometheus_client::encoding::{text::encode, EncodeMetric, MetricEncoder};
1+
use prometheus_client::encoding::{text::encode, EncodeMetric, MetricEncoder, NoLabelSet};
22
use prometheus_client::metrics::MetricType;
33
use prometheus_client::registry::Registry;
44

@@ -20,7 +20,7 @@ impl EncodeMetric for MyCustomMetric {
2020
// E.g. every CPU cycle spend in this method delays the response send to
2121
// the Prometheus server.
2222

23-
encoder.encode_counter::<(), _, u64>(&rand::random::<u64>(), None)
23+
encoder.encode_counter::<NoLabelSet, _, u64>(&rand::random::<u64>(), None)
2424
}
2525

2626
fn metric_type(&self) -> prometheus_client::metrics::MetricType {

src/encoding.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,8 @@ pub trait EncodeLabel {
248248
pub struct LabelEncoder<'a>(LabelEncoderInner<'a>);
249249

250250
/// Uninhabited type to represent the lack of a label set for a metric
251-
pub(crate) enum NoLabelSet {}
251+
#[derive(Debug)]
252+
pub enum NoLabelSet {}
252253

253254
#[derive(Debug)]
254255
enum LabelEncoderInner<'a> {

src/encoding/text.rs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -512,18 +512,39 @@ impl<'a> MetricEncoder<'a> {
512512
additional_labels.encode(LabelSetEncoder::new(self.writer).into())?;
513513
}
514514

515-
if let Some(labels) = &self.family_labels {
516-
let mut string_writer = String::new();
517-
labels.encode(LabelSetEncoder::new(&mut string_writer).into())?;
515+
/// Writer impl which prepends a comma on the first call to write output to the wrapped writer
516+
struct CommaPrependingWriter<'a> {
517+
writer: &'a mut dyn Write,
518+
should_prepend: bool,
519+
}
518520

519-
if !string_writer.is_empty() {
520-
if !self.const_labels.is_empty() || additional_labels.is_some() {
521-
self.writer.write_str(",")?;
521+
impl Write for CommaPrependingWriter<'_> {
522+
fn write_str(&mut self, s: &str) -> std::fmt::Result {
523+
if self.should_prepend {
524+
self.writer.write_char(',')?;
525+
self.should_prepend = false;
522526
}
523-
self.writer.write_str(string_writer.as_str())?;
527+
self.writer.write_str(s)
524528
}
525529
}
526530

531+
if let Some(labels) = self.family_labels {
532+
// if const labels or additional labels have been written, a comma must be prepended before writing the family labels.
533+
// However, it could be the case that the family labels are `Some` and yet empty, so the comma should _only_
534+
// be prepended if one of the `Write` methods are actually called when attempting to write the family labels.
535+
// Therefore, wrap the writer on `Self` with a CommaPrependingWriter if other labels have been written and
536+
// there may be a need to prepend an extra comma before writing additional labels.
537+
if !self.const_labels.is_empty() || additional_labels.is_some() {
538+
let mut writer = CommaPrependingWriter {
539+
writer: self.writer,
540+
should_prepend: true,
541+
};
542+
labels.encode(LabelSetEncoder::new(&mut writer).into())?;
543+
} else {
544+
labels.encode(LabelSetEncoder::new(self.writer).into())?;
545+
};
546+
}
547+
527548
self.writer.write_str("}")?;
528549

529550
Ok(())

0 commit comments

Comments
 (0)