prometheus / prometheus/client_rust

[IDEA]: Add helper for implementing `EncodeLabelSet` manually

Open
#288 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
606
Forks
113
Avg merge
9h 7m
Merged PRs (30d)
8

Description

Problem

Manually implementing EncodeLabelSet for a struct currently involves a lot of boilerplate:

pub struct MyLabels {
    pub method: &'static str,  // or String, Cow<'static, str>, etc.
    pub status: u16
   // many other fields
}

impl EncodeLabelSet for MyLabels {
    fn encode(&self, encoder: &mut LabelSetEncoder) -> Result<(), fmt::Error> {
        // label: method = self.method
        {
            let mut label = encoder.encode_label();
            let mut key = label.encode_label_key()?;
            // field name -> key string (handle raw-idents manually if you used them)
            EncodeLabelKey::encode("method", &mut key)?;

            let mut value = key.encode_label_value()?;
            EncodeLabelValue::encode(&self.method, &mut value)?;
            value.finish()?;
        }
        // label: status = self.status
        {
            let mut label = encoder.encode_label();
            let mut key = label.encode_label_key()?;
            EncodeLabelKey::encode("status", &mut key)?;
            let mut value = key.encode_label_value()?;
            EncodeLabelValue::encode(&self.status, &mut value)?;
            value.finish()?;
        }

        // repeated for all other fields

        Ok(())
    }
}

This can become a chore if you have to implement it manually (e.g. because you want conditional encoding).

Solution

A new LabelSetWriter struct that allows you to easily specify (key, value) pairs to encode.

Usage
 pub struct MyLabels {
     pub status: u16,
     pub method: &'static str,
     pub optional: Option<&'static str>,
     // suppose we want to flatten these (like `#[prometheus(flatten)]`)
     pub common: CommonLabels
 }

 impl EncodeLabelSet for MyLabels {
     fn encode(&self, enc: &mut LabelSetEncoder) -> Result<(), fmt::Error> {
         let mut writer = LabelSetWriter::new(enc);
             .kv("status", &self.status)?
             .kv("method", &self.method)?;

         // allows for conditional encoding
         if let Some(optional) = &self.optional {
             writer.kv("optional", optional)?;
         }
        // allows flattening
         writer.flatten(&self.common)?;

         Ok(())
     }
 }

Discussion

I've written the code for LabelSetWriter and it's helpful for me in reducing boilerplate.
Would anyone else find this useful? I'm open to making a PR if so.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing the existing EncodeLabelSet and LabelSetEncoder APIs, then compare the proposed LabelSetWriter usage for key/value encoding, conditional fields, and flattening. Done would require an agreed scope for the helper and validation that the proposed API fits the library's existing label-encoding model.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
observability-sre
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.