first commit

This commit is contained in:
2026-05-06 17:36:25 +08:00
commit c9c2fa8185
6 changed files with 707 additions and 0 deletions

230
lib/logger.lua Normal file
View File

@@ -0,0 +1,230 @@
-- 日志记录模块
-- 记录IP频率限制的详细日志信息
local _M = {}
-- 日志级别
_M.LOG_DEBUG = "DEBUG"
_M.LOG_INFO = "INFO"
_M.LOG_WARN = "WARN"
_M.LOG_ERROR = "ERROR"
-- 从公共配置加载日志配置
local common_config = require "config.common_config"
local config = common_config.log_config
-- 日志文件句柄缓存
config.log_file_handle = nil
-- 日志级别映射
local log_levels = {
DEBUG = ngx.DEBUG,
INFO = ngx.INFO,
WARN = ngx.WARN,
ERROR = ngx.ERR
}
-- 检查日志级别是否应该输出
local function should_log(level)
if not config.enabled then
return false
end
local current_level = log_levels[config.log_level] or ngx.INFO
local msg_level = log_levels[level] or ngx.INFO
return msg_level >= current_level
end
-- 清理字符串,防止日志注入
local function sanitize_string(str)
if not str then
return ""
end
-- 去除换行符、回车符等特殊字符
str = tostring(str)
str = str:gsub("[\r\n\t]", " ")
str = str:gsub("[%z\1-\31]", "")
-- 限制长度
if #str > 500 then
str = str:sub(1, 500) .. "..."
end
return str
end
-- 获取当前时间戳
local function get_timestamp()
return ngx.time()
end
-- 格式化日志消息
local function format_message(level, message, extra_info)
local timestamp = get_timestamp()
local date_str = ngx.http_time(timestamp)
local log_msg = string.format("%s %s [%s] %s",
"[IP_RATE_LIMIT]",
date_str,
level,
message
)
if extra_info and next(extra_info) then
local extras = {}
for k, v in pairs(extra_info) do
table.insert(extras, string.format("%s=%s", k, sanitize_string(v)))
end
log_msg = log_msg .. " | " .. table.concat(extras, " ")
end
-- 添加换行符
log_msg = log_msg .. "\n"
return log_msg
end
-- 打开日志文件
local function open_log_file()
if config.log_path == "" then
return nil
end
-- 如果已经有打开的文件句柄,直接返回
if config.log_file_handle then
return config.log_file_handle
end
-- 尝试打开文件(追加模式)
local file, err = io.open(config.log_path, "a")
if not file then
ngx.log(ngx.ERR, "[IP_RATE_LIMIT] Failed to open log file: ", config.log_path, " error: ", err)
return nil
end
config.log_file_handle = file
return file
end
-- 关闭日志文件
local function close_log_file()
if config.log_file_handle then
config.log_file_handle:flush()
config.log_file_handle:close()
config.log_file_handle = nil
end
end
-- 写入日志
local function write_log(log_msg, ngx_level)
-- 如果配置了日志文件路径,写入文件
if config.log_path ~= "" then
local file = open_log_file()
if file then
file:write(log_msg)
file:flush()
end
end
-- 同时写入Nginx error.log用于调试和备份
ngx.log(ngx_level, log_msg)
end
-- 记录日志
local function log(level, message, extra_info)
if not should_log(level) then
return
end
local log_msg = format_message(level, message, extra_info)
local ngx_level = log_levels[level] or ngx.INFO
write_log(log_msg, ngx_level)
end
-- ============================================
-- 公共API
-- ============================================
-- 记录放行日志
function _M.log_allowed(client_ip, request_count, time_window)
if not config.log_allowed then
return
end
log(_M.LOG_DEBUG, "Request allowed", {
ip = client_ip,
count = tostring(request_count),
window = tostring(time_window) .. "s",
action = "ALLOWED"
})
end
-- 记录限流日志
function _M.log_rate_limited(client_ip, request_count, max_requests, time_window)
log(_M.LOG_WARN, "Rate limit exceeded", {
ip = client_ip,
count = tostring(request_count),
max = tostring(max_requests),
window = tostring(time_window) .. "s",
action = "RATE_LIMITED",
status = "429"
})
end
-- 记录封禁日志
function _M.log_blocked(client_ip, request_count, max_requests, time_window)
log(_M.LOG_WARN, "Access blocked", {
ip = client_ip,
count = tostring(request_count),
max = tostring(max_requests),
window = tostring(time_window) .. "s",
action = "BLOCKED",
status = "403"
})
end
-- 记录请求详情
function _M.log_request_details(client_ip, uri, method, user_agent)
if not config.log_request_details then
return
end
log(_M.LOG_DEBUG, "Request details", {
ip = client_ip,
uri = sanitize_string(uri),
method = method,
ua = sanitize_string(user_agent or "")
})
end
-- 记录错误日志
function _M.log_error(message, error_info)
log(_M.LOG_ERROR, message, error_info)
end
-- 记录警告日志
function _M.log_warn(message, warn_info)
log(_M.LOG_WARN, message, warn_info)
end
-- 记录信息日志
function _M.log_info(message, info)
log(_M.LOG_INFO, message, info)
end
-- 刷新日志(确保所有日志已写入)
function _M.flush()
if config.log_file_handle then
config.log_file_handle:flush()
end
end
-- 清理资源在worker退出时调用
function _M.cleanup()
close_log_file()
end
return _M