-
Notifications
You must be signed in to change notification settings - Fork 844
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
Update autoconfigure to append signal path to otlp http endpoint if n… #3666
Update autoconfigure to append signal path to otlp http endpoint if n… #3666
Conversation
&& !endpoint.endsWith(signalPath)) { | ||
if (!endpoint.endsWith("/")) { | ||
endpoint += "/"; | ||
} |
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 took a little liberty from the spec here by appending a /
before the signal path if it doesn't exist.
A strict interpretation would cause http://localhost:4317/foo
to be transformed to http://localhost:4317/foov1/traces
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 think that's OK. If someone needs a path without slash (or something entirely different), they can configure it with the per-signal configuration.
Still, it should probably also be fixed in the spec.
Codecov Report
@@ Coverage Diff @@
## main #3666 +/- ##
============================================
+ Coverage 88.86% 89.57% +0.70%
- Complexity 3689 3784 +95
============================================
Files 442 450 +8
Lines 11562 11822 +260
Branches 1113 1134 +21
============================================
+ Hits 10275 10589 +314
+ Misses 906 858 -48
+ Partials 381 375 -6
Continue to review full report at Codecov.
|
String protocol = getOtlpProtocol(dataType, config); | ||
if (endpoint != null | ||
&& protocol.equals(PROTOCOL_HTTP_PROTOBUF) | ||
&& !endpoint.endsWith(signalPath)) { |
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 seems like it precludes someone pointing at a totally different endpoint of their own choosing. I'm not sure that's a good thing, since perhaps their pointing at a proxy or some such that is doing the mapping to the actual path?
If we want to force the signal path like this, I would recommend that we use URI.resolve(), rather than hand-crafting the URL to use. If we use that method, it doesn't matter what they've put on the end of their host/port...we'll use the one we know is right.
But, again, this seems dangerous and that it would break anyone who is doing something not 100% standard with their ingest.
Perhaps the best thing would be to construct a URI, then see if it has a path component, and leave it alone if it does, otherwise resolve it to the required signal-specific path.
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 only applies to otel.exporter.otlp.endpoint
configuration. No modification will be made to otel.exporter.otlp.traces.endpoint
or otel.exporter.otlp.metrics.endpoint
configuration. This seems to be the intention of the spec given the wording:
The per-signal endpoint configuration options take precedence and can be used to override this behavior.
I'm happy to use URI
to resolve the path. Did string concatenation because it was enough for the collector.
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. ok, I see how that probably makes sense since it probably wasn't really usable as-is, anyway. I think the URI.resolve() implementation is a little cleaner, since we don't have to worry about the trailing slashes, etc. Unless, of course, we think that people may want to pre-pend some sort of pre-path before the actual signal path? That's probably a very narrow edge case, though, and I'm not sure those people would have anything usable right now anyway.
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.
Pushed up a commit using URI.resolve()
instead of string concatenation. Personally, I prefer the string concatenation approach since URI required handling a checked exception and the behavior of .resolve(..)
isn't super intuitive. Don't feel strongly about 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.
I don't feel super strongly about it, either. Checking for validity seems like a positive thing from URI, though, doesn't it? And, yeah, resolve()
is a bit weird around slash-handling (which I honestly didn't remember to be the case when I suggested it).
assertThat(configureHttpProtobufEndpoint(DATA_TYPE_TRACES, "http://localhost:4317")) | ||
.isEqualTo("http://localhost:4317/v1/traces"); | ||
assertThat(configureHttpProtobufEndpoint(DATA_TYPE_TRACES, "http://localhost:4317/")) | ||
.isEqualTo("http://localhost:4317/v1/traces"); |
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.
Can you add a test for http://localhost:4317/collector/v1/traces
-> http://localhost:4317/collector/v1/traces
and http://localhost:4317/collector
-> http://localhost:4317/collector/v1/traces
?
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 think that would violate the spec. Configuring a per-signal endpoint should not append anything, so http://localhost:4317/collector
should stay as just that. As worded in the spec, the "/v1/traces" suffix is merely convention for the collector. If I have a backend that does not follow this convention, I should be able to override the per-signal endpoint with the per-signal configuration, without having anything changed in the URL.
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.
In the PR description and docs, you say you will append the per-signal path "if not present already". Do you mean "if the per-signal config is not already set"? If so, please clarify the wording.
assertThat(configureHttpProtobufEndpoint(DATA_TYPE_TRACES, "http://localhost:4317")) | ||
.isEqualTo("http://localhost:4317/v1/traces"); | ||
assertThat(configureHttpProtobufEndpoint(DATA_TYPE_TRACES, "http://localhost:4317/")) | ||
.isEqualTo("http://localhost:4317/v1/traces"); |
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 think that would violate the spec. Configuring a per-signal endpoint should not append anything, so http://localhost:4317/collector
should stay as just that. As worded in the spec, the "/v1/traces" suffix is merely convention for the collector. If I have a backend that does not follow this convention, I should be able to override the per-signal endpoint with the per-signal configuration, without having anything changed in the URL.
&& !endpoint.endsWith(signalPath)) { | ||
if (!endpoint.endsWith("/")) { | ||
endpoint += "/"; | ||
} |
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 think that's OK. If someone needs a path without slash (or something entirely different), they can configure it with the per-signal configuration.
Still, it should probably also be fixed in the spec.
...xtensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/OtlpConfigUtil.java
Outdated
Show resolved
Hide resolved
By "if not present already" I'm referring to the current wording of the spec which says that the signal specific path (i.e. I see you have a PR that changes the spec wording to say that the signal specific path MUST always be appended. That would change the behavior as follows:
I think the java SDK follow the behavior as currently worded until your spec PR is merged. I'm happy to update the behavior when that occurs. |
What will the metric export URL be with the current behavior if I specify If we interpret the spec very literally, it could also be |
This is the behavior of the
This is the behavior of the PR as it currently stands. |
It sounds like we definitely need some spec clarification on this. :) |
The commit I just pushed up now adds URL validation for endpoints that is more strict, but also more in inline with the spec, as I suggested I could do in this comment. It also has test cases that show:
I suggest that we don't add any more scope to this PR. |
.isEqualTo("http://localhost:4317/foo/v1/traces"); | ||
assertThat(configureHttpProtobufEndpoint(DATA_TYPE_TRACES, "http://localhost:4317/v1/traces")) | ||
.isEqualTo("http://localhost:4317/v1/traces"); | ||
assertThat(configureHttpProtobufEndpoint(DATA_TYPE_TRACES, "http://localhost:4317/v1/metrics")) |
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 a descriptive and good case to have here
The current wording of the OTLP endpoint config was confusing, especially the "if not present already" wording. Instead, clarify that we must always append when using the envvar for all signals (it was already clearly specified that the per-signal vars do not get the path appended). This came up in open-telemetry/opentelemetry-java#3650 and again at open-telemetry/opentelemetry-java#3666 (comment). Also make it a MUST (not SHOULD) since this kind of thing would be extremely annoying to have differences per-language in. Also, without appending, the variable cannot be used to configure more than one signal which would defeat its sole purpose.
assertThat(configureHttpProtobufEndpoint(DATA_TYPE_TRACES, "http://localhost:4317/v1/traces")) | ||
.isEqualTo("http://localhost:4317/v1/traces/v1/traces"); | ||
assertThat(configureHttpProtobufEndpoint(DATA_TYPE_TRACES, "http://localhost:4317/v1/metrics")) | ||
.isEqualTo("http://localhost:4317/v1/metrics/v1/traces"); |
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 one makes sense to me (I mean...it's almost certainly wrong, but it's what the user requested). However, the one where we add /v1/traces
when it's already on the end seems surprising to me. Yes, it's precisely what the user is requesting, but I suspect this will happen and users will complain. Are we were we don't want to check to make sure that the generic endpoint hasn't already had the /v1/<signal>
tacked onto it before duplicating it?
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 is exactly what the spec is forbidding. Better the users notice the error earlier than much later when they have deployed that config everywhere and want to start using metrics and it ends up at /v1/traces/v1/metrics
.
If you suggest that the configuration should be "smart" and detect any signal path (e.g., replacing /v1/metrics
with /v1/traces
for traces, leaving it in-place for metrics) I suggest a spec PR. I wouldn't be against it, if we can manage to log some warning at least...
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.
Maybe I misunderstood and you meant the signal-specific thing? That seems like a bug indeed. I'll comment above. #3666 (comment)
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.
No, I was suggesting that if the user provides a trace endpoint as the "generic" endpoint, that we don't mess with it for traces, since it's likely(?) that they are only using tracing, and don't care about metrics and what endpoint might have been populated for it.
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.
That's currently against the spec and I think delaying the error until the point in time where the user uses any other signal is not good. It would be better to ensure that the (404?) error that occurs with the doubled signal path is easy to find (e.g. by logging).
I could be convinced of the "fully smart" method, stripping off any known trailing signal path from the generic URL, but making only the configured path work seems to be a bad balance.
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.
ok. I understand that side of the argument. This is potentially a breaking change for some users, so we should be very sure to call it out very clearly in our release notes/CHANGELOG. @jack-berg Can you craft a CHANGELOG entry for this PR? Thanks!
assertThat(configureHttpProtobufEndpoint(DATA_TYPE_TRACES, "http://localhost:4317/v1/traces")) | ||
.isEqualTo("http://localhost:4317/v1/traces/v1/traces"); |
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.
assertThat(configureHttpProtobufEndpoint(DATA_TYPE_TRACES, "http://localhost:4317/v1/traces")) | |
.isEqualTo("http://localhost:4317/v1/traces/v1/traces"); | |
assertThat(configureHttpProtobufEndpoint(DATA_TYPE_TRACES, "http://localhost:4317/v1/traces")) | |
.isEqualTo("http://localhost:4317/v1/traces"); |
The spec mandates that signal-specific URLs are left alone and /v1/traces is not appended. What if you have an endpoint that needs an URL not ending in that? I don't think the OTLP spec mandates a particular URL path.
EDIT: That aspect did not change through my PR BTW.
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.
The per-signal endpoint configuration options take precedence and can be used to override this behavior (the URL is used as-is for them, without any modifications).
and
- For the per-signal variables (OTEL_EXPORTER_OTLP__ENDPOINT), the URL MUST be used as-is without any modification. The only exception is that if an URL contains no path part, the root path / MUST be used (see Example 2).
https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#endpoint-urls-for-otlphttp (and table above)
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.
OK, maybe I misunderstand what the test does because the implementation at L56 in sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/OtlpConfigUtil.java
looks like it's doing the right thing, not appending the signal path for the per-signal config.
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.
yes, it's quite hard to figure out what cases are being tested in these tests, at least from the github diff.
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.
Maybe a better function name could already help?
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.
yes, maybe configureHttpProtobufEndpoint
could be renamed to configureNonSignalSpecificEndpoint
or something to make it super clear in the OtlpConfigUtilTest
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.
OK, maybe I misunderstand what the test does because the implementation at L56 in sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/OtlpConfigUtil.java looks like it's doing the right thing, not appending the signal path for the per-signal config.
Yes these test cases are specifically testing the behavior of the signal agnostic otel.exporter.otlp.endpoint
property.
I can try to improve the names of the function(s) to improve clarity.
Hmm, not formally blocking the PR because I might not have time to monitor it further. But please fix 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.
With a CHANGELOG entry explaining the potential breakage, this is 👍🏽
…on generic http endpoint
} | ||
|
||
@Test | ||
void configureOtlpExporterBuilder_HttpGenericEndpointKey() { |
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've tried to improve the naming conventions in the tests. There are now 3 tests:
configureOtlpExporterBuilder_HttpGenericEndpointKey
validates the behavior when the genericotel.exporter.otlp.endpoint
is used to configure the http endpointconfigureOtlpExporterBuilder_HttpTracesEndpointKey
validates the behavior when the trace specificotel.exporter.otlp.traces.endpoint
is used to configure the http endpoint (included validating that it overrides the generic endpoint value)configureOtlpExporterBuilder_HttpMetricsEndpointKey
validates the behavior when the metrics specificotel.exporter.otlp.metrics.endpoint
is used to configure the http endpoint (included validating that it overrides the generic endpoint value)
I think the method names are pretty good. In any case, they're better than repeating a ~7 line ImmutableMap.of(..)
20+ times. If its not immediately obvious what the configureEndpointForHttp
does, take a quick look at it - IMO it greatly improves the readability of the test cases.
Thanks! |
The current wording of the OTLP endpoint config was confusing, especially the "if not present already" wording. Instead, clarify that we must always append when using the envvar for all signals (it was already clearly specified that the per-signal vars do not get the path appended). This came up in open-telemetry/opentelemetry-java#3650 and again at open-telemetry/opentelemetry-java#3666 (comment). Also make it a MUST (not SHOULD) since this kind of thing would be extremely annoying to have differences per-language in. Also, without appending, the variable cannot be used to configure more than one signal which would defeat its sole purpose.
The current wording of the OTLP endpoint config was confusing, especially the "if not present already" wording. Instead, clarify that we must always append when using the envvar for all signals (it was already clearly specified that the per-signal vars do not get the path appended). This came up in open-telemetry/opentelemetry-java#3650 and again at open-telemetry/opentelemetry-java#3666 (comment). Also make it a MUST (not SHOULD) since this kind of thing would be extremely annoying to have differences per-language in. Also, without appending, the variable cannot be used to configure more than one signal which would defeat its sole purpose.
…ot present
Resolves #3650.