-
Notifications
You must be signed in to change notification settings - Fork 801
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
Allow to add labels inside a context manager #730
Conversation
This way labels that depend on the result of the measured operation can be added more conveniently, e.g. the status code of an http request: from prometheus_client import Histogram from requests import get teapot = Histogram('teapot', 'A teapot', ['status']) with teapot.time() as metric: response = get('https://httpbin.org/status/418') metric.labels(status=response.status_code) Signed-off-by: Andreas Zeidler <andreas.zeidler@zeit.de>
For this to work the 'observability' check needs to be deferred as well, in case a label is added inside the context manager thereby making the metric observable. Signed-off-by: Andreas Zeidler <andreas.zeidler@zeit.de>
6544542
to
2b57f24
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR! I think the new behavior is certainly helpful, but I am not sure of the implementation yet. Happy to brainstorm on some ways to avoid __self__
and __name__
if possible.
self._callback(duration) | ||
|
||
def labels(self, *args, **kw): | ||
instance = self._callback.__self__ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't love the references back to __self__
and __name__
. I wonder if we could avoid that, but haven't come up with a great way yet. Perhaps a second variable to __init__
could help?
I suppose an easy change would be to pass both the metric instance and the name of the callback method when initializing Alternatively, an alias like Then again, it's only names. Slightly funny looking perhaps, but we use |
I definitely think passing the metric instance is nicer, and will work nicely with types hints as they are added. Perhaps it should be called with |
Agreed, but I think passing If that's okay with you, I can happily adjust the code tomorrow? 🙂 |
Ahh, right that makes sense. I think the callback name for now sounds good, we can always change it if it becomes a problem to maintain and the new functionality will be nice. |
This should make the code slightly more readable. Signed-off-by: Andreas Zeidler <andreas.zeidler@zeit.de>
d985bb3
to
4f400d0
Compare
Yesterday was too "full", but I think now it's really a bit clearer. And I suppose I should configure my git to sign off by default… 😅 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One small comment otherwise 👍.
You mentioned updating track_inprogress
as well, I don't think that would make sense as we inc
on __enter__
which means we would need to know any labels upfront. Am I missing something though?
|
||
def __exit__(self, typ, value, traceback): | ||
# Time can go backwards. | ||
duration = max(default_timer() - self._start, 0) | ||
self._callback(duration) | ||
self._metric._raise_if_not_observable() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually don't think this is necessary as set
or observe
will already call _raise_if_not_observable
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I was trying to "make up" for the call on the way in, essentially making sure that it's still called at all, but I didn't realize that the callbacks already do so anyway… 🙂
True. I had just spotted that (other) context manager, but not looked into the details yet. But yes, that wouldn't work, of course. |
The callbacks are already taking care of this anyway. Signed-off-by: Andreas Zeidler <andreas.zeidler@zeit.de>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
This is an attempt to use
time()
as a context manager, but still be able to add labels with values that depend on the result of the timed operation. In our case we'd like to monitor the duration of a number of web requests, but still distinguish/label them by the returned HTTP error code. It didn't seem possible to use the providedtime()
helper in this case, but instead we would have to measure the time ourselves and then use something likemetric.labels(status=status_code).observe(duration)
.Being able to add the label inside the context manager seemed a bit more convenient:
This should now work with all three
time()
helpers. However, the 'observability' checks for 'gauge' and 'summary' metrics had to be deferred (to their respective__exit__
methods; see 2b57f24), because there is no way to tell upfront if a label will in fact be added, making the metric 'observable'.I hope that's an acceptable change. If so, the
track_inprogress()
helper should probably also be changed accordingly.