diff --git a/tracing-spring-boot-autoconfigure/src/main/java/org/axonframework/extensions/tracing/autoconfig/TracingAutoConfiguration.java b/tracing-spring-boot-autoconfigure/src/main/java/org/axonframework/extensions/tracing/autoconfig/TracingAutoConfiguration.java index a838ac7..0d3c257 100644 --- a/tracing-spring-boot-autoconfigure/src/main/java/org/axonframework/extensions/tracing/autoconfig/TracingAutoConfiguration.java +++ b/tracing-spring-boot-autoconfigure/src/main/java/org/axonframework/extensions/tracing/autoconfig/TracingAutoConfiguration.java @@ -22,6 +22,7 @@ import org.axonframework.extensions.tracing.OpenTraceDispatchInterceptor; import org.axonframework.extensions.tracing.OpenTraceHandlerInterceptor; import org.axonframework.extensions.tracing.TracingCommandGateway; +import org.axonframework.extensions.tracing.TracingProperties; import org.axonframework.extensions.tracing.TracingProvider; import org.axonframework.extensions.tracing.TracingQueryGateway; import org.axonframework.messaging.correlation.CorrelationDataProvider; @@ -32,8 +33,11 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; /** * Auto configure a tracing capabilities. @@ -45,28 +49,42 @@ @Configuration @AutoConfigureAfter(EventProcessingAutoConfiguration.class) @ConditionalOnClass(io.opentracing.Tracer.class) +@Import(TracingAutoConfiguration.PropertiesConfiguration.class) public class TracingAutoConfiguration { + @Configuration + public static class PropertiesConfiguration{ + + //must be defined in a separate @Configuration to ensure creation order + @Bean + @ConfigurationProperties(prefix = "axon.tracing") + public TracingProperties tracingProperties() { + return new TracingProperties(); + } + } + @Bean public OpenTraceDispatchInterceptor traceDispatchInterceptor(Tracer tracer) { return new OpenTraceDispatchInterceptor(tracer); } @Bean - public OpenTraceHandlerInterceptor traceHandlerInterceptor(Tracer tracer) { - return new OpenTraceHandlerInterceptor(tracer); + public OpenTraceHandlerInterceptor traceHandlerInterceptor(Tracer tracer, TracingProperties properties) { + return new OpenTraceHandlerInterceptor(tracer, properties); } @Bean @ConditionalOnMissingBean public QueryGateway queryGateway(Tracer tracer, QueryBus queryBus, + TracingProperties properties, OpenTraceDispatchInterceptor openTraceDispatchInterceptor, OpenTraceHandlerInterceptor openTraceHandlerInterceptor) { queryBus.registerHandlerInterceptor(openTraceHandlerInterceptor); TracingQueryGateway tracingQueryGateway = TracingQueryGateway.builder() .delegateQueryBus(queryBus) .tracer(tracer) + .tracingProperties(properties) .build(); tracingQueryGateway.registerDispatchInterceptor(openTraceDispatchInterceptor); return tracingQueryGateway; @@ -76,12 +94,14 @@ public QueryGateway queryGateway(Tracer tracer, @ConditionalOnMissingBean public CommandGateway commandGateway(Tracer tracer, CommandBus commandBus, + TracingProperties properties, OpenTraceDispatchInterceptor openTraceDispatchInterceptor, OpenTraceHandlerInterceptor openTraceHandlerInterceptor) { commandBus.registerHandlerInterceptor(openTraceHandlerInterceptor); TracingCommandGateway tracingCommandGateway = TracingCommandGateway.builder() .tracer(tracer) .delegateCommandBus(commandBus) + .tracingProperties(properties) .build(); tracingCommandGateway.registerDispatchInterceptor(openTraceDispatchInterceptor); return tracingCommandGateway; diff --git a/tracing/src/main/java/org/axonframework/extensions/tracing/OpenTraceHandlerInterceptor.java b/tracing/src/main/java/org/axonframework/extensions/tracing/OpenTraceHandlerInterceptor.java index fc1bdb9..677616d 100644 --- a/tracing/src/main/java/org/axonframework/extensions/tracing/OpenTraceHandlerInterceptor.java +++ b/tracing/src/main/java/org/axonframework/extensions/tracing/OpenTraceHandlerInterceptor.java @@ -21,11 +21,13 @@ import io.opentracing.Tracer; import io.opentracing.propagation.Format; import io.opentracing.tag.Tags; +import org.axonframework.commandhandling.CommandMessage; import org.axonframework.messaging.InterceptorChain; import org.axonframework.messaging.Message; import org.axonframework.messaging.MessageHandlerInterceptor; import org.axonframework.messaging.MetaData; import org.axonframework.messaging.unitofwork.UnitOfWork; +import org.axonframework.queryhandling.QueryMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,21 +48,33 @@ public class OpenTraceHandlerInterceptor implements MessageHandlerInterceptor message = unitOfWork.getMessage(); - String operationName = "handle_" + SpanUtils.messageName(message); + String operationNamePrefix; + if (message instanceof CommandMessage) { + operationNamePrefix = spanOperationNamePrefix.getCommand(); + } else if (message instanceof QueryMessage) { + operationNamePrefix = spanOperationNamePrefix.getQuery(); + } else { + //fixme what prefix to use here? + operationNamePrefix = spanOperationNamePrefix.getCommand(); + } + String operationName = operationNamePrefix + SpanUtils.messageName(message); Tracer.SpanBuilder spanBuilder = getParentSpan(message) .map(parentSpan -> tracer.buildSpan(operationName).asChildOf(parentSpan)) diff --git a/tracing/src/main/java/org/axonframework/extensions/tracing/TracingCommandGateway.java b/tracing/src/main/java/org/axonframework/extensions/tracing/TracingCommandGateway.java index 20cb746..ea34583 100644 --- a/tracing/src/main/java/org/axonframework/extensions/tracing/TracingCommandGateway.java +++ b/tracing/src/main/java/org/axonframework/extensions/tracing/TracingCommandGateway.java @@ -61,6 +61,7 @@ public class TracingCommandGateway implements CommandGateway { private final Tracer tracer; private final CommandGateway delegate; + private final String spanOperationNamePrefix; /** * Instantiate a {@link TracingCommandGateway} based on the fields contained in the {@link Builder}. @@ -74,6 +75,7 @@ protected TracingCommandGateway(Builder builder) { builder.validate(); this.tracer = builder.tracer; this.delegate = builder.buildDelegateCommandGateway(); + this.spanOperationNamePrefix = builder.tracingProperties.getDispatch().getOperationNamePrefix().getCommand(); } /** @@ -95,7 +97,7 @@ public static Builder builder() { @Override public void send(C command, CommandCallback callback) { CommandMessage cmd = GenericCommandMessage.asCommandMessage(command); - sendWithSpan("send_" + SpanUtils.messageName(cmd), cmd, (childSpan) -> { + sendWithSpan(spanOperationNamePrefix + SpanUtils.messageName(cmd), cmd, (childSpan) -> { CompletableFuture resultReceived = new CompletableFuture<>(); delegate.send(cmd, (CommandCallback) (commandMessage, commandResultMessage) -> { try (Scope ignored = tracer.activateSpan(childSpan)) { @@ -147,7 +149,7 @@ private R doSendAndExtract(Object command, FutureCallback futureCallback = new FutureCallback<>(); CommandMessage cmd = GenericCommandMessage.asCommandMessage(command); - sendWithSpan("sendAndWait_" + SpanUtils.messageName(cmd), cmd, (childSpan) -> { + sendWithSpan(spanOperationNamePrefix + SpanUtils.messageName(cmd), cmd, (childSpan) -> { delegate.send(cmd, futureCallback); futureCallback.thenRun(() -> childSpan.log("resultReceived")); @@ -208,6 +210,7 @@ public static class Builder { private Tracer tracer; private CommandBus delegateBus; private CommandGateway delegateGateway; + private TracingProperties tracingProperties; /** * Sets the {@link Tracer} used to set a {@link Span} on dispatched {@link CommandMessage}s. @@ -248,6 +251,17 @@ public Builder delegateCommandGateway(CommandGateway delegateGateway) { return this; } + /** + * Sets the tracing properties. Only those applicable to command dispatching will be used. + * + * @return the current Builder instance, for fluent interfacing + */ + public Builder tracingProperties(TracingProperties tracingProperties) { + assertNonNull(tracingProperties, "Tracing properties may not be null"); + this.tracingProperties = tracingProperties; + return this; + } + /** * Initializes a {@link TracingCommandGateway} as specified through this Builder. * @@ -279,6 +293,7 @@ private CommandGateway buildDelegateCommandGateway() { */ protected void validate() throws AxonConfigurationException { assertNonNull(tracer, "The Tracer is a hard requirement and should be provided"); + assertNonNull(tracingProperties, "The tracing properties is a hard requirement and should be provided"); if (delegateBus == null) { assertNonNull( delegateGateway, "The delegate CommandGateway is a hard requirement and should be provided" diff --git a/tracing/src/main/java/org/axonframework/extensions/tracing/TracingProperties.java b/tracing/src/main/java/org/axonframework/extensions/tracing/TracingProperties.java new file mode 100644 index 0000000..879ba41 --- /dev/null +++ b/tracing/src/main/java/org/axonframework/extensions/tracing/TracingProperties.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2010-2020. Axon Framework + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.axonframework.extensions.tracing; + +/** + * Properties for tracing customizations. + */ +public class TracingProperties { + + //fixme which coordinates are better? + //axon.tracing.{dispatch/handle}.{command/query} + // vs + //axon.tracing.{command/query}.{dispatch/handle} + + public TracingProperties() { + setDefaults(); + } + + private Handle handle = new Handle(); + + private Dispatch dispatch = new Dispatch(); + + public Handle getHandle() { + return handle; + } + + public void setHandle(Handle handle) { + this.handle = handle; + } + + public Dispatch getDispatch() { + return dispatch; + } + + public void setDispatch(Dispatch dispatch) { + this.dispatch = dispatch; + } + + private void setDefaults() { + dispatch.getOperationNamePrefix().setCommand("fire_"); + dispatch.getOperationNamePrefix().setQuery("ask_"); + handle.getOperationNamePrefix().setCommand("handle_"); + handle.getOperationNamePrefix().setQuery("serve_"); + } + + private static abstract class HasOperationNamePrefix { + + private OperationNamePrefix operationNamePrefix = new OperationNamePrefix(); + + public OperationNamePrefix getOperationNamePrefix() { + return operationNamePrefix; + } + + public void setOperationNamePrefix(OperationNamePrefix operationNamePrefix) { + this.operationNamePrefix = operationNamePrefix; + } + } + + /** + * Customizations for message dispatching. + */ + public static class Dispatch extends HasOperationNamePrefix { + } + + /** + * Customizations for message dispatching. + */ + public static class Handle extends HasOperationNamePrefix { + + } + + /** + * Span operation name for messages. + */ + public static class OperationNamePrefix { + + /** + * Span operation name prefix for commands. + *

+ * E.g. given it's {@code "send_"}, the name would be {@code "send_MyCommand"} + */ + private String command; + + /** + * Span operation name prefix for queries. + *

+ * E.g. given it's {@code "send_"}, the name would be {@code "send_MyQuery"} + */ + private String query; + + public String getCommand() { + return command; + } + + public void setCommand(String command) { + this.command = command; + } + + public String getQuery() { + return query; + } + + public void setQuery(String query) { + this.query = query; + } + } +} diff --git a/tracing/src/main/java/org/axonframework/extensions/tracing/TracingQueryGateway.java b/tracing/src/main/java/org/axonframework/extensions/tracing/TracingQueryGateway.java index 5f396c6..82d1efd 100644 --- a/tracing/src/main/java/org/axonframework/extensions/tracing/TracingQueryGateway.java +++ b/tracing/src/main/java/org/axonframework/extensions/tracing/TracingQueryGateway.java @@ -54,8 +54,13 @@ */ public class TracingQueryGateway implements QueryGateway { + private static final String SPAN_TAG_QUERY_TYPE = "axon.message.query.type"; + private enum QueryType{ POINT_TO_POINT, SCATTER_GATHER, SUBSCRIPTION } + + private final Tracer tracer; private final QueryGateway delegate; + private final String spanOperationNamePrefix; /** * Instantiate a {@link TracingQueryGateway} based on the fields contained in the {@link Builder}. @@ -69,6 +74,7 @@ protected TracingQueryGateway(Builder builder) { builder.validate(); this.tracer = builder.tracer; this.delegate = builder.buildDelegateQueryGateway(); + this.spanOperationNamePrefix = builder.tracingProperties.getDispatch().getOperationNamePrefix().getQuery(); } /** @@ -91,14 +97,17 @@ public static Builder builder() { public CompletableFuture query(String queryName, Q query, ResponseType responseType) { Message queryMessage = GenericMessage.asMessage(query); return getWithSpan( - "query_" + SpanUtils.messageName(query.getClass(), queryName), + spanOperationNamePrefix + SpanUtils.messageName(query.getClass(), queryName), queryMessage, queryName, - (childSpan) -> delegate.query(queryName, queryMessage, responseType) - .whenComplete((r, e) -> { - childSpan.log("resultReceived"); - childSpan.finish(); - }) + (childSpan) -> { + childSpan.setTag(SPAN_TAG_QUERY_TYPE, QueryType.POINT_TO_POINT.name()); + return delegate.query(queryName, queryMessage, responseType) + .whenComplete((r, e) -> { + childSpan.log("resultReceived"); + childSpan.finish(); + }); + } ); } @@ -110,14 +119,17 @@ public Stream scatterGather(String queryName, TimeUnit timeUnit) { Message queryMessage = GenericMessage.asMessage(query); return getWithSpan( - "scatterGather_" + SpanUtils.messageName(query.getClass(), queryName), + spanOperationNamePrefix + SpanUtils.messageName(query.getClass(), queryName), queryMessage, queryName, - (childSpan) -> delegate.scatterGather(queryName, queryMessage, responseType, timeout, timeUnit) - .onClose(() -> { - childSpan.log("resultReceived"); - childSpan.finish(); - }) + (childSpan) -> { + childSpan.setTag(SPAN_TAG_QUERY_TYPE, QueryType.SCATTER_GATHER.name()); + return delegate.scatterGather(queryName, queryMessage, responseType, timeout, timeUnit) + .onClose(() -> { + childSpan.log("resultReceived"); + childSpan.finish(); + }); + } ); } @@ -130,10 +142,11 @@ public SubscriptionQueryResult subscriptionQuery(String queryNam int updateBufferSize) { Message queryMessage = GenericMessage.asMessage(query); return getWithSpan( - "subscriptionQuery_" + SpanUtils.messageName(query.getClass(), queryName), + spanOperationNamePrefix + SpanUtils.messageName(query.getClass(), queryName), queryMessage, queryName, (childSpan) -> { + childSpan.setTag(SPAN_TAG_QUERY_TYPE, QueryType.SUBSCRIPTION.name()); SubscriptionQueryResult subscriptionQueryResult = delegate.subscriptionQuery( queryName, queryMessage, initialResponseType, updateResponseType, backpressure, updateBufferSize @@ -179,6 +192,7 @@ public static class Builder { private Tracer tracer; private QueryBus delegateBus; private QueryGateway delegateGateway; + private TracingProperties tracingProperties; /** * Sets the {@link Tracer} used to set a {@link Span} on dispatched {@link QueryMessage}s. @@ -241,6 +255,17 @@ private QueryGateway buildDelegateQueryGateway() { : DefaultQueryGateway.builder().queryBus(delegateBus).build(); } + /** + * Sets the tracing properties. Only those applicable to query dispatching will be used. + * + * @return the current Builder instance, for fluent interfacing + */ + public Builder tracingProperties(TracingProperties tracingProperties) { + assertNonNull(tracingProperties, "Tracing properties may not be null"); + this.tracingProperties = tracingProperties; + return this; + } + /** * Validate whether the fields contained in this Builder as set accordingly. * @@ -249,6 +274,7 @@ private QueryGateway buildDelegateQueryGateway() { */ protected void validate() throws AxonConfigurationException { assertNonNull(tracer, "The Tracer is a hard requirement and should be provided"); + assertNonNull(tracingProperties, "The tracing properties is a hard requirement and should be provided"); if (delegateBus == null) { assertNonNull( delegateGateway, "The delegate QueryGateway is a hard requirement and should be provided"