![nginx:记录带有所有标头的完整请求/响应?](https://linux22.com/image/643700/nginx%EF%BC%9A%E8%AE%B0%E5%BD%95%E5%B8%A6%E6%9C%89%E6%89%80%E6%9C%89%E6%A0%87%E5%A4%B4%E7%9A%84%E5%AE%8C%E6%95%B4%E8%AF%B7%E6%B1%82%2F%E5%93%8D%E5%BA%94%EF%BC%9F.png)
我们有一个应用服务器有时会挂起。我们怀疑这是由于客户端的错误请求造成的。
nginx 可以将完整的请求/响应(如 fiddler 捕获)记录到文件中,以便我们可以看到挂起之前发送的请求吗?
(我们可能需要避免使用 pcap 和该方法,而是在 nginx 中完成所有操作)
如果 nginx 不是适合此目的的合适工具,那么除了网络分析器之外,还有什么适合呢?
答案1
要获取访问者发送的请求正文,请使用client_body_in_file_only on;
并将写入的“临时”文件记录到日志中,方法是将 var 附加$request_body_file
到日志格式。默认情况下,“临时”文件将位于 client_temp 目录中。
您也可以记录请求标头$http_<header>
并使用发送标头$sent_http_<header>
。
如果您有请求正文和标题,您应该能够重播它并获得访问者的回应。
还有类似戈尔应该高度考虑,以便您可以在其他环境中重放流量,在那里您可以让 nginx 写入这些临时文件而不会导致生产中的 IO 问题(nginx 不会清除它们的on
值,这就是为什么在这种情况下它不是那么“临时”)。
答案2
mitmproxy似乎是完成您要求的事情的正确工具。
mitmproxy 是一个具有控制台界面的交互式、支持 SSL 的 HTTP 中间人代理。
mitmdump 是 mitmproxy 的命令行版本。可以认为是 HTTP 的 tcpdump。
特征
- 拦截 HTTP 请求和响应并动态修改它们。
- 保存完整的 HTTP 对话以供稍后重播和分析。
- 重放 HTTP 对话的客户端。重放先前记录的服务器的 HTTP 响应。
- 反向代理模式将流量转发到指定的服务器。
- OSX 和 Linux 上的透明代理模式。
- 使用 Python 对 HTTP 流量进行脚本更改。
- 用于拦截的 SSL 证书是动态生成的。
反向代理模式可以让您捕获请求和响应,就像 Fiddler 一样。
答案3
我正在寻找这个问题的答案,并找到了这:
基本上,使用 lua 脚本来收集并记录所有标题:
ngx.log(ngx.ERR, "REQUEST capturing started")
json = require("json")
function getval(v, def)
if v == nil then
return def
end
return v
end
local data = {request={}, response={}}
local req = data["request"]
local resp = data["response"]
req["host"] = ngx.var.host
req["uri"] = ngx.var.uri
req["headers"] = ngx.req.get_headers()
req["time"] = ngx.req.start_time()
req["method"] = ngx.req.get_method()
req["get_args"] = ngx.req.get_uri_args()
req["post_args"] = ngx.req.get_post_args()
req["body"] = ngx.var.request_body
content_type = getval(ngx.var.CONTENT_TYPE, "")
resp["headers"] = ngx.resp.get_headers()
resp["status"] = ngx.status
resp["duration"] = ngx.var.upstream_response_time
resp["time"] = ngx.now()
resp["body"] = ngx.var.response_body
ngx.log(ngx.CRIT, json.encode(data));
然后从nginx配置中引用它:
location / {
proxy_pass http://127.0.0.1:8081/;
log_by_lua_file lua/request_logger.lua;
}
为了收集主体,nginx main.conf 中需要一个块:
#we must declare variables first, we cannot create vars in lua
set $response_body '';
body_filter_by_lua_block {
-- arg[1] contains a chunk of response content
local resp_body = string.sub(ngx.arg[1], 1, 1000)
ngx.ctx.buffered = string.sub((ngx.ctx.buffered or "") .. resp_body, 1, 1000)
-- arg[2] is true if this is the last chunk
if ngx.arg[2] then
ngx.var.response_body = ngx.ctx.buffered
end
}
上面链接的帖子对此进行了更详细的说明。