🔧 build: 调整 库 目录
parent
dd6ab6d6a7
commit
8dc9ed8a49
@ -0,0 +1,41 @@
|
|||||||
|
local skynet = require "skynet"
|
||||||
|
local mc = require "skynet.multicast"
|
||||||
|
local st = require "skynet.sharetable"
|
||||||
|
|
||||||
|
local shared
|
||||||
|
local channel
|
||||||
|
|
||||||
|
local M
|
||||||
|
|
||||||
|
local function init()
|
||||||
|
assert(not shared)
|
||||||
|
assert(not channel)
|
||||||
|
shared = skynet.uniqueservice "shared"
|
||||||
|
local name = skynet.call(shared, "lua", "channel")
|
||||||
|
channel = mc.new {
|
||||||
|
channel = name,
|
||||||
|
dispatch = function(_, _, filenames)
|
||||||
|
for _, filename in pairs(filenames) do
|
||||||
|
if M[filename] then
|
||||||
|
M[filename] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
M = setmetatable({}, {
|
||||||
|
__index = function(self, filename)
|
||||||
|
if not shared then
|
||||||
|
init()
|
||||||
|
end
|
||||||
|
|
||||||
|
local obj = st.query(filename)
|
||||||
|
if obj then
|
||||||
|
self[filename] = obj
|
||||||
|
end
|
||||||
|
return obj
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
local skynet = require "skynet"
|
||||||
|
local redlock = {}
|
||||||
|
|
||||||
|
local addr
|
||||||
|
local make_uuid
|
||||||
|
|
||||||
|
local function default_cb()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _lock(lockname, func, cb, timeout, ...)
|
||||||
|
cb = cb or default_cb
|
||||||
|
local uuid = make_uuid()
|
||||||
|
local ok, data = skynet.call(addr, "lua", "lock", lockname, uuid, timeout)
|
||||||
|
if not ok then
|
||||||
|
return cb(ok, data)
|
||||||
|
end
|
||||||
|
cb(pcall(func, ...))
|
||||||
|
skynet.call(addr, "lua", "unlock", lockname, uuid)
|
||||||
|
end
|
||||||
|
|
||||||
|
function redlock.lock(lockname, func, cb, timeout, ...)
|
||||||
|
skynet.fork(_lock, lockname, func, cb, timeout, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
skynet.init(function()
|
||||||
|
local conf = require "redlock_conf"
|
||||||
|
make_uuid = conf.make_uuid
|
||||||
|
addr = skynet.uniqueservice("redlockd")
|
||||||
|
end)
|
||||||
|
|
||||||
|
return redlock
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
local lfs = require "lfs"
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
local Share = class("Share")
|
||||||
|
|
||||||
|
function Share:initialize(root)
|
||||||
|
self._root = root
|
||||||
|
end
|
||||||
|
|
||||||
|
function Share:get_paths(rootpath, paths)
|
||||||
|
paths = paths or {}
|
||||||
|
for entry in lfs.dir(rootpath) do
|
||||||
|
if entry ~= "." and entry ~= ".." then
|
||||||
|
local path = string.format("%s/%s", rootpath, entry)
|
||||||
|
|
||||||
|
local attr = lfs.attributes(path)
|
||||||
|
assert(type(attr) == "table")
|
||||||
|
|
||||||
|
if attr.mode == "directory" then
|
||||||
|
self:get_paths(path, paths)
|
||||||
|
else
|
||||||
|
-- 获取文件
|
||||||
|
entry = entry:match("(.+)%.lua$")
|
||||||
|
if entry then
|
||||||
|
paths[entry] = path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return paths
|
||||||
|
end
|
||||||
|
|
||||||
|
function Share:read_file(fileName)
|
||||||
|
local f, errMsg = io.open(fileName, "r")
|
||||||
|
if not f then
|
||||||
|
return false, errMsg
|
||||||
|
end
|
||||||
|
|
||||||
|
local source = f:read("a")
|
||||||
|
io.close(f)
|
||||||
|
return true, source
|
||||||
|
end
|
||||||
|
|
||||||
|
function Share:load_file(fileName, ...)
|
||||||
|
local ok, source = self:read_file(fileName)
|
||||||
|
if ok then
|
||||||
|
local chunk, err = load(source)
|
||||||
|
if "function" == type(chunk) then
|
||||||
|
return pcall(chunk, ...)
|
||||||
|
else
|
||||||
|
return false, err
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local err = source
|
||||||
|
return false, err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Share:get_share_data()
|
||||||
|
local ret = {}
|
||||||
|
if not self._root or "table" ~= type(self._root) then
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
local files = {}
|
||||||
|
for _, rootPath in pairs(self._root) do
|
||||||
|
self:get_paths(rootPath, files)
|
||||||
|
end
|
||||||
|
for fileName, filePath in pairs(files) do
|
||||||
|
local ok, data = self:load_file(filePath)
|
||||||
|
assert(ok, string.format("%s err: %s", filePath, data))
|
||||||
|
ret[fileName] = data
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
return Share
|
||||||
|
|
||||||
@ -0,0 +1,99 @@
|
|||||||
|
local single_entity = require "single_entity"
|
||||||
|
local multi_entity = require "multi_entity"
|
||||||
|
local util = require "store_util"
|
||||||
|
|
||||||
|
local traceback = debug.traceback
|
||||||
|
|
||||||
|
local mt = {}
|
||||||
|
mt.__index = mt
|
||||||
|
|
||||||
|
function mt:ctor(opts)
|
||||||
|
self.conf = util.config(opts)
|
||||||
|
self.entities = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:load(id)
|
||||||
|
local conf = self.conf
|
||||||
|
local entity
|
||||||
|
if conf.multi then
|
||||||
|
entity = multi_entity.new(id, conf)
|
||||||
|
else
|
||||||
|
entity = single_entity.new(id, conf)
|
||||||
|
end
|
||||||
|
assert(entity)
|
||||||
|
self.entities[id] = entity
|
||||||
|
return entity
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:unload(id)
|
||||||
|
local entity = self.entities[id]
|
||||||
|
if not entity then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
entity:flush()
|
||||||
|
self.entities[id] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:unload_all()
|
||||||
|
self:flush()
|
||||||
|
self.entities = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:entity(id)
|
||||||
|
return self.entities[id]
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:get(id, ...)
|
||||||
|
local entity = self.entities[id]
|
||||||
|
assert(entity, id)
|
||||||
|
if self.conf.multi then
|
||||||
|
local subid = ...
|
||||||
|
return entity:get(subid)
|
||||||
|
else
|
||||||
|
return entity:get()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:set(id, ...)
|
||||||
|
local data1, data2 = ...
|
||||||
|
local entity = self.entities[id]
|
||||||
|
assert(entity, id)
|
||||||
|
if self.conf.multi then
|
||||||
|
entity:set(data1, data2)
|
||||||
|
else
|
||||||
|
entity:set(data1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:flush()
|
||||||
|
local rediscmds = {}
|
||||||
|
local sqls = {}
|
||||||
|
|
||||||
|
for _, entity in pairs(self.entities) do
|
||||||
|
entity:collect_dirty(rediscmds, sqls)
|
||||||
|
end
|
||||||
|
|
||||||
|
for addr, cmds in pairs(rediscmds) do
|
||||||
|
xpcall(util.redis_execute, traceback, addr, cmds)
|
||||||
|
end
|
||||||
|
|
||||||
|
for addr, sqls in pairs(sqls) do
|
||||||
|
xpcall(util.mysql_execute, traceback, addr, sqls)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:dump()
|
||||||
|
local obj = {}
|
||||||
|
for key, entity in pairs(self.entities) do
|
||||||
|
obj[key] = entity.rs
|
||||||
|
end
|
||||||
|
return util.encode(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
new = function(opts)
|
||||||
|
local obj = setmetatable({}, mt)
|
||||||
|
obj:ctor(opts)
|
||||||
|
return obj
|
||||||
|
end,
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
local skynet = require "skynet"
|
||||||
|
|
||||||
|
return function(dbconf)
|
||||||
|
local addr = skynet.uniqueservice("dbmgr")
|
||||||
|
return skynet.call(addr, "lua", "init", dbconf)
|
||||||
|
end
|
||||||
@ -0,0 +1,177 @@
|
|||||||
|
local util = require "store_util"
|
||||||
|
|
||||||
|
local strformat = string.format
|
||||||
|
local tinsert = table.insert
|
||||||
|
local mysql_execute = util.mysql_execute
|
||||||
|
local redis_execute = util.redis_execute
|
||||||
|
local redis_key_field = util.redis_execute
|
||||||
|
local quote_sql_str = util.quote_sql_str
|
||||||
|
local decode = util.decode
|
||||||
|
local encode = util.encode
|
||||||
|
local guid = util.guid
|
||||||
|
local traceback = debug.traceback
|
||||||
|
|
||||||
|
local mt = {}
|
||||||
|
mt.__index = mt
|
||||||
|
|
||||||
|
function mt:ctor(id, conf)
|
||||||
|
assert(conf)
|
||||||
|
assert(type(id) == "number")
|
||||||
|
self.conf = conf
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
local redisd_addr = conf.redisd_addr
|
||||||
|
local mysqld_addr = conf.mysqld_addr
|
||||||
|
|
||||||
|
-- load from redis
|
||||||
|
if redisd_addr then
|
||||||
|
local key, _ = redis_key_field(conf, 0)
|
||||||
|
local res = redis_execute(redisd_addr, "hgetall", key)
|
||||||
|
local sz = #res
|
||||||
|
if sz > 0 then
|
||||||
|
local rs = {}
|
||||||
|
for i = 1, sz, 2 do
|
||||||
|
local id = tonumber(res[i])
|
||||||
|
rs[id] = decode(res[i + 1])
|
||||||
|
end
|
||||||
|
self.rs = rs
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- load from mysql
|
||||||
|
if not self.rs and mysqld_addr then
|
||||||
|
local mysql_idname = conf.mysql_idname
|
||||||
|
local mysql_valname = conf.mysql_valname
|
||||||
|
local sql = strformat([[SELECT `%s`, `%s` FROM `%s` where `%s` = %d]], mysql_idname, mysql_valname, conf.tbl,
|
||||||
|
conf.mysql_owner_idname, self.id)
|
||||||
|
local res = mysql_execute(mysqld_addr, sql)
|
||||||
|
local sz = #res
|
||||||
|
if sz > 0 then
|
||||||
|
local rs = {}
|
||||||
|
for i = 1, sz do
|
||||||
|
local data = res[i]
|
||||||
|
local id = data[mysql_idname]
|
||||||
|
assert(type(id) == "number", id)
|
||||||
|
rs[id] = data[mysql_valname]
|
||||||
|
end
|
||||||
|
self.rs = rs
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not self.rs then
|
||||||
|
self.rs = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- dirty id
|
||||||
|
self.dirty = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:guid()
|
||||||
|
return guid()
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:get(id)
|
||||||
|
return self.rs[id]
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:set(id, val)
|
||||||
|
local conf = self.conf
|
||||||
|
if self.rs[id] then
|
||||||
|
if val then
|
||||||
|
-- update
|
||||||
|
self.dirty[id] = true
|
||||||
|
else
|
||||||
|
-- delete
|
||||||
|
self.dirty[id] = nil
|
||||||
|
if conf.mysqld_addr then
|
||||||
|
local sql = strformat([[delete from `%s` where `%s` = %d and `%s` = %d limit 1]], conf.tbl,
|
||||||
|
conf.mysql_idname, id, conf.mysql_owner_idname, self.id)
|
||||||
|
mysql_execute(conf.mysqld_addr, sql)
|
||||||
|
end
|
||||||
|
if conf.redisd_addr then
|
||||||
|
local key, field = redis_key_field(conf, id)
|
||||||
|
redis_execute(conf.redisd_addr, "hdel", key, field)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif val then
|
||||||
|
-- insert
|
||||||
|
local sval = encode(val)
|
||||||
|
assert(sval)
|
||||||
|
if conf.mysqld_addr then
|
||||||
|
local sql = strformat([[insert into `%s`(`%s`,`%s`,`%s`) values (%d,%d,%s)]], conf.tbl, conf.mysql_idname,
|
||||||
|
conf.mysql_owner_idname, conf.mysql_valname, id, self.id, quote_sql_str(sval))
|
||||||
|
mysql_execute(conf.mysqld_addr, sql)
|
||||||
|
end
|
||||||
|
if conf.redisd_addr then
|
||||||
|
local key, field = redis_key_field(conf, id)
|
||||||
|
redis_execute(conf.redisd_addr, "hset", key, field, sval)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.rs[id] = val
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:_collect_dirty(id, rediscmds, sqls)
|
||||||
|
local val = self.rs[id]
|
||||||
|
val = encode(val)
|
||||||
|
assert(val)
|
||||||
|
local conf = self.conf
|
||||||
|
local redisd_addr = conf.redid_addr
|
||||||
|
local mysqld_addr = conf.mysqld_addr
|
||||||
|
|
||||||
|
if redisd_addr then
|
||||||
|
local key, field = redis_key_field(conf, id)
|
||||||
|
local list = rediscmds[redisd_addr]
|
||||||
|
if not list then
|
||||||
|
list = {}
|
||||||
|
rediscmds[redisd_addr] = list
|
||||||
|
end
|
||||||
|
tinsert(list, {"HSET", key, field, val})
|
||||||
|
end
|
||||||
|
|
||||||
|
if mysqld_addr then
|
||||||
|
local sql = strformat([[UPDATE `%s` SET `%s` = %s where `%s` = %d and `%s` = %d limit 1]], conf.tbl,
|
||||||
|
conf.mysql_valname, quote_sql_str(val), conf.mysql_idname, id, conf.mysql_owner_idname, self.id)
|
||||||
|
|
||||||
|
local list = sqls[mysqld_addr]
|
||||||
|
if not list then
|
||||||
|
list = {}
|
||||||
|
sqls[mysqld_addr] = list
|
||||||
|
end
|
||||||
|
tinsert(list, sql)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:collect_dirty(rediscmds, sqls)
|
||||||
|
if not next(self.dirty) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for id, _ in pairs(self.dirty) do
|
||||||
|
xpcall(self._collect_dirty, traceback, self, id, rediscmds, sqls)
|
||||||
|
end
|
||||||
|
self.dirty = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:flush()
|
||||||
|
local conf = self.conf
|
||||||
|
local rediscmds = {}
|
||||||
|
local sqls = {}
|
||||||
|
|
||||||
|
self:collect_dirty(rediscmds, sqls)
|
||||||
|
|
||||||
|
if #rediscmds > 0 then
|
||||||
|
xpcall(redis_execute, traceback, conf.redisd_addr, rediscmds)
|
||||||
|
end
|
||||||
|
|
||||||
|
if #sqls > 0 then
|
||||||
|
mysql_execute(conf.mysqld_addr, sqls)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
new = function(...)
|
||||||
|
local obj = setmetatable({}, mt)
|
||||||
|
obj:ctor(...)
|
||||||
|
return obj
|
||||||
|
end,
|
||||||
|
}
|
||||||
@ -0,0 +1,133 @@
|
|||||||
|
local util = require "store_util"
|
||||||
|
|
||||||
|
local strformat = string.format
|
||||||
|
local tinsert = table.insert
|
||||||
|
local mysql_execute = util.mysql_execute
|
||||||
|
local redis_execute = util.redis_execute
|
||||||
|
local redis_key_field = util.redis_execute
|
||||||
|
local quote_sql_str = util.quote_sql_str
|
||||||
|
local decode = util.decode
|
||||||
|
local encode = util.encode
|
||||||
|
local traceback = debug.traceback
|
||||||
|
|
||||||
|
local mt = {}
|
||||||
|
mt.__index = mt
|
||||||
|
|
||||||
|
function mt:ctor(id, conf)
|
||||||
|
assert(conf)
|
||||||
|
assert(type(id) == "number")
|
||||||
|
self.conf = conf
|
||||||
|
self.id = id
|
||||||
|
-- load record
|
||||||
|
if conf.redisd_addr then
|
||||||
|
local key, field = redis_key_field(conf)
|
||||||
|
local ret = redis_execute(conf.redisd_addr, "hget", key, field)
|
||||||
|
self.rs = decode(ret)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not self.rs and conf.mysqld_addr then
|
||||||
|
local sql = strformat("select `%s` from `%s` where `%s` = %d limit 1", conf.mysql_valname, conf.tbl,
|
||||||
|
conf.mysql_idname, self.id)
|
||||||
|
|
||||||
|
local ret = mysql_execute(conf.mysqld_addr, sql)
|
||||||
|
if ret then
|
||||||
|
self.rs = decode(ret[1][conf.mysql_valname])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not self.rs and not conf.readonly then
|
||||||
|
self:set({})
|
||||||
|
end
|
||||||
|
|
||||||
|
self.dirty = false
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:get()
|
||||||
|
return self.rs
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:set(val)
|
||||||
|
local conf = self.conf
|
||||||
|
assert(not conf.readonly, conf.tbl)
|
||||||
|
|
||||||
|
local sval = encode(val)
|
||||||
|
if not sval then
|
||||||
|
assert(false, ("[tbl] %s [id] %s"):format(conf.tbl, self.id))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not self.rs then
|
||||||
|
-- insert
|
||||||
|
if conf.mysqld_addr then
|
||||||
|
local sql = strformat([[insert into `%s`(`%s`, `%s`) values (%d, %s)]], conf.tbl, conf.mysql_idname,
|
||||||
|
conf.mysql_valname, self.id, quote_sql_str(sval))
|
||||||
|
|
||||||
|
mysql_execute(conf.mysqld_addr, sql)
|
||||||
|
end
|
||||||
|
|
||||||
|
if conf.redisd_addr then
|
||||||
|
local key, field = redis_key_field(conf)
|
||||||
|
redis_execute(conf.redisd_addr, "hset", key, field, sval)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.dirty = true
|
||||||
|
end
|
||||||
|
self.rs = val
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:collect_dirty(rediscmds, sqls)
|
||||||
|
local conf = self.conf
|
||||||
|
if not self.dirty then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local val = encode(self.rs)
|
||||||
|
local redisd_addr = conf.redisd_addr
|
||||||
|
local mysqld_addr = conf.mysqld_addr
|
||||||
|
|
||||||
|
if redisd_addr then
|
||||||
|
local key, field = redis_key_field(conf)
|
||||||
|
local list = rediscmds[redisd_addr]
|
||||||
|
if not list then
|
||||||
|
list = {}
|
||||||
|
rediscmds[redisd_addr] = list
|
||||||
|
end
|
||||||
|
tinsert(list, {"hset", key, field, val})
|
||||||
|
end
|
||||||
|
|
||||||
|
if mysqld_addr then
|
||||||
|
local sql = strformat([[update `%s` set `%s` = %s where `%s` = %d limit 1]], conf.tbl, conf.mysql_valname,
|
||||||
|
quote_sql_str(val), conf.mysql_idname, self.id)
|
||||||
|
local list = sqls[mysqld_addr]
|
||||||
|
if not list then
|
||||||
|
list = {}
|
||||||
|
sqls[mysqld_addr] = list
|
||||||
|
end
|
||||||
|
tinsert(list, sql)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.dirty = false
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:flush()
|
||||||
|
local conf = self.conf
|
||||||
|
local rediscmds = {}
|
||||||
|
local sqls = {}
|
||||||
|
|
||||||
|
self:collect_dirty(rediscmds, sqls)
|
||||||
|
|
||||||
|
if #rediscmds > 0 then
|
||||||
|
xpcall(redis_execute, traceback, conf.redisd_addr, rediscmds)
|
||||||
|
end
|
||||||
|
|
||||||
|
if #sqls > 0 then
|
||||||
|
mysql_execute(conf.mysqld_addr, sqls)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
new = function(...)
|
||||||
|
local obj = setmetatable({}, mt)
|
||||||
|
obj:ctor(...)
|
||||||
|
return obj
|
||||||
|
end,
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
local skynet = require "skynet"
|
||||||
|
local entity_pool = requier "entity_pool"
|
||||||
|
local _store = {}
|
||||||
|
|
||||||
|
local inited = false
|
||||||
|
|
||||||
|
local function add_pool(name, opts)
|
||||||
|
if _store[name] then
|
||||||
|
error(("entity pool [%d] has exist"):format(name))
|
||||||
|
end
|
||||||
|
_store[name] = entity_pool.new(opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function open(flush_interval)
|
||||||
|
if flush_interval then
|
||||||
|
skynet.fork(function()
|
||||||
|
while inited do
|
||||||
|
for _, pool in pairs(_store) do
|
||||||
|
pool:flush()
|
||||||
|
skynet.yield()
|
||||||
|
end
|
||||||
|
skynet.sleep(flush_interval*100)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function close()
|
||||||
|
for _, pool in pairs(_store) do
|
||||||
|
pool:unload_all()
|
||||||
|
end
|
||||||
|
_store = {}
|
||||||
|
inited = false
|
||||||
|
end
|
||||||
|
|
||||||
|
local store = setmetatable(_store, {__index = function(self, name)
|
||||||
|
error(("can not find entity pool [%s]"):format(name))
|
||||||
|
end})
|
||||||
|
|
||||||
|
return store, add_pool, open, close
|
||||||
@ -0,0 +1,149 @@
|
|||||||
|
local skynet = require "skynet"
|
||||||
|
local json = require "json"
|
||||||
|
|
||||||
|
local strformat = string.format
|
||||||
|
local strgsub = string.gsub
|
||||||
|
local random = math.random
|
||||||
|
local traceback = debug.traceback
|
||||||
|
|
||||||
|
local mysqld_service_num = {}
|
||||||
|
local redisd_service_num = {}
|
||||||
|
local guid_generator_addrs
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.config(opts)
|
||||||
|
local schema = opts.schema
|
||||||
|
assert(type(schema.tbl) == "string", schema.tbl)
|
||||||
|
|
||||||
|
local conf = {}
|
||||||
|
conf.mysqld_addr = opts.mysqld_addr
|
||||||
|
conf.redisd_addr = opts.redisd_addr
|
||||||
|
conf.tbl = schema.tbl
|
||||||
|
conf.readonly = schema.readonly
|
||||||
|
conf.multi = false
|
||||||
|
if schema.multi then
|
||||||
|
conf.multi = true
|
||||||
|
end
|
||||||
|
if conf.mysqld then
|
||||||
|
local field = schema.field or {}
|
||||||
|
conf.mysql_valname = field.data or "data"
|
||||||
|
conf.mysql_idname = field.id or "id"
|
||||||
|
if conf.multi then
|
||||||
|
conf.mysql_owner_idname = field.owner_id or "owner_id"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return conf
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.mysql_sname(dbkey, index)
|
||||||
|
return ".mysqld." .. dbkey .. "." .. index
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.redis_sname(dbkey, index)
|
||||||
|
return ".redisd." .. dbkey .. "." .. index
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.balance_mysqld_addr(dbkey, id)
|
||||||
|
local num = mysqld_service_num[dbkey]
|
||||||
|
if not num then
|
||||||
|
num = skynet.call(".dbmgr", "lua", "mysql_service_num", dbkey)
|
||||||
|
mysqld_service_num[dbkey] = num
|
||||||
|
end
|
||||||
|
assert(num)
|
||||||
|
local sname = M.mysqld_sname(dbkey, (id % num) + 1)
|
||||||
|
return skynet.localname(sname)
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.balance_redisd_addr(dbkey, id)
|
||||||
|
local num = redisd_service_num[dbkey]
|
||||||
|
if not num then
|
||||||
|
num = skynet.call(".dbmgr", "lua", "redis_service_num", dbkey)
|
||||||
|
redisd_service_num[dbkey] = num
|
||||||
|
end
|
||||||
|
assert(num)
|
||||||
|
local sname = M.redis_sname(dbkey, (id % num) + 1)
|
||||||
|
return skynet.localname(sname)
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.guid()
|
||||||
|
if not guid_generator_addrs then
|
||||||
|
guid_generator_addrs = skynet.call(".dbmgr", "lua", "guid_generators")
|
||||||
|
end
|
||||||
|
assert(guid_generator_addrs, ".dbmgr must be init")
|
||||||
|
local num = #guid_generator_addrs
|
||||||
|
return skynet.call(guid_generator_addrs[random(1, num)], "lua")
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.encode(val)
|
||||||
|
if not val then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local ok, ret = xpcall(json.encode, traceback, val)
|
||||||
|
if not ok then
|
||||||
|
assert(false, strformat("encode error. val=[%s] ret=[%s]", tostring(val), tostring(ret)))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.decode(val)
|
||||||
|
if not val then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local ok, ret = xpcall(json.decode, traceback, val)
|
||||||
|
if not ok then
|
||||||
|
assert(false, strformat("decode error. val=[%s] ret=[%s]", tostring(val), tostring(ret)))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
local escape_map = {
|
||||||
|
['\0'] = "\\0",
|
||||||
|
['\b'] = "\\b",
|
||||||
|
['\n'] = "\\n",
|
||||||
|
['\r'] = "\\r",
|
||||||
|
['\t'] = "\\t",
|
||||||
|
['\26'] = "\\Z",
|
||||||
|
['\\'] = "\\\\",
|
||||||
|
["'"] = "\\'",
|
||||||
|
['"'] = '\\"',
|
||||||
|
}
|
||||||
|
|
||||||
|
function M.quote_sql_str(str)
|
||||||
|
return strformat("'%s'", strgsub(str, "[\0\b\n\r\t\26\\\'\"]", escape_map))
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.redis_key_field(conf, id)
|
||||||
|
if not conf.redisd_addr then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local key, field
|
||||||
|
if id then
|
||||||
|
key = conf.tbl .. ":" .. conf.id
|
||||||
|
field = tostring(id)
|
||||||
|
else
|
||||||
|
key = conf.tbl
|
||||||
|
field = conf.id
|
||||||
|
end
|
||||||
|
return key, field
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.mysql_execute(addr, sqls)
|
||||||
|
if type(sqls) == "table" then
|
||||||
|
return skynet.call(addr, "lua", "exec", sqls)
|
||||||
|
elseif type(sqls) == "string" then
|
||||||
|
return skynet.call(addr, "lua", "exec_one", sqls)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.redis_execute(addr, cmd, ...)
|
||||||
|
if type(cmd) == "table" then
|
||||||
|
return skynet.call(addr, "lua", "exec", cmd)
|
||||||
|
elseif type(cmd) == "string" then
|
||||||
|
return skynet.call(addr, "lua", "exec_one", cmd, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
local conf = {
|
||||||
|
|
||||||
|
-- redis服务器信息, redis服务器数为奇数
|
||||||
|
-- 最好将奇数台reids部署在不同机器, 达到容错、高可用目的
|
||||||
|
servers = {{
|
||||||
|
host = "127.0.0.1",
|
||||||
|
port = 6379,
|
||||||
|
db = 0,
|
||||||
|
auth = "123456",
|
||||||
|
}},
|
||||||
|
|
||||||
|
-- uuid生成器, 请替换成真uuid(可使用uuid库uuid_generate)
|
||||||
|
make_uuid = function()
|
||||||
|
local fn = function(x)
|
||||||
|
local r = math.random(16) - 1
|
||||||
|
r = (x == "x") and (r + 1) or (r % 4) + 9
|
||||||
|
return ("0123456789abcdef"):sub(r, r)
|
||||||
|
end
|
||||||
|
return (("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"):gsub("[xy]", fn))
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- 默认锁过期时间
|
||||||
|
timeout = 3000,
|
||||||
|
|
||||||
|
-- 用计算服务器漂移时间 (http://redis.io/topics/distlock)
|
||||||
|
drift_factor = 0.01,
|
||||||
|
|
||||||
|
-- 重试获得锁最大次数
|
||||||
|
retry_count = 10,
|
||||||
|
|
||||||
|
-- 重试获得锁间隔(毫秒)
|
||||||
|
retry_delay = 200,
|
||||||
|
|
||||||
|
-- 重试获得锁抖动时间(毫秒) https://www.awsarchitectureblog.com/2015/03/backoff.html
|
||||||
|
retry_jitter = 100,
|
||||||
|
|
||||||
|
-- 请求redis超时时间(毫秒)
|
||||||
|
request_timeout = 500,
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf
|
||||||
|
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new(sep)
|
||||||
|
sep = sep or ' '
|
||||||
|
|
||||||
|
return function(...)
|
||||||
|
local argc, argv = select('#', ...), {...}
|
||||||
|
for i = 1, argc do
|
||||||
|
argv[i] = tostring(argv[i])
|
||||||
|
end
|
||||||
|
return (table.concat(argv, sep))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
|
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new()
|
||||||
|
return function(msg)
|
||||||
|
return msg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
local string = require "string"
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new()
|
||||||
|
return string.format
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
-- compatiable with lualogging
|
||||||
|
--
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new(default)
|
||||||
|
if not default then
|
||||||
|
default = require"log.formatter.format".new()
|
||||||
|
end
|
||||||
|
|
||||||
|
return function(...)
|
||||||
|
if type((...)) == 'function' then
|
||||||
|
return (...)(select(2, ...))
|
||||||
|
end
|
||||||
|
|
||||||
|
if select('#', ...) < 2 then
|
||||||
|
return tostring((...))
|
||||||
|
end
|
||||||
|
|
||||||
|
return default(...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
local lpeg = require "lpeg"
|
||||||
|
local table = require "table"
|
||||||
|
local string = require "string"
|
||||||
|
|
||||||
|
local unpack = unpack or table.unpack
|
||||||
|
|
||||||
|
local HAS_A_FORMAT = pcall(string.format, '%a', 10)
|
||||||
|
|
||||||
|
local P, C, Cs, Ct, Cp, S, R = lpeg.P, lpeg.C, lpeg.Cs, lpeg.Ct, lpeg.Cp, lpeg.S, lpeg.R
|
||||||
|
|
||||||
|
local any = P(1)
|
||||||
|
local empty = P(0)
|
||||||
|
|
||||||
|
local esc = P '%%'
|
||||||
|
local flags = S '-+ #0'
|
||||||
|
local digit = R '09'
|
||||||
|
|
||||||
|
local fsym = S('cdiouxXeEfgGqs' .. (HAS_A_FORMAT and 'aA' or ''))
|
||||||
|
local width = digit * digit + digit
|
||||||
|
local precision = P '.' * (digit * digit + digit)
|
||||||
|
local format = (flags + empty) * (width + empty) * (precision + empty) * (P '.' + empty)
|
||||||
|
local valid_format = P '%' * format * fsym
|
||||||
|
local valid_format_capture = Cs(valid_format)
|
||||||
|
|
||||||
|
local any_fsym = any - (flags + digit + P '.')
|
||||||
|
local any_format = P '%' * (flags + digit + P '.') ^ 0 * any_fsym
|
||||||
|
|
||||||
|
local types = {
|
||||||
|
c = 'number',
|
||||||
|
d = 'number',
|
||||||
|
i = 'number',
|
||||||
|
o = 'number',
|
||||||
|
u = 'number',
|
||||||
|
x = 'number',
|
||||||
|
X = 'number',
|
||||||
|
a = 'number',
|
||||||
|
A = 'number',
|
||||||
|
e = 'number',
|
||||||
|
E = 'number',
|
||||||
|
f = 'number',
|
||||||
|
g = 'number',
|
||||||
|
G = 'number',
|
||||||
|
q = 'string',
|
||||||
|
s = 'string',
|
||||||
|
}
|
||||||
|
|
||||||
|
local function safe_format(protect_only_args, fmt, ...)
|
||||||
|
local n, args = 0, {...}
|
||||||
|
|
||||||
|
local function fix_fmt(f)
|
||||||
|
local fmt = valid_format_capture:match(f)
|
||||||
|
|
||||||
|
if not fmt then
|
||||||
|
if protect_only_args then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
return '%' .. f
|
||||||
|
end
|
||||||
|
|
||||||
|
local typ = string.sub(fmt, -1)
|
||||||
|
|
||||||
|
n = n + 1
|
||||||
|
|
||||||
|
if types[typ] ~= type(args[n]) then
|
||||||
|
args[n], fmt = tostring(args[n]), '%s'
|
||||||
|
end
|
||||||
|
|
||||||
|
return fmt
|
||||||
|
end
|
||||||
|
|
||||||
|
local pattern = Cs((esc + any_format / fix_fmt + any) ^ 0)
|
||||||
|
fmt = pattern:match(fmt)
|
||||||
|
|
||||||
|
return string.format(fmt, unpack(args, 1, n))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function buld_formatter(protect_only_args, no_warning)
|
||||||
|
return function(...)
|
||||||
|
local ok, msg = pcall(string.format, ...)
|
||||||
|
if not ok then
|
||||||
|
local err = msg
|
||||||
|
msg = safe_format(protect_only_args, ...)
|
||||||
|
if not no_warning then
|
||||||
|
msg = msg .. ' - ' .. 'WARNING: Error formatting log message: ' .. err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return msg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
new = buld_formatter,
|
||||||
|
}
|
||||||
@ -0,0 +1,223 @@
|
|||||||
|
---
|
||||||
|
-- @module log
|
||||||
|
--
|
||||||
|
local _COPYRIGHT = "Copyright (C) 2013-2016 Alexey Melnichuk";
|
||||||
|
local _VERSION = "0.1.7-dev"
|
||||||
|
|
||||||
|
local table = require "table"
|
||||||
|
local date = require "date"
|
||||||
|
|
||||||
|
local destroy_list = {}
|
||||||
|
local loggers_list = setmetatable({}, {
|
||||||
|
__mode = 'k',
|
||||||
|
})
|
||||||
|
local emptyfn = function()
|
||||||
|
end
|
||||||
|
|
||||||
|
local LOG_LVL = {
|
||||||
|
EMERG = 1,
|
||||||
|
ALERT = 2,
|
||||||
|
FATAL = 3,
|
||||||
|
ERROR = 4,
|
||||||
|
WARNING = 5,
|
||||||
|
NOTICE = 6,
|
||||||
|
INFO = 7,
|
||||||
|
DEBUG = 8,
|
||||||
|
TRACE = 9,
|
||||||
|
}
|
||||||
|
|
||||||
|
local writer_names = {}
|
||||||
|
local LOG_LVL_NAMES = {}
|
||||||
|
for k, v in pairs(LOG_LVL) do
|
||||||
|
LOG_LVL_NAMES[v] = k
|
||||||
|
writer_names[v] = k:lower()
|
||||||
|
end
|
||||||
|
|
||||||
|
local LOG_LVL_COUNT = #LOG_LVL_NAMES
|
||||||
|
|
||||||
|
local function lvl2number(lvl)
|
||||||
|
if type(lvl) == 'number' then
|
||||||
|
return lvl
|
||||||
|
end
|
||||||
|
if type(lvl) == 'string' then
|
||||||
|
lvl = lvl:upper()
|
||||||
|
local n
|
||||||
|
if lvl == 'NONE' then
|
||||||
|
n = 0
|
||||||
|
else
|
||||||
|
n = LOG_LVL[lvl]
|
||||||
|
end
|
||||||
|
if not n then
|
||||||
|
return nil, "unknown log level: '" .. lvl .. "'"
|
||||||
|
end
|
||||||
|
return n
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil, 'unsupported log leve type: ' .. type(lvl)
|
||||||
|
end
|
||||||
|
|
||||||
|
local Log = {}
|
||||||
|
Log._COPYRIGHT = _COPYRIGHT
|
||||||
|
Log._NAME = "log"
|
||||||
|
Log._VERSION = _VERSION
|
||||||
|
Log._LICENSE = "MIT"
|
||||||
|
|
||||||
|
Log.LVL = LOG_LVL
|
||||||
|
Log.LVL_NAMES = LOG_LVL_NAMES
|
||||||
|
|
||||||
|
Log.lvl2number = lvl2number
|
||||||
|
|
||||||
|
function Log.new(max_lvl, writer, formatter, logformat)
|
||||||
|
if max_lvl and type(max_lvl) ~= 'number' and type(max_lvl) ~= 'string' then
|
||||||
|
max_lvl, writer, formatter, logformat = nil, max_lvl, writer, formatter
|
||||||
|
end
|
||||||
|
|
||||||
|
max_lvl = assert(lvl2number(max_lvl or LOG_LVL.INFO))
|
||||||
|
|
||||||
|
if not writer then
|
||||||
|
writer = require"log.writer.stdout".new()
|
||||||
|
end
|
||||||
|
|
||||||
|
if not formatter then
|
||||||
|
formatter = require"log.formatter.default".new()
|
||||||
|
end
|
||||||
|
|
||||||
|
if not logformat then
|
||||||
|
logformat = require"log.logformat.default".new()
|
||||||
|
end
|
||||||
|
|
||||||
|
local write = function(lvl, ...)
|
||||||
|
local now = date()
|
||||||
|
writer(logformat, formatter(...), lvl, now)
|
||||||
|
end;
|
||||||
|
|
||||||
|
local dump = function(lvl, fn, ...)
|
||||||
|
local now = date()
|
||||||
|
writer(logformat, (fn(...) or ''), lvl, now)
|
||||||
|
end
|
||||||
|
|
||||||
|
local logger = {}
|
||||||
|
|
||||||
|
function logger.writer()
|
||||||
|
return writer
|
||||||
|
end
|
||||||
|
|
||||||
|
function logger.formatter()
|
||||||
|
return formatter
|
||||||
|
end
|
||||||
|
|
||||||
|
function logger.format()
|
||||||
|
return logformat
|
||||||
|
end
|
||||||
|
|
||||||
|
function logger.lvl()
|
||||||
|
return max_lvl
|
||||||
|
end
|
||||||
|
|
||||||
|
function logger.set_writer(value)
|
||||||
|
assert(value)
|
||||||
|
writer, value = value, writer
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
|
function logger.set_formatter(value)
|
||||||
|
assert(value)
|
||||||
|
formatter, value = value, formatter
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
|
function logger.set_format(value)
|
||||||
|
assert(value)
|
||||||
|
logformat, value = value, logformat
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
|
function logger.log(lvl, ...)
|
||||||
|
local err
|
||||||
|
lvl, err = lvl2number(lvl)
|
||||||
|
if not lvl then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
return write(lvl, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function logger.dump(lvl, ...)
|
||||||
|
local err
|
||||||
|
lvl, err = lvl2number(lvl)
|
||||||
|
if not lvl then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
return dump(lvl, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function logger.set_lvl(lvl)
|
||||||
|
local err
|
||||||
|
lvl, err = lvl2number(lvl)
|
||||||
|
if not lvl then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
max_lvl = lvl
|
||||||
|
for i = 1, max_lvl do
|
||||||
|
logger[writer_names[i]] = function(...)
|
||||||
|
write(i, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for i = 1, max_lvl do
|
||||||
|
logger[writer_names[i] .. '_dump'] = function(...)
|
||||||
|
dump(i, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for i = max_lvl + 1, LOG_LVL_COUNT do
|
||||||
|
logger[writer_names[i]] = emptyfn
|
||||||
|
end
|
||||||
|
for i = max_lvl + 1, LOG_LVL_COUNT do
|
||||||
|
logger[writer_names[i] .. '_dump'] = emptyfn
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(logger.set_lvl(max_lvl))
|
||||||
|
|
||||||
|
loggers_list[logger] = true;
|
||||||
|
|
||||||
|
return logger
|
||||||
|
end
|
||||||
|
|
||||||
|
function Log.add_cleanup(fn)
|
||||||
|
assert(type(fn) == 'function')
|
||||||
|
for k, v in ipairs(destroy_list) do
|
||||||
|
if v == fn then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(destroy_list, 1, fn)
|
||||||
|
return fn
|
||||||
|
end
|
||||||
|
|
||||||
|
function Log.remove_cleanup(fn)
|
||||||
|
for k, v in ipairs(destroy_list) do
|
||||||
|
if v == fn then
|
||||||
|
table.remove(destroy_list, k)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Log.close()
|
||||||
|
for _, fn in ipairs(destroy_list) do
|
||||||
|
pcall(fn)
|
||||||
|
end
|
||||||
|
for logger in pairs(loggers_list) do
|
||||||
|
logger.fotal = emptyfn;
|
||||||
|
logger.error = emptyfn;
|
||||||
|
logger.warning = emptyfn;
|
||||||
|
logger.info = emptyfn;
|
||||||
|
logger.notice = emptyfn;
|
||||||
|
logger.debug = emptyfn;
|
||||||
|
logger.closed = true;
|
||||||
|
loggers_list[logger] = nil
|
||||||
|
end
|
||||||
|
destroy_list = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
return Log
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
local string = require "string"
|
||||||
|
local Log = require "log"
|
||||||
|
|
||||||
|
local sformat = string.format
|
||||||
|
local function date_fmt(now)
|
||||||
|
local Y, M, D = now:getdate()
|
||||||
|
return sformat("%.4d-%.2d-%.2d %.2d:%.2d:%.2d", Y, M, D, now:gettime())
|
||||||
|
end
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new()
|
||||||
|
return function(msg, lvl, now)
|
||||||
|
return date_fmt(now) .. ' [' .. Log.LVL_NAMES[lvl] .. '] ' .. msg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
local packer = require "log.logformat.proxy.pack"
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new()
|
||||||
|
return function(now, lvl, msg)
|
||||||
|
return packer.pack(now, lvl, msg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
local Log = require "log"
|
||||||
|
local date = require "date"
|
||||||
|
local string = require "string"
|
||||||
|
local schar = string.char
|
||||||
|
local sbyte = string.byte
|
||||||
|
local sformat = string.format
|
||||||
|
local ssub = string.sub
|
||||||
|
local tn = tonumber
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.pack(msg, lvl, now)
|
||||||
|
local Y, M, D = now:getdate()
|
||||||
|
local h, m, s = now:gettime()
|
||||||
|
local now_s = sformat("%.4d-%.2d-%.2d %.2d:%.2d:%.2d", Y, M, D, h, m, s)
|
||||||
|
|
||||||
|
return schar(lvl) .. now_s .. msg
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.unpack(str)
|
||||||
|
local lvl = sbyte(ssub(str, 1, 1))
|
||||||
|
if not Log.LVL_NAMES[lvl] then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local now_s = ssub(str, 2, 20)
|
||||||
|
local Y, M, D = ssub(str, 2, 5), ssub(str, 7, 8), ssub(str, 10, 11)
|
||||||
|
local h, m, s = ssub(str, 13, 14), ssub(str, 16, 17), ssub(str, 19, 20)
|
||||||
|
Y, M, D, h, m, s = tn(Y), tn(M), tn(D), tn(h), tn(m), tn(s)
|
||||||
|
if not (Y and M and D and h and m and s) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
return ssub(str, 21), lvl, date(Y, M, D, h, m, s)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,148 @@
|
|||||||
|
local string = require "string"
|
||||||
|
local math = require "math"
|
||||||
|
local Log = require "log"
|
||||||
|
|
||||||
|
local mod, floor, ceil, abs, pow = math.fmod, math.floor, math.ceil, math.abs, math.pow
|
||||||
|
local fmt = string.format
|
||||||
|
|
||||||
|
-- removes the decimal part of a number
|
||||||
|
local function fix(n)
|
||||||
|
n = tonumber(n)
|
||||||
|
return n and ((n > 0 and floor or ceil)(n))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function lshift(a, b)
|
||||||
|
if math.pow then
|
||||||
|
return a * math.pow(2, b)
|
||||||
|
else
|
||||||
|
return a << b
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[ RFC5424
|
||||||
|
0 Emergency: system is unusable
|
||||||
|
1 Alert: action must be taken immediately
|
||||||
|
2 Critical: critical conditions
|
||||||
|
3 Error: error conditions
|
||||||
|
4 Warning: warning conditions
|
||||||
|
5 Notice: normal but significant condition
|
||||||
|
6 Informational: informational messages
|
||||||
|
7 Debug: debug-level messages
|
||||||
|
--]]
|
||||||
|
local SEVERITY = {
|
||||||
|
EMERG = 0,
|
||||||
|
ALERT = 1,
|
||||||
|
CRIT = 2,
|
||||||
|
ERR = 3,
|
||||||
|
WARNING = 4,
|
||||||
|
NOTICE = 5,
|
||||||
|
INFO = 6,
|
||||||
|
DEBUG = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
--[[ RFC5424
|
||||||
|
0 kernel messages
|
||||||
|
1 user-level messages
|
||||||
|
2 mail system
|
||||||
|
3 system daemons
|
||||||
|
4 security/authorization messages
|
||||||
|
5 messages generated internally by syslogd
|
||||||
|
6 line printer subsystem
|
||||||
|
7 network news subsystem
|
||||||
|
8 UUCP subsystem
|
||||||
|
9 clock daemon
|
||||||
|
10 security/authorization messages
|
||||||
|
11 FTP daemon
|
||||||
|
12 NTP subsystem
|
||||||
|
13 log audit
|
||||||
|
14 log alert
|
||||||
|
15 clock daemon (note 2)
|
||||||
|
16 local use 0 (local0)
|
||||||
|
17 local use 1 (local1)
|
||||||
|
18 local use 2 (local2)
|
||||||
|
19 local use 3 (local3)
|
||||||
|
20 local use 4 (local4)
|
||||||
|
21 local use 5 (local5)
|
||||||
|
22 local use 6 (local6)
|
||||||
|
23 local use 7 (local7)
|
||||||
|
--]]
|
||||||
|
local FACILITY = {
|
||||||
|
KERN = lshift(0, 3),
|
||||||
|
USER = lshift(1, 3),
|
||||||
|
MAIL = lshift(2, 3),
|
||||||
|
DAEMON = lshift(3, 3),
|
||||||
|
AUTH = lshift(4, 3),
|
||||||
|
SYSLOG = lshift(5, 3),
|
||||||
|
LPR = lshift(6, 3),
|
||||||
|
NEWS = lshift(7, 3),
|
||||||
|
UUCP = lshift(8, 3),
|
||||||
|
CRON = lshift(9, 3),
|
||||||
|
CLKD = lshift(9, 3),
|
||||||
|
AUTHPRIV = lshift(10, 3),
|
||||||
|
FTP = lshift(11, 3),
|
||||||
|
NTP = lshift(12, 3),
|
||||||
|
SECURITY = lshift(13, 3),
|
||||||
|
AUDIT = lshift(13, 3),
|
||||||
|
CONSOLE = lshift(14, 3),
|
||||||
|
ALERT = lshift(14, 3),
|
||||||
|
CLKD2 = lshift(15, 3),
|
||||||
|
LOCAL0 = lshift(16, 3),
|
||||||
|
LOCAL1 = lshift(17, 3),
|
||||||
|
LOCAL2 = lshift(18, 3),
|
||||||
|
LOCAL3 = lshift(19, 3),
|
||||||
|
LOCAL4 = lshift(20, 3),
|
||||||
|
LOCAL5 = lshift(21, 3),
|
||||||
|
LOCAL6 = lshift(22, 3),
|
||||||
|
LOCAL7 = lshift(23, 3),
|
||||||
|
}
|
||||||
|
|
||||||
|
local LVL2SYSLOG = {
|
||||||
|
[Log.LVL.EMERG] = SEVERITY.EMERG,
|
||||||
|
[Log.LVL.ALERT] = SEVERITY.ALERT,
|
||||||
|
[Log.LVL.FATAL] = SEVERITY.CRIT,
|
||||||
|
[Log.LVL.ERROR] = SEVERITY.ERR,
|
||||||
|
[Log.LVL.WARNING] = SEVERITY.WARNING,
|
||||||
|
[Log.LVL.NOTICE] = SEVERITY.NOTICE,
|
||||||
|
[Log.LVL.INFO] = SEVERITY.INFO,
|
||||||
|
[Log.LVL.DEBUG] = SEVERITY.DEBUG,
|
||||||
|
[Log.LVL.TRACE] = SEVERITY.DEBUG,
|
||||||
|
}
|
||||||
|
|
||||||
|
local function Date2SysLog(now)
|
||||||
|
local Y, M, D = now:getdate()
|
||||||
|
local h, m, s = now:gettime()
|
||||||
|
|
||||||
|
local b = -now:getbias();
|
||||||
|
local x = abs(b);
|
||||||
|
|
||||||
|
return fmt("%.4d-%.2d-%.2dT%.2d:%.2d:%.2d%s%.2d:%.2d", Y, M, D, h, m, s, b < 0 and "-" or "+", fix(x / 60),
|
||||||
|
floor(mod(x, 60)))
|
||||||
|
end
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new(facility, host_name, app_name, procid, msgid)
|
||||||
|
if not facility then
|
||||||
|
facility = FACILITY.USER
|
||||||
|
else
|
||||||
|
facility = FACILITY[facility:upper()]
|
||||||
|
end
|
||||||
|
host_name = host_name or '-'
|
||||||
|
app_name = app_name or '-'
|
||||||
|
procid = procid or '-'
|
||||||
|
msgid = msgid or '-'
|
||||||
|
|
||||||
|
-- HOSTNAME APP-NAME PROCID MSGID
|
||||||
|
local header = host_name .. ' ' .. app_name .. ' ' .. procid .. ' ' .. msgid
|
||||||
|
|
||||||
|
return function(msg, lvl, now)
|
||||||
|
local slvl = assert(LVL2SYSLOG[lvl])
|
||||||
|
return -- HEADER
|
||||||
|
-- PRI VERSION TIMESTAMP
|
||||||
|
'<' .. slvl + facility .. '>1 ' .. Date2SysLog(now) .. ' ' .. -- HEADER STRUCTURED-DATA MSG
|
||||||
|
header .. ' - ' .. msg
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,101 @@
|
|||||||
|
local function prequire(...)
|
||||||
|
local ok, mod = pcall(require, ...)
|
||||||
|
return ok and mod, mod or nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local llthreads = prequire "llthreads2"
|
||||||
|
if not llthreads then
|
||||||
|
llthreads = require "llthreads"
|
||||||
|
end
|
||||||
|
|
||||||
|
local runstring = function(code, ...)
|
||||||
|
code = [[do
|
||||||
|
local string = require "string"
|
||||||
|
local os = require "os"
|
||||||
|
local loadstring = loadstring or load
|
||||||
|
local lua_init = os.getenv("lua_init")
|
||||||
|
if lua_init and #lua_init > 0 then
|
||||||
|
if lua_init:sub(1,1) == '@' then dofile((lua_init:sub(2)))
|
||||||
|
else assert(loadstring(lua_init))() end
|
||||||
|
end
|
||||||
|
end;]] .. code
|
||||||
|
return llthreads.new(code, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
local sleep
|
||||||
|
do
|
||||||
|
repeat
|
||||||
|
local socket = prequire "socket"
|
||||||
|
if socket then
|
||||||
|
sleep = function(ms)
|
||||||
|
socket.sleep(ms / 1000)
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local ztimer = prequire "lzmq.timer"
|
||||||
|
if ztimer then
|
||||||
|
sleep = ztimer.sleep
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
-- @todo find another way (use os.execute("sleep " .. ms)) on *nix
|
||||||
|
|
||||||
|
sleep = function()
|
||||||
|
end
|
||||||
|
break
|
||||||
|
until true
|
||||||
|
end
|
||||||
|
|
||||||
|
local Worker = [=[
|
||||||
|
(function(server, maker, logformat, ...)
|
||||||
|
local Log = require "log"
|
||||||
|
local logformat = require(logformat).new()
|
||||||
|
|
||||||
|
local loadstring = loadstring or load
|
||||||
|
local writer = assert(loadstring(maker))()
|
||||||
|
|
||||||
|
require(server).run(writer, Log.close, logformat, ...)
|
||||||
|
end)(...)
|
||||||
|
]=]
|
||||||
|
|
||||||
|
local function run_server(server, maker, logformat, ...)
|
||||||
|
if type(maker) == 'function' then
|
||||||
|
maker = string.dump(maker)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(type(server) == 'string')
|
||||||
|
assert(type(maker) == 'string')
|
||||||
|
assert(type(logformat) == 'string')
|
||||||
|
|
||||||
|
local child_thread = assert(runstring(Worker, server, maker, logformat, ...))
|
||||||
|
child_thread:start(true, true)
|
||||||
|
sleep(500)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local Z
|
||||||
|
local function run_zserver(server, maker, logformat, ctx, ...)
|
||||||
|
Z = Z or require "log.writer.net.zmq._private.compat"
|
||||||
|
|
||||||
|
if type(maker) == 'function' then
|
||||||
|
maker = string.dump(maker)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(type(server) == 'string')
|
||||||
|
assert(type(maker) == 'string')
|
||||||
|
assert(type(logformat) == 'string')
|
||||||
|
assert(Z.is_ctx(ctx))
|
||||||
|
|
||||||
|
local zthreads = assert(Z.threads)
|
||||||
|
local child_thread = assert((zthreads.run or zthreads.runstring)(ctx, Worker, server, maker, logformat, ...))
|
||||||
|
child_thread:start(true)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
M.run = run_server
|
||||||
|
M.zrun = run_zserver
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
local server = require "log.writer.async.server.lane"
|
||||||
|
local packer = require "log.logformat.proxy.pack"
|
||||||
|
|
||||||
|
local function create_writer(channel, maker)
|
||||||
|
if maker then
|
||||||
|
server.run(channel, maker, "log.logformat.default")
|
||||||
|
end
|
||||||
|
|
||||||
|
local queue = server.channel()
|
||||||
|
local pack = packer.pack
|
||||||
|
|
||||||
|
return function(fmt, ...)
|
||||||
|
local msg = pack(...)
|
||||||
|
queue:send(channel, msg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
M.new = create_writer
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
local on_lane_create = function()
|
||||||
|
local loadstring = loadstring or load
|
||||||
|
local lua_init = os.getenv("lua_init")
|
||||||
|
if lua_init and #lua_init > 0 then
|
||||||
|
if lua_init:sub(1, 1) == '@' then
|
||||||
|
dofile(lua_init:sub(2))
|
||||||
|
else
|
||||||
|
assert(loadstring(lua_init))()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local LOG = require "log"
|
||||||
|
local lanes = require"lanes".configure {
|
||||||
|
with_timers = false,
|
||||||
|
on_state_create = on_lane_create,
|
||||||
|
}
|
||||||
|
local pack = require"log.logformat.proxy.pack".pack
|
||||||
|
|
||||||
|
local queue
|
||||||
|
|
||||||
|
local function context()
|
||||||
|
queue = queue or assert(lanes.linda())
|
||||||
|
return queue
|
||||||
|
end
|
||||||
|
|
||||||
|
local function log_thread_fn(maker, logformat, channel)
|
||||||
|
local log_packer = require "log.logformat.proxy.pack"
|
||||||
|
local logformat = require(logformat).new()
|
||||||
|
local unpack = log_packer.unpack
|
||||||
|
|
||||||
|
local loadstring = loadstring or load
|
||||||
|
local writer = assert(assert(loadstring(maker))())
|
||||||
|
while (true) do
|
||||||
|
local key, val = queue:receive(1.0, channel)
|
||||||
|
-- print(maker, channel, key, val)
|
||||||
|
if not (key and val) then
|
||||||
|
key, val = nil, 'timeout'
|
||||||
|
end
|
||||||
|
if key then
|
||||||
|
local msg, lvl, now = unpack(val)
|
||||||
|
if msg and lvl and now then
|
||||||
|
writer(logformat, msg, lvl, now)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if val ~= 'timeout' then
|
||||||
|
io.stderror:write('lane_logger: ', val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function start_log_thread(...)
|
||||||
|
local fn = assert(lanes.gen("*", log_thread_fn))
|
||||||
|
return assert(fn(...))
|
||||||
|
end
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
M.run = function(channel, maker, logformat)
|
||||||
|
logformat = logformat or "log.logformat.default"
|
||||||
|
context() -- init context
|
||||||
|
local child_thread = start_log_thread(maker, logformat, channel)
|
||||||
|
LOG.add_cleanup(function()
|
||||||
|
child_thread:cancel(60)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
M.channel = context
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
local server = require "log.writer.async._private.server"
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
M.run = function(host, port, maker, logformat)
|
||||||
|
logformat = logformat or "log.logformat.default"
|
||||||
|
server.run("log.writer.net.server.udp", maker, logformat, host, port)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
local Z = require "log.writer.net.zmq._private.compat"
|
||||||
|
local IMPL = require "log.writer.net.zmq._private.impl"
|
||||||
|
local server = require "log.writer.async._private.server"
|
||||||
|
|
||||||
|
local zmq, ETERM, zstrerror, zassert, zrecv = Z.zmq, Z.ETERM, Z.strerror, Z.assert, Z.recv
|
||||||
|
|
||||||
|
local function rand_str(n)
|
||||||
|
math.randomseed(os.time())
|
||||||
|
local str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||||
|
local res = ''
|
||||||
|
for i = 1, n do
|
||||||
|
local n = math.random(1, #str)
|
||||||
|
res = res .. str:sub(n, n)
|
||||||
|
end
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
|
local function create_server(ctx, addr, maker, logformat)
|
||||||
|
if ctx and not Z.is_ctx(ctx) then
|
||||||
|
ctx, addr, maker, logformat = nil, ctx, addr, maker
|
||||||
|
end
|
||||||
|
logformat = logformat or "log.logformat.default"
|
||||||
|
|
||||||
|
ctx = IMPL.context(ctx)
|
||||||
|
|
||||||
|
if maker then
|
||||||
|
local addr_sync = 'inproc://' .. rand_str(15)
|
||||||
|
local skt_sync = zassert(ctx:socket(zmq.PAIR))
|
||||||
|
zassert(skt_sync:bind(addr_sync))
|
||||||
|
server.zrun("log.writer.net.server.zmq", maker, logformat, ctx, false, 'PULL', addr, addr_sync)
|
||||||
|
zassert(skt_sync:recv())
|
||||||
|
skt_sync:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
M.run = create_server
|
||||||
|
|
||||||
|
M.context = IMPL.context
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
local server = require "log.writer.async.server.udp"
|
||||||
|
|
||||||
|
local function create_writer(host, port, maker)
|
||||||
|
if maker then
|
||||||
|
server.run(host, port, maker, "log.logformat.default")
|
||||||
|
end
|
||||||
|
|
||||||
|
local writer = require"log.writer.format".new(require"log.logformat.proxy".new(),
|
||||||
|
require"log.writer.net.udp".new(host, port))
|
||||||
|
|
||||||
|
return writer
|
||||||
|
end
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
M.new = create_writer
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
local Z = require "log.writer.net.zmq._private.compat"
|
||||||
|
local server = require "log.writer.async.server.zmq"
|
||||||
|
|
||||||
|
local function create_writer(ctx, addr, maker)
|
||||||
|
if ctx and not Z.is_ctx(ctx) then
|
||||||
|
ctx, addr, maker = nil, ctx, addr
|
||||||
|
end
|
||||||
|
|
||||||
|
if maker then
|
||||||
|
server.run(ctx, addr, maker, "log.logformat.default")
|
||||||
|
end
|
||||||
|
|
||||||
|
return require"log.writer.format".new(require"log.logformat.proxy".new(),
|
||||||
|
require"log.writer.net.zmq.push".new(ctx, addr))
|
||||||
|
end
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
M.new = create_writer
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1 @@
|
|||||||
|
return require "log.writer.stdout"
|
||||||
@ -0,0 +1,334 @@
|
|||||||
|
local io = require "io"
|
||||||
|
local os = require "os"
|
||||||
|
local Log = require "log"
|
||||||
|
|
||||||
|
local make_attr, color_writeln, COLORS
|
||||||
|
|
||||||
|
if not COLORS then -- afx cio
|
||||||
|
local ok, cio = pcall(require, "cio")
|
||||||
|
if ok then
|
||||||
|
COLORS = {
|
||||||
|
BLACK = 'n',
|
||||||
|
BLUE = 'b',
|
||||||
|
GREEN = 'g',
|
||||||
|
CYAN = 'c',
|
||||||
|
RED = 'r',
|
||||||
|
MAGENTA = 'm',
|
||||||
|
BROWN = 'y',
|
||||||
|
LIGHTGRAY = 'w',
|
||||||
|
DARKGRAY = 'n+',
|
||||||
|
LIGHTBLUE = 'b+',
|
||||||
|
LIGHTGREEN = 'g+',
|
||||||
|
LIGHTCYAN = 'c+',
|
||||||
|
LIGHTRED = 'r+',
|
||||||
|
LIGHTMAGENTA = 'm+',
|
||||||
|
YELLOW = 'y+',
|
||||||
|
WHITE = 'w+',
|
||||||
|
}
|
||||||
|
make_attr = function(F, B)
|
||||||
|
return F .. '/' .. B
|
||||||
|
end
|
||||||
|
color_writeln = function(attr, text)
|
||||||
|
cio.textattr(attr)
|
||||||
|
cio.writeln(text or "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not COLORS then -- conio
|
||||||
|
local ok, conio = pcall(require, "conio")
|
||||||
|
if ok then
|
||||||
|
COLORS = {
|
||||||
|
BLACK = conio.COLOR_BLACK,
|
||||||
|
BLUE = conio.COLOR_BLUE,
|
||||||
|
GREEN = conio.COLOR_GREEN,
|
||||||
|
CYAN = conio.COLOR_CYAN,
|
||||||
|
RED = conio.COLOR_RED,
|
||||||
|
MAGENTA = conio.COLOR_MAGENTA,
|
||||||
|
BROWN = conio.COLOR_BROWN,
|
||||||
|
LIGHTGRAY = conio.COLOR_LIGHTGRAY,
|
||||||
|
DARKGRAY = conio.COLOR_DARKGRAY,
|
||||||
|
LIGHTBLUE = conio.COLOR_LIGHTBLUE,
|
||||||
|
LIGHTGREEN = conio.COLOR_LIGHTGREEN,
|
||||||
|
LIGHTCYAN = conio.COLOR_LIGHTCYAN,
|
||||||
|
LIGHTRED = conio.COLOR_LIGHTRED,
|
||||||
|
LIGHTMAGENTA = conio.COLOR_LIGHTMAGENTA,
|
||||||
|
YELLOW = conio.COLOR_YELLOW,
|
||||||
|
WHITE = conio.COLOR_WHITE,
|
||||||
|
}
|
||||||
|
make_attr = function(F, B)
|
||||||
|
return {F, B}
|
||||||
|
end
|
||||||
|
color_writeln = function(attr, text)
|
||||||
|
conio.textcolor(attr[1])
|
||||||
|
conio.textbackground(attr[2])
|
||||||
|
conio.cputs((text or "") .. '\n')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not COLORS then -- ansicolors
|
||||||
|
local IS_WINDOWS = (package.config:sub(1, 1) == '\\')
|
||||||
|
if IS_WINDOWS and os.getenv("ANSICON") then
|
||||||
|
IS_WINDOWS = nil
|
||||||
|
end
|
||||||
|
local ok, c
|
||||||
|
if not IS_WINDOWS then
|
||||||
|
ok, c = pcall(require, "ansicolors")
|
||||||
|
end
|
||||||
|
if ok and (type(c) == 'table') then
|
||||||
|
if c.black then -- version above 1.0.2
|
||||||
|
COLORS = {
|
||||||
|
BLACK = 1,
|
||||||
|
BLUE = 2,
|
||||||
|
GREEN = 3,
|
||||||
|
CYAN = 4,
|
||||||
|
RED = 5,
|
||||||
|
MAGENTA = 6,
|
||||||
|
BROWN = 7,
|
||||||
|
LIGHTGRAY = 8,
|
||||||
|
DARKGRAY = 9,
|
||||||
|
LIGHTBLUE = 10,
|
||||||
|
LIGHTGREEN = 11,
|
||||||
|
LIGHTCYAN = 12,
|
||||||
|
LIGHTRED = 13,
|
||||||
|
LIGHTMAGENTA = 14,
|
||||||
|
YELLOW = 15,
|
||||||
|
WHITE = 16,
|
||||||
|
}
|
||||||
|
local reset = tostring(c.reset)
|
||||||
|
local fore = {
|
||||||
|
[COLORS.BLACK] = c.black,
|
||||||
|
[COLORS.BLUE] = c.blue,
|
||||||
|
[COLORS.GREEN] = c.green,
|
||||||
|
[COLORS.CYAN] = c.cyan,
|
||||||
|
[COLORS.RED] = c.red,
|
||||||
|
[COLORS.MAGENTA] = c.magenta,
|
||||||
|
[COLORS.BROWN] = c.yellow,
|
||||||
|
[COLORS.LIGHTGRAY] = c.white,
|
||||||
|
[COLORS.DARKGRAY] = c.black .. c.bright,
|
||||||
|
[COLORS.LIGHTBLUE] = c.blue .. c.bright,
|
||||||
|
[COLORS.LIGHTGREEN] = c.green .. c.bright,
|
||||||
|
[COLORS.LIGHTCYAN] = c.cyan .. c.bright,
|
||||||
|
[COLORS.LIGHTRED] = c.red .. c.bright,
|
||||||
|
[COLORS.LIGHTMAGENTA] = c.magenta .. c.bright,
|
||||||
|
[COLORS.YELLOW] = c.yellow .. c.bright,
|
||||||
|
[COLORS.WHITE] = c.white .. c.bright,
|
||||||
|
}
|
||||||
|
|
||||||
|
local back = {
|
||||||
|
[COLORS.BLACK] = c.onblack,
|
||||||
|
[COLORS.BLUE] = c.onblue,
|
||||||
|
[COLORS.GREEN] = c.ongreen,
|
||||||
|
[COLORS.CYAN] = c.oncyan,
|
||||||
|
[COLORS.RED] = c.onred,
|
||||||
|
[COLORS.MAGENTA] = c.onmagenta,
|
||||||
|
[COLORS.BROWN] = c.onyellow,
|
||||||
|
[COLORS.LIGHTGRAY] = c.onwhite,
|
||||||
|
[COLORS.DARKGRAY] = c.onblack .. c.bright,
|
||||||
|
[COLORS.LIGHTBLUE] = c.onblue .. c.bright,
|
||||||
|
[COLORS.LIGHTGREEN] = c.ongreen .. c.bright,
|
||||||
|
[COLORS.LIGHTCYAN] = c.oncyan .. c.bright,
|
||||||
|
[COLORS.LIGHTRED] = c.onred .. c.bright,
|
||||||
|
[COLORS.LIGHTMAGENTA] = c.onmagenta .. c.bright,
|
||||||
|
[COLORS.YELLOW] = c.onyellow .. c.bright,
|
||||||
|
[COLORS.WHITE] = c.onwhite .. c.bright,
|
||||||
|
}
|
||||||
|
|
||||||
|
make_attr = function(F, B)
|
||||||
|
return fore[F] .. back[B]
|
||||||
|
end
|
||||||
|
color_writeln = function(attr, text)
|
||||||
|
io.write(attr, text, reset, '\n')
|
||||||
|
end
|
||||||
|
elseif c.noReset then -- 1.0.2
|
||||||
|
COLORS = {
|
||||||
|
BLACK = 'black',
|
||||||
|
BLUE = 'blue',
|
||||||
|
GREEN = 'green',
|
||||||
|
CYAN = 'cyan',
|
||||||
|
RED = 'red',
|
||||||
|
MAGENTA = 'magenta',
|
||||||
|
BROWN = 'yellow',
|
||||||
|
LIGHTGRAY = 'white',
|
||||||
|
DARKGRAY = 'bright black',
|
||||||
|
LIGHTBLUE = 'bright blue',
|
||||||
|
LIGHTGREEN = 'bright green',
|
||||||
|
LIGHTCYAN = 'bright cyan',
|
||||||
|
LIGHTRED = 'bright red',
|
||||||
|
LIGHTMAGENTA = 'bright magenta',
|
||||||
|
YELLOW = 'bright yellow',
|
||||||
|
WHITE = 'bright white',
|
||||||
|
}
|
||||||
|
|
||||||
|
make_attr = function(F, B)
|
||||||
|
return c.noReset("%{" .. F .. " " .. B .. "bg}")
|
||||||
|
end
|
||||||
|
local RESET = c.noReset("%{reset}")
|
||||||
|
color_writeln = function(attr, text)
|
||||||
|
io.write(attr, (text), RESET, '\n')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if ok and (type(c) == 'function') then
|
||||||
|
COLORS = {
|
||||||
|
BLACK = 1,
|
||||||
|
BLUE = 2,
|
||||||
|
GREEN = 3,
|
||||||
|
CYAN = 4,
|
||||||
|
RED = 5,
|
||||||
|
MAGENTA = 6,
|
||||||
|
BROWN = 7,
|
||||||
|
LIGHTGRAY = 8,
|
||||||
|
DARKGRAY = 9,
|
||||||
|
LIGHTBLUE = 10,
|
||||||
|
LIGHTGREEN = 11,
|
||||||
|
LIGHTCYAN = 12,
|
||||||
|
LIGHTRED = 13,
|
||||||
|
LIGHTMAGENTA = 14,
|
||||||
|
YELLOW = 15,
|
||||||
|
WHITE = 16,
|
||||||
|
}
|
||||||
|
local fore = {
|
||||||
|
[COLORS.BLACK] = "%{black}",
|
||||||
|
[COLORS.BLUE] = "%{blue}",
|
||||||
|
[COLORS.GREEN] = "%{green}",
|
||||||
|
[COLORS.CYAN] = "%{cyan}",
|
||||||
|
[COLORS.RED] = "%{red}",
|
||||||
|
[COLORS.MAGENTA] = "%{magenta}",
|
||||||
|
[COLORS.BROWN] = "%{yellow}",
|
||||||
|
[COLORS.LIGHTGRAY] = "%{white}",
|
||||||
|
[COLORS.DARKGRAY] = "%{black}" .. "%{bright}",
|
||||||
|
[COLORS.LIGHTBLUE] = "%{blue}" .. "%{bright}",
|
||||||
|
[COLORS.LIGHTGREEN] = "%{green}" .. "%{bright}",
|
||||||
|
[COLORS.LIGHTCYAN] = "%{cyan}" .. "%{bright}",
|
||||||
|
[COLORS.LIGHTRED] = "%{red}" .. "%{bright}",
|
||||||
|
[COLORS.LIGHTMAGENTA] = "%{magenta}" .. "%{bright}",
|
||||||
|
[COLORS.YELLOW] = "%{yellow}" .. "%{bright}",
|
||||||
|
[COLORS.WHITE] = "%{white}" .. "%{bright}",
|
||||||
|
}
|
||||||
|
local back = {
|
||||||
|
[COLORS.BLACK] = "%{blackbg}",
|
||||||
|
[COLORS.BLUE] = "%{bluebg}",
|
||||||
|
[COLORS.GREEN] = "%{greenbg}",
|
||||||
|
[COLORS.CYAN] = "%{cyanbg}",
|
||||||
|
[COLORS.RED] = "%{redbg}",
|
||||||
|
[COLORS.MAGENTA] = "%{magentabg}",
|
||||||
|
[COLORS.BROWN] = "%{yellowbg}",
|
||||||
|
[COLORS.LIGHTGRAY] = "%{whitebg}",
|
||||||
|
[COLORS.DARKGRAY] = "%{blackbg}" .. "%{bright}",
|
||||||
|
[COLORS.LIGHTBLUE] = "%{bluebg}" .. "%{bright}",
|
||||||
|
[COLORS.LIGHTGREEN] = "%{greenbg}" .. "%{bright}",
|
||||||
|
[COLORS.LIGHTCYAN] = "%{cyanbg}" .. "%{bright}",
|
||||||
|
[COLORS.LIGHTRED] = "%{redbg}" .. "%{bright}",
|
||||||
|
[COLORS.LIGHTMAGENTA] = "%{magentabg}" .. "%{bright}",
|
||||||
|
[COLORS.YELLOW] = "%{yellowbg}" .. "%{bright}",
|
||||||
|
[COLORS.WHITE] = "%{whitebg}" .. "%{bright}",
|
||||||
|
}
|
||||||
|
make_attr = function(F, B)
|
||||||
|
return fore[F] .. back[B]
|
||||||
|
end
|
||||||
|
color_writeln = function(attr, text)
|
||||||
|
io.write(c(attr .. text), '\n')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not COLORS then
|
||||||
|
local ok, term = pcall(require, "term")
|
||||||
|
if ok then
|
||||||
|
COLORS = {
|
||||||
|
BLACK = 1,
|
||||||
|
BLUE = 2,
|
||||||
|
GREEN = 3,
|
||||||
|
CYAN = 4,
|
||||||
|
RED = 5,
|
||||||
|
MAGENTA = 6,
|
||||||
|
BROWN = 7,
|
||||||
|
LIGHTGRAY = 8,
|
||||||
|
DARKGRAY = 9,
|
||||||
|
LIGHTBLUE = 10,
|
||||||
|
LIGHTGREEN = 11,
|
||||||
|
LIGHTCYAN = 12,
|
||||||
|
LIGHTRED = 13,
|
||||||
|
LIGHTMAGENTA = 14,
|
||||||
|
YELLOW = 15,
|
||||||
|
WHITE = 16,
|
||||||
|
}
|
||||||
|
|
||||||
|
local c = term.colors
|
||||||
|
local reset = c.reset
|
||||||
|
local fore = {
|
||||||
|
[COLORS.BLACK] = c.black,
|
||||||
|
[COLORS.BLUE] = c.blue,
|
||||||
|
[COLORS.GREEN] = c.green,
|
||||||
|
[COLORS.CYAN] = c.cyan,
|
||||||
|
[COLORS.RED] = c.red,
|
||||||
|
[COLORS.MAGENTA] = c.magenta,
|
||||||
|
[COLORS.BROWN] = c.yellow,
|
||||||
|
[COLORS.LIGHTGRAY] = c.white,
|
||||||
|
[COLORS.DARKGRAY] = c.black .. c.bright,
|
||||||
|
[COLORS.LIGHTBLUE] = c.blue .. c.bright,
|
||||||
|
[COLORS.LIGHTGREEN] = c.green .. c.bright,
|
||||||
|
[COLORS.LIGHTCYAN] = c.cyan .. c.bright,
|
||||||
|
[COLORS.LIGHTRED] = c.red .. c.bright,
|
||||||
|
[COLORS.LIGHTMAGENTA] = c.magenta .. c.bright,
|
||||||
|
[COLORS.YELLOW] = c.yellow .. c.bright,
|
||||||
|
[COLORS.WHITE] = c.white .. c.bright,
|
||||||
|
}
|
||||||
|
|
||||||
|
local back = {
|
||||||
|
[COLORS.BLACK] = c.onblack,
|
||||||
|
[COLORS.BLUE] = c.onblue,
|
||||||
|
[COLORS.GREEN] = c.ongreen,
|
||||||
|
[COLORS.CYAN] = c.oncyan,
|
||||||
|
[COLORS.RED] = c.onred,
|
||||||
|
[COLORS.MAGENTA] = c.onmagenta,
|
||||||
|
[COLORS.BROWN] = c.onyellow,
|
||||||
|
[COLORS.LIGHTGRAY] = c.onwhite,
|
||||||
|
[COLORS.DARKGRAY] = c.onblack .. c.bright,
|
||||||
|
[COLORS.LIGHTBLUE] = c.onblue .. c.bright,
|
||||||
|
[COLORS.LIGHTGREEN] = c.ongreen .. c.bright,
|
||||||
|
[COLORS.LIGHTCYAN] = c.oncyan .. c.bright,
|
||||||
|
[COLORS.LIGHTRED] = c.onred .. c.bright,
|
||||||
|
[COLORS.LIGHTMAGENTA] = c.onmagenta .. c.bright,
|
||||||
|
[COLORS.YELLOW] = c.onyellow .. c.bright,
|
||||||
|
[COLORS.WHITE] = c.onwhite .. c.bright,
|
||||||
|
}
|
||||||
|
|
||||||
|
make_attr = function(F, B)
|
||||||
|
return fore[F] .. back[B]
|
||||||
|
end
|
||||||
|
color_writeln = function(attr, text)
|
||||||
|
io.write(attr .. text .. reset, '\n')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not COLORS then -- fallback to console
|
||||||
|
return require "log.writer.console"
|
||||||
|
end
|
||||||
|
|
||||||
|
local colors = {
|
||||||
|
[Log.LVL.EMERG] = make_attr(COLORS.WHITE, COLORS.LIGHTRED),
|
||||||
|
[Log.LVL.ALERT] = make_attr(COLORS.BLUE, COLORS.LIGHTRED),
|
||||||
|
[Log.LVL.FATAL] = make_attr(COLORS.BLACK, COLORS.LIGHTRED),
|
||||||
|
[Log.LVL.ERROR] = make_attr(COLORS.LIGHTRED, COLORS.BLACK),
|
||||||
|
[Log.LVL.WARNING] = make_attr(COLORS.LIGHTMAGENTA, COLORS.BLACK),
|
||||||
|
[Log.LVL.NOTICE] = make_attr(COLORS.LIGHTCYAN, COLORS.BLACK),
|
||||||
|
[Log.LVL.INFO] = make_attr(COLORS.WHITE, COLORS.BLACK),
|
||||||
|
[Log.LVL.DEBUG] = make_attr(COLORS.YELLOW, COLORS.BLACK),
|
||||||
|
[Log.LVL.TRACE] = make_attr(COLORS.LIGHTGREEN, COLORS.BLACK),
|
||||||
|
}
|
||||||
|
|
||||||
|
local function console_writer(fmt, msg, lvl, now)
|
||||||
|
color_writeln(colors[lvl], fmt(msg, lvl, now))
|
||||||
|
end
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new()
|
||||||
|
return console_writer
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
local LOG = require "log"
|
||||||
|
local file_logger = require "log.writer.file.private.impl"
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new(opt)
|
||||||
|
local logger = file_logger:new(opt)
|
||||||
|
LOG.add_cleanup(function()
|
||||||
|
logger:close()
|
||||||
|
end)
|
||||||
|
|
||||||
|
return function(fmt, msg, lvl, now)
|
||||||
|
logger:write((fmt(msg, lvl, now)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
local file = require "log.writer.file"
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new(log_dir, log_name, max_rows)
|
||||||
|
return file.new {
|
||||||
|
log_dir = log_dir,
|
||||||
|
log_name = log_name,
|
||||||
|
max_rows = max_rows,
|
||||||
|
by_day = true,
|
||||||
|
close_file = false,
|
||||||
|
flush_interval = 1,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
|
|
||||||
@ -0,0 +1,484 @@
|
|||||||
|
local Log = require "log"
|
||||||
|
local io = require "io"
|
||||||
|
local os = require "os"
|
||||||
|
local string = require "string"
|
||||||
|
local date = require "date"
|
||||||
|
local lfs = require "lfs"
|
||||||
|
|
||||||
|
local DIR_SEP = package.config:sub(1, 1)
|
||||||
|
local IS_WINDOWS = DIR_SEP == '\\'
|
||||||
|
|
||||||
|
local function remove_dir_end(str)
|
||||||
|
return (string.gsub(str, '[\\/]+$', ''))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ensure_dir_end(str)
|
||||||
|
return remove_dir_end(str) .. DIR_SEP
|
||||||
|
end
|
||||||
|
|
||||||
|
local function path_normolize_sep(P)
|
||||||
|
return (string.gsub(P, '\\', DIR_SEP):gsub('/', DIR_SEP))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function path_fullpath(P)
|
||||||
|
P = path_normolize_sep(P)
|
||||||
|
local ch1, ch2 = P:sub(1, 1), P:sub(2, 2)
|
||||||
|
if IS_WINDOWS then
|
||||||
|
if ch1 == DIR_SEP then -- \temp => c:\temp
|
||||||
|
local cwd = lfs.currentdir()
|
||||||
|
local disk = cwd:sub(1, 2)
|
||||||
|
P = disk .. P
|
||||||
|
elseif ch1 == '~' then -- ~\temp
|
||||||
|
local base = os.getenv('USERPROFILE') or (os.getenv('HOMEDRIVE') .. os.getenv('HOMEPATH'))
|
||||||
|
P = ((ch2 == DIR_SEP) and remove_dir_end(base) or ensure_dir_end(base)) .. string.sub(P, 2)
|
||||||
|
elseif ch2 ~= ':' then
|
||||||
|
P = ensure_dir_end(lfs.currentdir()) .. P
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if ch1 == '~' then -- ~/temp
|
||||||
|
local base = os.getenv('HOME')
|
||||||
|
P = ((ch2 == DIR_SEP) and remove_dir_end(base) or ensure_dir_end(base)) .. string.sub(P, 2)
|
||||||
|
else
|
||||||
|
if P:sub(1, 1) ~= '/' then
|
||||||
|
P = ensure_dir_end(lfs.currentdir()) .. P
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
P = string.gsub(P, DIR_SEP .. '%.' .. DIR_SEP, DIR_SEP):gsub(DIR_SEP .. DIR_SEP, DIR_SEP)
|
||||||
|
while true do
|
||||||
|
local first, last = string.find(P, DIR_SEP .. "[^" .. DIR_SEP .. "]+" .. DIR_SEP .. '%.%.' .. DIR_SEP)
|
||||||
|
if not first then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
P = string.sub(P, 1, first) .. string.sub(P, last + 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
return P
|
||||||
|
end
|
||||||
|
|
||||||
|
local function attrib(P, ...)
|
||||||
|
if IS_WINDOWS then
|
||||||
|
if #P < 4 and P:sub(2, 2) == ':' then
|
||||||
|
P = ensure_dir_end(P) -- c: => c:\
|
||||||
|
else
|
||||||
|
P = remove_dir_end(P) -- c:\temp\ => c:\temp
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return lfs.attributes(P, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function path_exists(P)
|
||||||
|
return attrib(P, 'mode') ~= nil and P
|
||||||
|
end
|
||||||
|
|
||||||
|
local function path_isdir(P)
|
||||||
|
return attrib(P, 'mode') == 'directory' and P
|
||||||
|
end
|
||||||
|
|
||||||
|
local function path_mkdir(P)
|
||||||
|
local P = path_fullpath(P)
|
||||||
|
local p = ''
|
||||||
|
|
||||||
|
for str in string.gmatch(ensure_dir_end(P), '.-' .. DIR_SEP) do
|
||||||
|
p = p .. str
|
||||||
|
if path_exists(p) then
|
||||||
|
if not path_isdir(p) then
|
||||||
|
return nil, 'can not create ' .. p
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local ok, err = lfs.mkdir(remove_dir_end(p))
|
||||||
|
if not ok then
|
||||||
|
return nil, err .. ' ' .. p
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function path_getctime(P)
|
||||||
|
return attrib(P, 'change')
|
||||||
|
end
|
||||||
|
|
||||||
|
local function path_getmtime(P)
|
||||||
|
return attrib(P, 'modification')
|
||||||
|
end
|
||||||
|
|
||||||
|
local function path_getatime(P)
|
||||||
|
return attrib(P, 'access')
|
||||||
|
end
|
||||||
|
|
||||||
|
local function path_getsize(P)
|
||||||
|
return attrib(P, 'size')
|
||||||
|
end
|
||||||
|
|
||||||
|
local function path_getrows(P)
|
||||||
|
local f, err = io.open(P, "r")
|
||||||
|
if not f then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
local count = 0
|
||||||
|
for _ in f:lines() do
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
|
f:close()
|
||||||
|
return count
|
||||||
|
end
|
||||||
|
|
||||||
|
local function path_remove(P)
|
||||||
|
return os.remove(P)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function path_rename(from, to)
|
||||||
|
path_remove(to)
|
||||||
|
return os.rename(from, to)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function reset_out(FileName, rewrite)
|
||||||
|
local END_OF_LINE = '\n'
|
||||||
|
local FILE_APPEND = 'a'
|
||||||
|
|
||||||
|
if rewrite then
|
||||||
|
local FILE_REWRITE = 'w+'
|
||||||
|
local f, err = io.open(FileName, FILE_REWRITE);
|
||||||
|
if not f then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
f:close();
|
||||||
|
end
|
||||||
|
|
||||||
|
return function(msg)
|
||||||
|
local f, err = io.open(FileName, FILE_APPEND)
|
||||||
|
if not f then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
f:write(msg, END_OF_LINE)
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function make_no_close_reset(flush_interval)
|
||||||
|
return function(FileName, rewrite)
|
||||||
|
local END_OF_LINE = '\n'
|
||||||
|
local FILE_APPEND = 'a'
|
||||||
|
|
||||||
|
if rewrite then
|
||||||
|
local FILE_REWRITE = 'w+'
|
||||||
|
local f, err = io.open(FileName, FILE_REWRITE);
|
||||||
|
if not f then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
local f, err = io.open(FileName, FILE_APPEND);
|
||||||
|
if not f then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
|
||||||
|
local writer
|
||||||
|
if flush_interval then
|
||||||
|
local flush_interval, counter = flush_interval, 0
|
||||||
|
writer = function(msg)
|
||||||
|
f:write(msg, END_OF_LINE)
|
||||||
|
counter = counter + 1
|
||||||
|
if counter >= flush_interval then
|
||||||
|
f:flush()
|
||||||
|
counter = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
writer = function(msg)
|
||||||
|
f:write(msg, END_OF_LINE)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return writer, function()
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function split_ext(fname)
|
||||||
|
local s1, s2 = string.match(fname, '([^\\/]*)([.][^.\\/]*)$')
|
||||||
|
if s1 then
|
||||||
|
return s1, s2
|
||||||
|
end
|
||||||
|
s1 = string.match(fname, '([^\\/]+)$')
|
||||||
|
if s1 then
|
||||||
|
return s1, ''
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function assert_2(f1, f2, v1, v2)
|
||||||
|
assert(f1 == v1, string.format("Expected '%s' got '%s'", tostring(f1), tostring(v1)))
|
||||||
|
assert(f2 == v2, string.format("Expected '%s' got '%s'", tostring(f2), tostring(v2)))
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_2("events", ".log", split_ext("events.log"))
|
||||||
|
assert_2("events", '', split_ext("events"))
|
||||||
|
assert_2(nil, nil, split_ext("events\\"))
|
||||||
|
assert_2('', '.log', split_ext("events\\.log"))
|
||||||
|
assert_2('log', '', split_ext("events\\log"))
|
||||||
|
|
||||||
|
local file_logger = {}
|
||||||
|
|
||||||
|
local FILE_LOG_DATE_FMT = "%Y%m%d"
|
||||||
|
local EOL_SIZE = IS_WINDOWS and 2 or 1
|
||||||
|
|
||||||
|
local function get_file_date(fname)
|
||||||
|
local mdate = path_getmtime(fname)
|
||||||
|
if mdate then
|
||||||
|
mdate = date(mdate):tolocal()
|
||||||
|
else
|
||||||
|
mdate = date()
|
||||||
|
end
|
||||||
|
return mdate:fmt(FILE_LOG_DATE_FMT)
|
||||||
|
end
|
||||||
|
|
||||||
|
function file_logger:close()
|
||||||
|
if self.private_.logger and self.private_.logger_close then
|
||||||
|
self.private_.logger_close()
|
||||||
|
end
|
||||||
|
self.private_.logger = nil
|
||||||
|
self.private_.logger_close = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function file_logger:open()
|
||||||
|
local full_name = self:current_name()
|
||||||
|
|
||||||
|
local logger, err = self.private_.reset_out(full_name)
|
||||||
|
if not logger then
|
||||||
|
return nil, string.format("can not create logger for file '%s':", full_name, err)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.private_.logger = logger
|
||||||
|
self.private_.logger_close = err
|
||||||
|
self.private_.log_date = os.date(FILE_LOG_DATE_FMT)
|
||||||
|
self.private_.log_rows = 0
|
||||||
|
self.private_.log_size = 0
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function file_logger:current_name()
|
||||||
|
return self.private_.log_dir .. self.private_.log_name
|
||||||
|
end
|
||||||
|
|
||||||
|
function file_logger:archive_roll_name(i)
|
||||||
|
return self.private_.log_dir .. string.format("%s.%.5d.log", self.private_.arc_pfx, i)
|
||||||
|
end
|
||||||
|
|
||||||
|
function file_logger:archive_date_name(d, i)
|
||||||
|
return self.private_.log_dir .. string.format("%s.%s.%.5d.log", self.private_.arc_pfx, d, i)
|
||||||
|
end
|
||||||
|
|
||||||
|
function file_logger:reset_log_by_roll()
|
||||||
|
self:close()
|
||||||
|
|
||||||
|
local full_name = self:current_name()
|
||||||
|
local first_name = self:archive_roll_name(1)
|
||||||
|
|
||||||
|
-- we must "free" space for current file
|
||||||
|
if path_exists(first_name) then
|
||||||
|
for i = self.private_.roll_count - 1, 1, -1 do
|
||||||
|
local fname1 = self:archive_roll_name(i)
|
||||||
|
local fname2 = self:archive_roll_name(i + 1)
|
||||||
|
path_rename(fname1, fname2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if path_exists(full_name) then
|
||||||
|
local ok, err = path_rename(full_name, first_name)
|
||||||
|
if not ok then
|
||||||
|
return nil, string.format("can not rename '%s' to '%s' : %s", full_name, first_name, err or '')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self:open()
|
||||||
|
end
|
||||||
|
|
||||||
|
function file_logger:next_date_name(log_date)
|
||||||
|
local id = self.private_.id
|
||||||
|
|
||||||
|
local fname = self:archive_date_name(log_date, id)
|
||||||
|
while path_exists(fname) do
|
||||||
|
id = id + 1
|
||||||
|
fname = self:archive_date_name(log_date, id)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.private_.id = id
|
||||||
|
return fname
|
||||||
|
end
|
||||||
|
|
||||||
|
function file_logger:reset_log_by_date(log_date)
|
||||||
|
self:close()
|
||||||
|
|
||||||
|
local full_name = self:current_name()
|
||||||
|
if path_exists(full_name) then -- previews file
|
||||||
|
log_date = log_date or get_file_date(full_name)
|
||||||
|
local next_fname = self:next_date_name(log_date)
|
||||||
|
local ok, err = path_rename(full_name, next_fname)
|
||||||
|
if not ok then
|
||||||
|
return nil, string.format("can not rename '%s' to '%s' : ", full_name, next_fname, err or '')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self:open()
|
||||||
|
end
|
||||||
|
|
||||||
|
function file_logger:reset_log(...)
|
||||||
|
if self.private_.roll_count then
|
||||||
|
return self:reset_log_by_roll(...)
|
||||||
|
end
|
||||||
|
return self:reset_log_by_date(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function file_logger:check()
|
||||||
|
if self.private_.by_day then
|
||||||
|
local now = os.date(FILE_LOG_DATE_FMT)
|
||||||
|
if self.private_.log_date ~= now then
|
||||||
|
local ok, err = self:reset_log_by_date(self.private_.log_date)
|
||||||
|
self.private_.id = 1
|
||||||
|
return ok, err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.private_.max_rows and (self.private_.log_rows >= self.private_.max_rows) then
|
||||||
|
return self:reset_log()
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.private_.max_size and (self.private_.log_size >= self.private_.max_size) then
|
||||||
|
return self:reset_log()
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function file_logger:write(msg)
|
||||||
|
local ok, err = self:check()
|
||||||
|
if not ok then
|
||||||
|
io.stderr:write("logger error: ", err, '\n')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
self.private_.logger(msg)
|
||||||
|
self.private_.log_rows = self.private_.log_rows + 1
|
||||||
|
self.private_.log_size = self.private_.log_size + #msg + EOL_SIZE
|
||||||
|
end
|
||||||
|
|
||||||
|
function file_logger:init(opt)
|
||||||
|
|
||||||
|
if (opt.by_day or opt.roll_count) then
|
||||||
|
assert(not (opt.by_day and opt.roll_count), "Can not set 'by_day' and 'roll_count' fields at the same time!")
|
||||||
|
end
|
||||||
|
assert(opt.log_name, 'field log_name is required')
|
||||||
|
|
||||||
|
local log_dir = path_fullpath(opt.log_dir or '.')
|
||||||
|
|
||||||
|
if path_exists(log_dir) then
|
||||||
|
assert(path_isdir(log_dir))
|
||||||
|
else
|
||||||
|
assert(path_mkdir(log_dir))
|
||||||
|
end
|
||||||
|
|
||||||
|
local log_name, log_ext = string.match(opt.log_name, '([^\\/]+)([.][^.\\/]+)$')
|
||||||
|
assert(log_name and log_ext)
|
||||||
|
|
||||||
|
log_dir = ensure_dir_end(log_dir)
|
||||||
|
local full_name = log_dir .. log_name .. log_ext
|
||||||
|
local current_size = path_getsize(full_name)
|
||||||
|
if 0 == current_size then
|
||||||
|
-- prevent rename zero size logfile
|
||||||
|
path_remove(full_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
local flush_interval = opt.flush_interval and
|
||||||
|
assert(tonumber(opt.flush_interval), 'flush_interval must be a number') or 1
|
||||||
|
self.private_ = {
|
||||||
|
-- options
|
||||||
|
log_dir = log_dir,
|
||||||
|
log_name = log_name .. log_ext,
|
||||||
|
max_rows = opt.max_rows or math.huge,
|
||||||
|
max_size = opt.max_size or math.huge,
|
||||||
|
reset_out = opt.close_file and reset_out or make_no_close_reset(flush_interval),
|
||||||
|
arc_pfx = opt.archive_prefix or log_name,
|
||||||
|
roll_count = opt.roll_count and assert(tonumber(opt.roll_count), 'roll_count must be a number'),
|
||||||
|
by_day = not not opt.by_day,
|
||||||
|
|
||||||
|
-- state
|
||||||
|
-- log_date = ; -- date when current log file was create
|
||||||
|
-- log_rows = 0; -- how many lines in current log file
|
||||||
|
-- log_size = 0;
|
||||||
|
id = 1, -- numbers of file in current log_date
|
||||||
|
}
|
||||||
|
if self.private_.roll_count then
|
||||||
|
assert(self.private_.roll_count > 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
local reuse_log = opt.reuse
|
||||||
|
|
||||||
|
if reuse_log and current_size and (current_size > 0) then
|
||||||
|
self.private_.log_date = get_file_date(full_name)
|
||||||
|
|
||||||
|
if opt.max_rows then
|
||||||
|
self.private_.log_rows = path_getrows(full_name) or 0
|
||||||
|
else
|
||||||
|
self.private_.log_rows = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if opt.max_size then
|
||||||
|
self.private_.log_size = path_getsize(full_name) or 0
|
||||||
|
else
|
||||||
|
self.private_.log_size = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local logger, err = self.private_.reset_out(full_name)
|
||||||
|
if not logger then
|
||||||
|
error(string.format("can not create logger for file '%s':", full_name, err))
|
||||||
|
end
|
||||||
|
|
||||||
|
self.private_.logger = logger
|
||||||
|
self.private_.logger_close = err
|
||||||
|
|
||||||
|
else
|
||||||
|
assert(self:reset_log())
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function file_logger:new(...)
|
||||||
|
local o = setmetatable({}, {
|
||||||
|
__index = self,
|
||||||
|
}):init(...)
|
||||||
|
Log.add_cleanup(function()
|
||||||
|
o:close()
|
||||||
|
end)
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
|
local function do_profile()
|
||||||
|
require"profiler".start()
|
||||||
|
|
||||||
|
local logger = file_logger:new{
|
||||||
|
log_dir = './logs',
|
||||||
|
log_name = "events.log",
|
||||||
|
max_rows = 1000,
|
||||||
|
max_size = 70,
|
||||||
|
roll_count = 11,
|
||||||
|
-- by_day = true;
|
||||||
|
close_file = false,
|
||||||
|
flush_interval = 1,
|
||||||
|
reuse = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
for i = 1, 10000 do
|
||||||
|
local msg = string.format("%5d", i)
|
||||||
|
logger:write(msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
logger:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
return file_logger
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
local file = require "log.writer.file"
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new(log_dir, log_name, roll_count, max_size)
|
||||||
|
return file.new {
|
||||||
|
log_dir = log_dir,
|
||||||
|
log_name = log_name,
|
||||||
|
max_size = max_size or 10 * 1024 * 1024,
|
||||||
|
roll_count = assert(roll_count),
|
||||||
|
close_file = false,
|
||||||
|
flush_interval = 1,
|
||||||
|
reuse = true,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
|
|
||||||
@ -0,0 +1 @@
|
|||||||
|
return require "log.writer.filter.lvl.le"
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
local Log = require "log"
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new(max_lvl, writer)
|
||||||
|
max_lvl = assert(Log.lvl2number(max_lvl))
|
||||||
|
return function(fmt, msg, lvl, now)
|
||||||
|
if lvl == max_lvl then
|
||||||
|
writer(fmt, msg, lvl, now)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
local Log = require "log"
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new(max_lvl, writer)
|
||||||
|
max_lvl = assert(Log.lvl2number(max_lvl))
|
||||||
|
return function(fmt, msg, lvl, now)
|
||||||
|
if lvl <= max_lvl then
|
||||||
|
writer(fmt, msg, lvl, now)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new(newfmt, writer)
|
||||||
|
return function(oldfmt, msg, lvl, now)
|
||||||
|
writer(newfmt, msg, lvl, now)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
local log = require "log"
|
||||||
|
|
||||||
|
local freeswitch = assert(freeswitch)
|
||||||
|
|
||||||
|
local FS_LVL = {
|
||||||
|
console = 0,
|
||||||
|
alert = 1,
|
||||||
|
crit = 2,
|
||||||
|
err = 3,
|
||||||
|
warning = 4,
|
||||||
|
notice = 5,
|
||||||
|
info = 6,
|
||||||
|
debug = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
local LOG2FS = {
|
||||||
|
[log.LVL.EMERG] = FS_LVL.alert,
|
||||||
|
[log.LVL.ALERT] = FS_LVL.alert,
|
||||||
|
[log.LVL.FATAL] = FS_LVL.crit,
|
||||||
|
[log.LVL.ERROR] = FS_LVL.err,
|
||||||
|
[log.LVL.WARNING] = FS_LVL.warning,
|
||||||
|
[log.LVL.NOTICE] = FS_LVL.notice,
|
||||||
|
[log.LVL.INFO] = FS_LVL.info,
|
||||||
|
[log.LVL.DEBUG] = FS_LVL.debug,
|
||||||
|
[log.LVL.TRACE] = FS_LVL.debug,
|
||||||
|
}
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new(session)
|
||||||
|
if session then
|
||||||
|
return function(fmt, msg, lvl, now)
|
||||||
|
session:consoleLog(LOG2FS[lvl], msg .. '\n')
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return function(fmt, msg, lvl, now)
|
||||||
|
freeswitch.consoleLog(LOG2FS[lvl], msg .. '\n')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new(...)
|
||||||
|
local writers = {...}
|
||||||
|
return function(...)
|
||||||
|
for i = 1, #writers do
|
||||||
|
writers[i](...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
|
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
local socket = require "socket"
|
||||||
|
local log_packer = require "log.logformat.proxy.pack"
|
||||||
|
|
||||||
|
local _M = {}
|
||||||
|
|
||||||
|
function _M.run(writer, final, logformat, host, port)
|
||||||
|
local uskt = assert(socket.udp())
|
||||||
|
assert(uskt:setsockname(host, port))
|
||||||
|
local unpack = log_packer.unpack
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local msg, err = uskt:receivefrom()
|
||||||
|
if msg then
|
||||||
|
local msg, lvl, now = unpack(msg)
|
||||||
|
if msg and lvl and now then
|
||||||
|
writer(logformat, msg, lvl, now)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if err ~= 'timeout' then
|
||||||
|
io.stderr:write('log.writer.net.udp.server: ', err, '\n')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- @todo
|
||||||
|
-- if final then final() end
|
||||||
|
end
|
||||||
|
|
||||||
|
return _M
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
local Z = require "log.writer.net.zmq._private.compat"
|
||||||
|
local IMPL = require "log.writer.net.zmq._private.impl"
|
||||||
|
|
||||||
|
local zmq, ETERM, zstrerror, zassert, zrecv = Z.zmq, Z.ETERM, Z.strerror, Z.assert, Z.recv
|
||||||
|
local zerrcode = Z.errcode
|
||||||
|
|
||||||
|
local log_packer = require "log.logformat.proxy.pack"
|
||||||
|
|
||||||
|
local _M = {}
|
||||||
|
|
||||||
|
function _M.run(writer, final, logformat, ctx, stype, address, addr_sync)
|
||||||
|
-- print(writer, logformat, ctx, stype, address, addr_sync)
|
||||||
|
local stypes = {
|
||||||
|
SUB = zmq.SUB,
|
||||||
|
PULL = zmq.PULL,
|
||||||
|
}
|
||||||
|
stype = assert(stypes[stype], 'Unsupported socket type')
|
||||||
|
|
||||||
|
ctx = IMPL.context(ctx)
|
||||||
|
|
||||||
|
local skt = zassert(ctx:socket(stype))
|
||||||
|
zassert(skt:bind(address))
|
||||||
|
|
||||||
|
if addr_sync then
|
||||||
|
local skt_sync = zassert(ctx:socket(zmq.PAIR))
|
||||||
|
zassert(skt_sync:connect(addr_sync))
|
||||||
|
skt_sync:send("")
|
||||||
|
skt_sync:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
local unpack = log_packer.unpack
|
||||||
|
|
||||||
|
while (true) do
|
||||||
|
local msg, err = zrecv(skt)
|
||||||
|
if msg then
|
||||||
|
local msg, lvl, now = unpack(msg)
|
||||||
|
if msg and lvl and now then
|
||||||
|
writer(logformat, msg, lvl, now)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if zerrcode(err) == ETERM then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
io.stderr:write('log.writer.net.zmq.server: ', tostring(err), zstrerror(err), '\n')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if final then
|
||||||
|
final()
|
||||||
|
end
|
||||||
|
|
||||||
|
skt:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
return _M
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
local Log = require "log"
|
||||||
|
local sendmail = require "sendmail"
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new(from, to, server, subject)
|
||||||
|
assert(to, "'to' parameter is required")
|
||||||
|
assert(from, "'from' parameter is required")
|
||||||
|
assert(server, "'server' parameter is required")
|
||||||
|
|
||||||
|
subject = subject or ''
|
||||||
|
|
||||||
|
return function(fmt, msg, lvl, now)
|
||||||
|
msg = fmt(msg, lvl, now)
|
||||||
|
sendmail(from, to, server, {
|
||||||
|
subject = now:fmt("%F %T") .. ' [' .. Log.LVL_NAMES[lvl] .. '] ' .. subject,
|
||||||
|
file = {
|
||||||
|
name = 'message.txt',
|
||||||
|
data = msg,
|
||||||
|
},
|
||||||
|
text = msg,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
local socket = require("socket")
|
||||||
|
|
||||||
|
local function create_socket(host, port, timeout)
|
||||||
|
local skt = assert(socket.udp())
|
||||||
|
assert(skt:settimeout(timeout or 0.1))
|
||||||
|
assert(skt:setpeername(host, port))
|
||||||
|
return skt
|
||||||
|
end
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new(host, port, timeout)
|
||||||
|
local skt = create_socket(host, port, timeout)
|
||||||
|
return function(fmt, ...)
|
||||||
|
skt:send((fmt(...)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1 @@
|
|||||||
|
return require "log.writer.net.zmq.pub"
|
||||||
@ -0,0 +1,150 @@
|
|||||||
|
local function prequire(...)
|
||||||
|
local ok, mod = pcall(require, ...)
|
||||||
|
return ok and mod, mod or nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local zmq, zthreads, zpoller
|
||||||
|
local zstrerror, zassert, ETERM
|
||||||
|
local zconnect, zbind
|
||||||
|
local zrecv_all, zrecv
|
||||||
|
local zerrcode
|
||||||
|
|
||||||
|
local function has_member(t, key)
|
||||||
|
local ok, has
|
||||||
|
if type(key) == "table" then
|
||||||
|
ok, has = pcall(function()
|
||||||
|
for _, k in ipairs(key) do
|
||||||
|
if nil == t[k] then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
ok, has = pcall(function()
|
||||||
|
return nil ~= t[key]
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
return ok and has
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_ctx(ctx)
|
||||||
|
local tname = type(ctx)
|
||||||
|
if (tname ~= 'table') and (tname ~= 'userdata') then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return has_member(ctx, {'socket', 'term'})
|
||||||
|
end
|
||||||
|
|
||||||
|
zmq = prequire "lzmq"
|
||||||
|
if zmq then
|
||||||
|
zpoller = prequire "lzmq.poller"
|
||||||
|
zthreads = prequire "lzmq.threads"
|
||||||
|
ETERM = zmq.errors.ETERM
|
||||||
|
zstrerror = function(err)
|
||||||
|
if type(err) == "number" then
|
||||||
|
return zmq.strerror(err)
|
||||||
|
end
|
||||||
|
if type(err) == "string" then
|
||||||
|
return err
|
||||||
|
end
|
||||||
|
return err:msg()
|
||||||
|
end
|
||||||
|
zerrcode = function(err)
|
||||||
|
if type(err) == "number" then
|
||||||
|
return err
|
||||||
|
end
|
||||||
|
if type(err) == "string" then
|
||||||
|
return err
|
||||||
|
end -- @todo extract no from string
|
||||||
|
return err:no()
|
||||||
|
end
|
||||||
|
zassert = zmq.assert
|
||||||
|
zrecv_all = function(skt)
|
||||||
|
return skt:recv_all()
|
||||||
|
end
|
||||||
|
zconnect = function(skt, addr)
|
||||||
|
return skt:connect(addr)
|
||||||
|
end
|
||||||
|
zbind = function(skt, addr)
|
||||||
|
return skt:bind(addr)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
zmq = require "zmq"
|
||||||
|
zpoller = require "zmq.poller"
|
||||||
|
zthreads = prequire "zmq.threads"
|
||||||
|
ETERM = 'closed'
|
||||||
|
zstrerror = function(err)
|
||||||
|
return err
|
||||||
|
end
|
||||||
|
zerrcode = function(err)
|
||||||
|
return err
|
||||||
|
end
|
||||||
|
zassert = assert
|
||||||
|
zrecv_all = function(skt)
|
||||||
|
local t = {}
|
||||||
|
local r, err = skt:recv()
|
||||||
|
if not r then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
table.insert(t, r)
|
||||||
|
while skt:rcvmore() == 1 do
|
||||||
|
r, err = skt:recv()
|
||||||
|
if not r then
|
||||||
|
return nil, err, t
|
||||||
|
end
|
||||||
|
table.insert(t, r)
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
zconnect = function(skt, addr)
|
||||||
|
if type(addr) == 'table' then
|
||||||
|
for i, a in ipairs(addr) do
|
||||||
|
local ok, err = skt:connect(a)
|
||||||
|
if not ok then
|
||||||
|
return nil, err, i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return skt:connect(addr)
|
||||||
|
end
|
||||||
|
zbind = function(skt, addr)
|
||||||
|
if type(addr) == 'table' then
|
||||||
|
for i, a in ipairs(addr) do
|
||||||
|
local ok, err = skt:bind(a)
|
||||||
|
if not ok then
|
||||||
|
return nil, err, i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return skt:bind(addr)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
zrecv = function(skt)
|
||||||
|
local ok, err, t = zrecv_all(skt)
|
||||||
|
if not ok then
|
||||||
|
if t and t[1] then
|
||||||
|
return t[1]
|
||||||
|
end
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
return ok[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
zmq = zmq,
|
||||||
|
threads = zthreads,
|
||||||
|
poller = zpoller,
|
||||||
|
connect = zconnect,
|
||||||
|
bind = zbind,
|
||||||
|
recv_all = zrecv_all,
|
||||||
|
recv = zrecv,
|
||||||
|
strerror = zstrerror,
|
||||||
|
errcode = zerrcode,
|
||||||
|
assert = zassert,
|
||||||
|
ETERM = ETERM,
|
||||||
|
is_ctx = is_ctx,
|
||||||
|
}
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
local Log = require "log"
|
||||||
|
local Z = require "log.writer.net.zmq._private.compat"
|
||||||
|
|
||||||
|
local zmq, zthreads = Z.zmq, Z.threads
|
||||||
|
local zstrerror, zassert = Z.strerror, Z.assert
|
||||||
|
local ETERM = Z.ETERM
|
||||||
|
local zconnect, zbind = Z.connect, Z.bind
|
||||||
|
|
||||||
|
local log_ctx
|
||||||
|
|
||||||
|
local function context(ctx)
|
||||||
|
-- we have to use same context for all writers
|
||||||
|
if ctx and log_ctx then
|
||||||
|
assert(ctx == log_ctx)
|
||||||
|
end
|
||||||
|
|
||||||
|
if log_ctx then
|
||||||
|
return log_ctx
|
||||||
|
end
|
||||||
|
|
||||||
|
log_ctx = ctx or (zthreads and zthreads.get_parent_ctx()) or zassert(zmq.init(1))
|
||||||
|
|
||||||
|
return log_ctx
|
||||||
|
end
|
||||||
|
|
||||||
|
local function socket(ctx, stype, is_srv, addr, timeout)
|
||||||
|
local stypes = {
|
||||||
|
PUSH = zmq.PUSH,
|
||||||
|
PUB = zmq.PUB,
|
||||||
|
}
|
||||||
|
stype = assert(stypes[stype], 'Unsupported socket type')
|
||||||
|
timeout = timeout or 100
|
||||||
|
ctx = context(ctx)
|
||||||
|
|
||||||
|
local skt = ctx:socket(stype)
|
||||||
|
if ctx.autoclose then
|
||||||
|
ctx:autoclose(skt)
|
||||||
|
end
|
||||||
|
skt:set_sndtimeo(timeout)
|
||||||
|
skt:set_linger(timeout)
|
||||||
|
if is_srv then
|
||||||
|
zassert(zbind(skt, addr))
|
||||||
|
else
|
||||||
|
zassert(zconnect(skt, addr))
|
||||||
|
end
|
||||||
|
if not ctx.autoclose then
|
||||||
|
Log.add_cleanup(function()
|
||||||
|
skt:close()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
return skt
|
||||||
|
end
|
||||||
|
|
||||||
|
local function init(stype, is_srv)
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new(ctx, addr, timeout)
|
||||||
|
if ctx and not Z.is_ctx(ctx) then
|
||||||
|
ctx, addr, timeout = nil, ctx, addr
|
||||||
|
end
|
||||||
|
|
||||||
|
local skt = socket(ctx, stype, is_srv, addr, timeout)
|
||||||
|
return function(fmt, ...)
|
||||||
|
skt:send((fmt(...)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
init = init,
|
||||||
|
context = context,
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
return require"log.writer.net.zmq._private.impl".init('PUB', false)
|
||||||
@ -0,0 +1 @@
|
|||||||
|
return require"log.writer.net.zmq._private.impl".init('PUSH', false)
|
||||||
@ -0,0 +1 @@
|
|||||||
|
return require"log.writer.net.zmq._private.impl".init('PUB', true)
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new(prefix, writer)
|
||||||
|
return function(fmt, msg, lvl, now)
|
||||||
|
writer(fmt, prefix .. msg, lvl, now)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
local io = require "io"
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new()
|
||||||
|
return function(fmt, ...)
|
||||||
|
io.stderr:write(fmt(...), '\n')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
local io = require "io"
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.new()
|
||||||
|
return function(fmt, ...)
|
||||||
|
io.stdout:write(fmt(...), '\n')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
local skynet = require "skynet"
|
||||||
|
local queue = require "skynet.queue"
|
||||||
|
local util = require "store_util"
|
||||||
|
require "skynet.manager"
|
||||||
|
|
||||||
|
local dbconf
|
||||||
|
local lock = queue()
|
||||||
|
local guid_generator_addrs = {}
|
||||||
|
|
||||||
|
local CMD = {}
|
||||||
|
|
||||||
|
function CMD.init(conf)
|
||||||
|
assert(not dbconf, "dbmgr has been initialized.")
|
||||||
|
dbconf = conf -- init is allowed only once
|
||||||
|
|
||||||
|
local uidconf = dbconf.guid_generator
|
||||||
|
assert(uidconf)
|
||||||
|
for _, worker_id in pairs(uidconf.worker_ids) do
|
||||||
|
local addr = skynet.newservice("guid_generator", worker_id)
|
||||||
|
table.insert(guid_generator_addrs, addr)
|
||||||
|
end
|
||||||
|
|
||||||
|
local redisconf = dbconf.redis
|
||||||
|
for dbkey, conf in pairs(redisconf) do
|
||||||
|
for index = 1, conf.service_num do
|
||||||
|
local addr = skynet.newservice("redisd", dbkey, index)
|
||||||
|
local ok = skynet.call(addr, "lua", "init", conf)
|
||||||
|
if not ok then
|
||||||
|
assert(false, ("redisd init failed. [dbkey] %s [id] %d"):format(dbkey, index))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local mysqlconf = dbconf.mysql
|
||||||
|
for dbkey, conf in pairs(mysqlconf) do
|
||||||
|
for index = 1, conf.service_num do
|
||||||
|
local addr = skynet.newservice("mysqld", dbkey, index)
|
||||||
|
local ok = skynet.call(addr, "lua", "init", conf)
|
||||||
|
if not ok then
|
||||||
|
assert(false, ("mysqld init failed. [dbkey] %s [id] %d"):format(dbkey, index))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function CMD.mysql_service_num(dbkey)
|
||||||
|
if not dbconf then return end
|
||||||
|
local mysqlconf = dbconf.mysql
|
||||||
|
if not mysqlconf then return end
|
||||||
|
local conf = mysqlconf[dbkey]
|
||||||
|
if not conf then return end
|
||||||
|
return conf.service_num
|
||||||
|
end
|
||||||
|
|
||||||
|
function CMD.redis_service_num(dbkey)
|
||||||
|
if not dbconf then return end
|
||||||
|
local redisconf = dbconf.redis
|
||||||
|
if not redisconf then return end
|
||||||
|
local conf = redisconf[dbkey]
|
||||||
|
if not conf then return end
|
||||||
|
return conf.service_num
|
||||||
|
end
|
||||||
|
|
||||||
|
function CMD.guid_generators()
|
||||||
|
return guid_generator_addrs
|
||||||
|
end
|
||||||
|
|
||||||
|
skynet.start(function()
|
||||||
|
skynet.dispatch("lua", function(_, _, cmd, ...)
|
||||||
|
local f = CMD[cmd]
|
||||||
|
assert(f, cmd)
|
||||||
|
skynet.retpack(f(...))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
skynet.register(".dbmgr")
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
local skynet = require "skynet"
|
||||||
|
local st = require "skynet.sharetable"
|
||||||
|
local mc = require "skynet.multicast"
|
||||||
|
|
||||||
|
local channel
|
||||||
|
|
||||||
|
local CMD = {}
|
||||||
|
|
||||||
|
function CMD.query(filename)
|
||||||
|
assert(channel)
|
||||||
|
return st.query(filename)
|
||||||
|
end
|
||||||
|
|
||||||
|
function CMD.loadfiles(filenames)
|
||||||
|
assert(type(filenames) == "table")
|
||||||
|
for _, filename in pairs(filenames) do
|
||||||
|
st.loadfile(filename)
|
||||||
|
end
|
||||||
|
channel:publish(filenames)
|
||||||
|
end
|
||||||
|
|
||||||
|
function CMD.channel()
|
||||||
|
return channel.channel
|
||||||
|
end
|
||||||
|
|
||||||
|
skynet.start(function()
|
||||||
|
channel = mc.new()
|
||||||
|
skynet.dispatch("lua", function(_, _, cmd, ...)
|
||||||
|
local f = CMD[cmd]
|
||||||
|
assert(f, cmd)
|
||||||
|
skynet.retpack(f(...))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
local skynet = require "skynet"
|
||||||
|
local time = skynet.time
|
||||||
|
local floor = math.floor
|
||||||
|
local ceil = math.ceil
|
||||||
|
|
||||||
|
--[[
|
||||||
|
sign | delta seconds | worker id | sequence
|
||||||
|
1bit | 30bits | 20bits | 13bits
|
||||||
|
|
||||||
|
sign - 0为正数 1为负数
|
||||||
|
delta seconds - 2020/01/01到当前时间时间差, 30bits可以用到2054-01-09
|
||||||
|
worker id - 0~1048575 每个服务worker id都应该不一样,才能保证唯一性, worker_id需使用者自行规划
|
||||||
|
sequence - 0~8191 当前时刻的id序列
|
||||||
|
]]
|
||||||
|
|
||||||
|
local DELTA_SECS_BITS = 30
|
||||||
|
local WORKER_ID_BITS = 20
|
||||||
|
local SEQ_BITS = 13
|
||||||
|
local TIME_LEFT_SHIFT_BITS = WORKER_ID_BITS + SEQ_BITS
|
||||||
|
|
||||||
|
local START_TIME = 1577808000 -- 2020/01/01
|
||||||
|
local MAX_WORKER_ID = (1<<WORKER_ID_BITS) - 1
|
||||||
|
local MAX_SEQ = (1<<SEQ_BITS) - 1
|
||||||
|
|
||||||
|
local worker_id = tonumber(...)
|
||||||
|
assert(worker_id >= 0 and worker_id <= MAX_WORKER_ID)
|
||||||
|
|
||||||
|
local seq = 0
|
||||||
|
local last_delta_time = 0
|
||||||
|
local gen_guid
|
||||||
|
|
||||||
|
gen_guid = function()
|
||||||
|
local now = time()
|
||||||
|
local floor_now = floor(now)
|
||||||
|
local delta_time = floor_now - START_TIME
|
||||||
|
if last_delta_time ~= delta_time then
|
||||||
|
last_delta_time = delta_time
|
||||||
|
seq = 0
|
||||||
|
elseif seq < MAX_SEQ then
|
||||||
|
seq = seq + 1
|
||||||
|
else
|
||||||
|
skynet.sleep(100 - ceil(now - floor_now)*100)
|
||||||
|
return gen_guid()
|
||||||
|
end
|
||||||
|
return (delta_time<<TIME_LEFT_SHIFT_BITS) + (worker_id<<SEQ_BITS) + seq
|
||||||
|
end
|
||||||
|
|
||||||
|
skynet.start(function()
|
||||||
|
skynet.dispatch("lua", function()
|
||||||
|
skynet.retpack(gen_guid())
|
||||||
|
end)
|
||||||
|
end)
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
local skynet = require "skynet"
|
||||||
|
local mysql = require "skynet.db.mysql"
|
||||||
|
local util = require "store_util"
|
||||||
|
require "skynet.manager"
|
||||||
|
|
||||||
|
local traceback = debug.traceback
|
||||||
|
|
||||||
|
local dbkey, index = ...
|
||||||
|
local db
|
||||||
|
|
||||||
|
local CMD = {}
|
||||||
|
|
||||||
|
local function success(ret)
|
||||||
|
if not ret or ret.err or ret.badresult then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function CMD.init(conf)
|
||||||
|
db = mysql.connect(conf)
|
||||||
|
db:query("set names utf8mb4")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function CMD.exec_one(sql)
|
||||||
|
local ok, ret = xpcall(db.query, traceback, db, sql)
|
||||||
|
if not ok or not success(ret) then
|
||||||
|
assert(false, ("sql=[%s] ret=[%s]"):format(sql, util.encode(ret)))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
function CMD.exec(sqls)
|
||||||
|
for i = 1, #sqls do
|
||||||
|
local sql = sqls[i]
|
||||||
|
CMD.exec_one(sql)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
skynet.start(function()
|
||||||
|
skynet.dispatch("lua", function(_, _, cmd, ...)
|
||||||
|
local f = CMD[cmd]
|
||||||
|
assert(f, cmd)
|
||||||
|
skynet.retpack(f(...))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
skynet.register(util.mysql_sname(dbkey, index))
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
local skynet = require "skynet"
|
||||||
|
local redis = require "skynet.db.redis"
|
||||||
|
local util = require "store_util"
|
||||||
|
require "skynet.manager"
|
||||||
|
|
||||||
|
local traceback = debug.traceback
|
||||||
|
local tunpack = table.unpack
|
||||||
|
local tconcat = table.concat
|
||||||
|
local dbkey, index = ...
|
||||||
|
local db
|
||||||
|
|
||||||
|
local CMD = {}
|
||||||
|
|
||||||
|
function CMD.init(conf)
|
||||||
|
db = redis.connect(conf)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function CMD.exec_one(cmd, ...)
|
||||||
|
local ok, ret = xpcall(db[cmd], traceback, db, ...)
|
||||||
|
if not ok then
|
||||||
|
assert(false, ("cmd=[%s %s] ret=[%s]"):format(cmd, tconcat({...}, " "), ret))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
function CMD.exec(cmds)
|
||||||
|
for _, cmd in pairs(cmds) do
|
||||||
|
xpcall(CMD.exec_one, traceback, tunpack(cmd))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
skynet.start(function()
|
||||||
|
skynet.dispatch("lua", function(_, _, cmd, ...)
|
||||||
|
local f = CMD[cmd]
|
||||||
|
assert(f, cmd)
|
||||||
|
skynet.retpack(f(...))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
skynet.register(util.redis_sname(dbkey, index))
|
||||||
@ -0,0 +1,198 @@
|
|||||||
|
local skynet = require "skynet"
|
||||||
|
local redis = require "skynet.db.redis"
|
||||||
|
local crypt = require "skynet.crypt"
|
||||||
|
|
||||||
|
local math_floor = math.floor
|
||||||
|
local math_ceil = math.ceil
|
||||||
|
local math_random = math.random
|
||||||
|
|
||||||
|
local function hash(script)
|
||||||
|
local key = crypt.sha1(script)
|
||||||
|
return crypt.hexencode(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
local QUORUM
|
||||||
|
|
||||||
|
local SCRIPT = {
|
||||||
|
LOCK = [[
|
||||||
|
local key = KEYS[1]
|
||||||
|
if redis.call("exists", key) == 1 then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
redis.call("set", key, ARGV[1], "PX", ARGV[2])
|
||||||
|
return 1
|
||||||
|
]],
|
||||||
|
UNLOCK = [[
|
||||||
|
local key = KEYS[1]
|
||||||
|
if redis.call("get", key) == ARGV[1] then
|
||||||
|
redis.pcall("del", key)
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
]],
|
||||||
|
EXTEND = [[
|
||||||
|
local key = KEYS[1]
|
||||||
|
if redis.call("get", key) ~= ARGV[1] then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
redis.call("set", key, ARGV[1], "PX", ARGV[2])
|
||||||
|
return 1
|
||||||
|
]],
|
||||||
|
}
|
||||||
|
|
||||||
|
local SCRIPT_HASH = {
|
||||||
|
LOCK = hash(SCRIPT.LOCK),
|
||||||
|
UNLOCK = hash(SCRIPT.UNLOCK),
|
||||||
|
EXTEND = hash(SCRIPT.EXTEND),
|
||||||
|
}
|
||||||
|
|
||||||
|
local conf
|
||||||
|
local dbs = {}
|
||||||
|
local sessions = {}
|
||||||
|
|
||||||
|
local function execute_script(db, type, s)
|
||||||
|
local ok, ret = pcall(db["evalsha"], db, SCRIPT_HASH[type], 1, s.lockname, s.uuid, s.timeout)
|
||||||
|
if not ok and ret:find("NOSCRIPT") then
|
||||||
|
ok, ret = pcall(db["eval"], db, SCRIPT[type], 1, s.lockname, s.uuid, s.timeout)
|
||||||
|
end
|
||||||
|
if not ok then
|
||||||
|
skynet.error("redis execute_script err.", ret, s.lockname, s.uuid, s.timeout)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if ret == 1 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function execute_script_timeout(db, type, s)
|
||||||
|
local co = coroutine.running()
|
||||||
|
local ok, ret = false, "timeout"
|
||||||
|
|
||||||
|
skynet.fork(function()
|
||||||
|
ok, ret = execute_script(db, type, s)
|
||||||
|
if co then
|
||||||
|
skynet.wakeup(co)
|
||||||
|
co = nil
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
skynet.sleep(conf.request_timeout / 10)
|
||||||
|
if co then
|
||||||
|
co = nil
|
||||||
|
end
|
||||||
|
return ok, ret
|
||||||
|
end
|
||||||
|
|
||||||
|
local function calc_time(s)
|
||||||
|
local now = skynet.now() * 10
|
||||||
|
local drift = math_floor(conf.drift_factor * s.timeout) + 2
|
||||||
|
s.starttime = now
|
||||||
|
s.endtime = now + s.timeout - drift
|
||||||
|
end
|
||||||
|
|
||||||
|
local function make_session(lockname, uuid, timeout)
|
||||||
|
local s = {
|
||||||
|
lockname = lockname,
|
||||||
|
uuid = uuid,
|
||||||
|
timeout = timeout,
|
||||||
|
attempts = 0,
|
||||||
|
starttime = 0,
|
||||||
|
endtime = 0,
|
||||||
|
}
|
||||||
|
calc_time(s)
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unlock(s)
|
||||||
|
s.endtime = 0
|
||||||
|
for _, db in pairs(dbs) do
|
||||||
|
execute_script(db, "UNLOCK", s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function attempt(s, is_extend)
|
||||||
|
s.attempts = s.attempts + 1
|
||||||
|
local votes = 0
|
||||||
|
for _, db in pairs(dbs) do
|
||||||
|
local ok
|
||||||
|
if is_extend then
|
||||||
|
ok = execute_script_timeout(db, "EXTEND", s)
|
||||||
|
else
|
||||||
|
ok = execute_script_timeout(db, "LOCK", s)
|
||||||
|
end
|
||||||
|
if ok then
|
||||||
|
votes = votes + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local now = skynet.now() * 10
|
||||||
|
if votes >= QUORUM and s.endtime > now then
|
||||||
|
local ti = s.timeout / 3 - (now - s.starttime)
|
||||||
|
ti = math_floor(ti / 10)
|
||||||
|
if ti < 0 then
|
||||||
|
ti = 0
|
||||||
|
end
|
||||||
|
skynet.timeout(ti, function()
|
||||||
|
if s.endtime == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
s.attempts = 0
|
||||||
|
calc_time(s)
|
||||||
|
attempt(s, true)
|
||||||
|
end)
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
unlock(s)
|
||||||
|
-- retry
|
||||||
|
if conf.retry_count == -1 or s.attempts <= conf.retry_count then
|
||||||
|
local t = conf.retry_delay + math_floor((math_random() * 2 - 1) * conf.retry_jitter)
|
||||||
|
skynet.sleep(math_ceil(t / 10))
|
||||||
|
calc_time(s)
|
||||||
|
return attempt(s)
|
||||||
|
end
|
||||||
|
-- failed
|
||||||
|
sessions[s.uuid] = nil
|
||||||
|
return false, "timeout"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local CMD = {}
|
||||||
|
|
||||||
|
function CMD.lock(lockname, uuid, timeout)
|
||||||
|
timeout = timeout or conf.timeout
|
||||||
|
local s = sessions[uuid]
|
||||||
|
if s then
|
||||||
|
return false, "session exist"
|
||||||
|
end
|
||||||
|
s = make_session(lockname, uuid, timeout)
|
||||||
|
sessions[uuid] = s
|
||||||
|
|
||||||
|
return attempt(s)
|
||||||
|
end
|
||||||
|
|
||||||
|
function CMD.unlock(_, uuid)
|
||||||
|
local s = sessions[uuid]
|
||||||
|
if not s then
|
||||||
|
return false, "session not exist."
|
||||||
|
end
|
||||||
|
sessions[uuid] = nil
|
||||||
|
return unlock(s)
|
||||||
|
end
|
||||||
|
|
||||||
|
skynet.init(function()
|
||||||
|
conf = require "redlock_conf"
|
||||||
|
for _, client in ipairs(conf.servers) do
|
||||||
|
table.insert(dbs, redis.connect(client))
|
||||||
|
end
|
||||||
|
QUORUM = math_floor(#conf.servers / 2) + 1
|
||||||
|
end)
|
||||||
|
|
||||||
|
skynet.start(function()
|
||||||
|
skynet.dispatch("lua", function(_, _, cmd, ...)
|
||||||
|
local f = CMD[cmd]
|
||||||
|
assert(f, cmd)
|
||||||
|
skynet.retpack(f(...))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
@ -1 +1,60 @@
|
|||||||
--统一的服务基础逻辑
|
-- 统一的服务基础逻辑
|
||||||
|
local _service_name, _service_path = ...
|
||||||
|
assert(_service_name, "_service_name nil")
|
||||||
|
assert(_service_path, "_service_path nil")
|
||||||
|
|
||||||
|
SERVICE_NAME = _service_name
|
||||||
|
local _arguments = table.pack(...)
|
||||||
|
|
||||||
|
local skynet = require "skynet"
|
||||||
|
require "skynet.manager"
|
||||||
|
|
||||||
|
require "sharetable"
|
||||||
|
require "cluster"
|
||||||
|
require "multicast"
|
||||||
|
require "datacenter"
|
||||||
|
|
||||||
|
local function _exception(e)
|
||||||
|
skynet.error(e)
|
||||||
|
return e
|
||||||
|
end
|
||||||
|
|
||||||
|
local _xpcall = function(f, ...)
|
||||||
|
return xpcall(f, _exception, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
local single -- 服务逻辑单例对象
|
||||||
|
|
||||||
|
local _info = function()
|
||||||
|
end
|
||||||
|
|
||||||
|
local _dispatch = function(session, source, cmd, ...)
|
||||||
|
local func = single:get_cmd_func(cmd)
|
||||||
|
if not func then
|
||||||
|
skynet.retpack({
|
||||||
|
ok = false,
|
||||||
|
msg = string.format("[%s]UNKNOWN_CMD[%s]FROM[%s]", _service_name, cmd, tostring(source)),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if session > 0 then
|
||||||
|
skynet.retpack(_xpcall(func, single, ...))
|
||||||
|
else
|
||||||
|
_xpcall(func, single, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
skynet.start(function()
|
||||||
|
single = require(_service_path):instance(table.unpack(_arguments, 2, _arguments.n))
|
||||||
|
assert(single.dispatch, "zeus service dispatch function nil")
|
||||||
|
|
||||||
|
skynet.dispatch("lua", _dispatch)
|
||||||
|
skynet.info_func(_info)
|
||||||
|
|
||||||
|
local fsm = single:get_fsm()
|
||||||
|
fsm.initial()
|
||||||
|
fsm.running()
|
||||||
|
|
||||||
|
single:add_timer(_info, 3600)
|
||||||
|
end)
|
||||||
|
|||||||
Loading…
Reference in New Issue