feat(服务): 新增日志服务

develop
xiaojin 5 years ago
parent fbe6800cb0
commit a5502e8aef

@ -0,0 +1,83 @@
local skynet = require "skynet"
local coroutine = coroutine
local xpcall = xpcall
local traceback = debug.traceback
--- Parent queue, and where lock the parent
function skynet.lockable_queue(parent_queue, parent_lock)
local current_thread = nil
local ref = 0
local thread_queue = {}
local parent_queue = parent_queue
local parent_lock = parent_lock
local locked_by = nil
local function xpcall_ret(ok, ...)
ref = ref - 1
if ref == 0 then
if locked_by == current_thread then
locked_by = nil
end
current_thread = table.remove(thread_queue, 1)
if current_thread then
skynet.wakeup(current_thread)
end
end
if not ok then
return nil, ...
end
-- assert(ok, (...))
return ...
end
local lockable_queue = function(f, lock, ...)
local thread = coroutine.running()
--- If queue is locked and current thread is not the running one
if locked_by and current_thread ~= thread then
return nil, "Queue is locked"
end
--- Set the locked flag even current task is not running for avoid any new task comming
if lock then
locked_by = thread
end
--- If not in recursive lock, and current is running, wait for previous one finished
if current_thread and current_thread ~= thread then
table.insert(thread_queue, thread)
skynet.wait()
assert(ref == 0) -- current_thread == thread
end
--- Set the current running thread
current_thread = thread
--- Increase the ref
ref = ref + 1
--- Execute the function
return xpcall_ret(xpcall(f, traceback, ...))
end
if parent_queue then
return function(f, lock, ...)
return parent_queue(lockable_queue, parent_lock, f, lock, ...)
--[[
return parent_queue(function(...)
return lockable_queue(f, lock, ...)
end, parent_lock, ...)
]] --
end
else
return lockable_queue
end
end
---
-- Lockable queue
-- @tparam function f The function to execute in this queue
-- @tparam boolean lock Lock current queue until current task exeucted completely or task cloud be queued for execution
-- @param ...
-- @return false if queue is lock, or the first value from your function f
-- @returns
return skynet.lockable_queue

@ -5,25 +5,31 @@ local M = {
M.Nodes = {
[1] = {
nodeid = 1,
name = "login",
name = "login_1",
},
[50] = {
nodeid = 50,
name = "center",
name = "center_1",
},
[100] = {
nodeid = 100,
name = "game",
name = "game_1",
},
[110] = {
nodeid = 110,
name = "storage",
name = "storage_1",
},
}
M.Logger = {
log_root = "logs/", -- 日志目录
log_console = true, -- 输出终端
log_level = 1, -- 日志等级 1~5
}
function M.get_node_conf(nodeid)
return M.Nodes[nodeid]
end

@ -1,16 +0,0 @@
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

@ -1,9 +0,0 @@
local M = {}
function M.new()
return function(msg)
return msg
end
end
return M

@ -1,9 +0,0 @@
local string = require "string"
local M = {}
function M.new()
return string.format
end
return M

@ -1,24 +0,0 @@
---
-- 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

@ -1,93 +0,0 @@
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,
}

