-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add KeyValues to Observations using annotations #6667
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
Conversation
|
Thank you for the detailed pull request.
I haven't had a chance to look closely at the changes and consider alternatives, but my initial reaction is I would like to avoid adding this if it is feasible to avoid it. We'll take a closer look later and give a proper review, but I wanted to give that feedback in case you already have ideas of another good way to implement this. |
|
Thanks for opinion @shakuzen. |
|
Do you think it would be possible to add support for deriving tags from the returned object using annotations? |
|
Yes, I think it's possible to add. |
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, it looks very complete. I left some comments. I see you changed from the ImmutableExtendedKeyValue approach - thanks for that. I think the current solution is better, but I'm still wondering if somehow there's a better way to handle this. I don't have a good idea right now, but I'll think on it some more.
...eter-observation/src/main/java/io/micrometer/observation/annotation/ObservedKeyValueTag.java
Outdated
Show resolved
Hide resolved
...eter-observation/src/main/java/io/micrometer/observation/aop/ObservedKeyValueTagSupport.java
Outdated
Show resolved
Hide resolved
Sure, I think it's possible. From the perspective of making progress, it may be faster to get this PR merged without that feature and follow up with another pull request to add that functionality, though. |
Very thanks for careful review and opinion @shakuzen!
|
micrometer-commons/src/main/java/io/micrometer/common/annotation/AnnotationHandler.java
Outdated
Show resolved
Hide resolved
micrometer-commons/src/main/java/io/micrometer/common/annotation/AnnotationHandler.java
Outdated
Show resolved
Hide resolved
micrometer-observation/src/main/java/io/micrometer/observation/aop/ObservedAspect.java
Outdated
Show resolved
Hide resolved
|
|
||
| private final BiFunction<Annotation, Object, KeyValue> toKeyValue; | ||
|
|
||
| private final BiPredicate<Annotation, Object> validToAdd; |
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 we should not do this, see my comment on ObservedKeyValueAnnotationHandler.
| this.highCardinalityAnnotationHandler = new AnnotationHandler<>( | ||
| (keyValue, context) -> context.addHighCardinalityKeyValue(keyValue), resolverProvider, | ||
| expressionResolverProvider, ObservedKeyValue.class, (annotation, object) -> { | ||
| ObservedKeyValue observedKeyValue = (ObservedKeyValue) annotation; | ||
|
|
||
| return KeyValue.of(ObservedKeyValueSupport.resolveTagKey(observedKeyValue), ObservedKeyValueSupport | ||
| .resolveTagValue(observedKeyValue, object, resolverProvider, expressionResolverProvider)); | ||
| }, (annotation, object) -> { | ||
| if (annotation.annotationType() != ObservedKeyValue.class) { | ||
| return false; | ||
| } | ||
|
|
||
| ObservedKeyValue observedKeyValue = (ObservedKeyValue) annotation; | ||
| if (observedKeyValue.cardinality() != CardinalityType.HIGH) { | ||
| return false; | ||
| } | ||
|
|
||
| return true; | ||
| }); | ||
| this.lowCardinalityAnnotationHandler = new AnnotationHandler<>( | ||
| (keyValue, context) -> context.addLowCardinalityKeyValue(keyValue), resolverProvider, | ||
| expressionResolverProvider, ObservedKeyValue.class, (annotation, object) -> { | ||
| ObservedKeyValue observedKeyValue = (ObservedKeyValue) annotation; |
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 not very readable, I think refactoring these two out to separate private static inner classes would help.
But before we would try that I think it would make sense to check if this is the best solution for routing low and high cardinality keyvalues.
As far as I can tell the only difference between the two implementations here are
addLowCardinalityKeyValue/addHighCardinalityKeyValue in the keyValueConsumer (first line) and CardinalityType.LOW/CardinalityType.HIGH in the validToAdd BiPredicate. I'm wondering if it would make sense to skip the BiPredicate entirely and do this decision in the keyValueConsumer: pass the cardinality information and the context in an object that also contains the context.
Does this make sense? Please let me know if not/you want me to help/write some code to show what I'm thinking (at this point I haven't tried but it seems possible).
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 understood your opinion.
I think that way seems to be risky.
That was my first approach to implement this issue.
There's problem with that way.
keyValueConsumer has this signature BiConsumer<KeyValue, T>.
And, KeyValue has only String key, String value.
public interface KeyValue extends Comparable<KeyValue> {
/**
* Use this if you want to indicate that the value is missing.
*/
String NONE_VALUE = "none";
String getKey();
String getValue();
...
We have to do something(can make side effect) to pass CardinalityType information.
- Change signature of
keyValueConsumerinAnnotationHandler.
It's likely to cause problems at implementations ofAnnotationHandler, so it doesn't seem like a good way. - Extend KeyValue and pass variable.
I tried this.
Make someExtendedKeyValuelike this.
class ExtendedKeyValue<T> extends KeyValue {
T extended;
...
}
But, this is weired.
Because we have to cast KeyValue to ExtendedKeyValue.
The structure in which an interface abstracting motion should be cast and used as a child class does not seem good.
So, I decided to separate two handlers and combine.
However, I accept that it's not easy to read.
I will try to make it better to read.
But, I don't get it how to refactor these two handers to static inner classes.
There's resolverProvider, expressionResolverProvider that makes these handlers stateful.
I think static inner classes are not stateful.
micrometer-observation/src/main/java/io/micrometer/observation/aop/ObservedKeyValueSupport.java
Outdated
Show resolved
Hide resolved
|
This is great, thank you very much! |
|
Really thanks for very detail review @jonatan-ivanov! Heres what I followed(left 👍 emojis).
And, heres what still in conversation. |
|
And, I'm watching why only jdk17 test fails. |
If you click on "view details" of the failure, CI says this:
This means that probably there is nothing wrong with the PR, CI killed the build process for some reason (unfortunately it happens sometime, not sure why). I triggered a re-run, it should be fine soon. |
micrometer-commons/src/main/java/io/micrometer/common/annotation/AnnotationHandler.java
Outdated
Show resolved
Hide resolved
...eter-observation/src/main/java/io/micrometer/observation/aop/ObservationKeyValueSupport.java
Outdated
Show resolved
Hide resolved
...eter-observation/src/main/java/io/micrometer/observation/aop/ObservationKeyValueSupport.java
Outdated
Show resolved
Hide resolved
micrometer-observation/src/main/java/io/micrometer/observation/aop/ObservedAspect.java
Outdated
Show resolved
Hide resolved
micrometer-observation/src/main/java/io/micrometer/observation/aop/ObservedAspect.java
Show resolved
Hide resolved
|
Hi, I pushed some changes to the PR, most of the commits are polishing things except one that changes the way low- and high cardinality information is passed.Connected to this discussion: #6667 (comment) Please notice that this commit rolled back the changes in Please let us know what you think. |
|
Hi @jonatan-ivanov, First, so thanks for polishing in detail my codes! Because of down casting. Above the code, Here is content that shows
|
|
Thank you for your feedback!
|
|
Thank you so much for the detailed explanation, @jonatan-ivanov. I can understand those.
|
|
Fyi: I slightly polished the tests. @isanghaessi Thank you very much again for the PR, we really appreciate your help! |
6b5c786 to
0ebb9d7
Compare
Signed-off-by: Seungyong Hong <[email protected]> Closes micrometer-metricsgh-4030 Co-authored-by: Jonatan Ivanov <[email protected]>
0ebb9d7 to
449b8c1
Compare
|
|
||
| /** | ||
| * Represents the cardinality of a key-value. There are two types of cardinality and | ||
| * treated in different ways. |
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.
Probably worth clarifying this is for use with the annotation, specifically. There's no way to use it with KeyValue otherwise. We can polish this post-merge.
Summary
Add parameter-based tagging via @ObservedKeyValueTag(s) and handler support (#5826) with AOP.
Because there's inconvenience making span with tags like below.
This PR introduces parameter-based tag (key-value) support for @observed by adding:
And, changing some build.gradle.
This brings parity with the developer experience of @MeterTag used in Counted/Timed.
It's just like cherrypick parameter-tag-binding feature of @MeterTag.
Changes
API
AOP
Common
Tests
How to use(Same as @TImed/@MeterTag)
Evaluation order and fallbacks(Same as @MeterTag)
Support for async/CompletionStage
Compatibility
Review Points
Reference
Closes #5826.