mirror of
https://gitee.com/iresty/apisix.git
synced 2024-12-05 13:37:34 +08:00
257 lines
6.6 KiB
Lua
257 lines
6.6 KiB
Lua
--
|
|
-- Licensed to the Apache Software Foundation (ASF) under one or more
|
|
-- contributor license agreements. See the NOTICE file distributed with
|
|
-- this work for additional information regarding copyright ownership.
|
|
-- The ASF licenses this file to You under the Apache License, Version 2.0
|
|
-- (the "License"); you may not use this file except in compliance with
|
|
-- the License. You may obtain a copy of the License at
|
|
--
|
|
-- http://www.apache.org/licenses/LICENSE-2.0
|
|
--
|
|
-- Unless required by applicable law or agreed to in writing, software
|
|
-- distributed under the License is distributed on an "AS IS" BASIS,
|
|
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
-- See the License for the specific language governing permissions and
|
|
-- limitations under the License.
|
|
--
|
|
local require = require
|
|
local yaml = require("tinyyaml")
|
|
local log = require("apisix.core.log")
|
|
local profile = require("apisix.core.profile")
|
|
local lfs = require("lfs")
|
|
local inspect = require("inspect")
|
|
local io = io
|
|
local ngx = ngx
|
|
local re_find = ngx.re.find
|
|
local get_headers = ngx.req.get_headers
|
|
local type = type
|
|
local pairs = pairs
|
|
local setmetatable = setmetatable
|
|
local pcall = pcall
|
|
local ipairs = ipairs
|
|
local unpack = unpack
|
|
local debug_yaml_path = profile:yaml_path("debug")
|
|
local debug_yaml
|
|
local debug_yaml_ctime
|
|
|
|
|
|
local _M = {version = 0.1}
|
|
|
|
|
|
local function read_debug_yaml()
|
|
local attributes, err = lfs.attributes(debug_yaml_path)
|
|
if not attributes then
|
|
log.notice("failed to fetch ", debug_yaml_path, " attributes: ", err)
|
|
return
|
|
end
|
|
|
|
-- log.info("change: ", json.encode(attributes))
|
|
local last_change_time = attributes.change
|
|
if debug_yaml_ctime == last_change_time then
|
|
return
|
|
end
|
|
|
|
local f, err = io.open(debug_yaml_path, "r")
|
|
if not f then
|
|
log.error("failed to open file ", debug_yaml_path, " : ", err)
|
|
return
|
|
end
|
|
|
|
local found_end_flag
|
|
for i = 1, 10 do
|
|
f:seek('end', -i)
|
|
|
|
local end_flag = f:read("*a")
|
|
-- log.info(i, " flag: ", end_flag)
|
|
if re_find(end_flag, [[#END\s*]], "jo") then
|
|
found_end_flag = true
|
|
break
|
|
end
|
|
end
|
|
|
|
if not found_end_flag then
|
|
f:seek("set")
|
|
local size = f:seek("end")
|
|
f:close()
|
|
|
|
if size > 8 then
|
|
log.warn("missing valid end flag in file ", debug_yaml_path)
|
|
end
|
|
return
|
|
end
|
|
|
|
f:seek('set')
|
|
local yaml_config = f:read("*a")
|
|
f:close()
|
|
|
|
local debug_yaml_new = yaml.parse(yaml_config)
|
|
if not debug_yaml_new then
|
|
log.error("failed to parse the content of file " .. debug_yaml_path)
|
|
return
|
|
end
|
|
|
|
debug_yaml_new.hooks = debug_yaml_new.hooks or {}
|
|
debug_yaml = debug_yaml_new
|
|
debug_yaml_ctime = last_change_time
|
|
end
|
|
|
|
|
|
local sync_debug_hooks
|
|
do
|
|
local pre_mtime
|
|
local enabled_hooks = {}
|
|
|
|
local function apple_new_fun(module, fun_name, file_path, hook_conf)
|
|
local log_level = hook_conf.log_level or "warn"
|
|
|
|
if not module or type(module[fun_name]) ~= "function" then
|
|
log.error("failed to find function [", fun_name,
|
|
"] in module:", file_path)
|
|
return
|
|
end
|
|
|
|
local fun = module[fun_name]
|
|
local fun_org
|
|
if enabled_hooks[fun] then
|
|
fun_org = enabled_hooks[fun].org
|
|
enabled_hooks[fun] = nil
|
|
else
|
|
fun_org = fun
|
|
end
|
|
|
|
local t = {fun_org = fun_org}
|
|
local mt = {}
|
|
|
|
function mt.__call(self, ...)
|
|
local arg = {...}
|
|
local http_filter = debug_yaml.http_filter
|
|
local api_ctx = ngx.ctx.api_ctx
|
|
local enable_by_hook = not (http_filter and http_filter.enable)
|
|
local enable_by_header_filter = (http_filter and http_filter.enable)
|
|
and (api_ctx and api_ctx.enable_dynamic_debug)
|
|
if hook_conf.is_print_input_args then
|
|
if enable_by_hook or enable_by_header_filter then
|
|
log[log_level]("call require(\"", file_path, "\").", fun_name,
|
|
"() args:", inspect(arg))
|
|
end
|
|
end
|
|
|
|
local ret = {self.fun_org(...)}
|
|
if hook_conf.is_print_return_value then
|
|
if enable_by_hook or enable_by_header_filter then
|
|
log[log_level]("call require(\"", file_path, "\").", fun_name,
|
|
"() return:", inspect(ret))
|
|
end
|
|
end
|
|
return unpack(ret)
|
|
end
|
|
|
|
setmetatable(t, mt)
|
|
enabled_hooks[t] = {
|
|
org = fun_org, new = t, mod = module,
|
|
fun_name = fun_name
|
|
}
|
|
module[fun_name] = t
|
|
end
|
|
|
|
|
|
function sync_debug_hooks()
|
|
if not debug_yaml_ctime or debug_yaml_ctime == pre_mtime then
|
|
return
|
|
end
|
|
|
|
for _, hook in pairs(enabled_hooks) do
|
|
local m = hook.mod
|
|
local name = hook.fun_name
|
|
m[name] = hook.org
|
|
end
|
|
|
|
enabled_hooks = {}
|
|
|
|
local hook_conf = debug_yaml.hook_conf
|
|
if not hook_conf.enable then
|
|
pre_mtime = debug_yaml_ctime
|
|
return
|
|
end
|
|
|
|
local hook_name = hook_conf.name or ""
|
|
local hooks = debug_yaml[hook_name]
|
|
if not hooks then
|
|
pre_mtime = debug_yaml_ctime
|
|
return
|
|
end
|
|
|
|
for file_path, fun_names in pairs(hooks) do
|
|
local ok, module = pcall(require, file_path)
|
|
if not ok then
|
|
log.error("failed to load module [", file_path, "]: ", module)
|
|
|
|
else
|
|
for _, fun_name in ipairs(fun_names) do
|
|
apple_new_fun(module, fun_name, file_path, hook_conf)
|
|
end
|
|
end
|
|
end
|
|
|
|
pre_mtime = debug_yaml_ctime
|
|
end
|
|
|
|
end --do
|
|
|
|
|
|
local function sync_debug_status(premature)
|
|
if premature then
|
|
return
|
|
end
|
|
|
|
read_debug_yaml()
|
|
sync_debug_hooks()
|
|
end
|
|
|
|
|
|
local function check()
|
|
if not debug_yaml or not debug_yaml.http_filter then
|
|
return false
|
|
end
|
|
|
|
local http_filter = debug_yaml.http_filter
|
|
if not http_filter or not http_filter.enable_header_name or not http_filter.enable then
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
function _M.dynamic_debug(api_ctx)
|
|
if not check() then
|
|
return
|
|
end
|
|
|
|
if get_headers()[debug_yaml.http_filter.enable_header_name] then
|
|
api_ctx.enable_dynamic_debug = true
|
|
end
|
|
end
|
|
|
|
|
|
function _M.enable_debug()
|
|
if not debug_yaml or not debug_yaml.basic then
|
|
return false
|
|
end
|
|
|
|
return debug_yaml.basic.enable
|
|
end
|
|
|
|
|
|
function _M.init_worker()
|
|
local process = require("ngx.process")
|
|
if process.type() ~= "worker" then
|
|
return
|
|
end
|
|
|
|
sync_debug_status()
|
|
ngx.timer.every(1, sync_debug_status)
|
|
end
|
|
|
|
|
|
return _M
|