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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Sending errors in Spring WebFlux integration. #1819

Closed
wants to merge 2 commits into from

Conversation

maciejwalkowiak
Copy link
Contributor

@maciejwalkowiak maciejwalkowiak commented Nov 25, 2021

馃摐 Description

Fix sending errors in Spring WebFlux integration.

This time, without trying to find a workaround but rather using recommended reactive/reactor way of doing things: instead of copying hub every time executing thread changes, hub is set on Reactor context, which means:

Pros:

  • we don't affect the performance as much as we did before - as copying hub happens only once per request
  • there is no way that threads interfere with each other

Cons:

  • when Sentry static methods are used, scope is not attached to events
  • Logging frameworks also have no access to Sentry scope

馃挕 Motivation and Context

Fixes #1790

馃挌 How did you test it?

Integration tests, sample project running on the side.

馃摑 Checklist

  • I reviewed the submitted code
  • I added tests to verify the changes
  • I updated the docs if needed
  • No breaking changes

Only classes marked as experimental are changed.

馃敭 Next steps

@maciejwalkowiak maciejwalkowiak marked this pull request as ready for review November 25, 2021 19:04
@codecov-commenter
Copy link

codecov-commenter commented Nov 25, 2021

Codecov Report

Merging #1819 (8276861) into main (9f87477) will increase coverage by 0.04%.
The diff coverage is 80.85%.

Impacted file tree graph

@@             Coverage Diff              @@
##               main    #1819      +/-   ##
============================================
+ Coverage     75.51%   75.56%   +0.04%     
- Complexity     2239     2243       +4     
============================================
  Files           225      225              
  Lines          8001     8016      +15     
  Branches        846      850       +4     
============================================
+ Hits           6042     6057      +15     
+ Misses         1549     1544       -5     
- Partials        410      415       +5     
Impacted Files Coverage 螖
...o/sentry/spring/webflux/SentryRequestResolver.java 73.68% <33.33%> (-2.51%) 猬囷笍
...ntry/spring/webflux/SentryWebExceptionHandler.java 86.66% <75.00%> (-6.20%) 猬囷笍
...n/java/io/sentry/spring/webflux/SentryReactor.java 76.92% <76.92%> (酶)
...java/io/sentry/spring/webflux/SentryWebFilter.java 90.32% <88.00%> (-9.68%) 猬囷笍
...ry/spring/boot/SentryWebfluxAutoConfiguration.java 100.00% <100.00%> (+33.33%) 猬嗭笍
...n/java/io/sentry/transport/ReusableCountLatch.java 82.35% <0.00%> (-5.89%) 猬囷笍

Continue to review full report at Codecov.

Legend - Click here to learn more
螖 = absolute <relative> (impact), 酶 = not affected, ? = missing data
Powered by Codecov. Last update 9f87477...8276861. Read the comment docs.

@bruno-garcia
Copy link
Member

Cons

  • when Sentry static methods are used, scope is not attached to events
  • Logging frameworks also have no access to Sentry scope

These are pretty big cons. Wouldn't this break the relationship of breadcrumbs and events from web-requests?
Also, this would break then any static use of Sentry.captureException in terms of having any web-request related information?

Not to say that it's not a better/right thing to do, but it brings to question if it's worth building this integration altogether since it's still fairly broken (though possible less broken if now we have info from the wrong request appended)

@maciejwalkowiak
Copy link
Contributor Author

maciejwalkowiak commented Nov 27, 2021

Wouldn't this break the relationship of breadcrumbs and events from web-requests?
No, as long as breadcrumbs or events are added through a hub retrieved from Reactor context. So our out-of-the-box integration for error handling works as expected.

Also, this would break then any static use of Sentry.captureException in terms of having any web-request related information?

Yes, I agree this is sub-optimal. I did not find a way to make it work, but will continue investigating.

@maciejwalkowiak
Copy link
Contributor Author

As far as I can tell - there is no way to do it without a considerable performance hit. Sleuth integration with Reactor, initially worked in a way that scope was copied between threads, now starting from version 3.0 they recommend "manual" mode: https://docs.spring.io/spring-cloud-sleuth/docs/current-SNAPSHOT/reference/html/integrations.html#sleuth-reactor-integration

