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

AddingSpanAttributes annotation #7787

Merged
merged 12 commits into from
Apr 24, 2023
Merged

Conversation

sfriberg
Copy link
Contributor

@sfriberg sfriberg commented Feb 10, 2023

Expand the capabilities of SpanAttribute annotations to allow adding method parameters to and existing span on not only when the method is annotated with WithSpan.

To add to an existing span use the AddingSpanAttributes annotation on the method and apply SpanAttribute annotations as you would do with WithSpan.

As an example the following annotated method would if auto instrumentation is enabled add two attributes on every execution to the current span that is in context. If there is no current span no new span will be created. Similar to setAttribute any existing attributes will be overwritten.

  @AddingSpanAttributes
  public String withSpanAttributes(
      @SpanAttribute String implicitName,
      @SpanAttribute("explicitName") String parameter,
      String noAttr) {

    return "hello!";
  }

Suggestion for implementation of the discussion we had in the OTel Java meeting on 2023-02-02 and 2023-03-02.

@sfriberg sfriberg requested a review from a team as a code owner February 10, 2023 17:37
@linux-foundation-easycla
Copy link

linux-foundation-easycla bot commented Feb 10, 2023

CLA Signed

The committers listed above are authorized under a signed CLA.

Copy link
Member

@mateuszrzeszutek mateuszrzeszutek left a comment

Choose a reason for hiding this comment

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

Thanks @sfriberg !

Copy link
Contributor

@breedx-splk breedx-splk left a comment

Choose a reason for hiding this comment

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

I like this idea a lot and think it could be useful. I wonder if @WithSpan(useParent=true, ...) might possibly be more useful and less clashy? In any case, I think a few behaviors need to be clarified. Thanks!

Comment on lines 30 to 31
name "external"
kind INTERNAL
Copy link
Contributor

Choose a reason for hiding this comment

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

Seeing external and INTERNAL on the same span can be slightly misleading. Maybe rename external to parent or root for clarity?

Comment on lines 24 to 26
@WithSpan
@WithCurrentSpan
public String withSpanAttributes(
Copy link
Contributor

Choose a reason for hiding this comment

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

I think a reader would quickly question this use of seemingly conflicting annotations. One explicitly creates a new span, one explicitly does not and uses the parent context. You can't have it both ways, so which one wins? It's not easily understood.

Looking at the test, it seems like @WithCurrentSpan wins here, because there's only 1 span in the resulting trace. Can you confirm that that is intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that is intentional. The WithCurrenSpan instrumentation explicitly filters out any method that happens for some reason to be annotated with both.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

isAnnotatedWith(
                named("application.io.opentelemetry.instrumentation.annotations.WithCurrentSpan"))
            // Avoid repeat extraction if method is already annotation with WithSpan
            .and(
                not(
                    isAnnotatedWith(
                        named(
                            "application.io.opentelemetry.instrumentation.annotations.WithSpan"))));

Choose a reason for hiding this comment

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

What will happen if in the scope of a single span, 2 different methods with @WithCurrentSpan and their particular @SpanAttribute are invoked in chain?

Copy link
Contributor Author

@sfriberg sfriberg Feb 25, 2023

Choose a reason for hiding this comment

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

It will overwrite in the same way calls to span.setAttribute(k,v) would if the key was already used.

}
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like there's also a missing test or two here --

  • what's the behavior if/when @WithCurrentSpan is used when there is no parent context?
  • what's the behavior if/when the current span already has an attribute with the same name defined?

@linux-foundation-easycla
Copy link

linux-foundation-easycla bot commented Feb 13, 2023

CLA Signed

The committers listed above are authorized under a signed CLA.

@sfriberg
Copy link
Contributor Author

I like this idea a lot and think it could be useful. I wonder if @WithSpan(useParent=true, ...) might possibly be more useful and less clashy?

I think this could be an interesting path. Probably name the variable useCurrent instead since you won't create a new span so won't really be a parent.

@sfriberg
Copy link
Contributor Author

My ByteBuddy knowledge isn't good enough to figure out how to write an element matcher that also looks at the value of an annotation field, or if it is even possible.

So far what I have seen is that you can access that in your Advice methods which would be a bit of a hassle since you don't want to do extra work in the instrumented code.

@mateuszrzeszutek
Copy link
Member

mateuszrzeszutek commented Feb 16, 2023

My ByteBuddy knowledge isn't good enough to figure out how to write an element matcher that also looks at the value of an annotation field, or if it is even possible.

I think you could do sth like this:

    declaresAnnotation(
        annotationType(
            named(
                "application.io.opentelemetry.instrumentation.annotations.WithSpan"))
            .and(new AnnotationHasBooleanValue("useCurrent", true)));

where AnnotationHasBooleanValue is

  static final class AnnotationHasBooleanValue implements ElementMatcher<AnnotationDescription> {

    private final String fieldName;
    private final boolean fieldValue;

    AnnotationHasBooleanValue(String fieldName, boolean fieldValue) {
      this.fieldName = fieldName;
      this.fieldValue = fieldValue;
    }

    @Override
    public boolean matches(AnnotationDescription target) {
      AnnotationValue<?, ?> value = target.getValue(fieldName);
      return value.resolve(boolean.class) == fieldValue;
    }
  }

