Giter VIP home page Giter VIP logo

extension-tracing's Introduction

Axon Framework logo

Axon Framework

Build modern event-driven systems with AxonIQ technology.
Product Description »

Code Samples Repo · Technical Overview · Feature / Bug Request

Axon Framework

Maven Central Build Status SonarCloud Status

Axon Framework is a framework for building evolutionary, event-driven microservice systems based on the principles of Domain-Driven Design (DDD), Command-Query Responsibility Separation (CQRS), and Event Sourcing.

Bootstrap logo

Axon Framework provides you with the necessary building blocks to follow these principles. Examples of building blocks are aggregate design handles, aggregate repositories, command buses, saga design handles, event stores, query buses, and more. The framework provides sensible defaults for all of these components out of the box.

The messaging support for commands, events, and queries is at the core of these building blocks. It is the messaging basics that enable an evolutionary approach towards microservices through the location transparency they provide.

Axon will also assist in distributing applications to support scalability or fault tolerance, for example. The most accessible and quick road forward would be to use Axon Server to seamlessly adjust message buses to distributed implementations. Axon Server provides a distributed command bus, event bus, query bus, and an efficient event store implementation for scalable event sourcing. Additionally, the Axon Framework organization has several extensions that can help in this space.

All this helps to create a well-structured application without worrying about the infrastructure. Hence, your focus can shift from non-functional requirements to your business functionality.

For more information on anything Axon, please visit our website, http://axoniq.io.

Getting started

Numerous resources can help you on your journey in using Axon Framework. A good starting point is AxonIQ Developer Portal, which provides links to resources like blogs, videos, and descriptions.

Furthermore, below are several other helpful resources:

  • The quickstart page of the documentation provides a simplified entry point into the framework with the quickstart project.
  • We have our very own academy! The introductory courses are free, followed by more in-depth (paid) courses.
  • When ready, you can quickly and easily start your very own Axon Framework based application at https://start.axoniq.io/. Note that this solution is only feasible if you want to stick to the Spring ecosphere.
  • The reference guide explains all of the components maintained within Axon Framework's products.
  • If the guide doesn't help, our forum provides a place to ask questions you have during development.
  • The hotel demo shows a fleshed-out example of using Axon Framework.
  • The code samples repository contains more in-depth samples you can benefit from.

Receiving help

Are you having trouble using any of our libraries or products? Know that we want to help you out the best we can! There are a couple of things to consider when you're traversing anything Axon:

  • Checking the reference guide should be your first stop.
  • When the reference guide does not cover your predicament, we would greatly appreciate it if you could file an issue for it.
  • Our forum provides a space to communicate with the Axon community to help you out. AxonIQ developers will help you out on a best-effort basis. And if you know how to help someone else, we greatly appreciate your contributions!
  • We also monitor Stack Overflow for any question tagged with axon. Similarly to the forum, AxonIQ developers help out on a best-effort basis.

Feature requests and issue reporting

We use GitHub's issue tracking system) for new feature requests, framework enhancements, and bugs. Before filing an issue, please verify that it's not already reported by someone else. Furthermore, make sure you are adding the issue to the correct repository!

When filing bugs:

  • A description of your setup and what's happening helps us figure out what the issue might be.
  • Do not forget to provide the versions of the Axon products you're using, as well as the language and version.
  • If possible, share a stack trace. Please use Markdown semantics by starting and ending the trace with three backticks (```).

When filing a feature or enhancement:

  • Please provide a description of the feature or enhancement at hand. Adding why you think this would be beneficial is also a great help to us.
  • (Pseudo-)Code snippets showing what it might look like will help us understand your suggestion better. Similarly as with bugs, please use Markdown semantics for code snippets, starting and ending with three backticks (```).
  • If you have any thoughts on where to plug this into the framework, that would be very helpful too.
  • Lastly, we value contributions to the framework highly. So please provide a Pull Request as well!

extension-tracing's People

Contributors

abuijze avatar aupodogov avatar azzazzel avatar codedrivenmitch avatar corradom avatar dependabot[bot] avatar dzonekl avatar gerloh avatar ghilaima avatar github-actions[bot] avatar gklijs avatar jvanderkallen avatar kad-hesseg avatar lfgcampos avatar pasquatch913 avatar schananas avatar smcvb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

