Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Easy way to initialize counter vecs? #190

Open
beorn7 opened this issue Jan 21, 2016 · 5 comments
Open

Easy way to initialize counter vecs? #190

beorn7 opened this issue Jan 21, 2016 · 5 comments

Comments

@beorn7
Copy link
Member

beorn7 commented Jan 21, 2016

See code like https://github.com/prometheus/prometheus/blob/b0adfea8d5e883cbab5c7e4113f8ea91f7cbc099/rules/manager.go#L75-L78 .

If you know in advance all the values each label can take, it is recommended to initialize all combinations so that each possible counter is exported with zero as a value.

This could be a utility function/method or be done somehow during construction.

This is a concern not specific to client_golang. Other client libraries usually have the same issue.

@fabxc @brian-brazil Feel free to comment…

@beorn7 beorn7 self-assigned this Jan 21, 2016
@brian-brazil
Copy link
Contributor

Python and Java work the same way as Go for this.

@beorn7 beorn7 added this to the v0.9 milestone Nov 3, 2016
@beorn7
Copy link
Member Author

beorn7 commented Oct 4, 2017

I have an idea how to do this quite neatly. But it's a breaking change. I'll move this issue over to the v0.10 milestone and will flesh out as soon as I find some time.

@beorn7
Copy link
Member Author

beorn7 commented Jun 2, 2021

I mostly assigned this to myself as a “default” because I used to be the maintainer of this repo. Therefore, I'm un-assigning this from myself now. New maintainers @bwplotka & @kakkoyun, please deal with this as you see fit.

To get my aforementioned idea sketched out, at least vaguely (feel free to consult me for details, whoever might be working on this in the future):

Labels will be managed by a new label package. This will both be used for labels itself as well as label specs. The below are rough ideas for the interface. Needs refinement, and it all has to be done in a way that allows efficient implementations avoiding allocs etc.

func New(name, value string) Label creates a single label. There's a type []Label Labels with the contract of being sorted lexicographically and having no duplicates. It has a Normalize method to make sure of that. There is a convenience constructor func Set(nv ...string) Labels, which takes an even number of arguments (name1 value1 name2 value2 ...). They don't need to be sorted, but if they are, it will not cause allocations.

The above all implement an interface Spec. They are all full specs as they fully describe the label or labels. A func Names(names ...string) Spec is the least specific spec, as it only has the names. But then we can have “partial” specs that allow to specify all possible values a label might have: func WithValues(name string, values ...string) Spec. Perhaps additionally an “open” spec as well that specifies possible values but allows more than the listed ones: func WithIncompleteValues(name string, values ...string) Spec.

The constructor for a CounterVec would then look like func NewCounterVec(opts CounterOpts, labels ...label.Spec) *CounterVec (modulo other changes in v2). Example:

	httpAPIReqs := prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "http_requests_total",
			Help: "How many HTTP requests processed, partitioned by status code and HTTP method.",
		},
		label.WithValues("method", "GET", "POST"),
		label.WithIncompleteValues("code", "200", "404"),
		label.New("endpoint", "/api"), // Full spec, replaces old const labels.
	) // Automatically initializes children for GET/200, POST/200, GET/404, POST/404

	httpAPIReqs.With(label.Set("code", "200", "method", "GET")).Inc()  // Increments pre-initialized child.
	httpAPIReqs.With(label.Set("code", "503", "method", "GET")).Inc()  // Creates new child on the fly and increments it.
	httpAPIReqs.With(label.Set("code", "200", "method", "PUT")).Inc()  // Error because method="PUT" is not allowed.

@ivan-section-io
Copy link

FWIW in such situations - I've found myself with a helper function to address this:

// prometheusNewCounterVecZeroed creates prometheus.CounterVec with cartesian product of known labels zeroed
func prometheusNewCounterVecZeroed(opts prometheus.CounterOpts, labelNamesValues prometheusLabelNamesValues) *prometheus.CounterVec {
	labelNames := make([]string, 0, len(labelNamesValues))
	for i := range labelNamesValues {
		labelNames = append(labelNames, i)
	}

	counterVec := prometheus.NewCounterVec(opts, labelNames)
	indexes := make([]int, len(labelNames))
outer:
	for {
		labelValues := make([]string, len(labelNames))
		for i := range indexes {
			labelValues[i] = labelNamesValues[labelNames[i]][indexes[i]]
		}
		counterVec.WithLabelValues(labelValues...)
		for i := range indexes {
			indexes[i]++
			if indexes[i] == len(labelNamesValues[labelNames[i]]) {
				indexes[i] = 0
				continue
			}
			continue outer
		}
		break
	}
	return counterVec
}

It ain't pretty, is likely buggy, but makes Collector definition more readable until I discover how I "should" be doing it.

@beorn7
Copy link
Member Author

beorn7 commented Feb 3, 2023

Note that #1151 has paved the road towards auto-initialize vectors that have only constrained labels.

@dosubot dosubot bot added the stale label Aug 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants