| Server IP : 104.21.31.197 / Your IP : 172.70.49.41 Web Server : nginx/1.20.2 System : Linux 172-104-110-161.ip.linodeusercontent.com 3.10.0-1160.36.2.el7.x86_64 #1 SMP Wed Jul 21 11:57:15 UTC 2021 x86_64 User : www ( 1000) PHP Version : 8.1.9 Disable Function : passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /www/server/total/ |
Upload File : |
local version = "1.9"
local cpath = "/www/server/total/"
if not package.cpath:find(cpath) then
package.cpath = cpath .. "?.so;" .. package.cpath ..";;"
end
if not package.path:find(cpath) then
package.path = cpath .. "?.lua;" .. package.path
end
local server_name,ip,today,day,body_length,config,method,httpd,cache,tms,cache_count
local hour_str
local cache_count_threshold, cache_interval_threshold = 300, 10
local cjson, sqlite3, memcached, socket
local total_config
local update_day
local data_dir
local no_binding_server_name
local number_day
local day_column
local flow_column
local spider_column
local day_hour1_column = "day_hour1"
local flow_hour1_column = "flow_hour1"
local spider_hour1_column = "spider_flow_hour1"
local client_port
local fake_spider = 77
local total_db_name = "total.db"
local total_db, logs_db
local domains
local ONE_DAY_TIMEOUT = 86400
local SEVEN_DAY_TIMEOUT = 86400
local site_config
local debug_mode = false
local function debug(msg)
if not debug_mode then return true end
local fp = io.open('/www/server/total/debug.log', 'ab')
if fp == nil then
return nil
end
local localtime = os.date("%Y-%m-%d %H:%M:%S")
if server_name then
fp:write(server_name.."/"..localtime..":"..tostring(msg) .. "\n")
else
fp:write(localtime..":"..tostring(msg) .. "\n")
end
fp:flush()
fp:close()
return true
end
local function get_config(site)
local config_data = total_config
-- local config_data = json.decode(read_file_body_bylog(cpath..'/config.json'))
local config = nil
if config_data["global"] == nil then return nil end
global_config = config_data["global"]
if key == "monitor" then
if not global_config["monitor"] then
return global_config["monitor"]
end
end
if config_data[server_name] == nil then
config = global_config
else
config = config_data[server_name]
for k, v in pairs(global_config) do
if config[k] == nil then
config[k] = v
end
end
end
return config
end
local function file_exists(file)
local file = io.open(file, "rb")
if file then
file:close()
return true
end
return false
end
function check_dir(path)
local file = io.open(path, "rb")
if file then file:close() end
return file ~= nil
end
local function arrlen_bylog(arr)
if not arr then return 0 end
count = 0
for _,v in ipairs(arr)
do
count = count + 1
end
return count
end
local function split_bylog( str,reps )
local resultStrList = {}
string.gsub(str,'[^'..reps..']+',function(w)
table.insert(resultStrList,w)
end)
return resultStrList
end
function get_server_name(c_name)
if c_name == no_binding_server_name then
return no_binding_server_name
end
if c_name == "127.0.0.1" or domains[c_name] then
return c_name
end
local my_name = cache:get(c_name)
if my_name then return my_name end
local cache_timeout = 3600
local simple_domain
local s,e = string.find(c_name, "[.]")
if s and e then
simple_domain = string.sub(c_name, s+1)
end
local determined_name
local _normal
for site_name,v in pairs(domains)
do
_normal = v["normal"]
if _normal then
if _normal[c_name] then
cache:set(c_name, site_name, cache_timeout)
return site_name
end
if simple_domain and _normal[simple_domain] then
determined_name = site_name
end
end
end
if determined_name then
cache:set(c_name, determined_name,cache_timeout)
return determined_name
end
local tconf = httpd:activeconfig()
if not tconf then
cache:set(c_name, no_binding_server_name,cache_timeout)
return no_binding_server_name
end
local tmp = split_bylog(tconf[1].file,'/')
if not tmp then
cache:set(c_name, no_binding_server_name,cache_timeout)
return no_binding_server_name
end
local other = string.gsub(tmp[arrlen_bylog(tmp)],'.conf$','')
cache:set(c_name, other,cache_timeout)
return other
end
function create_dir(path, create_server_name)
if httpd:regex(path,"(\\&|\\{|\\}|\\%|\\$|\\+|\\=|\\*|\\@|\\'|\\\"|\\[|\\]|/\\.\\./)",0x01) then
-- debug("非法路径.")
return false
end
local check_name = get_server_name(create_server_name)
if check_ame == "httpd-vhosts" then
return false
end
httpd:mkrdir(path)
return true
end
function write_file_bylog(filename,body,mode)
local fp = io.open(filename,mode)
if fp == nil then
return nil
end
fp:write(body)
fp:flush()
fp:close()
return true
end
local function load_global_exclude_ip()
-- 全局的排除IP规则缓存设置和更新
local log_dir = cpath .. 'logs'
local global_exclude_file = log_dir.."/reload_exclude_ip.pl"
local load_key = "GLOBAL_EXCLUDE_IP_LOADED"
if not file_exists(global_exclude_file) then
if cache:get(load_key) then
-- debug("Global not need to reload exclude ips.")
return true
end
end
-- debug("to load golbal exclude ip.")
local info_obj = package.loaded["total_config"]
if info_obj then
package.loaded["total_config"] = nil
total_config = require "total_config"
end
local global_old_exclude_ip = total_config["global"]["old_exclude_ip"]
-- debug("global old exclude ip:" ..tostring(global_old_exclude_ip))
if global_old_exclude_ip then
-- 加载站点的排除规则
for k, _ip in pairs(global_old_exclude_ip)
do
-- debug("delete global old exclude ip: ".._ip)
cache:delete("GLOBAL_EXCLUDE_IP_".._ip)
end
end
-- 更新全局排除规则
local global_exclude_ip = total_config["global"]["exclude_ip"]
if global_exclude_ip then
for i, _ip in pairs(global_exclude_ip)
do
-- global
if not cache:get("GLOBAL_EXCLUDE_IP_".._ip) then
-- debug("set global exclude ip: ".._ip)
cache:set("GLOBAL_EXCLUDE_IP_".._ip, true)
end
end
end
-- 删除更新规则标志
if file_exists(global_exclude_file) then
os.execute("rm -rf "..global_exclude_file)
end
-- set tag
cache:set(load_key, true)
-- debug("loaded global exclude ip settings.")
end
local function load_exclude_ip(server_name)
-- 加载排除IP
-- 两种情况下需要重新加载排除IP:
-- 1. 插件端改了配置文件,在对应的目录生成reload_exclude_ip.pl文件标记需要重新加载。
-- 2. Nginx重新启动,Key=EXCLUDE_IP_LOADED的缓存为空。
local log_dir = cpath .. 'logs'
local site_exclude_file = log_dir.."/"..server_name.."/reload_exclude_ip.pl"
local load_key = server_name .. "_EXCLUDE_IP_LOADED"
if not file_exists(site_exclude_file) then
if cache:get(load_key) then
-- debug("Not need to reload exclude ips.")
return true
end
end
local info_obj = package.loaded["total_config"]
if info_obj then
package.loaded["total_config"] = nil
total_config = require "total_config"
end
local site_config = total_config[server_name]
local site_old_exclude_ip = nil
if site_config then
site_old_exclude_ip = site_config["old_exclude_ip"]
end
if site_old_exclude_ip then
-- 加载站点的排除规则
for k, _ip in pairs(site_old_exclude_ip)
do
-- debug("delete old exclude ip: ".._ip)
cache:delete(server_name .. "_EXCLUDE_IP_".._ip)
-- cache:delete("GLOBAL_EXCLUDE_IP_".._ip)
end
end
local site_exclude_ip = nil
if site_config then
site_exclude_ip = site_config["exclude_ip"]
end
if site_exclude_ip then
for i, _ip in pairs(site_exclude_ip)
do
-- debug("set exclude ip: ".._ip)
cache:set(server_name .. "_EXCLUDE_IP_".._ip, true)
end
end
-- 删除更新规则标志
if file_exists(site_exclude_file) then
os.execute("rm -rf "..site_exclude_file)
end
-- set tag
cache:set(load_key, true)
-- debug('load exclude over.')
return true
end
local function exclude_ip(ip)
if config["exclude_ip"] then
if config["exclude_ip"][ip] then
-- debug("Exclude request from ip:"..ip)
return true
end
end
return false
end
local function get_suffix(uri)
if not uri then return nil end
local res = httpd:regex(uri, "[.][^.]+$", 0x01)
if res then
return string.sub(res[0], 2)
end
return nil
end
local function exclude_extension(uri)
if not uri then return false end
local conf = config['exclude_extension']
if not conf then return false end
local suffix = get_suffix(uri)
if not suffix then return false end
if conf[suffix] then
-- debug("Exclude suffix:"..suffix)
return true
end
return false
end
local function exclude_url(uri)
if not uri then return false end
if not config['exclude_url'] then return false end
-- local the_uri = string.sub(httpd.unparsed_uri, 2)
local the_uri = uri
for _,v in pairs(config['exclude_url'])
do
local url = v["url"]
local mode = v["mode"]
if mode == "regular" then
if httpd:regex(the_uri,url,0x01) then
-- debug("Exclude url("..mode..") by "..url.." Match:"..uri)
return true
end
else
if the_uri == url then
-- debug("Exclude url("..mode..") by "..url.." Match:"..uri)
return true
end
end
end
return false
end
local function exclude_status(status_code)
if not config["exclude_status"] then return false end
local str_status_code = tostring(status_code)
if config["exclude_status"][str_status_code] then
-- debug("Exclude reponse status code: "..str_status_code)
return true
end
return false
end
local function is_migrating(store_server_name)
local file = io.open("/www/server/total/migrating", "rb")
if file then return true end
local file = io.open("/www/server/total/logs/"..store_server_name.."/migrating", "rb")
if file then return true end
return false
end
function read_file_body_bylog(filename)
fp = nil
local ok,err = pcall(function() fp=io.open(filename,'rb') end)
if not fp then
return nil
end
fbody = fp:read("*a")
fp:close()
if fbody == '' then
return nil
end
return fbody
end
local function is_ipaddr_bylog(client_ip)
local cipn = split_bylog(client_ip,'.')
if arrlen_bylog(cipn) < 4 then return false end
for _,v in ipairs({1,2,3,4})
do
local ipv = tonumber(cipn[v])
if ipv == nil then return false end
if ipv > 255 or ipv < 0 then return false end
end
return true
end
local function get_client_ip_bylog()
local client_ip = "unknown"
local cdn = config["cdn"]
if cdn == true then
for _,v in pairs(config['cdn_headers'])
do
if request_header[v] ~= nil and request_header[v] ~= "" then
client_ip = split_bylog(httpd.headers_in[v],',')[1]
break;
end
end
end
if type(client_ip) == 'table' then client_ip = "" end
if client_ip ~= "unknown" and string.match(client_ip,"^[%w:]+$") then
return client_ip
end
if string.match(client_ip,"^%d+%.%d+%.%d+%.%d+$") == nil or not is_ipaddr_bylog(client_ip) then
client_ip = httpd.useragent_ip
if client_ip == nil then
client_ip = "unknown"
end
end
return client_ip
end
local function arrip_bylog(ipstr)
if ipstr == 'unknown' then return {0,0,0,0} end
iparr = split_bylog(ipstr,'.')
iparr[1] = tonumber(iparr[1])
iparr[2] = tonumber(iparr[2])
iparr[3] = tonumber(iparr[3])
iparr[4] = tonumber(iparr[4])
return iparr
end
function is_min_bylog(ip1,ip2)
n = 0
for _,v in ipairs({1,2,3,4})
do
if ip1[v] == ip2[v] then
n = n + 1
elseif ip1[v] > ip2[v] then
break
else
return false
end
end
return true
end
function is_max_bylog(ip1,ip2)
n = 0
for _,v in ipairs({1,2,3,4})
do
if ip1[v] == ip2[v] then
n = n + 1
elseif ip1[v] < ip2[v] then
break
else
return false
end
end
return true
end
function compare_ip_bylog(ips)
if ip == 'unknown' then return true end
if not is_max_bylog(ipn,ips[2]) then return false end
if not is_min_bylog(ipn,ips[1]) then return false end
return true
end
function get_length()
local clen = httpd.headers_out['Content-Length']
if clen == nil then clen = 0 end
if clen == 0 and httpd.filename then
tmp = httpd:stat(httpd.filename)
if tmp then clen = tmp.size end
end
return tonumber(clen)
end
function get_end_time()
local s_time = os.time()
local n_date = os.date("*t",s_time + 86400)
n_date.hour = 0
n_date.min = 0
n_date.sec = 0
d_time = os.time(n_date)
return d_time - s_time
end
function get_data_on(filename)
local data = cache:get(filename)
if not data then
data = read_file_body_bylog(filename)
if not data then
data = {}
else
data = json.decode(data)
end
else
data = json.decode(data)
end
return data
end
-- function save_data_on(filename,data)
-- local newdata = json.encode(data)
-- local extime = 0;
-- if string.find(filename,"%d+-%d+-%d+") then extime = 88400 end
-- cache:set(filename,newdata,extime)
-- if not cache:get(filename..'_lock') then
-- cache:set(filename..'_lock',1,10)
-- write_file_bylog(filename,newdata,'wb')
-- end
-- end
-- local function get_request_time()
-- return -1
-- end
local function get_spiders()
return require "spiders"
end
function get_spider_json(name)
-- 读取蜘蛛IP库并缓存
sfile_name = name
if cache:get(cpath..sfile_name..'SPIDER') then return cache:get(cpath..sfile_name..'SPIDER') end
file_path=cpath.."xspiders/"..sfile_name..'.json'
data = read_file_body_bylog(file_path)
if not data then
data={}
end
cache:set(cpath..sfile_name..'SPIDER',data,10000)
return data
end
function check_spider(name,ip)
-- 验证蜘蛛真假
-- name: 蜘蛛IP库分类名称
-- ip: 请求IP
-- @return: true代表真蜘蛛或者无法验证的蜘蛛;false代表假蜘蛛
name = tostring(name)
data=get_spider_json(name)
if not data then
return true
end
local ok,zhizhu_list_data = pcall(function()
return json.decode(data)
end)
if not ok then
return true
end
spiders_key = "SPIDERS_"..name
sp_key = "SPIDER_"..ip
if cache:get(spiders_key) then
if cache:get(sp_key) then
return true
end
return false
end
cache:set(spiders_key,'1',86400)
for _,k in ipairs(zhizhu_list_data)
do
cache:set("SPIDER_"..k,'1',86400)
end
if cache:get(sp_key) then
return true
end
return false
end
local function match_spider(client_ip)
-- TODO 加入IP区间判断是否是蜘蛛请求
-- 匹配蜘蛛请求
local ua = ''
if httpd.headers_in['user-agent'] then
ua = httpd.headers_in['user-agent']
end
if not ua then
return false, nil, 0
end
local is_spider = false
local spider_name = nil
local spider_table = {
["baidu"] = 1,
["bing"] = 2,
["qh360"] = 3,
["google"] = 4,
["bytes"] = 5,
["sogou"] = 6,
["youdao"] = 7,
["soso"] = 8,
["dnspod"] = 9,
["yandex"] = 10,
["yisou"] = 11,
["other"] = 12,
["mpcrawler"] = 13,
["yahoo"] = 14,
["duckduckgo"] = 15
}
local res,err = httpd:regex(ua, "(Baiduspider|Bytespider|360Spider|Sogou web spider|Sosospider|Googlebot|bingbot|AdsBot-Google|Google-Adwords|YoudaoBot|Yandex|DNSPod-Monitor|YisouSpider|mpcrawler)", 0x01)
check_res = true
if res then
is_spider = true
spider_match = string.lower(res[0])
if string.find(spider_match, "baidu") then
spider_name = "baidu"
check_res = check_spider("1", client_ip)
elseif string.find(spider_match, "bytes") then
spider_name = "bytes"
check_res = check_spider("7", client_ip)
elseif string.find(spider_match, "360") then
spider_name = "qh360"
check_res = check_spider("3", client_ip)
elseif string.find(spider_match, "sogou") then
spider_name = "sogou"
check_res = check_spider("4", client_ip)
elseif string.find(spider_match, "soso") then
spider_name = "soso"
elseif string.find(spider_match, "google") then
spider_name = "google"
check_res = check_spider("2", client_ip)
elseif string.find(spider_match, "bingbot") then
spider_name = "bing"
check_res = check_spider("6", client_ip)
elseif string.find(spider_match, "youdao") then
spider_name = "youdao"
elseif string.find(spider_match, "dnspod") then
spider_name = "dnspod"
elseif string.find(spider_match, "yandex") then
spider_name = "yandex"
elseif string.find(spider_match, "yisou") then
spider_name = "yisou"
elseif string.find(spider_match, "mpcrawler") then
spider_name = "mpcrawler"
end
end
if is_spider then
if not check_res then
return is_spider, spider_name, fake_spider
end
return is_spider, spider_name, spider_table[spider_name]
end
local other_res, err = httpd:regex(ua, "(Yahoo|Slurp|DuckDuckGo)", 0x01)
if other_res then
other_res = string.lower(other_res[0])
if string.find(other_res, "yahoo") then
spider_name = "yahoo"
check_res = check_spider("5", client_ip)
elseif string.find(other_res, "slurp") then
spider_name = "yahoo"
elseif string.find(other_res, "duckduckgo") then
spider_name = "duckduckgo"
end
if not check_res then
return true, spider_name, fake_spider
end
return true, spider_name, spider_table[spider_name]
end
return false, nil, 0
end
local function statistics_ipc()
local ipc = 0
local ip_token = server_name..'_'..ip
if not cache:get(ip_token) then
ipc = 1
cache:set(ip_token,1,get_end_time())
end
return ipc
end
local function statistics_request()
-- 计算pv uv
local pvc = 0
local uvc = 0
if method == 'GET' and tonumber(httpd.status) == 200 and body_length > 200 then
local ua = ''
if httpd.headers_in['user-agent'] then
ua = string.lower(httpd.headers_in['user-agent'])
end
if httpd.headers_out['content-type'] then
if string.find(httpd.headers_out['content-type'],'text/html') then
-- if not httpd:regex(httpd.uri,"[.](js|css|png|jpeg|jpg|gif)$",0x01) then
-- 判断IP是否重复的时间限定范围是请求的当前时间+24小时
pvc = 1
if ua then
if string.find(ua,'mozilla') then
local _today = os.date("%Y-%m-%d")
local uv_token = httpd:md5(ip .. httpd.headers_in['user-agent'] .. _today)
if not cache:get(uv_token) then
uvc = 1
cache:set(uv_token,1,get_end_time())
end
end
end
end
end
end
return pvc, uvc
end
local function get_clients()
-- local clients_json = cpath .. '/config/clients.json'
-- local clients = get_data_on(clients_json)
return require "clients"
end
local function match_pc_client(ua, clients)
local msie = nil
local msie_pattern = nil
local safari = nil
local safari_pattern = nil
local chrome = nil
local chrome_pattern = nil
for column, pattern in pairs(clients) do
if column == "safari" then
safari_pattern = pattern
elseif column == "chrome" then
chrome_pattern = pattern
elseif column == "msie" then
msie_pattern = pattern
elseif httpd:regex(ua, pattern, 0x01) then
return column
end
end
if msie_pattern and httpd:regex(ua, msie_pattern, 0x01) then
return "msie"
end
if chrome_pattern and httpd:regex(ua, chrome_pattern, 0x01) then
return "chrome"
end
if safari_pattern and httpd:regex(ua, safari_pattern, 0x01) then
return "safari"
end
return nil
end
local function match_norepeat_client(ua, clients)
if clients == nil then return end
for name, pattern in pairs(clients) do
if httpd:regex(ua, pattern, 0x01) then
return name
end
end
end
local function get_update_field(field, value)
return field.."="..field.."+"..tostring(value)
end
local function match_client()
-- 匹配客户端
local ua = ''
if request_header['user-agent'] then
ua = request_header['user-agent']
end
if not ua then
return false, nil
end
local client_stat_fields = ""
local clients_map = {
["android"] = "android",
["iphone"] = "iphone",
["ipod"] = "iphone",
["ipad"] = "iphone",
["firefox"] = "firefox",
["msie"] = "msie",
["trident"] = "msie",
["360se"] = "qh360",
["360ee"] = "qh360",
["360browser"] = "qh360",
["qihoo"] = "qh360",
["the world"] = "theworld",
["theworld"] = "theworld",
["tencenttraveler"] = "tt",
["maxthon"] = "maxthon",
["opera"] = "opera",
["qqbrowser"] = "qq",
["ucweb"] = "uc",
["ubrowser"] = "uc",
["safari"] = "safari",
["chrome"] = "chrome",
["metasr"] = "metasr",
["2345explorer"] = "pc2345",
["edge"] = "edeg",
["edg"] = "edeg",
["windows"] = "windows",
["linux"] = "linux",
["macintosh"] = "mac",
["mobile"] = "mobile"
}
local mobile_regx = "(Mobile|Android|iPhone|iPod|iPad)"
local mobile_res = httpd:regex(ua, mobile_regx, 0x01)
--mobile
if mobile_res then
client_stat_fields = client_stat_fields..","..get_update_field("mobile", 1)
mobile_res = string.lower(mobile_res[0])
if mobile_res ~= "mobile" then
client_stat_fields = client_stat_fields..","..get_update_field(clients_map[mobile_res], 1)
end
else
--pc
-- 匹配结果的顺序,与ua中关键词的顺序有关
-- lua的正则不支持|语法
local pc_regx1 = "(360SE|360EE|360browser|Qihoo|TheWorld|TencentTraveler|Maxthon|Opera|QQBrowser|UCWEB|UBrowser|MetaSr|2345Explorer|Edg[e]*)"
local pc_res = httpd:regex(ua, pc_regx1, 0x01)
local cls_pc = nil
if not pc_res then
if string.find(ua, "[Ff]irefox") then
cls_pc = "firefox"
elseif string.find(ua, "MSIE") or string.find(ua, "Trident") then
cls_pc = "msie"
elseif string.find(ua, "[Cc]hrome") then
cls_pc = "chrome"
elseif string.find(ua, "[Ss]afari") then
cls_pc = "safari"
end
else
cls_pc = string.lower(pc_res[0])
end
-- debug("UA:"..ua)
-- debug("Cls pc:"..cls_pc)
if cls_pc then
client_stat_fields = client_stat_fields..","..get_update_field(clients_map[cls_pc], 1)
else
-- machine and other
local machine_res, err = httpd:regex(ua, "([Cc]url|HeadlessChrome|[a-zA-Z]+[Bb]ot|[Ww]get|[Ss]pider|[Cc]rawler|[Ss]crapy|zgrab|[Pp]ython|java)", 0x01)
if machine_res then
client_stat_fields = client_stat_fields..","..get_update_field("machine", 1)
else
-- 移动端+PC端+机器以外 归类到 其他
client_stat_fields = client_stat_fields..","..get_update_field("other", 1)
end
end
local os_regx = "(Windows|Linux|Macintosh)"
local os_res = httpd:regex(ua, os_regx, 0x01)
if os_res then
os_res = string.lower(os_res[0])
client_stat_fields = client_stat_fields..","..get_update_field(clients_map[os_res], 1)
end
end
local other_regx = "MicroMessenger"
local other_res = string.find(ua, other_regx)
if other_res then
client_stat_fields = client_stat_fields..","..get_update_field("weixin", 1)
end
if client_stat_fields then
client_stat_fields = string.sub(client_stat_fields, 2)
end
return client_stat_fields
end
local function total_flow()
local flow_key = server_name .. '_flow'
local flow_write = server_name .. '_flow_write'
if cache:get(flow_write) then
cache:incr(flow_key,body_length)
else
local write_time = now_timestamp
local now_flow = cache:get(flow_key)
if not now_flow then now_flow = 0 end
now_flow = now_flow + body_length
local realtime_data = tostring(now_flow)..","..tostring(write_time)
-- debug("Current realtime data:"..tostring(realtime_data))
local queue_length = 10
local position_key = flow_key.."_positon"
local queue_key = flow_key.."_queue_"
local req_position = 0
if cache:get(position_key) == nil then
cache:set(position_key, 1)
req_position = 1
else
cache:incr(position_key, 1)
req_position = cache:get(position_key)
if req_position >= queue_length then
cache:set(position_key, 1)
req_position = 1
end
end
cache:set(queue_key..tostring(req_position), realtime_data)
local data = ""
for i=1, queue_length, 1 do
local _d = cache:get(queue_key..tostring(i))
if _d ~= nil then
data = data.."\n".._d
end
end
local path = cpath .. 'logs/' .. server_name
if not check_dir(path) then
local create_res = create_dir(path, server_name)
if not create_res then
return create_res
end
end
local filename = path .. '/flow_sec.json'
write_file_bylog(filename,tostring(data),'w+')
cache:set(flow_key,body_length,2)
cache:set(flow_write,1,1)
end
end
local function total_req_sec()
local _key = server_name .. '_req'
local _write = server_name .. '_req_write'
if cache:get(_write) then
cache:incr(_key,1)
else
local write_time = now_timestamp
local now_ = cache:get(_key)
if not now_ then now_ = 0 end
now_ = now_ + 1
local realtime_data = tostring(now_)..","..tostring(write_time)
local queue_length = 10
local position_key = _key.."_positon"
local queue_key = _key.."_queue_"
local req_position = 0
if cache:get(position_key) == nil then
cache:set(position_key, 1)
req_position = 1
else
cache:incr(position_key, 1)
req_position = cache:get(position_key)
if req_position >= queue_length then
cache:set(position_key, 1)
req_position = 1
end
end
cache:set(queue_key..tostring(req_position), realtime_data)
local data = ""
for i=1, queue_length, 1 do
local _d = cache:get(queue_key..tostring(i))
if _d ~= nil then
data = data.."\n".._d
end
end
local path = cpath .. 'logs/' .. server_name
if not check_dir(path) then
local create_res = create_dir(path, server_name)
if not create_res then
return create_res
end
end
local filename = path .. '/req_sec.json'
local write_res = write_file_bylog(filename,data,'w+')
cache:set(_key,1,2)
cache:set(_write,1,1)
end
end
local function get_domain()
local domain = request_header['host']
if domain ~= nil then
domain = string.gsub(domain, "_", ".")
else
domain = "unknown"
end
return domain
end
local function is_storing(store_server_name)
local store_status = cache:get(store_server_name.."_STORING")
if store_status ~= nil and store_status == true then
return true
end
return false
end
local function get_store_key()
return os.date("%Y%m%d%H", os.time())
end
local function update_stat(update_server_name, db, stat_table, key, columns)
-- 根据指定表名,更新统计数据
if not columns then return end
local stmt = db:prepare(string.format("INSERT INTO %s(time) SELECT :time WHERE NOT EXISTS(SELECT time FROM %s WHERE time=:time);", stat_table, stat_table))
if not stmt then
return
end
stmt:bind_names{time=key}
stmt:step()
stmt:finalize()
local update_sql = "UPDATE ".. stat_table .. " SET " .. columns
update_sql = update_sql .. " WHERE time=" .. key
status, errorString = db:exec(update_sql)
end
local function init_logs_db(log_dir, server_name, db_name)
-- 初始化日志db
local path = log_dir .."/".. server_name
if not check_dir(path) then
local res = create_dir(path, server_name)
if not res then
return false
end
end
local db_path = path .. "/" .. db_name
if cache:get(db_path) then
return
end
local db = sqlite3.open(db_path)
local table_name = "site_logs"
local stmt = db:prepare("SELECT COUNT(*) FROM sqlite_master where type='table' and name=?")
local rows = 0
if stmt ~= nil then
stmt:bind_values(table_name)
stmt:step()
rows = stmt:get_uvalues()
stmt:finalize()
end
if stmt ~= nil and rows >0 then
cache:set(db_path, true)
else
status, errorString = db:exec([[PRAGMA synchronous = 0]])
status, errorString = db:exec([[PRAGMA page_size = 4096]])
status, errorString = db:exec([[PRAGMA journal_mode = wal]])
status, errorString = db:exec([[PRAGMA journal_size_limit = 1073741824]])
status,errorString = db:exec[[
CREATE TABLE site_logs (
time INTEGER,
ip TEXT,
domain TEXT,
server_name TEXT,
method TEXT,
status_code INTEGER,
uri TEXT,
body_length INTEGER,
referer TEXT DEFAULT "",
user_agent TEXT,
is_spider INTEGER DEFAULT 0,
protocol TEXT,
request_time INTEGER,
request_headers TEXT DEFAULT "",
ip_list TEXT DEFAULT "",
client_port INTEGER DEFAULT -1
)]]
status, errorString = db:exec([[CREATE INDEX time_inx ON site_logs(time)]])
cache:set(db_path, true)
-- debug("初始化日志数据库完成。")
end
if db and db:isopen() then
db:close()
end
return true
end
local function init_total_db(log_dir, server_name, db_name)
-- 初始化日志db
local path = log_dir .."/".. server_name
if not check_dir(path) then
local res = create_dir(path, server_name)
if not res then
return false
end
end
local db_path = path .. "/" .. db_name
if cache:get(db_path) then
return
end
local db = sqlite3.open(db_path)
if not db then
return false
end
local table_name = "request_stat"
local stmt = db:prepare("SELECT COUNT(*) FROM sqlite_master where type='table' and name=?")
local rows = 0
if stmt ~= nil then
stmt:bind_values(table_name)
stmt:step()
rows = stmt:get_uvalues()
stmt:finalize()
end
if stmt ~= nil and rows >0 then
cache:set(db_path, true)
else
status, errorString = db:exec([[PRAGMA synchronous = 0]])
status, errorString = db:exec([[PRAGMA page_size = 4096]])
status, errorString = db:exec([[PRAGMA journal_mode = wal]])
status, errorString = db:exec([[PRAGMA journal_size_limit = 1073741824]])
-- 客户端统计明细,按小时统计
-- 客户端的定义: {client:"Android", column_name:"android"}
status, errorString = db:exec[[
CREATE TABLE client_stat(
time INTEGER PRIMARY KEY,
weixin INTEGER DEFAULT 0,
android INTEGER DEFAULT 0,
iphone INTEGER DEFAULT 0,
mac INTEGER DEFAULT 0,
windows INTEGER DEFAULT 0,
linux INTEGER DEFAULT 0,
edeg INTEGER DEFAULT 0,
firefox INTEGER DEFAULT 0,
msie INTEGER DEFAULT 0,
metasr INTEGER DEFAULT 0,
qh360 INTEGER DEFAULT 0,
theworld INTEGER DEFAULT 0,
tt INTEGER DEFAULT 0,
maxthon INTEGER DEFAULT 0,
opera INTEGER DEFAULT 0,
qq INTEGER DEFAULT 0,
uc INTEGER DEFAULT 0,
pc2345 INTEGER DEFAULT 0,
safari INTEGER DEFAULT 0,
chrome INTEGER DEFAULT 0,
machine INTEGER DEFAULT 0,
mobile INTEGER DEFAULT 0,
other INTEGER DEFAULT 0
)
]]
-- 请求分类统计明细,按小时统计
-- time: 2021032500 时间刻度, 以小时为单位
status, errorString = db:exec[[
CREATE TABLE request_stat(
time INTEGER PRIMARY KEY,
req INTEGER DEFAULT 0,
pv INTEGER DEFAULT 0,
uv INTEGER DEFAULT 0,
ip INTEGER DEFAULT 0,
length INTEGER DEFAULT 0,
spider INTEGER DEFAULT 0,
status_500 INTEGER DEFAULT 0,
status_501 INTEGER DEFAULT 0,
status_502 INTEGER DEFAULT 0,
status_503 INTEGER DEFAULT 0,
status_504 INTEGER DEFAULT 0,
status_505 INTEGER DEFAULT 0,
status_506 INTEGER DEFAULT 0,
status_507 INTEGER DEFAULT 0,
status_509 INTEGER DEFAULT 0,
status_510 INTEGER DEFAULT 0,
status_400 INTEGER DEFAULT 0,
status_401 INTEGER DEFAULT 0,
status_402 INTEGER DEFAULT 0,
status_403 INTEGER DEFAULT 0,
status_404 INTEGER DEFAULT 0,
http_get INTEGER DEFAULT 0,
http_post INTEGER DEFAULT 0,
http_put INTEGER DEFAULT 0,
http_patch INTEGER DEFAULT 0,
http_delete INTEGER DEFAULT 0
)
]]
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_405 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_406 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_407 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_408 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_409 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_410 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_411 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_412 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_413 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_414 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_415 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_416 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_417 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_418 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_421 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_422 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_423 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_424 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_425 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_426 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_449 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_451 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN status_499 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE request_stat ADD COLUMN fake_spider INTEGER DEFAULT 0;")
-- 蜘蛛统计明细,按小时统计
-- 按照已有蜘蛛分类,分别建立对应的统计列。
status, errorString = db:exec[[
CREATE TABLE spider_stat(
time INTEGER PRIMARY KEY,
bytes INTEGER DEFAULT 0,
bing INTEGER DEFAULT 0,
soso INTEGER DEFAULT 0,
yahoo INTEGER DEFAULT 0,
sogou INTEGER DEFAULT 0,
google INTEGER DEFAULT 0,
baidu INTEGER DEFAULT 0,
qh360 INTEGER DEFAULT 0,
youdao INTEGER DEFAULT 0,
yandex INTEGER DEFAULT 0,
dnspod INTEGER DEFAULT 0,
yisou INTEGER DEFAULT 0,
mpcrawler INTEGER DEFAULT 0,
duckduckgo INTEGER DEFAULT 0,
bytes_flow INTEGER DEFAULT 0,
bing_flow INTEGER DEFAULT 0,
soso_flow INTEGER DEFAULT 0,
yahoo_flow INTEGER DEFAULT 0,
sogou_flow INTEGER DEFAULT 0,
google_flow INTEGER DEFAULT 0,
baidu_flow INTEGER DEFAULT 0,
qh360_flow INTEGER DEFAULT 0,
youdao_flow INTEGER DEFAULT 0,
yandex_flow INTEGER DEFAULT 0,
dnspod_flow INTEGER DEFAULT 0,
yisou_flow INTEGER DEFAULT 0,
mpcrawler_flow INTEGER DEFAULT 0,
duckduckgo_flow INTEGER DEFAULT 0,
other_flow INTEGER DEFAULT 0,
other INTEGER DEFAULT 0
)
]]
local sql_str1 = [[
CREATE TABLE uri_stat (
uri_md5 CHAR(32) PRIMARY KEY,
uri TEXT,
day1 INTEGER DEFAULT 0,
day2 INTEGER DEFAULT 0,
day3 INTEGER DEFAULT 0,
day4 INTEGER DEFAULT 0,
day5 INTEGER DEFAULT 0,
day6 INTEGER DEFAULT 0,
day7 INTEGER DEFAULT 0,
day8 INTEGER DEFAULT 0,
day9 INTEGER DEFAULT 0,
day10 INTEGER DEFAULT 0,
day11 INTEGER DEFAULT 0,
day12 INTEGER DEFAULT 0,
day13 INTEGER DEFAULT 0,
day14 INTEGER DEFAULT 0,
day15 INTEGER DEFAULT 0,
day16 INTEGER DEFAULT 0,
day17 INTEGER DEFAULT 0,
day18 INTEGER DEFAULT 0,
day19 INTEGER DEFAULT 0,
day20 INTEGER DEFAULT 0,
day21 INTEGER DEFAULT 0,
day22 INTEGER DEFAULT 0,
day23 INTEGER DEFAULT 0,
day24 INTEGER DEFAULT 0,
day25 INTEGER DEFAULT 0,
day26 INTEGER DEFAULT 0,
day27 INTEGER DEFAULT 0,
day28 INTEGER DEFAULT 0,
day29 INTEGER DEFAULT 0,
day30 INTEGER DEFAULT 0,
day31 INTEGER DEFAULT 0,
day32 INTEGER DEFAULT 0
)]]
status, errorString = db:exec(sql_str1)
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow1 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow2 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow3 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow4 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow5 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow6 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow7 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow8 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow9 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow10 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow11 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow12 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow13 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow14 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow15 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow16 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow17 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow18 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow19 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow20 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow21 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow22 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow23 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow24 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow25 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow26 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow27 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow28 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow29 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow30 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow31 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow1 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow2 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow3 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow4 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow5 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow6 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow7 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow8 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow9 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow10 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow11 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow12 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow13 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow14 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow15 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow16 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow17 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow18 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow19 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow20 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow21 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow22 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow23 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow24 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow25 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow26 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow27 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow28 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow29 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow30 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow31 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN day_hour1 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN flow_hour1 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE uri_stat ADD COLUMN spider_flow_hour1 INTEGER DEFAULT 0;")
local sql_str1 = [[
CREATE TABLE ip_stat (
ip CHAR(15) PRIMARY KEY,
area CHAR(8) DEFAULT "",
day1 INTEGER DEFAULT 0,
day2 INTEGER DEFAULT 0,
day3 INTEGER DEFAULT 0,
day4 INTEGER DEFAULT 0,
day5 INTEGER DEFAULT 0,
day6 INTEGER DEFAULT 0,
day7 INTEGER DEFAULT 0,
day8 INTEGER DEFAULT 0,
day9 INTEGER DEFAULT 0,
day10 INTEGER DEFAULT 0,
day11 INTEGER DEFAULT 0,
day12 INTEGER DEFAULT 0,
day13 INTEGER DEFAULT 0,
day14 INTEGER DEFAULT 0,
day15 INTEGER DEFAULT 0,
day16 INTEGER DEFAULT 0,
day17 INTEGER DEFAULT 0,
day18 INTEGER DEFAULT 0,
day19 INTEGER DEFAULT 0,
day20 INTEGER DEFAULT 0,
day21 INTEGER DEFAULT 0,
day22 INTEGER DEFAULT 0,
day23 INTEGER DEFAULT 0,
day24 INTEGER DEFAULT 0,
day25 INTEGER DEFAULT 0,
day26 INTEGER DEFAULT 0,
day27 INTEGER DEFAULT 0,
day28 INTEGER DEFAULT 0,
day29 INTEGER DEFAULT 0,
day30 INTEGER DEFAULT 0,
day31 INTEGER DEFAULT 0,
day32 INTEGER DEFAULT 0
)]]
status,errorString = db:exec(sql_str1)
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow1 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow2 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow3 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow4 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow5 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow6 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow7 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow8 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow9 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow10 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow11 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow12 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow13 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow14 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow15 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow16 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow17 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow18 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow19 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow20 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow21 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow22 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow23 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow24 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow25 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow26 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow27 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow28 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow29 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow30 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow31 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN day_hour1 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE ip_stat ADD COLUMN flow_hour1 INTEGER DEFAULT 0;")
local sql_str_referer = [[
CREATE TABLE referer2_stat (
referer_md5 CHAR(32) PRIMARY KEY,
referer TEXT,
day1 INTEGER DEFAULT 0,
day2 INTEGER DEFAULT 0,
day3 INTEGER DEFAULT 0,
day4 INTEGER DEFAULT 0,
day5 INTEGER DEFAULT 0,
day6 INTEGER DEFAULT 0,
day7 INTEGER DEFAULT 0,
day8 INTEGER DEFAULT 0,
day9 INTEGER DEFAULT 0,
day10 INTEGER DEFAULT 0,
day11 INTEGER DEFAULT 0,
day12 INTEGER DEFAULT 0,
day13 INTEGER DEFAULT 0,
day14 INTEGER DEFAULT 0,
day15 INTEGER DEFAULT 0,
day16 INTEGER DEFAULT 0,
day17 INTEGER DEFAULT 0,
day18 INTEGER DEFAULT 0,
day19 INTEGER DEFAULT 0,
day20 INTEGER DEFAULT 0,
day21 INTEGER DEFAULT 0,
day22 INTEGER DEFAULT 0,
day23 INTEGER DEFAULT 0,
day24 INTEGER DEFAULT 0,
day25 INTEGER DEFAULT 0,
day26 INTEGER DEFAULT 0,
day27 INTEGER DEFAULT 0,
day28 INTEGER DEFAULT 0,
day29 INTEGER DEFAULT 0,
day30 INTEGER DEFAULT 0,
day31 INTEGER DEFAULT 0,
day32 INTEGER DEFAULT 0
)]]
status,errorString = db:exec(sql_str_referer)
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow1 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow2 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow3 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow4 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow5 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow6 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow7 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow8 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow9 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow10 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow11 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow12 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow13 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow14 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow15 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow16 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow17 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow18 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow19 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow20 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow21 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow22 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow23 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow24 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow25 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow26 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow27 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow28 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow29 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow30 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow31 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN day_hour1 INTEGER DEFAULT 0;")
status, errorString = db:exec("ALTER TABLE referer2_stat ADD COLUMN flow_hour1 INTEGER DEFAULT 0;")
cache:set(db_path, true)
-- debug("初始化日志数据库完成。")
end
if db and db:isopen() then
db:close()
end
return true
end
local function cache_value(id, key, value)
local line_id_key = "logline_"..server_name.."_"..id.."_"..key
cache:set(line_id_key, value)
end
local function clear_value(store_server_name, id, key)
local line_id_key = "logline_"..store_server_name.."_"..id.."_"..key
cache:delete(line_id_key)
end
local function get_value(store_server_name, id, key)
local line_id_key = "logline_"..store_server_name.."_"..id.."_"..key
local value = cache:get(line_id_key)
-- cache:delete(line_id_key)
return value
end
local function load_update_hour(update_server_name)
local _file = cpath.."logs/"..update_server_name.."/update_hour.log"
local cache_val = cache:get(_file)
if cache_val then
return cache_val
end
local val = read_file_body_bylog(_file)
if not val then
val = hour_str
end
cache:set(_file, val, ONE_DAY_TIMEOUT)
return val
end
local function load_update_day(update_server_name)
local _file = cpath.."logs/"..update_server_name.."/update_day.log"
local cache_val = cache:get(_file)
if cache_val then
-- debug("load update day from cache: "..cache_val)
return cache_val
end
local val = read_file_body_bylog(_file)
if not val then
-- 处理未读取到记录的情况
val = today
end
cache:set(_file, val, ONE_DAY_TIMEOUT)
return val
end
local function write_update_hour(update_server_name)
local update_hour = tostring(hour_str)
local _file = cpath.."logs/"..update_server_name.."/update_hour.log"
write_file_bylog(_file, update_hour, "w")
cache:set(_file, update_hour, ONE_DAY_TIMEOUT)
end
local function write_update_day(update_server_name)
local update_day = tostring(today)
local _file = cpath.."logs/"..update_server_name.."/update_day.log"
write_file_bylog(_file, update_day, "w")
cache:set(_file, update_day, ONE_DAY_TIMEOUT)
end
local function statistics_referer(db, referer, referer_md5, body_length)
local stat_sql = nil
stat_sql = "INSERT INTO referer2_stat(referer, referer_md5) SELECT \""..referer.."\",\""..referer_md5.."\" WHERE NOT EXISTS (SELECT referer_md5 FROM referer2_stat WHERE referer_md5=\""..referer_md5.."\");"
local res, err = db:exec(stat_sql)
local hour_update_sql = ","..day_hour1_column.."="..day_hour1_column.."+1,"..flow_hour1_column.."="..flow_hour1_column.."+"..body_length
stat_sql = "UPDATE referer2_stat SET "..day_column.."="..day_column.."+1,"..flow_column.."="..flow_column.."+"..body_length..hour_update_sql.." WHERE referer_md5=\""..referer_md5.."\""
res, err = db:exec(stat_sql)
return true
end
local function statistics_uri(db, stat_server_name, uri, uri_md5, body_length, is_spider)
-- 统计uri请求次数和流量
local open_statistics_uri = site_config["statistics_uri"]
if not open_statistics_uri then return true end
local stat_sql = nil
stat_sql = "INSERT INTO uri_stat(uri_md5,uri) SELECT \""..uri_md5.."\",\""..uri.."\" WHERE NOT EXISTS (SELECT uri_md5 FROM uri_stat WHERE uri_md5=\""..uri_md5.."\");"
local res, err = db:exec(stat_sql)
if not is_spider then
is_spider = 0
end
if is_spider>0 and is_spider ~= fake_spider then
local hour_update_sql = ","..day_hour1_column.."="..day_hour1_column.."+1,"..spider_hour1_column.."="..spider_hour1_column.."+"..body_length
stat_sql = "UPDATE uri_stat SET "..day_column.."="..day_column.."+1,"..spider_column.."="..spider_column.."+"..body_length..hour_update_sql.." WHERE uri_md5=\""..uri_md5.."\""
else
local hour_update_sql = ","..day_hour1_column.."="..day_hour1_column.."+1,"..flow_hour1_column.."="..flow_hour1_column.."+"..body_length
stat_sql = "UPDATE uri_stat SET "..day_column.."="..day_column.."+1,"..flow_column.."="..flow_column.."+"..body_length..hour_update_sql.." WHERE uri_md5=\""..uri_md5.."\""
end
local res, err = db:exec(stat_sql)
-- debug("stat uri:"..tostring(uri))
-- debug("stat uri res:"..tostring(res))
return true
end
local function statistics_ip(db, stat_server_name, ip, body_length)
local open_statistics_ip = site_config["statistics_ip"]
if not open_statistics_ip then return true end
local stat_sql = nil
stat_sql = "INSERT INTO ip_stat(ip) SELECT \""..ip.."\" WHERE NOT EXISTS (SELECT ip FROM ip_stat WHERE ip=\""..ip.."\");"
local res, err = db:exec(stat_sql)
local hour_update_sql = ","..day_hour1_column.."="..day_hour1_column.."+1,"..flow_hour1_column.."="..flow_hour1_column.."+"..body_length
stat_sql = "UPDATE ip_stat SET "..day_column.."="..day_column.."+1,"..flow_column.."="..flow_column.."+"..body_length..hour_update_sql.." WHERE ip=\""..ip.."\""
local res, err = db:exec(stat_sql)
return true
end
-- 存储单行日志
local function store_line(db, stmt, store_server_name, lineno)
local logvalue = get_value(store_server_name, lineno, "logline")
if not logvalue then return false end
local logline = json.decode(logvalue)
local time = logline["time"]
local time_key = logline["time_key"]
local ip = logline["ip"]
local ip_list = logline["ip_list"]
local domain = logline["domain"]
local server_name = logline["server_name"]
local real_server_name = logline["real_server_name"]
local method = logline["method"]
local status_code = logline["status_code"]
local uri = logline["uri"]
local request_uri = logline["request_uri"]
if not request_uri then
request_uri = uri
end
local body_length = logline["body_length"]
local referer = logline["referer"]
local user_agent = logline["user_agent"]
local protocol = logline["protocol"]
local request_time = logline["request_time"]
local is_spider = logline["is_spider"]
local request_headers = logline["request_headers"]
local excluded = logline["excluded"]
local id = logline["id"]
local client_port = logline["client_port"]
if not client_port then
client_port = -1
end
local request_stat_fields = nil
local client_stat_fields = nil
local spider_stat_fields = nil
local stat_fields = get_value(store_server_name, id, "STAT_FIELDS")
if stat_fields == nil then
debug("Log stat fields is nil.")
debug("Logdata:"..logvalue)
else
stat_fields = split_bylog(stat_fields, ";")
request_stat_fields = stat_fields[1]
client_stat_fields = stat_fields[2]
spider_stat_fields = stat_fields[3]
if "x" == client_stat_fields then
client_stat_fields = nil
end
if "x" == spider_stat_fields then
spider_stat_fields = nil
end
end
-- debug("Request stat fields:"..tostring(request_stat_fields))
-- debug("Spider stat fields:"..tostring(spider_stat_fields))
-- debug("Client stat fields:"..tostring(client_stat_fields))
if not excluded then
stmt:bind_names{
time=time,
ip=ip,
domain=domain,
server_name=real_server_name,
method=method,
status_code=status_code,
uri=request_uri,
body_length=body_length,
referer=referer,
user_agent=user_agent,
protocol=protocol,
request_time=request_time,
is_spider=is_spider,
request_headers=request_headers,
ip_list=ip_list,
client_port=client_port
}
local res, err = stmt:step()
if tostring(res) == "5" then
return false
end
stmt:reset()
update_stat(store_server_name, db, "client_stat", time_key, client_stat_fields)
update_stat(store_server_name, db, "spider_stat", time_key, spider_stat_fields)
if referer and string.len(referer) > 0 then
local in_site_check_res = httpd:regex(referer,"^(http[s]*://)*"..store_server_name,0x01)
if not in_site_check_res then
local new_referer
local s, e = string.find(referer, "?")
if e ~= nil then
if e>0 then
e = e - 1
else
e = 0
end
new_referer = string.sub(referer, 0, e)
end
if not new_referer then
new_referer = referer
end
-- debug("statistics referer: "..referer.."/simple referer:"..new_referer)
statistics_referer(db, new_referer, httpd:md5(new_referer), body_length)
end
end
local total_uri
if not request_uri then
total_uri = uri
else
total_uri = request_uri
end
if total_uri then
local new_uri
local s, e = string.find(total_uri, "?")
if e ~= nil then
if e>0 then
e = e - 1
else
e = 0
end
new_uri = string.sub(total_uri, 0, e)
end
if not new_uri then
new_uri = total_uri
end
statistics_uri(db, store_server_name, new_uri, httpd:md5(new_uri), body_length, is_spider)
end
if ip then
statistics_ip(db, store_server_name, ip, body_length)
end
end
update_stat(store_server_name, db, "request_stat", time_key, request_stat_fields)
return true
end
local function store_logs(store_server_name)
if is_migrating(store_server_name) == true then
-- debug("Migrating...")
return
end
local flush_data = false
local waiting_store_key = store_server_name .. "_WAITING_STORE"
local flush_data_key = store_server_name .. "_FLUSH_DATA"
local cache_count_id_key = store_server_name.."_CACHE_COUNT"
local cache_count = cache:get(cache_count_id_key)
if not cache_count then cache_count = 0 end
-- debug("if:"..tostring(cache:get(waiting_store_key) and cache_count < cache_count_threshold))
if not cache:get(waiting_store_key) or cache_count>cache_count_threshold then
local x = "go"
else
if cache:get(flush_data_key) then
flush_data = true
else
return
end
end
if is_storing(store_server_name) == true then
-- debug("其他worker正在存储中,稍候存储。")
cache:delete(flush_data_key)
return
end
today = os.date("%Y%m%d")
day = os.date("%d")
number_day = tonumber(day)
day_column = "day"..number_day
flow_column = "flow"..number_day
spider_column = "spider_flow"..number_day
hour_str = os.date("%Y%m%d%H")
-- debug("Cache count:"..cache_count)
-- debug("flush data:"..tostring(flush_data))
local storing_key = store_server_name.."_STORING"
cache:set(storing_key,true, 60)
if flush_data == false then
cache:set(waiting_store_key, os.time(), cache_interval_threshold)
end
cache:delete(flush_data_key)
-- 开始存储数据
local last_insert_id_key = store_server_name.."_LAST_INSERT_ID"
local store_start_id_key = store_server_name.."_STORE_START"
-- 1.计算存储数据区间段
local last_id = tonumber(cache:get(last_insert_id_key))
local store_start = tonumber(cache:get(store_start_id_key))
-- debug("store start:"..tostring(store_start))
-- debug("flush data:"..tostring(flush_data))
if store_start == nil then
store_start = 1
end
local store_end = last_id
if not store_end then
store_end = 1
end
site_config = get_config(store_server_name)
-- debug("start store.")
local store_count = 0
-- local store_start_time = socket.gettime() * 1000
-- debug("start:"..tostring(store_start).."/end:"..tostring(store_end))
local stmt2 = nil
local log_dir = cpath .. 'logs'
data_dir = site_config["data_dir"]
if data_dir then
log_dir = data_dir
end
local base_path = log_dir .. '/' .. store_server_name .. "/"
local total_db_path = base_path .. total_db_name
local logs_db_name = today .. ".db"
local logs_db_path = base_path .. logs_db_name
local logs_init_res = true
local total_init_res = true
-- debug("logs db path:"..tostring(logs_db_path))
-- debug("total db path:"..tostring(total_db_path))
if not cache:get(logs_db_path) then
-- debug("to init by shared dict control.")
logs_init_res = init_logs_db(log_dir, store_server_name, logs_db_name)
end
if not file_exists(logs_db_path) then
-- debug("to init by file check.")
cache:set(logs_db_path, nil)
logs_init_res = init_logs_db(log_dir, store_server_name, logs_db_name)
end
if not cache:get(total_db_path) then
total_init_res = init_total_db(log_dir, store_server_name, total_db_name)
end
if not file_exists(total_db_path) then
cache:set(total_db_path, nil)
total_init_res = init_total_db(log_dir, store_server_name, total_db_name)
end
if not logs_init_res or not total_init_res then
-- debug("logs:"..tostring(logs_init_res))
-- debug("total:"..tostring(total_init_res))
-- debug("初始化数据库异常。")
cache:set(storing_key, false)
return false
end
local logs_db, err1 = sqlite3.open(logs_db_path)
local total_db, err2 = sqlite3.open(total_db_path)
-- debug("open err1:"..tostring(err1))
-- debug("open err2:"..tostring(err2))
if logs_db ~= nil then
stmt2 = logs_db:prepare[[INSERT INTO site_logs(
time, ip, domain, server_name, method, status_code, uri, body_length,
referer, user_agent, protocol, request_time, is_spider, request_headers, ip_list)
VALUES(:time, :ip, :domain, :server_name, :method, :status_code, :uri,
:body_length, :referer, :user_agent, :protocol, :request_time, :is_spider,
:request_headers, :ip_list)]]
end
if logs_db == nil or stmt2 == nil or total_db == nil then
-- debug("网站监控报表数据库连接异常。")
cache:set(storing_key, false)
if total_db and total_db:isopen() then
total_db:close()
end
if logs_db and logs_db:isopen() then
logs_db:close()
end
return true
end
-- local start_transaction_time = socket.gettime()*1000
status, errorString = logs_db:exec([[BEGIN TRANSACTION]])
status, errorString = total_db:exec([[BEGIN TRANSACTION]])
-- debug("begin transaction.")
local update_hour = load_update_hour(store_server_name)
if not update_hour or tostring(update_hour) ~= tostring(hour_str) then
debug("reset hour data.")
-- reset
local update_sql = "UPDATE uri_stat SET "..day_hour1_column.."=0,"..spider_hour1_column.."=0,"..flow_hour1_column.."=0"
status, errorString = total_db:exec(update_sql)
update_sql = "UPDATE ip_stat SET "..day_hour1_column.."=0,"..flow_hour1_column.."=0"
status, errorString = total_db:exec(update_sql)
update_sql = "UPDATE referer2_stat SET "..day_hour1_column.."=0,"..flow_hour1_column.."=0"
status, errorString = total_db:exec(update_sql)
write_update_hour(store_server_name)
end
update_day = load_update_day(store_server_name)
-- debug("update day:"..type(update_day).."/today:"..type(today)..tostring(tostring(update_day)==tostring(today)))
if not update_day or tostring(update_day) ~= tostring(today) then
-- reset
local update_sql = "UPDATE uri_stat SET "..day_column.."=0,"..spider_column.."=0,"..flow_column.."=0"
status, errorString = total_db:exec(update_sql)
update_sql = "UPDATE ip_stat SET "..day_column.."=0,"..flow_column.."=0"
status, errorString = total_db:exec(update_sql)
update_sql = "UPDATE referer2_stat SET "..day_column.."=0,"..flow_column.."=0"
status, errorString = total_db:exec(update_sql)
-- debug("update day status:"..tostring(status))
-- debug("update day error:"..tostring(errorString))
write_update_day(store_server_name)
end
if store_end >= store_start then
for i=store_start, store_end, 1 do
if store_line(total_db, stmt2, store_server_name, i) then
store_count = store_count + 1
clear_value(store_server_name, i, "logline")
clear_value(store_server_name, i, "STAT_FIELDS")
end
end
else
local _tmp_store_end = store_end
store_end = max_log_id
for i=store_start, store_end, 1 do
if store_line(total_db, stmt2, store_server_name, i) then
store_count = store_count + 1
clear_value(store_server_name, i, "logline")
clear_value(store_server_name, i, "STAT_FIELDS")
end
end
store_start = 1
store_end = _tmp_store_end
for i=store_start, store_end, 1 do
if store_line(total_db, stmt2, store_server_name, i) then
store_count = store_count + 1
clear_value(store_server_name, i, "logline")
clear_value(store_server_name, i, "STAT_FIELDS")
end
end
end
local res, err = stmt2:finalize()
if tostring(res) ~= "5" then
local res, err = total_db:execute([[COMMIT]])
local res, err = logs_db:execute([[COMMIT]])
-- debug("commited.")
cache:set(store_start_id_key, store_end)
cache:decr(cache_count_id_key, store_count)
local cache_count = cache:get(cache_count_id_key)
if not cache_count then cache_count = 0 end
if cache_count > cache_count_threshold or cache_count < 0 then
cache:delete(cache_count_id_key)
end
end
if total_db and total_db:isopen() then
total_db:close()
end
if logs_db and logs_db:isopen() then
logs_db:close()
end
-- debug("Stored.")
cache:set(waiting_store_key, os.time(), cache_interval_threshold)
cache:set(storing_key, false)
end
local function cache_logs()
local excluded = false
ip = get_client_ip_bylog()
local request_uri = httpd.unparsed_uri
local uri = httpd.uri
local status_code = httpd.status
excluded = exclude_status(status_code) or exclude_extension(uri) or exclude_url(request_uri) or exclude_ip(ip)
domain = get_domain()
method = httpd.method
if method == "" or not method then return apache2.OK end
if not httpd.notes['RT'] then
tms = 1
else
tms = split_bylog(tostring((httpd:clock() - httpd.notes['RT']) / 1000),'.')[1]
end
client_port = -1
local ip_list = httpd.headers_in['x-forwarded-for']
if ip and not ip_list then
ip_list = ip
end
local remote_addr = httpd.useragent_ip
if not string.find(ip_list, remote_addr) then
if remote_addr then
ip_list = ip_list .. "," .. remote_addr
end
end
-- ipn = arrip_bylog(ip)
local last_insert_id_key = server_name .. "_LAST_INSERT_ID"
local generating_id_key = server_name .. "GENERATING"
local cache_count_id_key = server_name .. "_CACHE_COUNT"
if not cache:get(server_name.."_CLEAR_CACHE_COUNT") then
cache:delete(server_name.."_CACHE_COUNT")
cache:set(server_name.."_CLEAR_CACHE_COUNT", true)
-- debug("Cleared cache count.")
end
if not cache:get(cache_count_id_key) then
cache:set(cache_count_id_key, 1)
else
cache:incr(cache_count_id_key, 1)
end
local new_id = nil
if not cache:get(last_insert_id_key) then
cache:set(last_insert_id_key, 1)
new_id = 1
else
new_id = tonumber(cache:incr(last_insert_id_key, 1))
if new_id >= max_log_id then
cache:set(last_insert_id_key, 1)
new_id = 1
end
end
-- debug("Store count2:"..tostring(cache:get(cache_count_id_key)))
-- debug("New id:"..tostring(new_id))
local request_time = tms
local referer = httpd.headers_in['referer']
local protocol = httpd.protocol
local is_spider = 0
local time_key = get_store_key()
local user_agent = httpd.headers_in['user-agent']
local real_server_name = server_name
if server_name == no_binding_server_name then
real_server_name = httpd.server_name
end
local logline = {
id=new_id,
time_key=time_key,
time=os.time(),
ip=ip,
domain=domain,
server_name=server_name,
real_server_name=real_server_name,
method=method,
status_code=status_code,
uri=uri,
request_uri=request_uri,
body_length=body_length,
referer=referer,
user_agent=request_header['user-agent'],
protocol=protocol,
is_spider=0,
request_time=request_time,
excluded=excluded,
request_headers="",
ip_list=ip_list
}
local request_stat_fields = "req=req+1,length=length+"..body_length
local spider_stat_fields = "x"
local client_stat_fields = "x"
local is_spider = false
local request_spider = nil
local spider_index = 0
-- debug("Excluded:"..tostring(excluded).."/uri:"..httpd.uri)
if not excluded then
-- if status_code == 500 or (method=="POST" and config["record_post_args"]==true) then
-- local data = ""
-- local ok, err = pcall(function() data=get_http_original() end)
-- if ok and not err then
-- logline["request_headers"] = data
-- end
-- debug("Get http orgininal ok:"..tostring(ok))
-- debug("Get http orgininal res:"..tostring(data))
-- end
if string.find("500,501,502,503,504,505,506,507,509,510,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,421,422,423,424,425,426,449,451,499", status_code) then
local field = "status_"..status_code
request_stat_fields = request_stat_fields .. ","..field.."="..field.."+1"
end
local lower_method = string.lower(method)
if string.find("get,post,put,patch,delete", lower_method) then
local field = "http_"..lower_method
request_stat_fields = request_stat_fields .. ","..field.."="..field.."+1"
end
local ipc = 0
local pvc = 0
local uvc = 0
is_spider, request_spider, spider_index = match_spider(ip)
if not is_spider then
client_stat_fields = match_client()
-- debug("Client stat fields:"..tostring(client_stat_fields))
if #client_stat_fields == 0 then
client_stat_fields = request_stat_fields..",other=other+1"
else
if string.find(client_stat_fields, "machine") then
if statistics_machine_access then
pvc, uvc = statistics_request()
end
else
pvc, uvc = statistics_request()
end
end
ipc = statistics_ipc()
else
logline["is_spider"] = spider_index
local field = "spider"
if spider_index ~= fake_spider then
spider_stat_fields = request_spider.."="..request_spider.."+"..1
else
field = "fake_spider"
end
request_stat_fields = request_stat_fields .. ","..field.."="..field.."+"..1
end
if ipc > 0 then
request_stat_fields = request_stat_fields..",ip=ip+1"
end
if uvc > 0 then
request_stat_fields = request_stat_fields..",uv=uv+1"
end
if pvc > 0 then
request_stat_fields = request_stat_fields..",pv=pv+1"
end
-- debug("Is spider:"..tostring(is_spider))
end
cache_value(new_id, "STAT_FIELDS", request_stat_fields..";"..client_stat_fields..";"..spider_stat_fields)
cache_value(new_id, "logline", json.encode(logline))
-- debug("Cached.")
end
function set_request_time(r)
r.notes['RT'] = r:clock()
return apache2.DECLINED
end
function run_logs(request)
httpd = request
if httpd.uri == '/favicon.ico' or httpd.status == 0 or httpd.status == 416 then return apache2.OK end
local presult, err = pcall(
function()
json = require "cjson"
memcached = require "memcached"
sqlite3 = require "lsqlite3"
total_config = require "total_config"
end
)
if not presult then
-- debug("引入依赖出现错误:"..tostring(err))
return apache2.OK
end
-- debug("connect to memcached.")
cache = memcached.Connect("127.0.0.1", 11211)
if not cache then
cache = memcached.Connect("localhost", 11211)
end
if not cache then return apache2.OK end
no_binding_server_name = "btunknownbt"
local c_name = httpd.server_name
domains = require "domains"
server_name = string.gsub(get_server_name(c_name),'_','.')
if server_name == 'phpinfo' then return apache2.OK end
-- debug("Server name:"..server_name)
-- debug("uri:"..httpd.uri)
config = get_config(server_name)
if config == nil then return true end
monitor = config["monitor"]
if monitor == false then
-- debug("------------------------站点:"..server_name.."关闭监控----------------------")
return apache2.OK
end
max_log_id = 99999999999999
request_header = httpd.headers_in
out_header = httpd.headers_out
-- /bt_total_flush_data 请求由插件前端发起,用于刷新缓存数据实时显示数据。
-- 刷新数据请求不被记录到缓存和统计数据
local client_ip = httpd.useragent_ip
if httpd.uri == "/bt_total_flush_data" and client_ip == "127.0.0.1" then
-- debug("To flush data.")
local args, multi_args = httpd:parseargs()
local site = args["server_name"]
if #site > 128 then
return apache2.OK
end
site = get_server_name(site)
-- print("check server name:"..site)
if site == no_binding_server_name or site == "httpd-vhosts" then
-- debug("未知站点名称.")
return false
end
if site then
-- debug("To flush site:"..site)
cache:set(site.."_FLUSH_DATA", true, 1)
store_logs(site)
end
return apache2.OK
end
if server_name == "httpd-vhosts" or server_name == "127.0.0.1" then
return apache2.OK
end
-- debug("Monitor status:"..tostring(monitor))
-- error_log()
body_length = get_length()
now_timestamp = os.time(os.date("*t"), os.time())
total_flow()
total_req_sec()
statistics_machine_access = config["statistics_machine_access"]
-- debug("Statistics machine access:"..tostring(statistics_machine_access))
if server_name ~= no_binding_server_name then
load_global_exclude_ip()
load_exclude_ip(server_name)
end
-- debug("to cache.")
cache_logs()
-- debug('to store.')
store_logs(server_name)
return apache2.OK
end