Main Purpose: I want to simply count the execution time for any API / route, and add it into the Response Header with key: ExecutionTime.
I am very open to another alternative, since my method may wrong as I am still new in Go-Echo framework.
What I have been doing: So this is my middleware code:
func (s *Stats) ExecTime(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
c.Response().Header().Set("ExecutionStartedAt", time.Now().String())
if err := next(c); err != nil { //exec main process
c.Error(err)
}
c.Response().Header().Set("ExecutionDoneAt", time.Now().String())
return nil
}
}
Current Result:
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=UTF-8
< Executionstartedat: 2022-09-11 16:32:15.612045 +0700 WIB m=+3.619364986
< Server: Echo/3.0
< Date: Sun, 11 Sep 2022 09:32:21 GMT
< Content-Length: 856
Please note: There is an Executionstartedat added. But unfortunatelly there is no ExecutionDoneAt as expected.
Expected Result:
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=UTF-8
< Executionstartedat: 2022-09-11 16:32:15.612045 +0700 WIB m=+3.619364986
< Executiondoneat: 2022-09-11 16:32:18.612045 +0700 WIB m=+3.619364986
< Server: Echo/3.0
< Date: Sun, 11 Sep 2022 09:32:21 GMT
< Content-Length: 856
I am following this documentation: https://echo.labstack.com/cookbook/middleware/#response
Any help, any idea, any alternative ways to accomplish same result would be very very appreciated. Thank you very much.
Edited #1:
What else I have tried:
I have tried to use another middleware, the timing is good, just as I expected. But unfortunately the header is still empty.
e.Use(middleware.BodyDump(func(c echo.Context, reqBody, resBody []byte) {
fmt.Println("this is from BodyDump 7: ", time.Now())
c.Response().Header().Set("ExecutionTime7", "Exec Time: " + time.Now().String())
}))
The result is exactly like above, no change at all.
Edited #2
here is the working code example:
// ExecTime is the middleware function.
func (s *Stats) ExecTime(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
before := time.Now()
c.Response().Header().Set("ExecutionStartedAt", before.String())
c.Response().Before(func() {
after := time.Now()
elapsed := time.Since(before)
c.Response().Header().Set("ExecutionDoneAt", after.String())
c.Response().Header().Set("ExecutionTime", elapsed.String())
})
if err := next(c); err != nil { //exec main process
c.Error(err)
}
return nil
}
}
In HTTP the headers go before the body, as the name "header" implies. So once you write the body you cannot write the header. You may try using HTTP/2 Trailers. Or execute the process, but before writing its output to the body, calculate the execution time, add the header, and then write the body.
I don't use echo
so I don't know what the correct approach is, but it seems to me like it'd be better if you use the response before hook.
Example by @JackySupit:
// ExecTime is the middleware function.
func (s *Stats) ExecTime(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
before := time.Now()
c.Response().Header().Set("ExecutionStartedAt", before.String())
c.Response().Before(func() {
after := time.Now()
elapsed := time.Since(before)
c.Response().Header().Set("ExecutionDoneAt", after.String())
c.Response().Header().Set("ExecutionTime", elapsed.String())
})
if err := next(c); err != nil { //exec main process
c.Error(err)
}
return nil
}
}