@ -1,377 +0,0 @@
local inspect = {
_VERSION = "inspect.lua 3.1.0",
_URL = "http://github.com/kikito/inspect.lua",
_DESCRIPTION = "human-readable representations of tables",
_LICENSE = [[
MIT LICENSE
Copyright (c) 2013 Enrique García Cota
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
]],
}
local tostring = tostring
local setmetatable = setmetatable
local next = next
local type = type
local rawget = rawget
local getmetatable = getmetatable
inspect.KEY = setmetatable({}, {
__tostring = function()
return "inspect.KEY"
end,
})
inspect.METATABLE = setmetatable({}, {
__tostring = function()
return "inspect.METATABLE"
end,
})
local function rawpairs(t)
return next, t, nil
end
-- Apostrophizes the string if it has quotes, but not aphostrophes
-- Otherwise, it returns a regular quoted string
local function smartQuote(str)
if str:match('"') and not str:match("'") then
return "'" .. str .. "'"
end
return '"' .. str:gsub('"', '\\"') .. '"'
end
-- \a => '\\a', \0 => '\\0', 31 => '\31'
local shortControlCharEscapes = {
["\a"] = "\\a",
["\b"] = "\\b",
["\f"] = "\\f",
["\n"] = "\\n",
["\r"] = "\\r",
["\t"] = "\\t",
["\v"] = "\\v",
}
local longControlCharEscapes = {} -- \a => nil, \0 => \000, 31 => \031
for i = 0, 31 do
local ch = string.char(i)
if not shortControlCharEscapes[ch] then
shortControlCharEscapes[ch] = "\\" .. i
longControlCharEscapes[ch] = string.format("\\%03d", i)
end
end
local function escape(str)
return (str:gsub("\\", "\\\\"):gsub("(%c)%f[0-9]", longControlCharEscapes):gsub("%c", shortControlCharEscapes))
end
local function isIdentifier(str)
return type(str) == "string" and str:match("^[_%a][_%a%d]*$")
end
local function isSequenceKey(k, sequenceLength)
return type(k) == "number" and 1 <= k and k <= sequenceLength and math.floor(k) == k
end
local defaultTypeOrders = {
["number"] = 1,
["boolean"] = 2,
["string"] = 3,
["table"] = 4,
["function"] = 5,
["userdata"] = 6,
["thread"] = 7,
}
local function sortKeys(a, b)
local ta, tb = type(a), type(b)
-- strings and numbers are sorted numerically/alphabetically
if ta == tb and (ta == "string" or ta == "number") then
return a < b
end
local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb]
-- Two default types are compared according to the defaultTypeOrders table
if dta and dtb then
return defaultTypeOrders[ta] < defaultTypeOrders[tb]
elseif dta then
return true -- default types before custom ones
elseif dtb then
return false -- custom types after default ones
end
-- custom types are sorted out alphabetically
return ta < tb
end
-- For implementation reasons, the behavior of rawlen & # is "undefined" when
-- tables aren't pure sequences. So we implement our own # operator.
local function getSequenceLength(t)
local len = 1
local v = rawget(t, len)
while v ~= nil do
len = len + 1
v = rawget(t, len)
end
return len - 1
end
local function getNonSequentialKeys(t)
local keys, keysLength = {}, 0
local sequenceLength = getSequenceLength(t)
for k, _ in rawpairs(t) do
if not isSequenceKey(k, sequenceLength) then
keysLength = keysLength + 1
keys[keysLength] = k
end
end
table.sort(keys, sortKeys)
return keys, keysLength, sequenceLength
end
local function countTableAppearances(t, tableAppearances)
tableAppearances = tableAppearances or {}
if type(t) == "table" then
if not tableAppearances[t] then
tableAppearances[t] = 1
for k, v in rawpairs(t) do
countTableAppearances(k, tableAppearances)
countTableAppearances(v, tableAppearances)
end
countTableAppearances(getmetatable(t), tableAppearances)
else
tableAppearances[t] = tableAppearances[t] + 1
end
end
return tableAppearances
end
local copySequence = function(s)
local copy, len = {}, #s
for i = 1, len do
copy[i] = s[i]
end
return copy, len
end
local function makePath(path, ...)
local keys = {...}
local newPath, len = copySequence(path)
for i = 1, #keys do
newPath[len + i] = keys[i]
end
return newPath
end
local function processRecursive(process, item, path, visited)
if item == nil then
return nil
end
if visited[item] then
return visited[item]
end
local processed = process(item, path)
if type(processed) == "table" then
local processedCopy = {}
visited[item] = processedCopy
local processedKey
for k, v in rawpairs(processed) do
processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited)
if processedKey ~= nil then
processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited)
end
end
local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited)
if type(mt) ~= "table" then
mt = nil
end -- ignore not nil/table __metatable field
setmetatable(processedCopy, mt)
processed = processedCopy
end
return processed
end
-------------------------------------------------------------------
local Inspector = {}
local Inspector_mt = {
__index = Inspector,
}
function Inspector:puts(...)
local args = {...}
local buffer = self.buffer
local len = #buffer
for i = 1, #args do
len = len + 1
buffer[len] = args[i]
end
end
function Inspector:down(f)
self.level = self.level + 1
f()
self.level = self.level - 1
end
function Inspector:tabify()
self:puts(self.newline, string.rep(self.indent, self.level))
end
function Inspector:alreadyVisited(v)
return self.ids[v] ~= nil
end
function Inspector:getId(v)
local id = self.ids[v]
if not id then
local tv = type(v)
id = (self.maxIds[tv] or 0) + 1
self.maxIds[tv] = id
self.ids[v] = id
end
return tostring(id)
end
function Inspector:putKey(k)
if isIdentifier(k) then
return self:puts(k)
end
self:puts("[")
self:putValue(k)
self:puts("]")
end
function Inspector:putTable(t)
if t == inspect.KEY or t == inspect.METATABLE then
self:puts(tostring(t))
elseif self:alreadyVisited(t) then
self:puts("<table ", self:getId(t), ">")
elseif self.level >= self.depth then
self:puts("{...}")
else
if self.tableAppearances[t] > 1 then
self:puts("<", self:getId(t), ">")
end
local nonSequentialKeys, nonSequentialKeysLength, sequenceLength = getNonSequentialKeys(t)
local mt = getmetatable(t)
self:puts("{")
self:down(function()
local count = 0
for i = 1, sequenceLength do
if count > 0 then
self:puts(",")
end
self:puts(" ")
self:putValue(t[i])
count = count + 1
end
for i = 1, nonSequentialKeysLength do
local k = nonSequentialKeys[i]
if count > 0 then
self:puts(",")
end
self:tabify()
self:putKey(k)
self:puts(" = ")
self:putValue(t[k])
count = count + 1
end
if type(mt) == "table" then
if count > 0 then
self:puts(",")
end
self:tabify()
self:puts("<metatable> = ")
self:putValue(mt)
end
end)
if nonSequentialKeysLength > 0 or type(mt) == "table" then -- result is multi-lined. Justify closing }
self:tabify()
elseif sequenceLength > 0 then -- array tables have one extra space before closing }
self:puts(" ")
end
self:puts("}")
end
end
function Inspector:putValue(v)
local tv = type(v)
if tv == "string" then
self:puts(smartQuote(escape(v)))
elseif tv == "number" or tv == "boolean" or tv == "nil" or tv == "cdata" or tv == "ctype" then
self:puts(tostring(v))
elseif tv == "table" then
self:putTable(v)
else
self:puts("<", tv, " ", self:getId(v), ">")
end
end
-------------------------------------------------------------------
function inspect.inspect(root, options)
options = options or {}
local depth = options.depth or math.huge
local newline = options.newline or "\n"
local indent = options.indent or " "
local process = options.process
if process then
root = processRecursive(process, root, {}, {})
end
local inspector = setmetatable({
depth = depth,
level = 0,
buffer = {},
ids = {},
maxIds = {},
newline = newline,
indent = indent,
tableAppearances = countTableAppearances(root),
}, Inspector_mt)
inspector:putValue(root)
return table.concat(inspector.buffer)
end
setmetatable(inspect, {
__call = function(_, ...)
return inspect.inspect(...)
end,
})
return inspect

