prometheus / prometheus/client_rust

Feature Request: Add `#[prometheus(skip_encoding_if = "...")]` for conditional label encoding in EncodeLabelSet derive

Open
#284 2 comments 1 reaction 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

Is your feature request related to a problem? Please describe.

Currently, when using #[derive(EncodeLabelSet)], there is no straightforward way to conditionally skip a field from being encoded as a label based on its value. A common use case is for fields of type Option<T>, where you might only want to include the label if the value is Some(T) and omit it entirely if it's None.

Without this feature, an Option<String> field that is None gets encoded to an empty string, resulting in an output like my_metric{..., optional_label=""} 1. This can be misleading or add unnecessary cardinality/noise to the metrics.

The only current workaround is to manually implement EncodeLabelSet for the struct. This is verbose and negates the convenience of using the derive macro, especially for structs with many fields.

Describe the solution you'd like

I propose adding a new helper attribute, #[prometheus(skip_encoding_if = "path::to::function")], that can be applied to fields within a struct deriving EncodeLabelSet.

This attribute would work as follows:

  • It accepts a string literal containing a path to a function.
  • The derive macro will generate code that calls this function, passing it a reference to the field's value.
  • The function must have a signature like fn(&T) -> bool, where T is the type of the field.
  • If the function returns true, the field is completely skipped during label encoding.
  • If it returns false, the field is encoded as usual.

This provides a flexible and powerful way to control label generation. The most obvious use case is with Option::is_none.

Example:

use prometheus_client::encoding::EncodeLabelSet;

#[derive(Clone, Debug, Hash, PartialEq, Eq, EncodeLabelSet)]
pub struct RequestLabels {
    pub http_method: String,
    pub path: String,
    
    // This label should only be present if the value is `Some`.
    #[prometheus(skip_encoding_if = "Option::is_none")]
    pub user_id: Option<u64>,
}

// With a `user_id` of `Some(123)`, the output would be:
// http_requests_total{http_method="GET",path="/api/v1/users",user_id="123"} 1

// With a `user_id` of `None`, the desired output would be:
// http_requests_total{http_method="GET",path="/api/v1/users"} 1
// (The `user_id` label is omitted entirely, not just empty.)

This functionality would be similar to the #[serde(skip_serializing_if = "...")] attribute in serde, which is a well-understood and highly useful pattern in the Rust ecosystem. Implementing this would involve extending the EncodeLabelSet derive macro to parse this new attribute and wrap the encoding logic for the specific field in a conditional block.

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 locating the EncodeLabelSet derive macro, then read how its field attributes are parsed and how each field is encoded. Add support for skip_encoding_if with a function path and verify that matching fields are omitted while others retain normal encoding behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
observability-sre
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.