extension-tracing's Issues

Scope not closed?

I don't see the first created parent scope being closed. According to the opentracing documentation :
"It is a programming error to neglect to call {@link Scope#close()} on the returned instance "
Probably the scope is closed when the thread ends, but that could lead to strange behaviour when using a threadpool.
Indeed i sometimes see (too long) lived traces, when used with other tracing instrumentated modules like "opentracing-spring-jaeger-web-starter" itself.
Of course this is only an issue if axon is creating the scope, which, mostly, is not the case, as there is almost always a rest endpoind or some other starting point, but i think it should not be a default assumption.

Several implementation suggestions based on sample project

So, I have finally finished the MVP of what I was trying to do with tracing and it is available here:
https://github.com/Sam-Kruglov/bike-rental-demo

I ask you to take a look and we can discuss whether the way I changed this extension fits your vision or not. When we agree on something I will create a PR. Sorry for not creating a PR right away, that would probably be easier, but it will take time and I already have this working, so while I do that, you can already check it out.

Screen Shot 2019-06-17 at 15 26 56

New features

  • At every step, we have our Message payload included in the tags of the span.
  • All messages are logged:
    • Command payloads at DEBUG
    • User-friendly events at INFO
    • Event payloads at DEBUG
    • Query payloads at TRACE
    • Query return values at TRACE
  • Span names actually contain the Message names, like "Send BikeRentCommand" instead of "sendCommandMessage"
  • Event publishing is a separate Span with a user-friendly event name.
  • All message handler exceptions are caught
    • Expected exceptions are only logged at DEBUG without a stacktrace and reported to the span as a log with an error.expected key.
    • Unexpected exceptions are logged at ERROR with a stacktrace and reported to the span as a log with an error.unexpected key.
  • All application logs mentioned above include tracer id.
  • Event handling Spans have names that are dynamically resolved with AOP using package names (questionable, but otherwise you only have application name to know the distinguish the projections)

Instructions to run

  1. Checkout the master branch
  2. Start Jaeger UI (you can use the included docker-componse.yml for that, localhost:16686)
  3. Start the app.
  4. Use requests.http to fire some commands
  5. Look at Jaeger UI

Add aggegrate identifier as trace tag

Would it be nice/usefull to add the aggregateIdentifier als a tracing tag ?
(Only in case of a DomainEventMessage)
That way it is more easy to find a specific trace without having to lookup the traceId first, or by just looking at the start time .

Unnecessary span tags

org.axonframework.extensions.tracing.SpanUtils#withMessageTags creates some tags for a message:
message id, message type (query/command name or payload class name), payload class name, command name, aggregate id.
It is added in TracingCommandGateway and in OpenTraceHandlerInterceptor.

I think the only useful are the payload class name and aggregate id, others can be removed. In my project, I just included the whole payload into a tag. These messages are supposed to be small anyway.


P.S. it itches to make this refactoring inside the interceptor:

private Optional<SpanContext> getParentSpan(Message<?> message) {
    MetaData metaData = message.getMetaData();
    MapExtractor extractor = new MapExtractor(metaData);
    try {
        return Optional.ofNullable(tracer.extract(Format.Builtin.TEXT_MAP, extractor));
    } catch (IllegalArgumentException e) {
        log.error("corrupted parent span", e); //this shouldn't happen but if it does, better print the stacktrace
        return Optional.empty();
    }
}

Any example for integrating with skywalking?

Feature Description

Integrate tracing with apache-skywalking

Current Behaviour

Opentracing is no longer active, and skywalking is only supporting opentracing 0.30;
Extension-tracing is built with opentracing >=0.33?

Wanted Behaviour

Integrate with skywalking directly

Possible Workarounds

No idea

EventBus.publish() does not propagate context

Basic information

  • Axon Framework version: 4.5.9
  • JDK version: 11.0.13
  • Tracing Extension version: 4.5.2
  • Complete executable reproducer if available (e.g. GitHub Repo):

