-
Notifications
You must be signed in to change notification settings - Fork 815
UTF-8 Content Negotiation #1102
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
base: master
Are you sure you want to change the base?
Conversation
prometheus_client/exposition.py
Outdated
@@ -37,7 +37,7 @@ | |||
'write_to_textfile', | |||
) | |||
|
|||
CONTENT_TYPE_LATEST = 'text/plain; version=0.0.4; charset=utf-8' | |||
CONTENT_TYPE_PLAIN = 'text/plain; version=0.0.4; charset=utf-8' |
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.
text/plain v 0.0.4 is not the latest, so I thought it best to rename this
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.
Though I agree with you, this will be a breaking change as other programs use CONTENT_TYPE_LATEST
. Perhaps we should update this to version=1.0.0 instead?
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.
Since this is needed for compatibility, but LATEST and PLAIN are both really different versions of PLAIN that is confusing for external users to use. I'd propose a couple options:
- Keep
CONTENT_TYPE_PLAIN
internal, so rename it to_CONTENT_TYPE_PLAIN
. - Support content types for each version, so have
CONTENT_TYPE_PLAIN_0_4_0
,CONTENT_TYPE_PLAIN_1_0_0
, and then aliasCONTENT_TYPE_LATEST
toCONTENT_TYPE_PLAIN_1_0_0
.
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 like option 2
TODO need more openmetrics/exposition tests |
33ea581
to
630fb65
Compare
Signed-off-by: Owen Williams <owen.williams@grafana.com>
prometheus_client/exposition.py
Outdated
@@ -37,7 +37,7 @@ | |||
'write_to_textfile', | |||
) | |||
|
|||
CONTENT_TYPE_LATEST = 'text/plain; version=0.0.4; charset=utf-8' | |||
CONTENT_TYPE_PLAIN = 'text/plain; version=0.0.4; charset=utf-8' |
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.
Though I agree with you, this will be a breaking change as other programs use CONTENT_TYPE_LATEST
. Perhaps we should update this to version=1.0.0 instead?
tools/simple_client.py
Outdated
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.
Is this used anywhere? Or just a nice example for testing locally?
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.
just for testing locally
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.
but is also pretty nice for people to get off the ground
we do still need a 0.0.4 constant as the fallback when all else fails. What I could do is update LATEST to be 1.0.0 and then create the new PLAIN constant for 0.0.4 |
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.
Generally looks to be getting there! A few comments mostly around the content types still. CONTENT_TYPE_LATEST from just exposition.py (not open metrics) doesn't actually seem to be used?
prometheus_client/exposition.py
Outdated
@@ -37,7 +37,7 @@ | |||
'write_to_textfile', | |||
) | |||
|
|||
CONTENT_TYPE_LATEST = 'text/plain; version=0.0.4; charset=utf-8' | |||
CONTENT_TYPE_PLAIN = 'text/plain; version=0.0.4; charset=utf-8' |
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.
Since this is needed for compatibility, but LATEST and PLAIN are both really different versions of PLAIN that is confusing for external users to use. I'd propose a couple options:
- Keep
CONTENT_TYPE_PLAIN
internal, so rename it to_CONTENT_TYPE_PLAIN
. - Support content types for each version, so have
CONTENT_TYPE_PLAIN_0_4_0
,CONTENT_TYPE_PLAIN_1_0_0
, and then aliasCONTENT_TYPE_LATEST
toCONTENT_TYPE_PLAIN_1_0_0
.
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.
When everything is specified it seems to work well, though I ran into a few edge cases that need fixes and testing.
prometheus_client/exposition.py
Outdated
escaping = _get_escaping(toks) | ||
# Only return an escaping header if we have a good version and | ||
# mimetype. | ||
if Version(version) >= Version('1.0.0'): |
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.
If no version
is specified, e.g. Accept: application/openmetrics-text
, this is throwing an packaging.version.InvalidVersion
due to version being an empty string.
We should have a tests for sure, but also what version would we want to use in this case? I guess LATEST for each should be fine?
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 believe the correct behavior is to always fall back to 0.0.4 rather than use the latest (https://github.com/prometheus/docs/blob/main/docs/instrumenting/content_negotiation.md#selection-of-format)
Python doesn't seem to support open metrics 0.0.4 so I believe we have to fall back to prometheus plain there too.
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.
Added some test cases
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.
Specifying Accept: application/openmetrics-text
then changing the format all the way to prometheus plain just because of no version doesn't seem correct, though perhaps that doc needs updating? From https://github.com/prometheus/OpenMetrics/blob/main/specification/OpenMetrics.md#protocol-negotiation it sounds like we should fall back to 1.0.0 if any openmetrics-text format is requested and there is no version information/the version information is invalid.
prometheus_client/exposition.py
Outdated
# Only return an escaping header if we have a good version and | ||
# mimetype. | ||
if Version(version) >= Version('1.0.0'): | ||
return (openmetrics.generate_latest_fn(escaping), |
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.
This returns an openmetrics payload wrapped inside of text/plain
which is not correct, it needs to be calling generate_latest
in this module instead. I'd suggest using partial(generate_latest, escaping=escaping)
from functools
.
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.
hm is there some way we can add a test for that? It seems difficult to have to generate lines just to confirm the function returned is the right one
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.
You could probably use mocks to avoid generating lines and then parsing them appropriately. See https://docs.python.org/3/library/unittest.mock.html#where-to-patch for how to patch a method in a module. Just checking for the presence of # EOF
at the end of the output could also tell the difference between plain and openmetrics if you do generate lines.
Part of #1013
The remaining piece is configuration to force escaping even if a scraper does not request it