--- --- Generated by MC(https://www.magentochina.org/) --- Created by Shua1. --- DateTime: 2024/04/29 14:32 --- local log = require("waf.log") local _M = { version = 0.1, name = "request-log2file" } -- 内存队列,用于缓存日志数据 local logQueue = {} -- 获取当前日期 local function getCurrentDate() return os.date("%Y-%m-%d") end -- 日志文件路径 local logFilePath = "/uuwaf/logs/access_log_" .. getCurrentDate() .. ".json" -- 记录的请求数量阈值,降低IOPS local LOG_THRESHOLD = 10 -- 计数器,用于跟踪已记录的请求数量 local requestCounter = 0 -- 将日志信息写入文件 local function logToFile(filename, logs) local file = io.open(filename, "a") if not file then ngx.log(ngx.ERR, "Failed to open log file: ", filename) return end for _, info in ipairs(logs) do local json_str = log.encodeJson(info) file:write(json_str .. "\n") end file:close() end -- 判断是否应该记录该请求 local function shouldLogRequest(waf) -- 如果是POST请求,则无论URI是否匹配静态文件,都记录 if ngx.var.request_method == "POST" then return true end -- 排除以静态文件格式为结尾的请求,可根据自身需求修改 local uri = ngx.var.uri if uri:match("/[^/]*%.(js|css|jpg|jpeg|png|gif|svg|webp)$") then return false end return true end -- 将日志信息写入内存队列 local function logToMemory(info) table.insert(logQueue, info) -- 写入文件 logToFile(logFilePath, logQueue) end -- 将内存队列中的日志写入文件 local function flushLogsToFile(premature, filename) if not premature then if #logQueue > 0 then logToFile(filename, logQueue) logQueue = {} -- 清空内存队列 end end end -- 截断字符串到指定长度并进行Base64处理 local function truncateString(str, length) if str and #str > length then str = str:sub(1, length) end return ngx.encode_base64(str) end -- 请求阶段后过滤 function _M.log_pre_filter(waf) -- 判断是否应该记录该请求 if shouldLogRequest(waf) then local request_body_short = "" local block_action = "" local waf_rule_id = "" if ngx.var.request_method == "POST" and waf.reqContentLength > 2 then local body_data = (waf.form and waf.form["RAW"]) or '' if body_data then request_body_short = truncateString(body_data, 1000) -- 截断 request_body_short 并进行 Base64 处理 end end if waf.msg then block_action = "uuWaf" waf_rule_id = waf.rule_id end local info = { ["__time__"] = math.floor(ngx.var.msec), ["block_action"] = block_action, ["waf_rule_id"] = waf_rule_id, ["time"] = ngx.var.time_iso8601, ["real_client_ip"] = waf.ip, ["server_addr"] = ngx.var.server_addr, ["remote_addr"] = ngx.var.http_x_forwarded_for, ["scheme"] = ngx.var.scheme, ["request_method"] = ngx.var.request_method, ["request_uri"] = ngx.var.request_uri, ["request_length"] = ngx.var.request_length, ["uri"] = ngx.var.uri, ["request_time"] = ngx.var.request_time, ["body_bytes_sent"] = ngx.var.body_bytes_sent, ["request_body"] = request_body_short, ["bytes_sent"] = ngx.var.bytes_sent, ["status"] = ngx.var.status, ["upstream_time"] = ngx.var.upstream_response_time, ["upstream_host"] = ngx.var.upstream_addr, ["upstream_status"] = ngx.var.upstream_status, ["host"] = ngx.var.host, ["http_referer"] = ngx.var.http_referer, ["http_user_agent"] = ngx.var.http_user_agent, ["http_cookie"] = ngx.var.http_cookie } -- 将日志信息存入内存队列 table.insert(logQueue, info) -- 更新计数器 requestCounter = requestCounter + 1 -- 如果达到记录阈值,则执行写入操作,并重置计数器 if requestCounter >= LOG_THRESHOLD then logToFile(logFilePath, logQueue) logQueue = {} -- 清空内存队列 requestCounter = 0 -- 重置计数器 end end end return _M