-
Notifications
You must be signed in to change notification settings - Fork 101
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
, whereT
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.