Steps to reproduce

We have Application A that has its tracing context that publish an event using EventBus.
This event is handled by Application B, that does not continue with Application A context but starts a new tracing context.

The problem is that the OpenTraceDispatchInterceptor doesn't intercept the EventBus.publish() call and then does not extract
tracingId from tracing context and does not add metadata to published event.

As workaround, we insert manually the header in the published message.

Our code below:

@AllArgsConstructor
public class SyncEventPublisherAxon implements SyncEventPublisher {

   private final EventBus eventBus;
   private final Tracer tracer;

   @Override
   public void publish( final SyncEvent event ) {
      eventBus.publish( message( event ) );

   }

   protected GenericEventMessage<SyncEvent> message( final SyncEvent syncEvent ) {
      final ScopeManager scopeManager = tracer.scopeManager();
      final Optional<SpanContext> spanContextOptional = Optional.ofNullable( scopeManager )
            .map( ScopeManager::activeSpan )
            .map( Span::context );

      final GenericEventMessage<SyncEvent> message = new GenericEventMessage<>( syncEvent );
      return spanContextOptional.map( ( spanContext ) -> {
         MapInjector injector = new MapInjector();
         this.tracer.inject( spanContext, Format.Builtin.TEXT_MAP, injector );
         return message.andMetaData( injector.getMetaData() );
      } ).orElseGet( () -> message );
   }

}

Expected behaviour

We expect that OpenTracing context 'continues' between applications.

Actual behaviour

Event published in the EventStore by Application A does not contains jaeger uber-trace-id metadata and then Application B starts a new context.

Operation names proposal

I would like to suggest other operation names for the spans. Now it is
"sendCommand" , "sendAndWaitCommand" and "handleEventMessage" ,"handleCommand" ,
but it also "handle<QueryName>".
This not consistent and not really clear when looking at the traces in Jeager.
I would like to propose :
"send<CommandName>"
"sendAndWait<CommandName>"
"send<QueryName>"
"handle<CommandName>
"handle<QueryName>"
"handle<EventPayloadtype>
Also use simpleNames if the Command and Query classNames are not overruled by commandName and queryName

From the messagenames it mostly is clear that it is an event, command or query.

See #25

So it looks like this :

image

TracingCommandGateway should deliver CommandMessage to parent send() method

In send you call GenericCommandMessage#asCommandMessage and use the result to build the span tags, including message id. But you actually send just the payload in super#send, so the command message is created again somewhere up the chain.

I don't know what is the best solution here. I am writing my own gateway, maybe if I get somewhere nice I will submit a PR

TracingCommandGateway clean up suggestions

The private sendWithSpan method uses a wrong parameter order for the SpanConsumer functional interface.
Specifically, I am talking about the consumer parameter, where parent and child places are mixed

Also, there is no need for passing Tracer instance there since it's a field.

Dedicated spans for events

In #15 @smcvb said the following:

For now we feel that event publication is always paired with another type of message.

But take a look at this picture
image

