All Products
Search
Document Center

Microservices Engine:Use the Lua plug-in

Last Updated:Jun 20, 2026

Lua is a lightweight and highly efficient scripting language. In gateway development, Lua can be used to write and execute various gateway programs, such as API gateways, message gateways, and reverse proxies. Developers can use Lua to write scripts for request routing, filtering, and authentication, and to perform customized processing. Lua can be embedded into some proxies, such as NGINX and Envoy for request and response processing, log output, and other customized operations. In this topic, Lua is embedded into the Envoy proxy for request and response processing, and you'll learn how to use it to log request and response headers and bodies.

Limitations

  • The version of your Microservices Engine (MSE) cloud-native gateway must be 1.2.11 or later.

  • For security purposes, the following Lua libraries and functions are disabled for MSE cloud-native gateways 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 tab, click the Custom tab, and then click the lua resource card.

  5. On the Plug-in Configuration tab, select the rule level and configure it as follows.

    Note

    All requests received by a gateway match plug-in rules in the following order of precedence: route-level plug-in rules → domain-level plug-in rules → instance-level plug-in rules.

    Route-level plug-in rules

    1. Click Add Rule. On the page that appears, enable the rule, select a target route, and then enter the Lua script in the Configure rule editor.

    2. Click OK.

    Domain-level plug-in rules

    1. Click Add Rule. On the page that appears, enable the rule, select a target domain, and then enter the Lua script in the Configure rule editor.

      Important

      Domain names that contain an asterisk (*) are not supported in a Lua plug-in rule.

    2. Click OK.

    Instance-level plug-in rules

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

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

    2. Click Save.

API references

For more information about the Lua APIs that are provided by cloud-native gateways, see Lua.

The following methods allow you to use JSON:

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

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

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

Common use cases

Display complete request and response information in plug-in logs

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

    In the sample code, only the following request bodies and response bodies of the content type are displayed. The body size cannot exceed 1,024 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 logs of the plug-in

    This feature requires you to enable log shipping for gateway plug-ins. If this feature is not enabled, click Immediately Enable Log Shipping. After you enable log shipping, the gateway ships plug-in logs to Log Service (SLS), where you can view them. You can use the x-request-id from the access log to find the complete request and response information in the Lua plug-in log.

    On the plug-in details page, click the Plugin logs tab. In the search bar, enter a query statement, for example, module:lua and cluster_id:<your-gateway-instance-ID>, and add the x-request-id value from the access log as a filter. The query results in the raw log section display the complete HTTP request header, request body, response header, and response body for the matching request, with the x-request-id field highlighted.

Display complete request and response information in plug-in logs

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

    You can configure custom dynamic metadata for the access log parameters of a gateway. In this case, you must define the metadata in the plug-in. The following sample code defines four types of metadata information, 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 left-side navigation pane for the gateway instance, click Parameters and then click Default Format (Manual Input). Add the corresponding metadata to the custom log fields to view the information in the access log.

    Add four custom log fields. For the request information, select Request dynamic metadata as the Field type and set the field values to envoy.lua:request_headers and envoy.lua:request_body. For the response information, select Response dynamic metadata as the Field type and set the field values to envoy.lua:response_headers and envoy.lua:response_body.