pull/139/head
midoks 3 years ago
parent 81bf1be508
commit 07f7024e25
  1. 56
      plugins/webstats/class/LuaMaker.py
  2. 6
      plugins/webstats/conf/init.sql
  3. 3
      plugins/webstats/index.html
  4. 63
      plugins/webstats/index.py
  5. 18
      plugins/webstats/install.sh
  6. 290
      plugins/webstats/lua/web_stats_log.lua

@ -0,0 +1,56 @@
import sys
import os
class LuaMaker:
"""
lua 处理器
"""
@staticmethod
def makeLuaTable(table):
"""
table 转换为 lua table 字符串
"""
_tableMask = {}
_keyMask = {}
def analysisTable(_table, _indent, _parent):
if isinstance(_table, tuple):
_table = list(_table)
if isinstance(_table, list):
_table = dict(zip(range(1, len(_table) + 1), _table))
if isinstance(_table, dict):
_tableMask[id(_table)] = _parent
cell = []
thisIndent = _indent + " "
for k in _table:
if sys.version_info[0] == 2:
if type(k) not in [int, float, bool, list, dict, tuple]:
k = k.encode()
if not (isinstance(k, str) or isinstance(k, int) or isinstance(k, float)):
return
key = isinstance(
k, int) and "[" + str(k) + "]" or "[\"" + str(k) + "\"]"
if _parent + key in _keyMask.keys():
return
_keyMask[_parent + key] = True
var = None
v = _table[k]
if sys.version_info[0] == 2:
if type(v) not in [int, float, bool, list, dict, tuple]:
v = v.encode()
if isinstance(v, str):
var = "\"" + v + "\""
elif isinstance(v, bool):
var = v and "true" or "false"
elif isinstance(v, int) or isinstance(v, float):
var = str(v)
else:
var = analysisTable(v, thisIndent, _parent + key)
cell.append(thisIndent + key + " = " + str(var))
lineJoin = ",\n"
return "{\n" + lineJoin.join(cell) + "\n" + _indent + "}"
else:
pass
return analysisTable(table, "", "root")