Here bike registered event (span operation name is Bike #1234 registered in Vilnius ) has a dedicated span which is a child of handle register bike command span. I think it makes perfect sense. I also create a separate span here for each handling of this event, i.e. for each event listener.

This is the default behavior:
Screenshot 2020-04-22 at 17 59 24

Here is some code to make it work:

interface Describable {
    fun description(): String
}

data class BikeRegisteredEvent(val bikeId: String, val location: String) : Describable {
    override fun description() = "Bike #$bikeId: registered in $location"
}

fun Tracer.extractContextFrom(message: Message<*>): SpanContext? {
    return extract(
            Format.Builtin.TEXT_MAP,
            MapExtractor(message.metaData)
    )
}

/**
 * returns `null` if there is no active span
 */
@Suppress("UNCHECKED_CAST")
fun <T : Message<*>> T.andSpanMetaData(tracer: Tracer): T? {
    val activeSpan = tracer.activeSpan() ?: return null
    val injector = MapInjector()
    tracer.inject(activeSpan.context(), Format.Builtin.TEXT_MAP, injector)
    return andMetaData(injector.metaData) as T
}

@Bean
fun traceDispatchInterceptor(tracer: Tracer): MessageDispatchInterceptor<Message<*>> =
        object : OpenTraceDispatchInterceptor(tracer) {
            override fun handle(messages: MutableList<out Message<*>>?)
                    : BiFunction<Int, Message<*>, Message<*>> {
                return BiFunction { _, message ->
                    val payloadName = message.payloadType.simpleName
                    val span = if (message is EventMessage<*>) {
                        val operationName = try {
                            (message.payload as Describable).description()
                        } catch (e: ClassCastException) {
                            log.error("$payloadName does not implement ${Describable::class.simpleName}")
                            "Publish $payloadName"
                        }
                        tracer.buildSpan(operationName).start()
                    } else {
                        //parent must be present since it triggered the dispatching process
                        tracer.activeSpan()!!.setOperationName("Send $payloadName")
                    }
                    span.setTag(...)
                    if (message is EventMessage<*>) {
                        CurrentUnitOfWork.get().onCleanup { span.finish() }
                        return@BiFunction tracer.activateSpan(span).use { message.andSpanMetaData(tracer)!! }
                    }
                    message.andSpanMetaData(tracer)!!
                }
            }
        }

val EVENT_UOW_SPAN_KEY = "span"

@Bean
fun traceHandlerInterceptor(tracer: Tracer): MessageHandlerInterceptor<Message<*>> =
        object : OpenTraceHandlerInterceptor(tracer) {
            override fun handle(
                    unitOfWork: UnitOfWork<out Message<*>>,
                    interceptorChain: InterceptorChain
            ): Any? {
                val operationName = "Handle ${unitOfWork.message.payloadType.simpleName}"
                //span context must be present in the message metadata for handlers
                val parentSpan = tracer.extractContextFrom(unitOfWork.message)!!
                val span = tracer.buildSpan(operationName)
                        .asChildOf(parentSpan)
                        .withTag(...)
                        .start()
                val scope = tracer.activateSpan(span)
                //for queries this would be printed at the end, not very useful
                if (unitOfWork.message !is QueryMessage<*, *>) {
                    unitOfWork.onPrepareCommit { span.log("finished handling") }
                    if (unitOfWork.message is EventMessage<*>) {
                        /* If we simply embed this Span into this Message
                         * we would only be able to extract it into SpanContext
                         * which doesn't allow to change the Span operation name.
                         * see TracingEventHandlerAspect
                         */
                        unitOfWork.resources()[EVENT_UOW_SPAN_KEY] = span
                    }
                }
                unitOfWork.onCleanup {
                    scope.close()
                    span.finish()
                }
                return interceptorChain.proceed()
            }
        }

//could be as optional maven scope
@Aspect
@Service
class TracingEventHandlerAspect {
    companion object {
        private const val READ_MODEL_PACKAGE = "com.myproject.projections"
    }
    @Around("projectionServiceMethods() && @annotation(org.axonframework.eventhandling.EventHandler)")
    fun logStuff(p: ProceedingJoinPoint): Any? {
        val packageName = p.signature.declaringType.`package`.name
        //capitalized package after "read"
        val projectionName = packageName
                .replace(READ_MODEL_PACKAGE, "")
                .split(".")
                .joinToString(" ") { it.capitalize() }
                .trim()
        CurrentUnitOfWork.get()
                .getResource<Span>(EVENT_UOW_SPAN_KEY)
                .setOperationName("Updating $projectionName Projection")
        return p.proceed()
    }
    @Pointcut("execution(* $READ_MODEL_PACKAGE..*ViewService.*(..))")
    fun projectionServiceMethods() = Unit
}

package com.myproject.projections.main
@Service
class BikeViewService(private val repo: BikeViewRepo) {
    @EventHandler
    fun registered(event: BikeRegisteredEvent) {
        repo.save(BikeView(event.bikeId, event.location))
    }
}

TracingAutoConfiguration should not be auto-configured after InfraConfiguration

Basic information

  • Axon Framework version: 4.4.x
  • JDK version: any
  • Tracing Extension version: 4.4
  • Complete executable reproducer if available (e.g. GitHub Repo): look here

Steps to reproduce

It is similar to the linked issue on extension-kafka and was also noticed by our users on our discuss.

Expected behaviour

TracingCommandGateway and TracingQueryGateway should be used.

Actual behaviour

Default command and query gateway is always used, because AxonAutoConfiguration goes first, and defaultCommandGateway is created there. So tracingCommandGateway in TracingAutoConfiguration is ignored.

Throwing CommandExecutionException triggers a AxonServerRemoteCommandHandlingException

When throwing a CommandExecutionException the expected behavior is that my method decorated with @ExceptionHandler(CommandExecutionException.class) is called.

But once I've added the tracing extension as a dependency instead the AxonServerRemoteCommandHandlingException is triggered thus not reaching my handler.

Creating another handler to AxonServerRemoteCommandHandlingException is not a possibility as I need the getDetails for my use case.

Support tracing of Subscription queries

When emitting a query (i.e. From an event handler) , we should propagate trace, so the subscription query is also traced. This should be implemented as part of pulling this repository out of it's milestone-state.

Suggesting to put traceId into console logs

In a production environment, looking at console logs without traceId is nearly impossible because multiple requests from multiple machines write their own things into the same file, and it's very hard to make sense of it.

It is all right if we're looking at the logs via Open Tracing UI that are attached to a span but if we are talking about console logs, it's a different story.

Sensible to include and simple enough to achieve on a users own accord I think. Don't think this requires anything extension specific right now.
(c) @smcvb #15

I think library user is supposed to deal with it themselves in a usual case but this extension is aware of tracing, so, it should use it IMO.

Example:

log.info("[${tracer.activeSpan()?.context()?.toTraceId()}]: ${command.payloadType.simpleName} has been dispatched")

Will print:

[e66dc77b8a1e813b]: RegisterBikeCommand has been dispatched

Property for disabling an extension

Can you consider adding a property that will disable tracing?

It might be more convenient than poking around with dependencies if tracing, for some reason, isn't desired in different environments (dev, qa, prod, etc.).

Tnx.

Missing TracingEventGateway?

See picture in #15. I have my own tracing event store that sets up the span.
There is a BikeRegisteredEvent with custom span name "Bike #1234 registered in Vilnius" and it has 2 child spans for each projection that listens to this event. The span is finished as soon as the void publish(..) method returns (not sure if that's good enough though). There are some extra interceptors used to tie projection to that span.

I would like to see something like that out of the box.

The bean 'commandGateway', defined in class path resource [.../AxonAutoConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [.../TracingAutoConfiguration.class] and overriding is disabled.

Running with Spring Boot v2.3.3.RELEASE, Spring v5.2.8.RELEASE
When combining axon libraries for local development:

        <dependency>
            <groupId>org.axonframework</groupId>
            <artifactId>axon-spring-boot-starter</artifactId>
            <version>${axonframework.version}</version>
        </dependency>

        <dependency>
            <groupId>org.axonframework.extensions.tracing</groupId>
            <artifactId>axon-tracing-spring-boot-starter</artifactId>
            <version>4.3.1</version>
        </dependency>

Local development (mvn spring-boot:run) is unable to proceed due to error mentioned in the title.
This does not happen in any other environment aside from local dev. For the reference, I have the following configuration (and haven't configured any environment variable):

opentracing:
  jaeger:
    enabled: ${JAEGER_ENABLED:false}
    service-name: ${spring.application.name}
    udp-sender:
      host: "${JAEGER_HOST:localhost}"
      port: "${JAEGER_PORT:6831}"

Ideally (and aside from this bug), this extension should be picking a configuration flag instead of looking up for the presence of io.opentracking.Tracer class.

TracingQueryGateway does not implement streamingQuery

Basic information

  • Axon Framework version: 4.6.0-SNAPSHOT
  • JDK version: 16
  • Tracing Extension version: latest
  • Complete executable reproducer if available (e.g. GitHub Repo):

Steps to reproduce

Create an application using streaming queries and add the tracing extension.

Expected behaviour

Streaming query should work.

Actual behaviour

Throws exception on the streaming query:

java.lang.AbstractMethodError: Receiver class org.axonframework.extensions.tracing.TracingQueryGateway does not define or inherit an implementation of the resolved method 'abstract org.reactivestreams.Publisher streamingQuery(java.lang.String, java.lang.Object, java.lang.Class)' of interface org.axonframework.queryhandling.QueryGateway.

Opentracing removed deprecated methods

If I declare this:

<dependency>
    <groupId>org.axonframework.extensions.tracing</groupId>
    <artifactId>axon-tracing-spring-boot-starter</artifactId>
    <version>4.2-M1</version>
</dependency>
<dependency>
    <groupId>io.opentracing.contrib</groupId>
    <artifactId>opentracing-spring-jaeger-web-starter</artifactId>
    <version>3.1.1</version>
</dependency>

You are using deprecated methods inside opentracing-api:0.32.0, I bring in 0.33.0 which removed those methods, so, when I browse axon sources, I see "red method calls". For example, see io.opentracing.Tracer.SpanBuilder.

Also, if you visit opentracing.io, you will see they seem to be called OpenTelemetry now.

Support opentracing-api 0.33.0

For the moment the library is compatible with opentracing-api 0.31.0. Version 0.33.0 introduces non-backward compatible changes.

The extension must be reworked to use new opentracing API.

QueryGateway throws NPE on query with null payload

When using the TracingQueryGateway with a null query payload, the gateway throws a NullPointerException as it tries to deduce the message name from the payload of the query payload.

Instead, it should be able to rely entirely on the name of the query, as this already defaults to the payload and will only be different when the query name is explicitly configured.

Migrate tests to JUnit5

In order to be aligned with the Axon ecosystem, unit tests need to be migrated to JUnit5.

Subscription query in TracingQueryGateway won't work?

I didn't test it but your test doesn't look promising as it's using simply new DefaultSubscriptionQueryResult<>(Mono.empty(), Flux.empty(), () -> true) as a stub result.

If we unwrap the code this is essentially what it's doing:

Span childSpan = tracer.buildSpan(operation).start();
try (Scope ignored = tracer.activateSpan(childSpan)) {
    return new TraceableSubscriptionQueryResult<>(
                                   delegate.subscriptionQuery(
                                           queryName,
                                           query,
                                           initialResponseType,
                                           updateResponseType,
                                           backpressure,
                                           updateBufferSize),
                                   childSpan
                           ));
}
...
class TraceableSubscriptionQueryResult{
...
  @Override
  public Mono<I> initialResult() {
      span.log("initialResultReceived");
      return initialResult;
  }
  @Override
  public Flux<U> updates() {
      return updates.doOnEach(ignored -> span.log("updateReceived"));
  }
}

I think that when you call delegate.subscriptionQuery(...) it's going to return an async result and then populate it later. If that's true, the childSpan scope will close as soon as that happens. Later, on initialResult and on update we will call childSpan.log(...) which will fail for sure as it is out of scope.

One can use StepFerifier somehow or the Awaitility library introduced in #39 to test this with a delay.


P.S. Can we replace SpanSupplier<T> with java.util.function.Supplier<Span>?

Define default trace operation name and tags from messages.

Message information like:

  • Message id
  • Message type (Command, Query, Event etc..)
  • Payload type.
  • Payload size

Could be added as tags to the span or as the operation name of a span.

Now child spans are created with the following names:

Command Gateway -> child span named 'Command'
Query Gateway -> child span named 'Query'
HandlerInterceptor -> child span named 'Extracting'

DispatchInterceptor -> no child span.
CorrelationDataProvider -> no child span.

Propose:

Operation name

  • Message payload type

Tags:

  • Axon message id.
  • Message type.
  • Message payload type
  • Message payload size

@abuijze @smcvb what do you think?

Existing parent span not deactivated when invoking CommandGateway

Current Behavior
If I place a command on the TracingCommandGateway with send or sendAndWait and there already is an active span before the TracingCommandGateway is invoked then it will not be deactivated. I close the span after invoking the TracingCommandGateway. If I don't invoke the TracingCommandGateway then the span is closed.

Expected Behavior
I expect the span that was created before invoking the TracingCommandGateway to be deactivated at the end of the try with statement. I have attached a project to demonstrate the behavior. Am I doing something wrong or is this extension exhibiting undesirable behavior? If I don't invoke the CommandGateway within the try with statement then the span is deactivated.

Auto configuration is not flexible enough

I just want to define @Beans of MessageDispatchInterceptor, MessageHandlerInterceptor, and ListenerInvocationErrorHandler and I don't want to deal with anything else.

Take a look at how I'm defining those beans in TracingAxonConfig and how I'm catching those in AxonConfig.

Note, how I'm using @Bean @Order(0) on the tracing interceptor, @Bean @Onder(1) on exception handling ones, and @Bean (comes last) on others. That controls the ordering in the List that I'm catching later, and, mainly, the invocation order.

This concerns both TracingAutoConfiguration and AxonAutoConfiguration (from core).

Suggestions on exception handling

  1. On any exception caught, there should be a tag error=true set and some log entries like error.*, event, message, stack (see OpenTracing conventions). I also propose to have error.expected=true/false but it's minor.
  2. I think point-to-point queries don't have any error handling at all.
  3. I think eventHandler.getTargetType().getSimpleName() inside LoggingErrorHandler will include spring proxy like *EnhancerBySpring*.

See my error handlers in TracingAxonConfig. Logging. ErrorLoggingConfig

Validate OpenTelemetry API

Enhancement Description

This extension is based on the Open Tracing API. As stated on their website both Open Tracing and Open Census have merged in OpenTelemetry. It would be beneficial if we move over to this newer, merged API, to stay on track with recent developments in the tracing ecosphere.

Current Behaviour

This extension uses the OpenTracing API to support tracing.

Wanted Behaviour

Use the OpenTelemetry API to support tracing.

Support tracing of Scatter Gather queries

The current TracingQueryGateway does not support tracing for usages of the QueryGateway#scatterGather. This should be implemented as part of pulling this repository out of it's milestone-state.

Make FailureLoggingCallback into an optional interceptor.

In #15, I had to override a DefaultCommandGateway in order to copy-paste the send method to exclude the FailureLoggingCallback so that it doesn't pollute my logs. I have my own very cool logging interceptors that log all messages (see io.axoniq.demo.bikerental.config.tracing.TracingAxonConfig.Logging):

  • Command payloads at DEBUG
  • User-friendly events at INFO
  • Event payloads at DEBUG
  • Query payloads at TRACE
  • Query return values at TRACE

I suggest that you turn that callback into an interceptor that I can disable/replace or/and you can take my idea for logging.

P.S. whenever we log anything in the tracing context, we must also log trace id or it's going to be a mess.

Suggestion to not use camelcase for span tags

Currently, we have this:

private static final String TAG_AXON_ID = "axon.message.id";
private static final String TAG_AXON_AGGREGATE_ID = "axon.message.aggregateIdentifier";
private static final String TAG_AXON_MESSAGE_TYPE = "axon.message.type";
private static final String TAG_AXON_PAYLOAD_TYPE = "axon.message.payloadType";
private static final String TAG_AXON_MESSAGE_NAME = "axon.message.messageName";

I feel this would be better:

private static final String TAG_AXON_ID = "axon.message.id";
private static final String TAG_AXON_AGGREGATE_ID = "axon.message.aggregate.id";
private static final String TAG_AXON_MESSAGE_TYPE = "axon.message.type";
private static final String TAG_AXON_PAYLOAD_TYPE = "axon.message.payload.type";
private static final String TAG_AXON_MESSAGE_NAME = "axon.message.name";

My reasons:

  • cleaner look
  • Jaeger UI displays them in lower case
    Screenshot 2020-04-22 at 18 45 27

Share the traceId between threads

I just configured Jaeger, but then decided to also print traceId in the application logs.
First, Jaeger has an issue with it: opentracing-contrib/java-spring-jaeger#49

Second, when I use the workaround, I find that only the first log entry contains the traceId and others don't.
I suspect this is due to execution being scattered on different threads.

I would suspect that extending an executor mentioned here would help.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.