@ -1,223 +1,76 @@
---
-- @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 skynet = require "skynet"
local log_define = require "zlog.log_define"
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
local getinfo = debug.getinfo
local LOG_LEVEL = log_define.LOG_LEVEL
local DEFAULT_CATEGORY = log_define.DEFAULT_CATEGORY
local log_format = log_define.format
max_lvl = assert(lvl2number(max_lvl or LOG_LVL.INFO))
local category_addr = {}
if not writer then
writer = require"log.writer.stdout".new()
local function get_service(category)
local addr = category_addr[category]
if addr then
return addr
end
if not formatter then
formatter = require"log.formatter.default".new()
local root_addr = skynet.localname(".logger")
if not root_addr then
-- no logger config
root_addr = skynet.uniqueservice("logger")
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
addr = skynet.call(root_addr, "lua", "get_service", category)
category_addr[category] = addr
return addr
end
function logger.set_writer(value)
assert(value)
writer, value = value, writer
return value
end
local function sendlog(category, level, ...)
local di = getinfo(3, "Sl")
local msg = log_format(skynet.self(), level, di, ...)
skynet.call(get_service(category), "lua", "log", level, msg)
end
function logger.set_formatter(value)
assert(value)
formatter, value = value, formatter
return value
end
local M = {}
function logger.set_format(value)
assert(value)
logformat, value = value, logformat
return value
end
function M.d(...)
sendlog(DEFAULT_CATEGORY, LOG_LEVEL.DEBUG, ...)
end
function logger.log(lvl, ...)
local err
lvl, err = lvl2number(lvl)
if not lvl then
return nil, err
end
return write(lvl, ...)
end
function M.d2(category, ...)
sendlog(category, LOG_LEVEL.DEBUG, ...)
end
function logger.dump(lvl, ...)
local err
lvl, err = lvl2number(lvl)
if not lvl then
return nil, err
end
return dump(lvl, ...)
end
function M.i(...)
sendlog(DEFAULT_CATEGORY, LOG_LEVEL.INFO, ...)
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
function M.i2(category, ...)
sendlog(category, LOG_LEVEL.INFO, ...)
end
assert(logger.set_lvl(max_lvl))
function M.w(...)
sendlog(DEFAULT_CATEGORY, LOG_LEVEL.WARN, ...)
end
loggers_list[logger] = true;
function M.w2(category, ...)
sendlog(category, LOG_LEVEL.WARN, ...)
end
return logger
function M.e(...)
sendlog(DEFAULT_CATEGORY, LOG_LEVEL.ERROR, ...)
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
function M.e2(category, ...)
sendlog(category, LOG_LEVEL.ERROR, ...)
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
function M.f(...)
sendlog(DEFAULT_CATEGORY, LOG_LEVEL.FATAL, ...)
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 = {}
function M.f2(category, ...)
sendlog(category, LOG_LEVEL.FATAL, ...)
end
return Log
return M

