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代碼,最后單擊保存。
在生效開關區域,打開生效開關,然后在開啟對話框,單擊確定。
插件立即啟用。
API參考
關于網關提供的Lua API詳細信息,請參見Lua。
如果在序列化或反序列化時出現錯誤,Lua將調用error函數拋出錯誤,并終止當前處理。
常見用例
打印完整的請求應答信息到插件日志
登錄MSE網關管理控制臺,并在頂部菜單欄選擇地域。
在左側導航欄,選擇云原生網關 > 網關列表,單擊目標網關名稱。
在左側導航欄,單擊插件市場,然后單擊目標插件。
單擊插件配置,在插件配置中配置以下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插件日志中找到完整的請求和響應信息。
將完整的請求應答信息添加到訪問日志
登錄MSE網關管理控制臺,并在頂部菜單欄選擇地域。
在左側導航欄,選擇云原生網關 > 網關列表,單擊目標網關名稱。
在左側導航欄,單擊插件市場,然后單擊目標插件。
單擊插件配置,在插件配置中配置以下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
在左側導航欄,選擇參數配置。在可觀測性參數區域對日志格式進行調整。在自定義日志字段里添加對應的元數據信息,就可以在訪問日志中看到對應的信息。