Files
nginx_lua_waf/lib/logger.lua
2026-05-06 17:36:25 +08:00

231 lines
5.4 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- 日志记录模块
-- 记录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