@ -0,0 +1,77 @@
local string_format = string.format
local tconcat = table.concat
local os_date = os.date
local string_sub = string.sub
local os_clock = os.clock
local ESC = string.char(27, 91)
local RESET = ESC .. '0m'
local M = {}
M.LOG_LEVEL = {
DEBUG = 1,
INFO = 2,
WARN = 3,
ERROR = 4,
FATAL = 5,
}
M.LOG_LEVEL_NAME = {
[1] = "DEBUG",
[2] = "INFO ",
[3] = "WARN ",
[4] = "ERROR",
[5] = "FATAL",
}
M.LOG_COLOR = {
[1] = ESC .. '34m',
[2] = ESC .. '32m',
[3] = ESC .. '33m',
[4] = ESC .. '31m',
[5] = ESC .. '35m',
}
M.DEFAULT_CATEGORY = "root"
function M.log_dir(log_root, date)
return string_format("%s/%04d-%02d-%02d", log_root or ".", date.year, date.month, date.day)
end
function M.log_path(dir, prefix, category, date)
return string_format("%s/%s%s_%04d-%02d-%02d.log", dir or ".", prefix or "", category or M.DEFAULT_CATEGORY,
date.year, date.month, date.day)
end
function M.format(addr, level, di, ...)
local param = {...}
local date = os_date("*t")
local ms = string_sub(os_clock(), 3, 6)
local time = string_format("%02d:%02d:%02d.%02d", date.hour, date.min, date.sec, ms)
local fileline = ""
if di then
fileline = (" [%s:%d]"):format(di.short_src, di.currentline)
end
for k, v in pairs(param) do
param[k] = tostring(v)
end
local msg =
string_format("[:%08x][%s][%s] %s%s", addr, M.LOG_LEVEL_NAME[level], time, tconcat(param, " "), fileline)
return msg
end
function M.color(level, msg)
local c = M.LOG_COLOR[level]
if not c then
return msg
end
return c .. msg .. RESET
end
return M

@ -1,18 +0,0 @@
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

@ -1,11 +0,0 @@
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

@ -1,36 +0,0 @@
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

@ -1,148 +0,0 @@
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

@ -1,101 +0,0 @@
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

@ -1,22 +0,0 @@
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

@ -1,71 +0,0 @@
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

@ -1,10 +0,0 @@
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

@ -1,42 +0,0 @@
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

@ -1,18 +0,0 @@
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

@ -1,21 +0,0 @@
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

@ -1 +0,0 @@
return require "log.writer.stdout"

@ -1,334 +0,0 @@
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

@ -1,17 +0,0 @@
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

@ -1,17 +0,0 @@
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

@ -1,484 +0,0 @@
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

@ -1,18 +0,0 @@
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

@ -1 +0,0 @@
return require "log.writer.filter.lvl.le"

@ -1,14 +0,0 @@
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

@ -1,14 +0,0 @@
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

@ -1,9 +0,0 @@
local M = {}
function M.new(newfmt, writer)
return function(oldfmt, msg, lvl, now)
writer(newfmt, msg, lvl, now)
end
end
return M

@ -1,42 +0,0 @@
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

@ -1,13 +0,0 @@
local M = {}
function M.new(...)
local writers = {...}
return function(...)
for i = 1, #writers do
writers[i](...)
end
end
end
return M

@ -1,29 +0,0 @@
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

@ -1,55 +0,0 @@
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

@ -1,26 +0,0 @@
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

@ -1,19 +0,0 @@
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

@ -1 +0,0 @@
return require "log.writer.net.zmq.pub"

@ -1,150 +0,0 @@
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,
}

@ -1,74 +0,0 @@
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,
}

