Skip to content
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

add metric annotation instrumentation #11354

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
21cbf96
add metric annotation instrumentation
Duncan-tree-zhou May 14, 2024
ff05043
support returnValueAttribute and exception attribtue for Timed and Co…
Duncan-tree-zhou Jun 9, 2024
f5d339f
move timed and counted instrumentation into same gradle module
laurit Jun 10, 2024
22d6c43
Use a single instrumentation module for counted and timed instrumenta…
laurit Jun 10, 2024
aa3b47c
Paratmeter.getName() synthesizes a name when it is not present
laurit Jun 11, 2024
36a9ab5
Merge branch 'main' into add_metric_annotation_instrument
laurit Jun 17, 2024
d954ebe
cache counter/histogram per method
laurit Jun 18, 2024
9eac550
merge
laurit Jun 18, 2024
37733c1
make named required for @Counted and @Timed
laurit Jun 18, 2024
6cdfff9
change default unit to seconds
laurit Jun 18, 2024
771cc58
resue code from @WithSpan implementation to extract method paramter a…
laurit Jun 18, 2024
42aa73b
fractional duration
laurit Jun 18, 2024
c9ea9a0
update samples
laurit Jun 18, 2024
43855a5
handle async methods
laurit Jun 19, 2024
a844fa3
move new annotations to incubator module
laurit Jun 20, 2024
2c2c3d5
rework static attribute support
laurit Jun 21, 2024
7fefa91
Merge branch 'main' into add_metric_annotation_instrument
laurit Jun 21, 2024
3e3b100
disable indy
laurit Jun 21, 2024
de28d29
add javadoc
laurit Jun 26, 2024
99c2937
Merge branch 'main' into add_metric_annotation_instrument
laurit Jul 10, 2024
8a01999
move classes to incubator package
laurit Jul 10, 2024
172f2ac
Merge branch 'main' into add_metric_annotation_instrument
laurit Aug 13, 2024
6ba7771
address review comment
laurit Aug 13, 2024
0010367
move return value attribute to separate annotation
laurit Aug 14, 2024
430e690
Merge branch 'otel_main' into add_metric_annotation_instrument
Duncan-tree-zhou Jan 7, 2025
f6887ae
update module name
Duncan-tree-zhou Jan 7, 2025
630eeac
update module name
Duncan-tree-zhou Jan 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ configurations.configureEach {
substitute(module("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api")).using(project(":instrumentation-api"))
substitute(module("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator")).using(project(":instrumentation-api-incubator"))
substitute(module("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations")).using(project(":instrumentation-annotations"))
substitute(module("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations-incubator")).using(project(":instrumentation-annotations-incubator"))
substitute(module("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations-support")).using(
project(":instrumentation-annotations-support")
)
Expand Down
13 changes: 13 additions & 0 deletions instrumentation-annotations-incubator/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
plugins {
id("otel.java-conventions")
id("otel.japicmp-conventions")
id("otel.publish-conventions")

id("otel.animalsniffer-conventions")
}

group = "io.opentelemetry.instrumentation"

dependencies {
api("io.opentelemetry:opentelemetry-api")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.annotations.incubator;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* This annotation creates a {@link io.opentelemetry.api.metrics.LongCounter Counter} instrument
* recording the number of invocations of the annotated method or constructor.
*
* <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.
* <li><b>code.function:</b> The name of the annotated method.
* <li><b>exception.type:</b> This is only present if an Exception is thrown, and contains the
* {@link Class#getName name} of the Exception class.
* </ul>
*
* <p>Application developers can use this annotation to signal OpenTelemetry auto-instrumentation
* that the Counter instrument should be created.
*
* <p>If you are a library developer, then probably you should NOT use this annotation, because it
* is non-functional without the OpenTelemetry auto-instrumentation agent, or some other annotation
* processor.
*/
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface Counted {

/**
* Name of the Counter instrument.
*
* <p>The name should follow the instrument naming rule: <a
* href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#instrument-naming-rule">https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#instrument-naming-rule</a>
*/
String value();

/**
* Description of the instrument.
*
* <p>Description strings should follow the instrument description rules: <a
* href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#instrument-description">https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#instrument-description</a>
*
* <p>This property would not take effect if the value is not specified.
*/
String description() default "";

/**
* Unit of the instrument.
*
* <p>Unit strings should follow the instrument unit rules: <a
* href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#instrument-unit">https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#instrument-unit</a>
*
* <p>This property would not take effect if the value is not specified.
*/
String unit() default "{invocation}";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.annotations.incubator;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* This annotation marks that a parameter of a method or constructor annotated with {@link Timed} or
* {@link Counted} should be added as an attribute to the instrument.
*
* <p>Application developers can use this annotation to signal OpenTelemetry auto-instrumentation
* that the attribute should be created.
*
* <p>If you are a library developer, then probably you should NOT use this annotation, because it
* is non-functional without the OpenTelemetry auto-instrumentation agent, or some other annotation
* processor.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we doc the attribute type that will be used for the metric attribute?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The attribute type is chosen based on the same logic as with SpanAttribute. The logic for choosing the type in https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation-annotations-support/src/main/java/io/opentelemetry/instrumentation/api/annotation/support/AttributeBindingFactory.java is quite complicated but in my opinion still not complete. For example it does not handle byte and short and their arrays as numeric types. In the span attribute code there are differences between attribute types used in the default and kotlin coroutine implementation.

*
* <p>Warning: be careful using this because it might cause an explosion of the cardinality on your
* metric.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface MetricAttribute {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would call this @Attribute and would use it both for metrics and tracing...
We have the @SpanAttribute witch can record high cardinality data. However, @Attribute could have a field stating if we want it or not in the trace...
This would avoid having a ton of annotations in method params.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I totally agree that the current names are not ideal, but I think they are good enough for the first prototype and give a good base to build upon. To be honest I wasn't really interested in driving this effort I only wanted to help getting this PR into mergeable shape.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably worth to discuss this a bit because if we have to change it in the future, it will be yet another breaking change.


/**
* Optional name of the attribute.
*
* <p>If not specified and the code is compiled using the `{@code -parameters}` argument to
trask marked this conversation as resolved.
Show resolved Hide resolved
* `javac`, the parameter name will be used instead. If the parameter name is not available, e.g.,
* because the code was not compiled with that flag, the attribute will be ignored.
*/
String value() default "";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it's worth renaming this to name (even though it would then require providing name = ), so that it's not confusing that this is the "metric value", and consistent with @StaticMetricAttribute which uses name/value

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really like forcing users to use name = "foo" Micrometer https://github.com/micrometer-metrics/micrometer/blob/main/micrometer-core/src/main/java/io/micrometer/core/aop/MeterTag.java defines key and value and says that both represent the name of the tag and value is an alias for key.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.annotations.incubator;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* This annotation allows for adding method return value as attribute to the metrics recorded using
* {@link Timed} and {@link Counted} annotations.
*
* <p>Application developers can use this annotation to signal OpenTelemetry auto-instrumentation
* that the attribute should be created.
*
* <p>If you are a library developer, then probably you should NOT use this annotation, because it
* is non-functional without the OpenTelemetry auto-instrumentation agent, or some other annotation
* processor.
*
* <p>Warning: be careful using this because it might cause an explosion of the cardinality on your
* metric.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MetricAttributeForReturnValue {

/**
* Attribute name for the return value.
*
* <p>The name of the attribute for the return value of the method call. {@link Object#toString()}
* may be called on the return value to convert it to a String.
*/
String value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.annotations.incubator;

import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* This annotation allows for adding attributes to the metrics recorded using {@link Timed} and
* {@link Counted} annotations.
*
* <p>Application developers can use this annotation to signal OpenTelemetry auto-instrumentation
* that the attribute should be created.
*
* <p>If you are a library developer, then probably you should NOT use this annotation, because it
* is non-functional without the OpenTelemetry auto-instrumentation agent, or some other annotation
* processor.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(StaticMetricAttributes.class)
public @interface StaticMetricAttribute {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ StaticMetricAttribute is a very long name.


/** Name of the attribute. */
String name();

/** Value of the attribute. */
String value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.annotations.incubator;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* This annotation allows for adding attributes to the metrics recorded using {@link Timed} and
* {@link Counted} annotations.
*
* <p>Application developers can use this annotation to signal OpenTelemetry auto-instrumentation
* that the attribute should be created.
*
* <p>If you are a library developer, then probably you should NOT use this annotation, because it
* is non-functional without the OpenTelemetry auto-instrumentation agent, or some other annotation
* processor.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface StaticMetricAttributes {

/** Array of {@link StaticMetricAttribute} annotations describing the added attributes. */
StaticMetricAttribute[] value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.annotations.incubator;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;

/**
* This annotation creates a {@link io.opentelemetry.api.metrics.DoubleHistogram Histogram}
* instrument observing the duration of invocations of the annotated method or constructor.
*
* <p>By default, the Histogram instrument will have the following attributes:
*
* <ul>
* <li><b>code.namespace:</b> The fully qualified name of the class whose method is invoked.
* <li><b>code.function:</b> The name of the annotated method.
* <li><b>exception.type:</b> This is only present if an Exception is thrown, and contains the
* {@link Class#getName name} of the Exception class.
* </ul>
*
* <p>Application developers can use this annotation to signal OpenTelemetry auto-instrumentation
* that the Histogram instrument should be created.
*
* <p>If you are a library developer, then probably you should NOT use this annotation, because it
* is non-functional without the OpenTelemetry auto-instrumentation agent, or some other annotation
* processor.
*/
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface Timed {

/**
* Name of the Histogram instrument.
*
* <p>The name should follow the instrument naming rule: <a
* href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#instrument-naming-rule">https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#instrument-naming-rule</a>
*/
String value();

/**
* Description for the instrument.
*
* <p>Description strings should follow the instrument description rules: <a
* href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#instrument-description">https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#instrument-description</a>
*/
String description() default "";

/**
* The unit for the instrument.
*
* <p>Default is seconds.
*/
TimeUnit unit() default TimeUnit.SECONDS;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.annotations.incubator;

public class CountedUsageExamples {

@Counted("customizedName")
public void method() {}

@Counted("methodWithAttributes")
public void attributes(
@MetricAttribute String attribute1, @MetricAttribute("attribute2") long attribute2) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.annotations.incubator;

public class TimedUsageExamples {

@Timed("customizedName")
public void method() {}

@Timed("methodWithAttributes")
public void attributes(
@MetricAttribute String attribute1, @MetricAttribute("attribute2") long attribute2) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.api.annotation.support;

import io.opentelemetry.api.common.AttributesBuilder;
import java.lang.reflect.Method;
import java.util.function.BiConsumer;
import javax.annotation.Nullable;

/** Helper class for binding method parameters and return value to attributes. */
public final class MethodBinder {

/** Create binding for method return value. */
@Nullable
public static BiConsumer<AttributesBuilder, Object> bindReturnValue(
Method method, String attributeName) {
Class<?> returnType = method.getReturnType();
if (returnType == void.class) {
return null;
}
AttributeBinding binding = AttributeBindingFactory.createBinding(attributeName, returnType);
return binding::apply;
}

/** Create binding for method parameters. */
@Nullable
public static BiConsumer<AttributesBuilder, Object[]> bindParameters(
Method method, ParameterAttributeNamesExtractor parameterAttributeNamesExtractor) {
AttributeBindings bindings = AttributeBindings.bind(method, parameterAttributeNamesExtractor);
if (bindings.isEmpty()) {
return null;
}
return bindings::apply;
}

private MethodBinder() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.api.annotation.support.async;

import io.opentelemetry.context.Context;
import javax.annotation.Nullable;

/** Callback that is called when async computation completes. */
public interface AsyncOperationEndHandler<REQUEST, RESPONSE> {
void handle(
Context context, REQUEST request, @Nullable RESPONSE response, @Nullable Throwable error);
}
Loading
Loading