fix(otelecho): add config option to skip global error handler call#8025
Conversation
Add WithSkipErrorHandler configuration option to support Echo's native error handling pattern where handlers return errors instead of calling c.Error() explicitly. This prevents double responses on errors propagated to the otelecho middleware. Fixes open-telemetry#4420 otelecho: config option to skip global error handler call remove again correct naming correct changelog Update CHANGELOG.md Co-authored-by: Damien Mathieu <42@dmathieu.com> fix: clean up fix: lint
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8025 +/- ##
=====================================
Coverage 79.7% 79.7%
=====================================
Files 190 190
Lines 11912 11919 +7
=====================================
+ Hits 9495 9502 +7
Misses 2062 2062
Partials 355 355
🚀 New features to boost your workflow:
|
|
I'm not super fond of negative options ( I wonder if we could make the option more powerful by having a r.Use(otelecho.Middleware("my-server", WithOnError(func(c echo.Context, err error) {
c.Error(err)
}))And at the same time, as I write this, it seems a bit overfluous. If the child middleware returns and error, it's not for us to call |
|
Well, the reason i didn't remove |
|
Unstable packages don't need to keep backwards compatibility, they are unstable :) |
|
@dmathieu i understand) @scorpionknifes has no activity since February, should we wait for some other review or i can fix to remove the call completely? |
|
I would remove the option (no push force please. Also, a new commit is more easily reverted). |
|
I see some tests are failing. The issue is that the response status code should be read by this middleware, but it can be set in the error handler, which runs after all middlewares. Looking for a workaround. The only way i currently see is to do like in echo request logger (https://github.com/labstack/echo/blob/e644ff8f7bb01c694cacec3ad22a7471609ea106/middleware/request_logger.go#L337), but by doing so we will loose the status code. |
|
Ah! That would explain why |
|
I need some advice here :) r := httptest.NewRequest("GET", "/ping", http.NoBody)
w := httptest.NewRecorder()
router := echo.New()
router.Logger = log.New("echo")
router.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error {
fmt.Println("Request:", c.Request().Method, c.Request().URL.Path, "->", v.Status)
return nil
},
HandleError: false,
LogStatus: true,
}))
router.Use(otelecho.Middleware("foobar"))
router.GET("/ping", func(_ echo.Context) error {
return assert.AnError
})
handlerCalled := 0
router.HTTPErrorHandler = func(err error, c echo.Context) {
handlerCalled++
fmt.Println("Error handler called:", err, "should be:", assert.AnError)
err = c.NoContent(http.StatusTeapot)
if err != nil {
fmt.Println("Failed to write response:", err)
}
}
router.ServeHTTP(w, r)
fmt.Println("Response code:", w.Result().StatusCode, "should be:", http.StatusTeapot)
fmt.Println("Handler called times:", handlerCalled, "should be: 1")i tested the behaviour of the echo request logger. I found out that it logs |
|
labstack/echo#1948 |
I agree with this approach; we only need to provide an interface for users to manage how errors are handled, or even choose not to handle errors at all. Additionally, we may also need to clarify the purpose of the |
|
@flc1125 the main issue is that if the middleware doesn't call |
|
And if we call it, folks have issues as it's being called multiple times. With this method, I think it makes sense to keep the default behavior similar to what we have today. |
|
So we need to have default |
|
I have an idea to add a wrapper for |
|
@dmathieu any thoughts? |
|
I can try to research it to figure out the function of |
|
@flc1125 we've already figured it out, the call is needed to set the actual status code in the span if it was modified in custom HTTPErrorHandler |
|
|
||
| // DefaultOnError is the default function called when an error occurs during request processing. | ||
| // The call to c.Error(err) ensures tracing data is correct, but forces the global HTTPErrorHandler to be called twice. | ||
| var DefaultOnError = func(c echo.Context, err error) { c.Error(err) } |
There was a problem hiding this comment.
What's the need to make this public?
There was a problem hiding this comment.
no real need actually :)
|
currently waiting for owner review? |
|
Waiting for a second approver/maintainer review. |
|
I have completed my research and here are my conclusions: I lean towards removing the The reasons are: I believe Because of this, when we do not use Furthermore, I think the role of middleware, unless it triggers errors from uncontrollable underlying factors, should be to pass the error returned by the handler unchanged, and this error will ultimately affect the final response status code. Impact of adjusting according to this method: (and the impact is significant) For projects relying on this component, when an error occurs, the status code will change from 500 to 200, unless we return an error via If we decide to proceed with this, we need to document this change in the CHANGELOG. |
|
@flc1125 |
|
@asp3cto I believe that https://github.com/labstack/echo/blob/e644ff8f7bb01c694cacec3ad22a7471609ea106/echo.go#L887 must always be used in conjunction with the logic at https://github.com/labstack/echo/blob/e644ff8f7bb01c694cacec3ad22a7471609ea106/echo.go#L424-L430. Any scenario where either is unconfigured or fails to implement similar logic will result in an inability to fully manage status codes. |
|
I understand, but the main issue is custom HTTPErrorHandler. Builtin middlewares do the same: https://github.com/labstack/echo/blob/e644ff8f7bb01c694cacec3ad22a7471609ea106/middleware/request_logger.go#L287 They also check if HandleError is set and call c.Error if it is true to get the actual status. |
If there is a precedent, I think this logic makes sense. |
|
I'm using the merge of this PR to call out that otelecho is lacking a code owner, and will be deprecated/removed in a few weeks if nobody steps up. |
Add WithSkipErrorHandler configuration option to support Echo's native error handling pattern where handlers return errors instead of calling c.Error() explicitly.
This prevents double responses on errors propagated to the otelecho middleware.
Fixes #4419, related to #4420