-
Notifications
You must be signed in to change notification settings - Fork 1k
The one true configuration bridge #15641
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
The one true configuration bridge #15641
Conversation
35895af to
92f18c1
Compare
92f18c1 to
1f144e1
Compare
...lemetry/instrumentation/config/bridge/ConfigPropertiesBackedDeclarativeConfigProperties.java
Show resolved
Hide resolved
...ain/java/io/opentelemetry/javaagent/instrumentation/methods/MethodInstrumentationModule.java
Outdated
Show resolved
Hide resolved
zeitlinger
left a 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.
Very similar to what I did in #15622 - I focused more on the case where InstrumentationConfig is not available today - so it's mostly complementary (but I didn't check out the big PR yet)
...va/io/opentelemetry/instrumentation/api/incubator/config/internal/DeclarativeConfigUtil.java
Outdated
Show resolved
Hide resolved
|
|
||
| static { | ||
| DeclarativeConfigProperties graphqlConfig = | ||
| DeclarativeConfigUtil.getStructured(GlobalOpenTelemetry.get(), "java", empty()) |
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.
"java", empty()) will be there for every call, so I proposed to get rid of it in #15622
List<String> value =
ConfigProvider.noop()
.get("http")
.get("client")
.getScalarList("known-methods", String.class, Collections.singletonList("GET"));
assertThat(value).containsExactly("GET");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, we do need to be able to access general as well - so maybe just add another getJava for the common case
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 want to try leaning into using the Declarative Configuration API directly. If it has usability issues let's raise them to @jack-berg.
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 like that in many cases empty() will be present, but the motivation is that null is different from present and empty.
I'm open to ideas about adding syntactic sugar to facilitate this common task of walking the tree.
Maybe something like:
List<String> value = config.walk("java.http.client", empty()).getScalarList("known-methods", String.class, singletonList("GET"));
Where walk(String dotDelimetedPath, DeclarativeConfigProperties defaultValue) splits the path on ".", and calls getStructured(segment, defaultValue) for each segment.
Or maybe just:
List<String> value = config.walk("java").walk("http").walk("client").getScalarList("known-methods", String.class, singletonList("GET"));
Where walk is an alias for getStructured(String name, empty())
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 created open-telemetry/opentelemetry-java#7923 to address this
...ooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtendedOpenTelemetrySdkWrapper.java
Show resolved
Hide resolved
...lemetry/instrumentation/config/bridge/ConfigPropertiesBackedDeclarativeConfigProperties.java
Show resolved
Hide resolved
...lemetry/instrumentation/config/bridge/ConfigPropertiesBackedDeclarativeConfigProperties.java
Show resolved
Hide resolved
|
Good to merge from my point of view |
…a-agent-foundation
| static { | ||
| DeclarativeConfigProperties graphqlConfig = | ||
| DeclarativeConfigUtil.getStructured(GlobalOpenTelemetry.get(), "java", empty()) | ||
| .getStructured("graphql", empty()); |
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.
need to think about which instrumentation configurations are stable (e.g. as opposed to "graphql/development")
the downside to adding "/development" is that then we have to deal with deprecating the "/development" node later
maybe it's ok to be more aggressive on marking configurations as stable
cc @laurit
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.
We had already discussed that we want to keep accepting the development suffix even after stabilization
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.
An argument for stable is: the whole instrumentation block is already development - but that's more a formal argument
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.
We had already discussed that we want to keep accepting the development suffix even after stabilization
yeah, but it's not clear yet if this will be responsibility of each instrumentation to check both, or if this will be handled transparently in the SDK
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 it's ok to be more aggressive on marking configurations as stable
I think this is a good idea. With the current approach most of our instrumentations and their options will be perpetually experimental which isn't really useful.
| Configuration config = new Configuration(openTelemetry); | ||
|
|
||
| TELEMETRY = | ||
| GraphQLTelemetry.builder(openTelemetry) | ||
| .setCaptureQuery(config.captureQuery(true)) | ||
| .setSanitizeQuery(config.querySanitizerEnabled(true)) | ||
| .setAddOperationNameToSpanName(config.addOperationNameToSpanName(false)) | ||
| .build(); |
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.
how do folks like this Configuration class encapsulation as a first stab? the configuration code is kind of ugly (and hard to read) when strewn about
I think we've discussed this kind of thing before where we would ideally generate something like this from a declarative configuration definition somewhat similar to semconv generation
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 hard to read - I had to check the implementation. Why not assign the default value in the config class?
And yes, it's a great opportunity to do code generation from the metadata yaml file.
I think this could be done in a follow up pr.
@jaydeluca fyi
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.
Why not assign the default value in the config class?
oh, true, I forgot that default values are encoded in configuration schema now
though I'm not sure if defaultBehavior is structured enough to use it for code generation
cc @jack-berg
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.
oh, true, I forgot that default values are encoded in configuration schema now
Encoded is a stretch.. We define the behavior, but its up to the implementation to apply that behavior.
And yes, it's a great opportunity to do code generation from the metadata yaml file.
JSON schema does have a default keyword: https://json-schema.org/understanding-json-schema/reference/annotations
It just doesn't make sense to use in declarative config because quite often we have to define defaults which cannot be encoded in a value.
Java instrumentation might be able to use the default annotation, and leverage it in codegen, if it chooses to express the schema for its instrumentations in json schema.
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 particularly like it because the original code was simpler. We could consider building our own utilities with simpler api that use the declarative config api under the hood. 99% of time we are reading simple booleans or strings anyway.
We could have something like
class AgentConfiguration {
private final DeclarativeConfigProperties javaConfig;
private AgentConfiguration(DeclarativeConfigProperties javaConfig) {
this.javaConfig = javaConfig;
}
// could be a singleton
static AgentConfiguration get() {
OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
DeclarativeConfigProperties javaConfig = empty();
if (openTelemetry instanceof ExtendedOpenTelemetry) {
ExtendedOpenTelemetry extendedOpenTelemetry = (ExtendedOpenTelemetry) openTelemetry;
DeclarativeConfigProperties instrumentationConfig =
extendedOpenTelemetry.getConfigProvider().getInstrumentationConfig();
if (instrumentationConfig != null) {
javaConfig = instrumentationConfig.getStructured("java", empty());
}
}
return new AgentConfiguration(javaConfig);
}
boolean getBoolean(boolean defaultValue, String... key) {
if (key.length == 0) {
return defaultValue;
}
DeclarativeConfigProperties node = javaConfig;
for (int i = 0; i < key.length - 2; i++) {
node = node.getStructured(key[i], empty());
}
return node.getBoolean(key[key.length - 1], defaultValue);
}
}and you could use it with
AgentConfiguration.get().getBoolean(true, "graphql", "query_sanitizer", "enabled");could also add
boolean getBoolean(String key, boolean defaultValue) {
return getBoolean(defaultValue, key.split("\\."));
}then you could use it with
AgentConfiguration.get().getBoolean("graphql.query_sanitizer.enabled", true);which would be pretty much the same as the exiting code
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 part:
OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
DeclarativeConfigProperties javaConfig = empty();
if (openTelemetry instanceof ExtendedOpenTelemetry) {
ExtendedOpenTelemetry extendedOpenTelemetry = (ExtendedOpenTelemetry) openTelemetry;
DeclarativeConfigProperties instrumentationConfig =
extendedOpenTelemetry.getConfigProvider().getInstrumentationConfig();
if (instrumentationConfig != null) {
javaConfig = instrumentationConfig.getStructured("java", empty());
}
}
should soon be simplified to:
GlobalOpenTelemetry.get().getConfigProvider().getInstrumentationConfig().getStructured("java", empty())
(with stabilization of getConfigProvider and open-telemetry/opentelemetry-java#7920)
if the other part is still too complex for our preference, I'd like to try to improve the Declarative Configuration API ergonomics (instead of adding our own wrapper)
e.g. @zeitlinger has a proposal along these lines: open-telemetry/opentelemetry-java#7923
are there other things from your perspective that would make using the Declarative Configuration API directly better for us?
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.
AgentConfiguration.get().getBoolean("graphql.query_sanitizer.enabled", true);
With open-telemetry/opentelemetry-java#7920 and open-telemetry/opentelemetry-java#7923 (and eventual stabilization of these APIs) we'll end up with something similar:
GlobalOpenTelemetry.getConfigProvider().getInstrumentationConfig()
.get("graphql)
.get("query_sanitizer")
.getBoolean("enabled", true);
And the agent could provide instrumentation helpers for the repetitive GlobalOpenTelemetry.getConfigProvider().getInstrumentationConfig() part.
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.
Actually it would be
GlobalOpenTelemetry.get().getConfigProvider().getInstrumentationConfig()
.get("java")
.get("graphql")
.get("query_sanitizer")
.getBoolean("enabled", true);@jack-berg difference to what you wrote
GlobalOpenTelemetry.getConfigProvider()doesn't exist yet (but I think you implied that it would be added).get("java")was missing
This inspired me to create open-telemetry/opentelemetry-java#7927 so it can become
GlobalOpenTelemetry.get().getConfigProvider()
.get("graphql")
.get("query_sanitizer")
.getBoolean("enabled", true);- removes
.getInstrumentationConfig() - removes `.get("java")
3793fc2 to
4dca57c
Compare
4dca57c to
3baf749
Compare
...rc/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v20_0/GraphqlSingletons.java
Outdated
Show resolved
Hide resolved
...-bridge/src/main/java/io/opentelemetry/instrumentation/config/bridge/PeerServiceMapping.java
Show resolved
Hide resolved
|
@laurit I'm going to go ahead and merge so I can submit a couple of follow-ups, but feel free to leave comments here and I'll follow-up on them |
| return String.join(".", path) + "." + name; | ||
| } | ||
|
|
||
| private static String toPropertyKey(String fullPath) { |
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.
Do we need to add some way to customize this to make it usable in vendor distros that could need to map their of configuration properties?
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 my current PoC, I have a mapper class that some common default properties do declarative config, based on what was specified as system properties:
It leaves enough wiggle room for vendor conventions.
The first step towards