nginx:记录带有所有标头的完整请求/响应?

nginx:记录带有所有标头的完整请求/响应?

我们有一个应用服务器有时会挂起。我们怀疑这是由于客户端的错误请求造成的。

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
}

上面链接的帖子对此进行了更详细的说明。

相关内容