使用Lua插件
Lua是一種輕量級(jí)、高效的腳本語言,在網(wǎng)關(guān)開發(fā)中,Lua可以用于編寫和執(zhí)行各種網(wǎng)關(guān)程序,例如API網(wǎng)關(guān)、消息網(wǎng)關(guān)、反向代理等。通過Lua腳本,開發(fā)人員可以實(shí)現(xiàn)請(qǐng)求的路由、過濾、鑒權(quán)等功能,并進(jìn)行定制化的處理。在一些代理中,比如Nginx和Envoy,Lua可以被嵌入用于處理請(qǐng)求和響應(yīng),并進(jìn)行日志輸出和其他定制化操作。本文中的Lua腳本用于在Envoy代理中處理請(qǐng)求和響應(yīng),并將請(qǐng)求和響應(yīng)的頭部和正文信息以日志的形式輸出。
使用限制
MSE云原生網(wǎng)關(guān)版本為1.2.11及以上。
注意事項(xiàng)
出于安全考慮,MSE云原生網(wǎng)關(guān)默認(rèn)禁用以下Lua庫和函數(shù)。
debug.debug
debug.getfenv
debug.getregistry
dofile
io
loadfile
os.execute
os.getenv
os.remove
os.rename
os.tmpname
操作步驟
登錄MSE管理控制臺(tái),并在頂部菜單欄選擇地域。
在左側(cè)導(dǎo)航欄,選擇云原生網(wǎng)關(guān) > 網(wǎng)關(guān)列表,單擊目標(biāo)網(wǎng)關(guān)名稱。
在網(wǎng)關(guān)詳情頁面,在左側(cè)導(dǎo)航欄,選擇插件市場。
在全部插件頁簽,選擇自定義頁簽,然后單擊lua資源卡片。
在插件配置頁簽,配置如下參數(shù)。
在應(yīng)用范圍區(qū)域,選擇Lua插件的應(yīng)用范圍。
在插件配置區(qū)域,單擊編輯,然后在代碼編輯框中填寫Lua代碼,最后單擊保存。
在生效開關(guān)區(qū)域,打開生效開關(guān),然后在開啟對(duì)話框,單擊確定。
插件立即啟用。
API參考
關(guān)于網(wǎng)關(guān)提供的Lua API詳細(xì)信息,請(qǐng)參見Lua。
如果在序列化或反序列化時(shí)出現(xiàn)錯(cuò)誤,Lua將調(diào)用error函數(shù)拋出錯(cuò)誤,并終止當(dāng)前處理。
常見用例
打印完整的請(qǐng)求應(yīng)答信息到插件日志
登錄MSE網(wǎng)關(guān)管理控制臺(tái),并在頂部菜單欄選擇地域。
在左側(cè)導(dǎo)航欄,選擇云原生網(wǎng)關(guān) > 網(wǎng)關(guān)列表,單擊目標(biāo)網(wǎng)關(guān)名稱。
在左側(cè)導(dǎo)航欄,單擊插件市場,然后單擊目標(biāo)插件。
單擊插件配置,在插件配置中配置以下Lua代碼。
說明根據(jù)此代碼配置,只會(huì)打印以下
content-type
類型的請(qǐng)求Body和應(yīng)答B(yǎng)ody,且Body不能超過1024字節(jié)(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插件日志中找到完整的請(qǐng)求和響應(yīng)信息。
將完整的請(qǐng)求應(yīng)答信息添加到訪問日志
登錄MSE網(wǎng)關(guān)管理控制臺(tái),并在頂部菜單欄選擇地域。
在左側(cè)導(dǎo)航欄,選擇云原生網(wǎng)關(guān) > 網(wǎng)關(guān)列表,單擊目標(biāo)網(wǎng)關(guān)名稱。
在左側(cè)導(dǎo)航欄,單擊插件市場,然后單擊目標(biāo)插件。
單擊插件配置,在插件配置中配置以下Lua代碼。
網(wǎng)關(guān)的訪問日志參數(shù)支持配置自定義動(dòng)態(tài)元數(shù)據(jù),首先需要在插件中定義元數(shù)據(jù)。以下代碼一共定義了四個(gè)元數(shù)據(jù)信息,分別對(duì)應(yīng)請(qǐng)求頭、請(qǐng)求Body、響應(yīng)頭、響應(yīng)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
在左側(cè)導(dǎo)航欄,選擇參數(shù)配置。在可觀測性參數(shù)區(qū)域?qū)θ罩靖袷竭M(jìn)行調(diào)整。在自定義日志字段里添加對(duì)應(yīng)的元數(shù)據(jù)信息,就可以在訪問日志中看到對(duì)應(yīng)的信息。