Files
2026-07-13 12:32:21 +08:00

132 lines
4.0 KiB
Lua

local set_log_level = require("resty.kong.log").set_log_level
local cjson = require("cjson.safe")
local constants = require("kong.constants")
local LOG_LEVELS = constants.LOG_LEVELS
local DYN_LOG_LEVEL_KEY = constants.DYN_LOG_LEVEL_KEY
local DYN_LOG_LEVEL_TIMEOUT_AT_KEY = constants.DYN_LOG_LEVEL_TIMEOUT_AT_KEY
local ngx = ngx
local kong = kong
local pcall = pcall
local type = type
local tostring = tostring
local tonumber = tonumber
local get_log_level = require("resty.kong.log").get_log_level
local NODE_LEVEL_BROADCAST = false
local CLUSTER_LEVEL_BROADCAST = true
local DEFAULT_LOG_LEVEL_TIMEOUT = 60 -- 60s
local function handle_put_log_level(self, broadcast)
if kong.configuration.database == "off" then
local message = "cannot change log level when not using a database"
return kong.response.exit(405, { message = message })
end
local log_level = LOG_LEVELS[self.params.log_level]
local timeout = math.ceil(tonumber(self.params.timeout) or DEFAULT_LOG_LEVEL_TIMEOUT)
if type(log_level) ~= "number" then
return kong.response.exit(400, { message = "unknown log level: " .. self.params.log_level })
end
if timeout < 0 then
return kong.response.exit(400, { message = "timeout must be greater than or equal to 0" })
end
local cur_log_level = get_log_level()
if cur_log_level == log_level then
local message = "log level is already " .. self.params.log_level
return kong.response.exit(200, { message = message })
end
local ok, err = pcall(set_log_level, log_level, timeout)
if not ok then
local message = "failed setting log level: " .. err
return kong.response.exit(500, { message = message })
end
local data = {
log_level = log_level,
timeout = timeout,
}
-- broadcast to all workers in a node
ok, err = kong.worker_events.post("debug", "log_level", data)
if not ok then
local message = "failed broadcasting to workers: " .. err
return kong.response.exit(500, { message = message })
end
if broadcast then
-- broadcast to all nodes in a cluster
ok, err = kong.cluster_events:broadcast("log_level", cjson.encode(data))
if not ok then
local message = "failed broadcasting to cluster: " .. err
return kong.response.exit(500, { message = message })
end
end
-- store in shm so that newly spawned workers can update their log levels
ok, err = ngx.shared.kong:set(DYN_LOG_LEVEL_KEY, log_level, timeout)
if not ok then
local message = "failed storing log level in shm: " .. err
return kong.response.exit(500, { message = message })
end
ok, err = ngx.shared.kong:set(DYN_LOG_LEVEL_TIMEOUT_AT_KEY, ngx.time() + timeout, timeout)
if not ok then
local message = "failed storing the timeout of log level in shm: " .. err
return kong.response.exit(500, { message = message })
end
return kong.response.exit(200, { message = "log level changed" })
end
local routes = {
["/debug/node/log-level"] = {
GET = function(self)
local cur_level = get_log_level()
if type(LOG_LEVELS[cur_level]) ~= "string" then
local message = "unknown log level: " .. tostring(cur_level)
return kong.response.exit(500, { message = message })
end
return kong.response.exit(200, { message = "log level: " .. LOG_LEVELS[cur_level] })
end,
},
["/debug/node/log-level/:log_level"] = {
PUT = function(self)
return handle_put_log_level(self, NODE_LEVEL_BROADCAST)
end
},
}
local cluster_name
if kong.configuration.role == "control_plane" then
cluster_name = "/debug/cluster/control-planes-nodes/log-level/:log_level"
else
cluster_name = "/debug/cluster/log-level/:log_level"
end
routes[cluster_name] = {
PUT = function(self)
return handle_put_log_level(self, CLUSTER_LEVEL_BROADCAST)
end
}
return routes