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

Rename labelvalues to _labelvalues to make clear that it's internal #398

Closed
bm371613 opened this issue Apr 13, 2019 · 16 comments · Fixed by #660
Closed

Rename labelvalues to _labelvalues to make clear that it's internal #398

bm371613 opened this issue Apr 13, 2019 · 16 comments · Fixed by #660

Comments

@bm371613
Copy link

Metrics with labelvalues are not collected. In the example below, I would expect all 3 counters to be collected, but the one with labelvalues is not.

from prometheus_client import REGISTRY, Counter

c1 = Counter("c1", "c1")
c2 = Counter("c2", "c2", labelnames=["l1"])
c3 = Counter("c3", "c3", labelnames=["l1"], labelvalues=["v1"])

c1.inc()
c2.labels("v1").inc()
c3.inc()

for item in REGISTRY.collect():
    print(item)  # prints c1 and c2, but not c3
@brian-brazil
Copy link
Contributor

That should be producing an exception, that's invalid usage.

@bm371613
Copy link
Author

bm371613 commented Apr 13, 2019 via email

@brian-brazil
Copy link
Contributor

@bm371613
Copy link
Author

bm371613 commented Apr 13, 2019 via email

@brian-brazil
Copy link
Contributor

Non-existent features tend not to be documented.

@bm371613
Copy link
Author

bm371613 commented Apr 13, 2019 via email

@dash-rai
Copy link

dash-rai commented Nov 20, 2019

It is an argument of a constructor of a public class that is not documented.

I agree with @bm371613 here. It is easily assumed that labelvalues is meant for accepting default values for labels, and there is no indication that this is wrong -- like @brian-brazil said, it doesn't raise an exception.

I'm not sure what the intended use of labelvalues is, but it would be a useful feature to have the ability to set "default" labels or a base set of labels for all child metrics. Support for chaining calls to .labels() would probably be required too.

@ewtoombs
Copy link

ewtoombs commented Jan 14, 2020

labelvalues is for internal use only. It is used in the MetricWrapperBase.labels() method to create a new MetricWrapperBase with labelvalues set. Since the metric was already registered, there's an if statement in MetricWrapperBase's constructor that prevents registration of the metric if labelvalues is set.

So, that's why you're seeing the behaviour that you are. The user was never supposed to use the labelvalues argument of MetricWrapperBase in the first place.

This is terrible and it should be fixed! One way to do it is add an internal argument, _register=True, to MetricWrapperBase. Then, MetricWrapperBase.labels() initiates the child MetricWrapperBase with _register=False. Then, labelvalues will have the obvious and assumed behaviour.

Incidentally, if this is what you want, for now, initialise c3 like this instead, as a workaround:

c3 = Counter('c3', 'c3', labelnames=['l1']).labels('v1')

@jonerer
Copy link

jonerer commented May 20, 2020

IMO the current situation is quite confusing and quite time consuming. What do you feel about adding a couple of lines in the documentation to warn about this argument? Or perhaps just a runtime warning for users?

@brian-brazil
Copy link
Contributor

I'm not sure how changing the user docs will help if users are ignoring the docs and instead digging into the source and using undocumented internal APIs.

@jonerer
Copy link

jonerer commented May 21, 2020

In this case it's not clear that it's an internal API. Labelvalues is a kwarg on a public constructor, and it looks like it fills a need that the end user often feels (setting a default value for a Gauge's labels). If it's only meant to be internal then yeah maybe you're right, the best place to make hanger might not be in the documentation.

Renaming it by prepending a "_" is a common pythonic way of marking something as internal. Maybe that?

@brian-brazil
Copy link
Contributor

That sounds reasonable.

I'll note that a default value for a label doesn't really make sense, you need to have code to provide the actual label value one way or the other.

@belm0
Copy link

belm0 commented Jul 8, 2020

Having a static set of known label sets is the common (actually only) case for us. We want to register the known label sets at metric declaration time, with the least amount of fuss.

I've come up with this wrapper, but really I'd like to see a labelsets option in the metric constructors.

def init_labelsets(metric, labelsets: Iterable[Dict]):
    """Initialize known label sets of a Prometheus metric

    For a metric without labels, the client exports an initial value as
    expected (0 for Counter, etc.).  But when a metric has labels, the client
    cannot know the label values and combinations in advance.  This function
    can register known label sets so that the initial value is exported.

    Suggested use is as a wrapper of the metric declaration:

    >>> metric = init_labelsets(
    ...     prometheus_client.Counter('foo', 'count of foo', ['type']),
    ...     (dict(type=v) for v in ('A', 'B', 'C'))
    ... )
    >>> list(metric._samples())
    [('_total', {'type': 'A'}, 0.0),
     ('_total', {'type': 'B'}, 0.0),
     ('_total', {'type': 'C'}, 0.0)]

    Example with a label combination:

    >>> metric2 = init_labelsets(
    ...     prometheus_client.Counter('bar', 'count of bar', ['label1', 'label2']),
    ...     (dict(label1=v1, label2=v2) for v1 in ('A', 'B') for v2 in ('0', '1'))
    ... )
    >>> list(metric2._samples())
    [('_total', {'label1': 'A', 'label2': '0'}, 0.0),
     ('_total', {'label1': 'A', 'label2': '1'}, 0.0),
     ('_total', {'label1': 'B', 'label2': '0'}, 0.0),
     ('_total', {'label1': 'B', 'label2': '1'}, 0.0)]
    """
    for labelset in labelsets:
        metric.labels(**labelset)
    return metric

@brian-brazil
Copy link
Contributor

@belm0 That is unrelated to this issue.

@belm0
Copy link

belm0 commented Jul 8, 2020

It's related because the people attempting to use labelvalues are wanting precisely this functionality, as the OP concedes.

@brian-brazil
Copy link
Contributor

No, they're not looking for initialisation of childs. Please don't confuse the issue.

@brian-brazil brian-brazil changed the title Metrics with labelvalues are not collected Rename labelvalues to _labelvalues to make clear that it's internal Jan 26, 2021
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 a pull request may close this issue.

6 participants