Managing spans with annotations
Rationale
The main arguments for this features are
api-agnostic means to collaborate with a span
- use of annotations allows users to add to a span with no library dependency on a span api. This allows Sleuth to change its core api less impact to user code.
reduced surface area for basic span operations.
- without this feature one has to use the span api, which has lifecycle commands that could be used incorrectly. By only exposing scope, tag and log functionality, users can collaborate without accidentally breaking span lifecycle.
collaboration with runtime generated code
- with libraries such as Spring Data / Feign the implementations of interfaces are generated at runtime thus span wrapping of objects was tedious. Now you can provide annotations over interfaces and arguments of those interfaces
Creating new spans
If you really don’t want to take care of creating local spans manually you can profit from the @NewSpan
annotation. Also we give you the @SpanTag
annotation to add tags in an automated fashion.
Let’s look at some examples of usage.
@NewSpan
void testMethod();
Annotating the method without any parameter will lead to a creation of a new span whose name will be equal to annotated method name.
@NewSpan("customNameOnTestMethod4")
void testMethod4();
If you provide the value in the annotation (either directly or via the name
parameter) then the created span will have the name as the provided value.
// method declaration
@NewSpan(name = "customNameOnTestMethod5")
void testMethod5(@SpanTag("testTag") String param);
// and method execution
this.testBean.testMethod5("test");
You can combine both the name and a tag. Let’s focus on the latter. In this case whatever the value of the annotated method’s parameter runtime value will be - that will be the value of the tag. In our sample the tag key will be testTag
and the tag value will be test
.
@NewSpan(name = "customNameOnTestMethod3")
@Override
public void testMethod3() {
}
You can place the @NewSpan
annotation on both the class and an interface. If you override the interface’s method and provide a different value of the @NewSpan
annotation then the most concrete one wins (in this case customNameOnTestMethod3
will be set).
Continuing spans
If you want to just add tags and annotations to an existing span it’s enough to use the @ContinueSpan
annotation as presented below. Note that in contrast with the @NewSpan
annotation you can also add logs via the log
parameter:
// method declaration
@ContinueSpan(log = "testMethod11")
void testMethod11(@SpanTag("testTag11") String param);
// method execution
this.testBean.testMethod11("test");
That way the span will get continued and:
logs with name
testMethod11.before
andtestMethod11.after
will be createdif an exception will be thrown a log
testMethod11.afterFailure
will also be createdtag with key
testTag11
and valuetest
will be created
More advanced tag setting
There are 3 different ways to add tags to a span. All of them are controlled by the SpanTag
annotation. Precedence is:
try with the bean of
TagValueResolver
type and provided nameif one hasn’t provided the bean name, try to evaluate an expression. We’re searching for a
TagValueExpressionResolver
bean. The default implementation uses SPEL expression resolution.if one hasn’t provided any expression to evaluate just return a
toString()
value of the parameter
Custom extractor
The value of the tag for following method will be computed by an implementation of TagValueResolver
interface. Its class name has to be passed as the value of the resolver
attribute.
Having such an annotated method:
@NewSpan
public void getAnnotationForTagValueResolver(@SpanTag(key = "test", resolver = TagValueResolver.class) String test) {
}
and such a TagValueResolver
bean implementation
@Bean(name = "myCustomTagValueResolver")
public TagValueResolver tagValueResolver() {
return parameter -> "Value from myCustomTagValueResolver";
}
Will lead to setting of a tag value equal to Value from myCustomTagValueResolver
.
Resolving expressions for value
Having such an annotated method:
@NewSpan
public void getAnnotationForTagValueExpression(@SpanTag(key = "test", expression = "length() + ' characters'") String test) {
}
and no custom implementation of a TagValueExpressionResolver
will lead to evaluation of the SPEL expression and a tag with value 4 characters
will be set on the span. If you want to use some other expression resolution mechanism you can create your own implementation of the bean.
Using toString method
Having such an annotated method:
@NewSpan
public void getAnnotationForArgumentToString(@SpanTag("test") Long param) {
}
if executed with a value of 15
will lead to setting of a tag with a String value of "15"
.