There are some other solutions to this problem - mainly circulating around Reactor + MDC - but all are home-grown and each faces issues.

I think having out-of-the-box solution for capturing unhandled exceptions in Webflux is useful for users, but at this stage we should not ship integration with static methods or logging statements, as every implementation seems to be risky to me.

@maciejwalkowiak
Copy link
Contributor Author

Following Reactor docs, the one other way to make it work, including static api and logback as a result is to add following method that will set the thread local and reset it after operator finished work:

public static <T> Consumer<Signal<T>> withSentry(Consumer<T> logStatement) {
  return signal -> {
    if (!signal.isOnNext()) return;
      IHub currentHub = Sentry.getCurrentHub();
      IHub iHub = signal.getContextView().getOrDefault(SentryWebFilter.HUB_REACTOR_CONTEXT_ATTRIBUTE, null);
      if (iHub != null) {
        Sentry.setCurrentHub(iHub);
        logStatement.accept(signal.get());
        Sentry.setCurrentHub(currentHub);
      } else {
        logStatement.accept(signal.get());
      }
  };
}

Then, every call to static method on Sentry class, or logger, must be wrapped into withSentry.

Mono<Person> create(Person person) {
  return Mono.delay(Duration.ofMillis(100))
      .publishOn(Schedulers.boundedElastic())
      .doOnEach(withSentry(s -> Sentry.captureMessage("Creating person")))
      .doOnEach(withSentry(__ -> LOG.error("ERROR LOG")))
      .map(__ -> person);
}

We can either add it to the code base, or add to docs. I would still keep it experimental.

This marks the end of my investigation :-)

@bruno-garcia
Copy link
Member

Following Reactor docs, the one other way to make it work, including static api and logback as a result is to add following method that will set the thread local and reset it after operator finished work:

public static <T> Consumer<Signal<T>> withSentry(Consumer<T> logStatement) {
  return signal -> {
    if (!signal.isOnNext()) return;
      IHub currentHub = Sentry.getCurrentHub();
      IHub iHub = signal.getContextView().getOrDefault(SentryWebFilter.HUB_REACTOR_CONTEXT_ATTRIBUTE, null);
      if (iHub != null) {
        Sentry.setCurrentHub(iHub);
        logStatement.accept(signal.get());
        Sentry.setCurrentHub(currentHub);
      } else {
        logStatement.accept(signal.get());
      }
  };
}

Then, every call to static method on Sentry class, or logger, must be wrapped into withSentry.

Mono<Person> create(Person person) {
  return Mono.delay(Duration.ofMillis(100))
      .publishOn(Schedulers.boundedElastic())
      .doOnEach(withSentry(s -> Sentry.captureMessage("Creating person")))
      .doOnEach(withSentry(__ -> LOG.error("ERROR LOG")))
      .map(__ -> person);
}

We can either add it to the code base, or add to docs. I would still keep it experimental.

This marks the end of my investigation :-)

It sounds like this API is the way to go. We can document things to lead users the right path

@bruno-garcia
Copy link
Member

Just noticed the snippet u shared is missing a try/finally to undo the hub thing in the finally block if the callback throws

@maciejwalkowiak maciejwalkowiak marked this pull request as draft January 5, 2022 12:31
@maciejwalkowiak maciejwalkowiak marked this pull request as ready for review January 6, 2022 11:36
@marandaneto marandaneto marked this pull request as draft May 6, 2022 19:44
@oliverorav
Copy link

Is there any update on this PR and generally about Sentry and WebFlux integration? As from @adinauer comment this is also a blocker to move forward with #1807 .

@adinauer
Copy link
Member

@oliverorav I played around with this for a bit during our Hackweek and created this draft PR: #2224

But as you can see it leads to quite a mess in the PersonController and I never finished working on it. It's also net been tested in any real application.

A way to improve on it I can think of is to somehow apply the mess using some sort of bytecode manipulation and make the right hub available without requiring the user to write the mess. This is just an idea though and not very high on our list of priorities at the moment unfortunately.

@adinauer
Copy link
Member

Closing this in favor of #2546

@adinauer adinauer closed this Feb 24, 2023
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.

Spring Boot (Webflux): many unrelated http breadcrumbs are recorded with events
5 participants