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 Enrich API in NServiceBus OpenTelemetry Integration #7315

Open
benv-nti opened this issue Mar 19, 2025 · 5 comments
Open

Add Enrich API in NServiceBus OpenTelemetry Integration #7315

benv-nti opened this issue Mar 19, 2025 · 5 comments
Assignees

Comments

@benv-nti
Copy link

benv-nti commented Mar 19, 2025

Describe the suggested improvement

Is your improvement related to a problem? Please describe.

In our handlers, we typically do not catch exceptions. We let NServiceBus handle them (to retry) and log them via OpenTelemetry.
For some types of exceptions (like SqlException and ApiException<ProblemDetails>), we want to log additional details from the exception. Ideally, we would like to add these as tags to the exception that NServiceBus is logging.

Describe the suggested solution

The pattern among other OpenTelemetry instrumentation providers seems to be to provide an Enrich API, which allows applications to access the objects being logged and enrich the associated Activity with additional information. The HTTP Instrumentation library is one example.
For NServiceBus, I envision something similar:

endpointConfiguration.EnableOpenTelemetry(options => {
   options.EnrichWithException = (activity, exception) =>
        {
            if (exception is SqlException sqlex)
               activity.SetTag("exception.number", sqlex.Number);
        };
});

Describe alternatives you've considered

Catch/Log/Rethrow

In our handlers, catch the exceptions we are interested in, log the properties of interest (but not the exception), and rethrow for NServiceBus.

public async Task Handle(MyEvent message, IMessageHandlerContext context)
{
   try
   {
      ...
   }
   catch (SqlException ex)
   {
      Activity.Current?.SetTag("exception.number", ex.Number);
      throw;
   }
}

This gives us the desired outcome, but adds a fair amount of boilerplate code to our handlers.

OpenTelemetry Processor

I looked into creating a custom ActivityProcessor. This gives you a hook to modify an Activity when it starts and when it ends. Unfortunately, at this level we don't have access to the raw exception object - it's already been converted to event tags on the activity.

Additional Context

No response

@andreasohlund
Copy link
Member

Thanks for the feedback @benv-nti you can already achieve this with our messaging pipeline by adding a new behavior in the invoke handler part of the pipeline that adds the tag using the code you already provided.

Would that work for you?

@andreasohlund andreasohlund self-assigned this Mar 20, 2025
@benv-nti
Copy link
Author

That's a good idea, it basically allows us to write the try/catch once and apply it to all handlers, right?

Looking into this a little deeper, what I really want is to add tags to the ActivityEvent being logged here - this way the tags are associated with the exception itself rather than the Activity (which is all we have access to in a try/catch). Any thoughts on how to do that?

@benv-nti
Copy link
Author

I tried out the behavior, and it's slightly less useful than I expected. Since the behavior is around/outside the handler, it is also outside of the Activity for the handler. So adding tags to Activity.Current from the behavior adds them to parent activity, associated with processing the message. So if there were multiple handlers, you wouldn't know which one the exception message came from.

Image

Tagging from behavior shows up at the highest level (1).
Tagging from a try/catch in the handler itself shows up at level (2).
Ideally I would like to tag on the exception / ActivityEvent itself (3).

@andreasohlund
Copy link
Member

You are right, we have no way of intercepting individual handlers since the terminator is creating the activity context https://github.com/Particular/NServiceBus/blob/master/src/NServiceBus.Core/Pipeline/Incoming/InvokeHandlerTerminator.cs#L18

@lailabougria @danielmarbach I wonder if we should create that activity context in the connector instead https://github.com/Particular/NServiceBus/blob/master/src/NServiceBus.Core/Pipeline/Incoming/LoadHandlersConnector.cs#L44 ?

That would allow users to add to it via a invoke handler behaviour like @benv-nti mentions?

@danielmarbach
Copy link
Contributor

@andreasohlund My understanding was that the original design idea was to have the activity surrounding the invocation of the handler and not the entire part of the pipeline that leads to invoke the handler. What you are suggesting would work but would deviate from that original design decision (if I recall correctly, which might not be the case since I was only tangentially involved).

By looking at the message handling pipeline though, you can argue from an exception perspective, a single handler throwing leads to the entire pipeline being aborted and that is quite likely why the information is centered around the fact that an exception occurred. That being said, even with the current design the handler information would be available over the exception data. While you would still have to set the information on the parent activity, it would give you the desired information (for now, as a workaround)

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

No branches or pull requests

3 participants