Lua是一种轻量级、高效的脚本语言,在网关开发中,Lua可以用于编写和执行各种网关程序,例如API网关、消息网关、反向代理等。通过Lua脚本,开发人员可以实现请求的路由、过滤、鉴权等功能,并进行定制化的处理。在一些代理中,比如Nginx和Envoy,Lua可以被嵌入用于处理请求和响应,并进行日志输出和其他定制化操作。本文中的Lua脚本用于在Envoy代理中处理请求和响应,并将请求和响应的头部和正文信息以日志的形式输出。
使用限制
MSE云原生网关版本为1.2.11及以上。
出于安全考虑,MSE云原生网关默认禁用以下Lua库和函数。
debug.debug
debug.getfenv
debug.getregistry
dofile
io
loadfile
os.execute
os.getenv
os.remove
os.rename
os.tmpname
配置路由规则
登录MSE管理控制台,并在顶部菜单栏选择地域。
在左侧导航栏,选择云原生网关 > 网关列表,单击目标网关名称。
在网关详情页面,在左侧导航栏,选择插件市场。
在全部插件页签,选择自定义页签,然后单击lua资源卡片。
在插件配置页签,选择规则作用层级,并进行如下配置。
说明所有进入网关的请求,会按路由级→域名级→实例级 的方向依次匹配插件规则。
路由级插件规则
单击新建规则,在新建规则页面,打开生效开关,并选择生效目标路由,然后在配置规则编辑框中输入Lua脚本。
配置完成后单击确定。
域名级插件规则
单击新建规则,在新建规则页面,打开生效开关,并选择生效目标域名,然后在配置规则编辑框中输入Lua脚本。
重要LUA插件规则不支持配置带"*"的域名。
配置完成后单击确定。
实例级插件规则
lua插件默认已配置实例级插件规则,如需编辑,请关闭锁定编辑开关。
打开生效开关,然后在配置规则编辑框中输入Lua脚本。
完成后单击保存。
API参考
关于网关提供的Lua API详细信息,请参见Lua。
如果在序列化或反序列化时出现错误,Lua将调用error函数抛出错误,并终止当前处理。
常见用例
打印完整的请求应答信息到插件日志
配置路由规则时,使用以下Lua代码规则。
根据此代码配置,只会打印以下
content-type
类型的请求Body和应答Body,且Body不能超过1024字节(1 KB)。application/x-www-form-urlencoded
application/json
text/plain
local maxBodySize = 1024 function check_content_readable(type) if type == nil then return false end if string.find(type, "application/x-www-form-urlencoded",1,true) or string.find(type, "application/json",1,true) or string.find(type, "text/plain",1,true) then return true end return false end function envoy_on_request(request_handle) local headers = request_handle:headers() local headersStr = "" local contentType for key, value in pairs(headers) do if key == "content-type" then contentType = value end headersStr = headersStr .. key .. "=" .. value .. ", " end request_handle:streamInfo():dynamicMetadata():set("envoy.lua","request_headers",headersStr) local requestBody = "" if check_content_readable(contentType) then for chunk in request_handle:bodyChunks() do if (chunk:length() > 0) then requestBody = requestBody .. chunk:getBytes(0, chunk:length()) end if (#requestBody > maxBodySize) then requestBody = requestBody .. "<truncated>" break end end end request_handle:streamInfo():dynamicMetadata():set("envoy.lua","request_body",string.gsub(requestBody,"\n","\\n")) end function envoy_on_response(response_handle) local headers = response_handle:headers() local headersStr = "" local contentType local contentEncoding = false for key, value in pairs(headers) do if key == "content-type" then contentType = value elseif key == "content-encoding" then contentEncoding = true end headersStr = headersStr .. key .. "=" .. value .. ", " end local responseBody = "" if check_content_readable(contentType) and not contentEncoding then for chunk in response_handle:bodyChunks() do if (chunk:length() > 0) then responseBody = responseBody .. chunk:getBytes(0, chunk:length()) end if (#responseBody > maxBodySize) then responseBody = responseBody .. "<truncated>" break end end end local reqHeaders = "" local reqBody = "" local metadata = response_handle:streamInfo():dynamicMetadata():get("envoy.lua") if metadata ~= nil then local headers = response_handle:streamInfo():dynamicMetadata():get("envoy.lua")["request_headers"] if headers ~= nil then reqHeaders = headers end local body = response_handle:streamInfo():dynamicMetadata():get("envoy.lua")["request_body"] if body ~= nil then reqBody = body end end response_handle:logInfo("request Headers: [" .. reqHeaders .. "] request Body: [" .. string.gsub(reqBody,"\n","\\n") .. "] response Headers: [" .. headersStr .. "] response Body: [" .. string.gsub(responseBody,"\n","\\n") .. "]") end
查看插件日志。
此功能要求开启网关插件日志投递,如果还未开启,请单击立即开启日志投递功能。开启日志投递配置后,插件日志将投递到SLS并可在页面中查看。如下所示,可以使用访问日志中的
request-id
,在Lua插件日志中找到完整的请求和响应信息。
将完整的请求应答信息添加到访问日志
配置路由规则时,使用以下Lua代码规则。
网关的访问日志参数支持配置自定义动态元数据,首先需要在插件中定义元数据。以下代码一共定义了四个元数据信息,分别对应请求头、请求Body、响应头、响应Body:
envoy.lua:request_headers
envoy.lua:request_body
envoy.lua:response_headers
envoy.lua:response_body
local maxBodySize = 1024 function check_content_readable(type) if type == nil then return false end if string.find(type, "application/x-www-form-urlencoded",1,true) or string.find(type, "application/json",1,true) or string.find(type, "text/plain",1,true) then return true end return false end function envoy_on_request(request_handle) local headers = request_handle:headers() local headersStr = "" local contentType for key, value in pairs(headers) do if key == "content-type" then contentType = value end headersStr = headersStr .. key .. "=" .. value .. ", " end request_handle:streamInfo():dynamicMetadata():set("envoy.lua","request_headers",headersStr) local requestBody = "" if check_content_readable(contentType) then for chunk in request_handle:bodyChunks() do if (chunk:length() > 0) then requestBody = requestBody .. chunk:getBytes(0, chunk:length()) end if (#requestBody > maxBodySize) then requestBody = requestBody .. "<truncated>" break end end end request_handle:streamInfo():dynamicMetadata():set("envoy.lua","request_body",string.gsub(requestBody,"\n","\\n")) end function envoy_on_response(response_handle) local headers = response_handle:headers() local headersStr = "" local contentType local contentEncoding = false for key, value in pairs(headers) do if key == "content-type" then contentType = value elseif key == "content-encoding" then contentEncoding = true end headersStr = headersStr .. key .. "=" .. value .. ", " end response_handle:streamInfo():dynamicMetadata():set("envoy.lua","response_headers",headersStr) local responseBody = "" if check_content_readable(contentType) and not contentEncoding then for chunk in response_handle:bodyChunks() do if (chunk:length() > 0) then responseBody = responseBody .. chunk:getBytes(0, chunk:length()) end if (#responseBody > maxBodySize) then responseBody = responseBody .. "<truncated>" break end end end response_handle:streamInfo():dynamicMetadata():set("envoy.lua","response_body",string.gsub(responseBody,"\n","\\n")) end
调整日志格式。
在该网关实例的左侧导航栏,单击参数配置,然后单击默认格式(点击自定义)。在自定义日志字段里添加对应的元数据信息,就可以在访问日志中看到对应的信息。