@ -1,3 +1,9 @@
PRAGMA synchronous = 0;
PRAGMA page_size = 4096;
PRAGMA journal_mode = wal;
PRAGMA journal_size_limit = 1073741824;
CREATE TABLE IF NOT EXISTS `web_logs` (
`time` INTEGER,
`ip` TEXT,

@ -2,7 +2,8 @@
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('webstats');">服务</p>
<p onclick="pluginConfig('webstats');">概览</p>
<p onclick="wsGlobalSetting();">概览</p>
<p onclick="wsGlobalSetting();">网站日志</p>
<p onclick="wsGlobalSetting();">全局设置</p>
</div>
<div class="bt-w-con pd15">

@ -4,10 +4,12 @@ import sys
import io
import os
import time
import json
sys.path.append(os.getcwd() + "/class/core")
import mw
app_debug = False
if mw.isAppleSystem():
app_debug = True
@ -21,6 +23,10 @@ def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
sys.path.append(getPluginDir() + "/class")
from LuaMaker import LuaMaker
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
@ -60,11 +66,17 @@ def status():
return 'start'
def pSqliteDb(dbname='web_logs'):
file = getServerDir() + '/webstats.db'
name = 'webstats'
def pSqliteDb(dbname='web_logs', site_name='unset'):
db_dir = getServerDir() + '/logs/' + site_name
if not os.path.exists(db_dir):
mw.execShell('mkdir -p ' + db_dir)
name = 'logs'
file = db_dir + '/' + name + '.db'
if not os.path.exists(file):
conn = mw.M(dbname).dbPos(getServerDir(), name)
conn = mw.M(dbname).dbPos(db_dir, name)
sql = mw.readFile(getPluginDir() + '/conf/init.sql')
sql_list = sql.split(';')
for index in range(len(sql_list)):
@ -74,6 +86,30 @@ def pSqliteDb(dbname='web_logs'):
return conn
def makeSiteConfig():
siteM = mw.M('sites')
domainM = mw.M('domain')
slist = siteM.field('id,name').where(
'status=?', (1,)).order('id desc').select()
data = []
for s in slist:
tmp = {}
tmp['name'] = s['name']
dlist = domainM.field('id,name').where(
'pid=?', (s['id'],)).order('id desc').select()
_t = []
for d in dlist:
_t.append(d['name'])
tmp['domains'] = _t
data.append(tmp)
return data
def initDreplace():
service_path = getServerDir()
@ -94,10 +130,29 @@ def initDreplace():
mw.execShell('mkdir -p ' + lua_dir)
lua_tpl = getPluginDir() + '/lua/web_stats_log.lua'
content = mw.readFile(lua_tpl)
# content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$SERVER_APP}', service_path)
content = content.replace('{$ROOT_PATH}', mw.getServerDir())
mw.writeFile(lua_dst, content)
content = makeSiteConfig()
for index in range(len(content)):
pSqliteDb('web_log', content[index]['name'])
lua_site_json = lua_dir + "/sites.json"
if not os.path.exists(lua_site_json):
mw.writeFile(lua_site_json, json.dumps(content))
lua_site = lua_dir + "/sites.lua"
if not os.path.exists(lua_site):
config_sites = LuaMaker.makeLuaTable(content)
sites_str = "return " + config_sites
mw.writeFile(lua_site, sites_str)
log_path = getServerDir() + "/logs"
if not os.path.exists(log_path):
mw.execShell('mkdir -p ' + log_path)
debug_log = getServerDir() + "/debug.log"
if not os.path.exists(debug_log):
mw.execShell('mkdir -p ' + lua_dir)

@ -74,23 +74,11 @@ Install_App()
fi
# copy to code path
if [ -f $serverPath/webstats/luarocks/lib/lua/5.1/lsqlite3.so ];then
cp -rf $serverPath/webstats/luarocks/lib/lua/5.1/lsqlite3.so $serverPath/webstats/lua/lsqlite3.so
DEFAULT_DIR=$serverPath/webstats/luarocks/lib/lua/5.1
if [ -f ${DEFAULT_DIR}/lsqlite3.so ];then
cp -rf ${DEFAULT_DIR}/lsqlite3.so $serverPath/webstats/lua/lsqlite3.so
fi
# if [ ! -d $serverPath/source/webstats/luasql-2.6.0 ];then
# wget -O $serverPath/source/webstats/luasql_2.6.0.tar.gz https://github.com/keplerproject/luasql/archive/refs/tags/2.6.0.tar.gz
# cd $serverPath/source/webstats && tar xvf luasql_2.6.0.tar.gz
# fi
# PATH=${serverPath}/openresty/luajit:${serverPath}/openresty/luajit/include/luajit-2.1:$PATH
# export PATH
# export LUA_INCDIR=${serverPath}/openresty/luajit/include/luajit-2.1
# cd $serverPath/source/webstats/luasql-2.6.0 && make sqlite3
# $serverPath/webstats/luarocks/bin/luarocks install luasql-sqlite3 SQLITE_DIR=$serverPath/webstats/lua
echo "${VERSION}" > $serverPath/webstats/version.pl
echo '安装完成' > $install_tmp
# cd $rootPath && python3 ${rootPath}/plugins/webstats/index.py start

@ -1,8 +1,11 @@
log_by_lua_block {
local ver = '0.0.1'
local max_log_id = 99999999999999
local debug_mode = true
local unset_server_name = "unset"
local cpath = "{$SERVER_APP}/lua/"
if not package.cpath:find(cpath) then
@ -31,6 +34,7 @@ log_by_lua_block {
end
-- cache start ---
local cache = ngx.shared.mw_total
local function cache_set(server_name, id ,key, val)
@ -52,30 +56,296 @@ log_by_lua_block {
-- domain config is import
local server_name
local ip,today,day,body_length,method,config,cache_count
local db = nil
local json = require "cjson"
local sqlite3 = require "lsqlite3"
local request_header = ngx.req.get_headers()
local today,day,method,config,cache_count
--- default common var end ---
local function get_store_key()
return os.date("%Y%m%d%H", os.time())
end
local function get_length()
local clen = ngx.var.body_bytes_sent
if clen == nil then clen = 0 end
return tonumber(clen)
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 get_server_name(c_name)
local my_name = cache:get(c_name)
if my_name then return my_name end
D("get_server_name start")
local determined_name = nil
local sites = require "sites"
D("get_server_name"..json.encode(sites))
for _,v in ipairs(sites)
do
if c_name == v["name"] then
cache:set(c_name, v['name'],3600)
return v["name"]
end
for _,d_name in ipairs(v['domains'])
do
if c_name == d_name then
cache:set(c_name,v['name'],3600)
return v['name']
elseif string.find(d_name, "*") then
new_domain = string.gsub(d_name, '*', '.*')
debug("ngx server name:"..ngx.var.server_name.."/new_domain:"..new_domain)
if string.find(c_name, new_domain) then
-- cache:set(c_name, v['name'],3600)
-- return v['name']
-- debug("find deter name:"..v['name'])
determined_name = v['name']
end
end
end
end
D("get_server_name end")
if determined_name then
cache:set(c_name, determined_name,3600)
return determined_name
end
cache:set(c_name, unset_server_name, 3600)
return unset_server_name
end
local function cache_logs()
-- make new id
local new_id = nil
local last_insert_id_key = server_name .. "_last_id"
local err = nil
new_id, err = cache:incr(last_insert_id_key, 1, 0)
cache:incr(cache_count_id_key, 1, 0)
if new_id >= max_log_id then
cache:set(last_insert_id_key, 1)
new_id = cache:get(last_insert_id_key)
end
D("new_id:"..new_id)
local ip_list = request_header["x-forwarded-for"]
local ip = ngx.var.remote_addr
if ip and not ip_list then
ip_list = ip
end
local client_port = ngx.var.remote_port
local request_time = ngx.var.request_time
local real_server_name = server_name
local uri = ngx.var.uri
local status_code = ngx.status
local protocol = ngx.var.server_protocol
local request_uri = ngx.var.request_uri
local time_key = get_store_key()
local method = ngx.req.get_method()
local body_length = get_length()
local domain = get_domain()
local referer = ngx.var.http_referer
kv = {
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,
client_port=client_port
}
D(server_name..'__'..json.encode(kv))
cache_set(server_name, new_id, "log_kv", json.encode(kv))
end
local function store_logs_line(db, stmt, input_server_name, lineno)
local logvalue = cache_get(input_server_name, lineno, "log_kv")
D("store_logs_line:"..logvalue)
if not logvalue then return false end
local logline = json.decode(logvalue)
local time = logline["time"]
local id = logline["id"]
local protocol = logline["protocol"]
local client_port = logline["client_port"]
local status_code = logline["status_code"]
local uri = logline["uri"]
local method = logline["method"]
local body_length = logline["body_length"]
local referer = logline["referer"]
local ip = logline["ip"]
local ip_list = logline["ip_list"]
local request_time = logline["request_time"]
local is_spider = logline["is_spider"]
local domain = logline["domain"]
local server_name = logline["server_name"]
stmt:bind_names{
time=time,
ip=ip,
domain=domain,
server_name=server_name,
method=method,
status_code=status_code,
uri=uri,
body_length=body_length,
referer=referer,
user_agent="d",
protocol=protocol,
request_time=request_time,
is_spider=is_spider,
request_headers=".",
ip_list=ip_list,
client_port=client_port,
}
local res, err = stmt:step()
if tostring(res) == "5" then
D("Step res:"..tostring(res))
D("Step err:"..tostring(err))
D("Step数据库连接繁忙,稍候存储。")
return false
end
stmt:reset()
D("store_logs_line ok")
end
local function store_logs(input_server_name)
local last_insert_id_key = input_server_name.."_last_id"
local store_start_id_key = input_server_name.."_store_start"
local last_id = cache:get(last_insert_id_key)
local store_start = cache:get(store_start_id_key)
if store_start == nil then
store_start = 1
end
local store_end = last_id
if store_end == nil then
store_end = 1
end
local log_dir = "{$SERVER_APP}/logs"
local db_path= log_dir .. '/' .. input_server_name .. "/logs.db"
local db, err = sqlite3.open(db_path)
D("sqlite3 path:"..db_path)
-- python3 ./plugins/webstats/index.py reload && echo "" > /Users/midoks/Desktop/mwdev/server/webstats/debug.log && wget http://t1.cn/
D("sqlite3 error:"..tostring(err))
local stmt2 = nil
if db ~= nil then
stmt2 = db:prepare[[INSERT INTO web_logs(
time, ip, domain, server_name, method, status_code, uri, body_length,
referer, user_agent, protocol, request_time, is_spider, request_headers, ip_list, client_port)
VALUES(:time, :ip, :domain, :server_name, :method, :status_code, :uri,
:body_length, :referer, :user_agent, :protocol, :request_time, :is_spider,
:request_headers, :ip_list, :client_port)]]
end
if db == nil or stmt2 == nil then
D("网站监控报表数据库连接异常。")
-- cache:set(storing_key, false)
if db and db:isopen() then
db:close()
end
return true
end
status, errorString = db:exec([[BEGIN TRANSACTION]])
if store_end >= store_start then
for i=store_start, store_end, 1 do
D("store_start:"..store_start..":store_end:".. store_end)
if store_logs_line(db, stmt2, input_server_name, i) then
store_count = store_count + 1
cache_clear(input_server_name, i, "log_kv")
-- cache_clear(input_server_name, i, "STAT_FIELDS")
end
end
end
local res, err = stmt2:finalize()
if tostring(res) == "5" then
D("Finalize res:"..tostring(res))
D("Finalize err:"..tostring(err))
D("数据库连接繁忙,稍候存储.")
end
local res, err = db:execute([[COMMIT]])
if db and db:isopen() then
db:close()
end
end
local function run_app()
D("debug start")
D("------------ debug start ------------")
local c_name = ngx.var.server_name
server_name = string.gsub(get_server_name(c_name),'_','.')
D("c_name:"..c_name)
D("server_name:"..server_name)
cache_logs()
store_logs(server_name)
D("------------ debug end ------------")
end
local function run_app_ok()
if not debug_mode then return run_app() end
local presult, err = pcall(
function()
json = require "cjson"
sqlite3 = require "lsqlite3"
run_app()
end
)
if not presult then
D("depend on :"..tostring(err))
D("err on :"..tostring(err))
return true
end
D("debug end")
end
return run_app()
return run_app_ok()
}

Loading…
Cancel
Save