All Products
Search
Document Center

Microservices Engine:Use the Lua plugin

Last Updated:Aug 09, 2025

Lua is a lightweight and efficient scripting language. In gateway development, you can use Lua to write and execute various gateway programs, such as API gateways, message gateways, and reverse proxies. With Lua scripts, developers can implement features such as request routing, filtering, and authentication, and perform custom processing. You can embed Lua into proxies, such as Nginx and Envoy, to process requests and responses, output logs, and perform other custom operations. This topic describes how to use a Lua script in an Envoy proxy to process requests and responses and to output their headers and bodies as logs.

Limits

  • Your MSE cloud-native gateway must be version 1.2.11 or later.

  • For security reasons, MSE cloud-native gateways disable the following Lua libraries and functions by default.

    • debug.debug

    • debug.getfenv

    • debug.getregistry

    • dofile

    • io

    • loadfile

    • os.execute

    • os.getenv

    • os.remove

    • os.rename

    • os.tmpname

Configure routing rules

  1. Log on to the MSE console, and select a region in the top navigation bar.

  2. In the left-side navigation pane, choose Cloud-native Gateway > Gateways. On the Gateways page, click the ID of the gateway.

  3. In the left-side navigation pane of the gateway details page, click Plug-in Marketplace.

  4. On the All Plug-ins > Custom tab, click the lua resource card.

  5. On the Plugin Configuration tab, you can select the application level for the rule and configure the rule.

    Note

    All requests that are sent to the gateway are matched against plugin rules in the following order: route level, domain level, and then instance level.

    Route-level plugin rules

    1. Click Create Rule. On the page that appears, turn on the switch, select a target route, and enter the Lua script in the Configure Rule editor.

    2. After completing the configuration, click OK.

    Domain-level plugin rules

    1. Click Create Rule. In the panel that appears, turn on the switch, select the target domain name, and enter the Lua script in the Configure Rule editor.

      Important

      Lua plugin rules do not support domain names that contain an asterisk (*).

    2. After completing the configuration, click OK.

    Instance-level plugin rules

    An instance-level plugin rule is configured for the Lua plugin by default. To edit the rule, you must first disable the edit lock.

    1. Enable the switch and enter the Lua script in the Configure Rule editor.

    2. When you are finished, click Save.

API reference

For more information about the Lua APIs that cloud-native gateways provide, see Lua.

To use JSON, use the following methods.

  • Serialize JSON data: local json_str = json.encode(obj).

  • Deserialize JSON data: local obj = json.decode(json_str).

If an error occurs during serialization or deserialization, Lua calls the error function to throw an error and stops the current process.

Common use cases

Print complete request and response information to plugin logs

  1. When you configure the routing rule, use the following Lua code.

    This code prints only the request and response bodies for the following content-type values. The body cannot exceed 1024 bytes (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
  2. View the plugin logs.

    This feature requires you to enable log delivery for gateway plugins. If it is not enabled, click Enable Log Delivery. Once enabled, plugin logs are delivered to Simple Log Service (SLS) and can be viewed on the page. As shown in the following figure, you can use the request-id from the access log to find the complete request and response information in the Lua plugin log.

    1

Add complete request and response information to access logs

  1. When you configure the routing rule, use the following Lua code.

    The access log parameters of the gateway support custom dynamic metadata. You must first define the metadata in the plugin. The following code defines four metadata fields, which correspond to the request header, request body, response header, and response 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
  2. Adjust the log format.

    In the navigation pane on the left of the gateway instance, click Parameter Settings and then Default Format (Click To Customize). Add the corresponding metadata to the custom log fields. You can then view the metadata in the access logs.

    image