@ -1 +0,0 @@
return require"log.writer.net.zmq._private.impl".init('PUB', false)

@ -1 +0,0 @@
return require"log.writer.net.zmq._private.impl".init('PUSH', false)

@ -1 +0,0 @@
return require"log.writer.net.zmq._private.impl".init('PUB', true)

@ -1,9 +0,0 @@
local M = {}
function M.new(prefix, writer)
return function(fmt, msg, lvl, now)
writer(fmt, prefix .. msg, lvl, now)
end
end
return M

@ -1,11 +0,0 @@
local io = require "io"
local M = {}
function M.new()
return function(fmt, ...)
io.stderr:write(fmt(...), '\n')
end
end
return M

@ -1,11 +0,0 @@
local io = require "io"
local M = {}
function M.new()
return function(fmt, ...)
io.stdout:write(fmt(...), '\n')
end
end
return M

@ -0,0 +1,177 @@
local skynet = require "skynet"
local log_define = require "zlog.log_define"
local queue = require "skynet.queue"
require "skynet.manager"
local LOG_LEVEL = log_define.LOG_LEVEL
local DEFAULT_CATEGORY = log_define.DEFAULT_CATEGORY
local log_format = log_define.format
local color = log_define.color
local string_match = string.match
local skynet_env = require("skynet.env")
local nodeid = skynet_env.get("nodeid")
local zenv = require("zenv.init")
local define = zenv.Logger
local log_root = define.log_root
local log_level = define.log_level or LOG_LEVEL.INFO
local log_console = define.log_console
local name = zenv.get_node_conf(nodeid).name
local log_prefix = name .. "_"
local last_day = -1
local category = ...
local is_master = not category
local category_addr = {}
local lock = queue()
local file
local function close_file()
if not file then
return
end
file:close()
file = nil
end
local function open_file(date)
date = date or os.date("*t")
local dir = log_define.log_dir(log_root, date)
if not os.rename(dir, dir) then
os.execute("mkdir -p " .. dir)
end
if file then
close_file()
end
local path = log_define.log_path(dir, log_prefix, category, date)
local f, e = io.open(path, "a")
if not f then
print("logger error:", tostring(e))
return
end
file = f
last_day = date.day
end
local CMD = {}
function CMD.console(level, msg)
print(color(level, msg))
end
function CMD.log(level, msg)
if level < log_level then
return
end
msg = msg or ""
local date = os.date("*t")
if not file or date.day ~= last_day then
open_file(date)
end
file:write(msg .. '\n')
file:flush()
if log_console then
if is_master then
CMD.console(level, msg)
else
skynet.call(".logger", "lua", "console", level, msg)
end
end
end
function CMD.set_console(is_open)
log_console = is_open
if is_master then
for _, addr in pairs(category_addr) do
skynet.call(addr, "lua", "set_console", is_open)
end
end
end
function CMD.set_level(level)
log_level = level
if is_master then
for _, addr in pairs(category_addr) do
skynet.call(addr, "lua", "set_level", level)
end
end
end
function CMD.get_service(cate)
if not is_master then
return
end
local addr
lock(function()
addr = category_addr[cate]
if not addr then
addr = skynet.newservice("logger", cate)
category_addr[cate] = addr
end
end)
return addr
end
if is_master then
skynet.info_func(function()
return {
log_console = log_console,
log_level = log_level,
}
end)
skynet.register_protocol {
name = "text",
id = skynet.PTYPE_TEXT,
unpack = skynet.tostring,
dispatch = function(_, addr, msg)
local level = LOG_LEVEL.INFO
if string_match(msg, "maybe in an endless loop") then
level = LOG_LEVEL.WARN
end
if string_match(msg, "stack traceback:") then
level = LOG_LEVEL.ERROR
end
msg = log_format(addr, level, nil, msg)
CMD.log(level, msg)
end,
}
skynet.register_protocol {
name = "SYSTEM",
id = skynet.PTYPE_SYSTEM,
unpack = function(...)
return ...
end,
dispatch = function(_, addr)
local level = LOG_LEVEL.FATAL
local msg = log_format(addr, level, nil, "SIGHUP")
CMD.log(level, msg)
end,
}
category_addr[DEFAULT_CATEGORY] = skynet.self()
skynet.register(".logger")
end
open_file()
skynet.start(function()
skynet.dispatch("lua", function(_, _, cmd, ...)
local f = CMD[cmd]
assert(f, cmd)
return skynet.retpack(f(...))
end)
end)
Loading…
Cancel
Save