Skip to content

feat: add support for multiple enforced labels - #400

Open
lukasboettcher wants to merge 1 commit into
prometheus-community:mainfrom
lukasboettcher:feat/multiple-labels
Open

feat: add support for multiple enforced labels#400
lukasboettcher wants to merge 1 commit into
prometheus-community:mainfrom
lukasboettcher:feat/multiple-labels

Conversation

@lukasboettcher

@lukasboettcher lukasboettcher commented Jul 22, 2026

Copy link
Copy Markdown

This PR adds support for enforcing multiple ExtractLabelers in a single prom-label-proxy instance.
The goal is to keep backwards compatibility for the CLI and API interface.

Multiple ExtractLabelers can be created by specifying --label and --query-param or --header-name arguments multiple times. The ordering is important to associate labels with their value sources. The --label-value flag is excluded from this feature due to the ambiguity of mapping static label values to multiple labels.

Disclaimer: this PR was created with the help of AI and manually refined

fixes: #190

@simonpasquier simonpasquier left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for starting this. 2 remarks

  • Let's try to change only what's required. I get that help text could get better and be reworded but it should be part of a separate PR.
  • I think that we should improve the proposed UX.

Comment thread examples/prometheus/compose.yaml Outdated
Comment thread examples/prometheus/prometheus.yml Outdated
Comment thread README.md Outdated
-insecure-listen-address 127.0.0.1:8080
```

The same positional pairing applies when repeating `-query-param` instead of

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO it isn't a very good user experience as it's easy to get wrong. Maybe we've reached the limit of what we can configure using CLI arguments and need to support file-based configuration?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about that and I agree that the positional pairing would be easy to get wrong. I didn't want to change any existing or introduce any new config params, but a new file based config would be the cleanest imo.

I can rework the PR to include a file based config.

@SuperQ

SuperQ commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

This needs a rebase

@lukasboettcher

Copy link
Copy Markdown
Author

rebased onto main, and added a file-based configuration for multiple enforced labels. the logic for the old cli arguments remains unchanged.

@lukasboettcher
lukasboettcher force-pushed the feat/multiple-labels branch 2 times, most recently from 917cb5e to 3f4a367 Compare July 31, 2026 22:40
Comment thread injectproxy/config.go
}

// LabelConfig declares a single enforced label and the source of its values.
// Exactly one of Header, QueryParam or Values must be set.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a use case where someone would need to mix the source of enforced label values? E.g. label foo comes from the query parameters and label bar comes from HTTP headers. Maybe static values are a legit case though.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in our case we have one prom-label-proxy instance for a sensitive label enforcement like X-Tenant and another chained instance with query parameters where the user can apply their own filtering in the grafana datasource for a datacenter label. it costs nothing and I couldn't think of a reason to restrict this in the config.

Comment thread injectproxy/config.go Outdated
Comment thread injectproxy/silences.go
func assertSingleLabelValue(next http.HandlerFunc) http.HandlerFunc {
// assertSingleLabelValue verifies that each enforced label has only one value.
// If not, it will reply with "422 Unprocessable Content".
func (r *routes) assertSingleLabelValue(next http.HandlerFunc) http.HandlerFunc {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this change?

@lukasboettcher lukasboettcher Aug 4, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assertSingleLabelValue needs access to all r.labels so making it a method made sense to me.

Comment thread injectproxy/enforce.go Outdated
for _, enforcedMatcher := range ms.labelMatchers {
res = append(res, enforcedMatcher)
// Iterate in a deterministic order so that the injected matchers are
// always rendered the same way.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please make it a separate PR

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually on second thought, I don't believe this is needed at all. this is sorted in the prometheus parser anyway

Comment thread injectproxy/routes.go Outdated
//
// The label and extractLabeler arguments declare the first enforced label. They
// may be left empty and nil when the labels are declared with
// WithEnforcedLabel() or WithConfig() instead.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I understand and appreciate the attention towards downstream users I feel that it complicates the API.
Can we have a separate constructor maybe and NewRoutes becoming a shim?

func NewRoutesWithLabelers(upstream *url.URL, labelers []LabelEnforcer, opts ...Option) {
...
}

NewRoutes(upstream *url.URL, labelers []LabelEnforcer, opts ...Option) {
   // build labelers list.
   ...
   return NewRoutesWithLabelers(...)
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i will add NewRoutesWithLabelers and removed WithEnforcedLabel and WithConfig

Comment thread injectproxy/routes.go Outdated
Comment thread injectproxy/routes.go
Signed-off-by: Lukas Boettcher <1340215+lukasboettcher@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow different label names injected by an ExtractLabeler

3 participants