I haven't tested it though.

*/
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface WithCurrentSpan {}

Choose a reason for hiding this comment

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

What if there is not current span? Will it create one?
Should we name it @SpanAttributes (plural) or similar, instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If now span exists nothing will happen. Working on an updated impl which will instead reuse WithSpan with a variable useCurrent to select if you want a new Span or reuse an existing one.

Comment on lines 24 to 26
@WithSpan
@WithCurrentSpan
public String withSpanAttributes(

Choose a reason for hiding this comment

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

What will happen if in the scope of a single span, 2 different methods with @WithCurrentSpan and their particular @SpanAttribute are invoked in chain?

@sfriberg
Copy link
Contributor Author

@mateuszrzeszutek Thanks for the pointers was able to get things working. Only thing was that you must use Boolean.class rather than boolean.class it seems like.

However as I tested things and started to write some Javadoc I realized that reusing WithSpan will probably cause more confusion that simplify things. If you set useCurrent all other variable basically are ignored since we will not change the span name or kind which you can still set. So I think having a separate annotation will make it clearer what the behaviour is.

Since the focus is really just span attributes I kind of like the name suggested by @brunobat, but WithCurrentSpan also keeps it close to the current naming rather than changing things completely.

I should be able to join this coming Thursday and perhaps we can make a quick decision there.

  1. Agree on separate annotation
  2. If agggrement what should the name be @WithCurrentSpan or @SpanAttributes


public class SpanAttributesWithCurrentSpan {

@WithCurrentSpan
Copy link
Member

@trask trask Mar 2, 2023

Choose a reason for hiding this comment

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

alternatives:

  • WithSpanAttributes
  • UpdateCurrentSpan

@sfriberg sfriberg changed the title WithCurrentSpan annotation WithSpanAttributes annotation Mar 3, 2023
Copy link
Member

@mateuszrzeszutek mateuszrzeszutek left a comment

Choose a reason for hiding this comment

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

Thanks @sfriberg !

@sfriberg
Copy link
Contributor Author

Anything further for me todo, or ready to be approved and merged?

@mateuszrzeszutek
Copy link
Member

IMO it's 👍 as it is.
@trask @laurit is there anything you want to add here? (This PR adds a new thing to a stable instrumentation-annotations, which is why I'd like other maintainers to take a look at it as well)

Copy link
Member

@trask trask left a comment

Choose a reason for hiding this comment

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

thx @sfriberg!

@laurit
Copy link
Contributor

laurit commented Apr 4, 2023

@sfriberg could you rebase so that this pr could be merged

@trask
Copy link
Member

trask commented Apr 13, 2023

@sfriberg sorry, I had second thoughts on the annotation name because "with" implies to me "in the scope of", or possibly immutable, and this is mutating something already in scope. we discussed in Java SIG meeting and came up with @AddingSpanAttributes. If you don't mind renaming once more 🙇‍♂️

@tylerbenson
Copy link
Member

@sfriberg would you mind adding some code snippet examples in the PR description so it's easier to understand what this does and how it should be used?

sfriberg and others added 9 commits April 23, 2023 10:21
Co-authored-by: Mateusz Rzeszutek <mrzeszutek@splunk.com>
…ent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java

Co-authored-by: Mateusz Rzeszutek <mrzeszutek@splunk.com>
…ent/src/test/java/io/opentelemetry/test/annotation/WithSpanInstrumentationTest.java

Co-authored-by: Mateusz Rzeszutek <mrzeszutek@splunk.com>
…/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/instrumentationannotations/AnnotationInstrumentationModule.java

Co-authored-by: Mateusz Rzeszutek <mrzeszutek@splunk.com>
@sfriberg
Copy link
Contributor Author

@trask No problem, think the name changes make sense. Should be all updated now.
@tylerbenson Expand the PR intro to include a brief example. Hope this explains it enough, otherwise just let me know.

@sfriberg sfriberg changed the title WithSpanAttributes annotation AddingSpanAttributes annotation Apr 23, 2023
Copy link
Member

@tylerbenson tylerbenson left a comment

Choose a reason for hiding this comment

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

@sfriberg thanks for improving the description. That makes things much clearer.

@trask trask merged commit d1b7356 into open-telemetry:main Apr 24, 2023
@trask
Copy link
Member

trask commented Apr 24, 2023

@sfriberg thx 🎉 (and thx for your patience)

@LoganDev99
Copy link

Does @SpanAttribute support fields of an object or just scalar fields?

For example, using a Customer object. Does that work out the box? Or is it possible to pick only some fields from Customer?

  @AddingSpanAttributes
  public String withSpanAttributes(
      @SpanAttribute Customer customer
) {

    return "hello!";
  }

@mateuszrzeszutek
Copy link
Member

For example, using a Customer object. Does that work out the box?

AFAIR it'll just set the value to the result ofcustomer.toString(). We don't really support any nested object access, expression language etc. For more complex cases, you can always call

Span.current().setAttribute("something", customer.getSomething());

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants