英文:
Logging All HTTP Request and Response from done through an HTTP Client
问题
我有以下简单的http.Client
:
import (
"net/http"
"log"
)
...
func main() {
...
link = "http://example.com"
method = "GET"
req, _ := http.NewRequest(method, link, nil)
client := &http.Client{}
myZapLogger.Info("发送一个 %s 请求到 %s\n", method, link)
resp, err := client.Do(req)
if err != nil {
myZapLogger.Error(..., err) // 我记录而不是致命错误或其他
} else {
myZapLogger.Info("收到请求 X 的 %d\n", resp.StatusCode)
}
...
}
...
我正在寻找一种通过钩子(或其他方式)为每个请求执行上述操作的方法,以便每次自动触发。我可以编写一个函数来封装所有这些操作,但在将http客户端传递给其他包的情况下,我将无法以这种方式控制/记录此类请求(例如aws-go-sdk
)。
是否可以通过上下文或将钩子附加到客户端来实现这一点?
谢谢
英文:
I have the following simple http.Client
:
import (
"net/http"
"log"
)
...
func main() {
...
link = "http://example.com"
method = "GET"
req, _ := http.NewRequest(method, link, nil)
client := &http.Client{}
myZapLogger.Info("Sending a %s request to %s\n", method, link)
resp, err := client.Do(req)
if err != nil {
myZapLogger.Error(..., err) // I'm logging rather than fatal-ing or so
} else {
myZapLogger.Info("Received a %d on request X", resp.StatusCode)
}
...
}
...
I was looking for a way to do the above for each request through a hook (or so), so that it's triggered automatically each time. I can write a function the encloses all that, but in a case where I'm passing an http client to some other package, I wouldn't be able to control/log such requests that way (e.g. aws-go-sdk
).
Is there a way to do this through contexts or attaching hooks to the client?
Thanks
答案1
得分: 1
eudore的评论回答了这个问题;我只是将其转化为代码:
type MyRoundTripper struct {}
func (t MyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
// 在发送请求之前进行操作
resp, err := http.DefaultTransport.RoundTrip(req)
if err != nil {
return resp, err
}
// 在接收到响应之后进行操作
return resp, err
}
要使用它,只需将其传递给你的HTTP客户端:
rt := MyRoundTripper{}
client := http.Client{Transport: rt}
英文:
eudore's comment answers the question; I'll just put it into code:
type MyRoundTripper struct {}
func (t MyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
// Do work before the request is sent
resp, err := http.DefaultTransport.RoundTrip(req)
if err != nil {
return resp, err
}
// Do work after the response is received
return resp, err
}
To use it, you'll just pass it to your HTTP Client:
rt := MyRoundTripper{}
client := http.Client{Transport: rt}
答案2
得分: 0
我已经混合了几个不同的地方,并编写了这段代码:
package matrix
import (
"bytes"
"mylogger/colorlogger"
"io"
"net/http"
)
func filterAuthorization(headers http.Header) http.Header {
filteredHeaders := make(http.Header)
for k, v := range headers {
if k == "Authorization" {
filteredHeaders[k] = []string{"<removed>"}
} else {
filteredHeaders[k] = v
}
}
return filteredHeaders
}
type MyRoundTripper struct{}
func (t MyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
var reqStr string
var respStr string
if req.Body != nil {
reqbuf, e := io.ReadAll(req.Body)
if e != nil {
colorlogger.Log.Fatal("从 RoundTrip 注入器读取 HTTP 响应时出错:", e)
} else {
reqrdr1 := io.NopCloser(bytes.NewBuffer(reqbuf))
reqrdr2 := io.NopCloser(bytes.NewBuffer(reqbuf))
req.Body = reqrdr2
// 将 resp.Body 读取为字符串
breq, errreq := io.ReadAll(reqrdr1)
if errreq != nil {
colorlogger.Log.Error("从 RoundTrip 注入器读取 HTTP 请求时出错:", errreq)
} else {
reqStr = string(breq)
}
}
}
// 在发送请求之前进行处理
resp, err := http.DefaultTransport.RoundTrip(req)
if err != nil {
return resp, err
}
if resp.Body != nil {
respbuf, e := io.ReadAll(resp.Body)
if e != nil {
colorlogger.Log.Fatal("从 RoundTrip 注入器读取 HTTP 响应时出错:", e)
} else {
resprdr2 := io.NopCloser(bytes.NewBuffer(respbuf))
resp.Body = resprdr2
resprdr1 := io.NopCloser(bytes.NewBuffer(respbuf))
// 将 resp.Body 读取为字符串
b, errresp := io.ReadAll(resprdr1)
if errresp != nil {
colorlogger.Log.Error("从 RoundTrip 注入器读取 HTTP 响应时出错:", errresp)
} else {
respStr = string(b)
}
}
}
// 在接收到响应后进行处理
colorlogger.Log.Debugf("%s请求(%s) %s, Headers: %+v, Body: %s, 响应(%d), Headers: %+v, Body: %s%s",
colorlogger.Blue,
req.Method,
req.URL.Path,
filterAuthorization(req.Header),
reqStr,
resp.StatusCode,
resp.Header,
respStr,
colorlogger.CLR)
return resp, err
}
如下所示,这是如何集成的:
要使用它,只需将其传递给您的 HTTP 客户端:
rt := MyRoundTripper{}
client := http.Client{Transport: rt}
**注意:**由于存在“fatal”错误处理,我可能会稍微更改错误处理方式。
**注意:**我也喜欢 https://medium.com/@motemen/add-a-single-line-to-your-go-source-and-track-every-http-requests-responses-37f2f15eb72f,但我希望能够在运行时从调试模式切换到非调试模式。
英文:
I've remixed a few different places and came up with this code:
package matrix
import (
"bytes"
"mylogger/colorlogger"
"io"
"net/http"
)
func filterAuthorization(headers http.Header) http.Header {
filteredHeaders := make(http.Header)
for k, v := range headers {
if k == "Authorization" {
filteredHeaders[k] = []string{"<removed>"}
} else {
filteredHeaders[k] = v
}
}
return filteredHeaders
}
// https://stackoverflow.com/questions/23070876/reading-body-of-http-request-without-modifying-request-state
// https://stackoverflow.com/questions/69276445/logging-all-http-request-and-response-from-done-through-an-http-client
type MyRoundTripper struct{}
func (t MyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
var reqStr string
var respStr string
if req.Body != nil {
reqbuf, e := io.ReadAll(req.Body)
if e != nil {
colorlogger.Log.Fatal("Reading http response from RoundTrip injector error: ", e)
} else {
reqrdr1 := io.NopCloser(bytes.NewBuffer(reqbuf))
reqrdr2 := io.NopCloser(bytes.NewBuffer(reqbuf))
req.Body = reqrdr2
// read resp.Body to string
breq, errreq := io.ReadAll(reqrdr1)
if errreq != nil {
colorlogger.Log.Error("Reading http request from RoundTrip injector error: ", errreq)
} else {
reqStr = string(breq)
}
}
}
// Do work before the request is sent
resp, err := http.DefaultTransport.RoundTrip(req)
if err != nil {
return resp, err
}
if resp.Body != nil {
respbuf, e := io.ReadAll(resp.Body)
if e != nil {
colorlogger.Log.Fatal("Reading http response from RoundTrip injector error: ", e)
} else {
resprdr2 := io.NopCloser(bytes.NewBuffer(respbuf))
resp.Body = resprdr2
resprdr1 := io.NopCloser(bytes.NewBuffer(respbuf))
// read resp.Body to string
b, errresp := io.ReadAll(resprdr1)
if errresp != nil {
colorlogger.Log.Error("Reading http response from RoundTrip injector error: ", errresp)
} else {
respStr = string(b)
}
}
}
// Do work after the response is received
colorlogger.Log.Debugf("%sREQUEST(%s) %s, Headers: %+v, Body: %s, RESPONSE(%d), Headers: %+v, Body: %s%s",
colorlogger.Blue,
req.Method,
req.URL.Path,
filterAuthorization(req.Header),
reqStr,
resp.StatusCode,
resp.Header,
respStr,
colorlogger.CLR)
return resp, err
}
And as show below, this is integrated like this:
> To use it, you'll just pass it to your HTTP Client:
>
> rt := MyRoundTripper{} client := http.Client{Transport: rt}
Note: I might change error handling a bit because of the fatal.
Note: I also liked https://medium.com/@motemen/add-a-single-line-to-your-go-source-and-track-every-http-requests-responses-37f2f15eb72f but I'd like to be able to change from debug to none-debug during runtime later.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论