-
Notifications
You must be signed in to change notification settings - Fork 1k
add metric annotation instrumentation #11354
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: main
Are you sure you want to change the base?
add metric annotation instrumentation #11354
Conversation
|
There are some CI failures, firstly you can solve them by referring to https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/contributing/running-tests.md#troubleshooting-ci-test-failures |
f1797c8 to
f7dd6b9
Compare
...entation-annotations/src/main/java/io/opentelemetry/instrumentation/annotations/Counted.java
Outdated
Show resolved
Hide resolved
|
Hi @steverao, I found that there are lots of fails "to connect to localhost/127.0.0.1:4318". So I want to rerun the task, but I can't see the rerun button. Is it disabled for contributors? |
Yes, you can fix the problems firstly and push relevant commits. It will trigger to rerun the CI tasks. |
|
I mean I guest the failure is caused by the CI run time environment crush..... if it's able to rerun a task, It might save some time.... |
815bbfd to
95f0f0d
Compare
f9657ec to
5ab650b
Compare
|
I turn it ready for reviewing, and get some questions for discuss:
|
5024350 to
54866bf
Compare
54866bf to
21cbf96
Compare
| @Override | ||
| public <REQUEST, RESPONSE> Object end( | ||
| Instrumenter<REQUEST, RESPONSE> instrumenter, | ||
| AsyncOperationCallback<REQUEST, RESPONSE> handler, |
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.
Sorry for continuing of this.
I still have doubts about these async interfaces and how generic their names are.
This line is a perfect example of the problem. The interface is AsyncOperationCallback but we end up calling the attribute handler because it's more convenient but too abstract to actually mean anything in the code. What is it handling? business logic, telemetry?
If we cannot use the name of the interface as a name of an instance in the code, something is wrong.
On this comment #11354 (comment),
@Duncan-tree-zhou suggested AsyncEndHandler or AsyncCallback#onEnd and we ended up with AsyncOperationCallback#onEnd. In my mind I always ask: AsyncOperationCallback why? To where?
We also have AsyncOperationEndStrategy.java, AsyncOperationEndSupport
If we look at the package name:
io.opentelemetry.instrumentation.api.annotation.support.async.AsyncOperationCallback, it already includes async.
Probably we need some brainstorm around these names and what they actually do. Imagine some newbie looking at code using this... At first glance it doesn't seem related with telemetry.
What if we use one of:
InstrumentationCallbackTelemetryCallbackTelemetryObserver
I think InstrumentationCallback better aligns with the current OTel practice and also answer: why we do the callback and to where.
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.
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 about changing the interface name and the method name:
public interface AsyncOperationCallback<REQUEST, RESPONSE> {
void onEnd(
Context context, REQUEST request, @Nullable RESPONSE response, @Nullable Throwable error);
}
to
@FunctionalInterface
public interface AsyncOperationOnEnd<REQUEST, RESPONSE> {
void accept(
Context context, REQUEST request, @Nullable RESPONSE response, @Nullable Throwable error);
}
and changing the name of the var here:
| AsyncOperationCallback<REQUEST, RESPONSE> handler, | |
| AsyncOperationOnEnd<REQUEST, RESPONSE> onEnd, |
…e we don't have test task on it.
| * <p>By default, the Counter instrument will have the following attributes: | ||
| * | ||
| * <ul> | ||
| * <li><b>code.namespace:</b> The fully qualified name of the class whose method is invoked. |
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'm really looking forward for this to be merged. However I've noticed that code.namespace and code.function have been deprecated in the semantic conventions and has been replaced by code.function.name in the which is the fully qualified name. Should this be updated in this PR as well?
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.
Thank you for pointing out. the code.namespace and code.function are already replaced by code.function.name. check MetricsAnnotationHelper.java line 38.
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.
Pull request overview
This PR adds metric annotation instrumentation for @Counted and @Timed annotations, which allow developers to automatically record metrics for method invocations using annotations. The implementation also refactors the existing instrumentation-annotations module structure to better support code reuse between different annotation types.
Key Changes:
- Introduces new incubator annotations (
@Counted,@Timed,@Attribute,@ReturnValueAttribute,@StaticAttribute) for declarative metrics instrumentation - Restructures opentelemetry-instrumentation-annotations modules into a hierarchical structure with common, 1.16, and incubator submodules
- Introduces
AsyncOperationCallbackinterface to decouple async operation handling fromInstrumenterdependency
Reviewed changes
Copilot reviewed 41 out of 56 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| settings.gradle.kts | Adds new module includes for restructured instrumentation-annotations hierarchy |
| javaagent/build.gradle.kts | Updates dependency paths for restructured modules |
| opentelemetry-instrumentation-annotations-shaded-for-instrumenting/build.gradle.kts | Adds dependency on new annotations-incubator module |
| instrumentation-annotations-incubator/src/main/java/.../Counted.java | Defines @Counted annotation for counter metrics on methods |
| instrumentation-annotations-incubator/src/main/java/.../Timed.java | Defines @Timed annotation for histogram metrics measuring duration |
| instrumentation-annotations-incubator/src/main/java/.../Attribute.java | Defines @Attribute for marking method parameters as metric attributes |
| instrumentation-annotations-incubator/src/main/java/.../ReturnValueAttribute.java | Defines annotation for capturing return value as metric attribute |
| instrumentation-annotations-incubator/src/main/java/.../StaticAttribute.java | Defines annotation for adding static attributes to metrics |
| .../incubator/CountedInstrumentation.java | Implements bytecode instrumentation for @Counted annotation |
| .../incubator/CountedHelper.java | Helper class for recording counter metrics |
| .../incubator/TimedInstrumentation.java | Implements bytecode instrumentation for @Timed annotation |
| .../incubator/TimedHelper.java | Helper class for recording histogram metrics |
| .../incubator/MetricsAnnotationHelper.java | Shared base class for metric annotation helpers |
| .../incubator/MetricAttributeHelper.java | Helper for extracting and binding metric attributes |
| .../common/javaagent/MethodRequest.java | Common data class holding method and arguments |
| .../common/javaagent/KotlinCoroutineUtil.java | Utility for detecting Kotlin suspend methods |
| .../common/javaagent/AnnotationExcludedMethods.java | Configuration-based method exclusion matcher |
| .../annotations-1.16/.../WithSpanInstrumentation.java | Refactored @WithSpan instrumentation using common utilities |
| .../annotations-1.16/.../AddingSpanAttributesInstrumentation.java | Refactored @AddingSpanAttributes instrumentation |
| .../async/AsyncOperationCallback.java | New interface for async operation completion callbacks |
| .../async/AsyncOperationEndStrategy.java | Updated to support both Instrumenter and AsyncOperationCallback |
| .../async/AsyncOperationEndSupport.java | Refactored to use AsyncOperationCallback |
| .../async/Jdk8AsyncOperationEndStrategy.java | Updated for AsyncOperationCallback support |
| .../support/MethodBinder.java | New helper for binding method parameters and return values to attributes |
| rxjava-3-common/.../RxJava3AsyncOperationEndStrategy.java | Updated to use AsyncOperationCallback |
| rxjava-2.0/.../RxJava2AsyncOperationEndStrategy.java | Updated to use AsyncOperationCallback |
| reactor-3.1/.../ReactorAsyncOperationEndStrategy.java | Updated to use AsyncOperationCallback |
| guava-10.0/.../GuavaAsyncOperationEndStrategy.java | Updated to use AsyncOperationCallback |
| kotlinx-coroutines/.../FlowUtil.kt | Updated to use AsyncOperationCallback |
| instrumentation-annotations/README.md | Updated documentation to mention @Counted and @Timed |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
...y/javaagent/instrumentation/instrumentationannotations/incubator/CountedInstrumentation.java
Outdated
Show resolved
Hide resolved
...y/javaagent/instrumentation/instrumentationannotations/incubator/CountedInstrumentation.java
Outdated
Show resolved
Hide resolved
...try/javaagent/instrumentation/instrumentationannotations/incubator/TimedInstrumentation.java
Outdated
Show resolved
Hide resolved
...y/javaagent/instrumentation/instrumentationannotations/incubator/CountedInstrumentation.java
Outdated
Show resolved
Hide resolved
...try/javaagent/instrumentation/instrumentationannotations/incubator/TimedInstrumentation.java
Outdated
Show resolved
Hide resolved
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.
Pull request overview
Copilot reviewed 41 out of 56 changed files in this pull request and generated 5 comments.
| static void addStaticAttributes(Method method, AttributesBuilder attributesBuilder) { | ||
| attributesBuilder.put( | ||
| CodeAttributes.CODE_FUNCTION_NAME, | ||
| method.getDeclaringClass().getName() + "." + method.getName()); |
Copilot
AI
Nov 29, 2025
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.
[nitpick] Consider using method.getDeclaringClass().getCanonicalName() instead of getName() to provide a more readable fully qualified class name, especially for nested classes where getName() returns names with $ separators.
| method.getDeclaringClass().getName() + "." + method.getName()); | |
| method.getDeclaringClass().getCanonicalName() + "." + method.getName()); |
| @Test | ||
| void testExampleIgnore() throws Exception { | ||
| new TimedExample().exampleIgnore(); | ||
| Thread.sleep(500); |
Copilot
AI
Nov 29, 2025
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.
Using Thread.sleep() for timing assertions can lead to flaky tests. Consider using test utilities that wait for conditions with timeouts instead of fixed sleep durations.
| @Test | ||
| void testExampleIgnore() throws Exception { | ||
| new CountedExample().exampleIgnore(); | ||
| Thread.sleep(500); // sleep a bit just to make sure no metric is captured |
Copilot
AI
Nov 29, 2025
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.
Using Thread.sleep() for timing assertions can lead to flaky tests. Consider using test utilities that wait for conditions with timeouts instead of fixed sleep durations.
| CompletableFuture<String> future = new CompletableFuture<>(); | ||
| new CountedExample().completableFuture(future); | ||
|
|
||
| Thread.sleep(500); // sleep a bit just to make sure no metric is captured |
Copilot
AI
Nov 29, 2025
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.
Using Thread.sleep() for timing assertions can lead to flaky tests. Consider using test utilities that wait for conditions with timeouts instead of fixed sleep durations.
| CompletableFuture<String> future = new CompletableFuture<>(); | ||
| new TimedExample().completableFuture(future); | ||
|
|
||
| Thread.sleep(500); // sleep a bit just to make sure no metric is captured |
Copilot
AI
Nov 29, 2025
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.
Using Thread.sleep() for timing assertions can lead to flaky tests. Consider using test utilities that wait for conditions with timeouts instead of fixed sleep durations.
| * <li><b>error.type:</b> This is only present if an Exception is thrown, and contains the {@link | ||
| * Class#getName name} of the Exception class. | ||
| * </ul> | ||
| * |
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 document what the default is for buckets?
| // This module does not have tests, but has example classes in the test directory. Gradle 9 fails | ||
| // the build when there are source files in the test directory but no tests to run so we disable | ||
| // the test task. |
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'd suggest add a test that just loads and calls those example usages. It will essentially be a no-op test, but that's ok.
refer to #7030
add metric annotation instrumentation