Commit df883a114d53e40339eded5e5d209566565812ce
1 parent
cfeb432d
日志处理
Showing
2 changed files
with
228 additions
and
126 deletions
Show diff stats
src/models/RoleLog.lua
1 | 1 | ||
2 | - | ||
3 | --- logType 日志 mapping 信息(确定后不能更改类型) 修改需要设置 es 日志mapping | ||
4 | - | ||
5 | - | ||
6 | - | 2 | +-- logType |
7 | local LogType = { | 3 | local LogType = { |
8 | - create = { | ||
9 | - ip = "string" | ||
10 | - }, | ||
11 | - login = { | ||
12 | - ip = "string" | ||
13 | - }, | ||
14 | - logout = { | ||
15 | - online = "number" | ||
16 | - }, | ||
17 | - | 4 | + create = "common", |
5 | + login = "common", | ||
6 | + logout = "common", | ||
18 | } | 7 | } |
19 | 8 | ||
9 | +-- 如要修改 要提前修改 _template mapping -- 对应 mapping 为 gamelog-* | ||
10 | +local Mapping = { | ||
11 | + -- 预留一些数据格式 | ||
12 | + common = { | ||
13 | + desc = "keyword",--索引的短字符串 | ||
14 | + ucode = "keyword",--关联日志对应ucode | ||
15 | + key1 = "keyword", --可索引的短字符串 | ||
16 | + key2 = "keyword", --可索引的短字符串 | ||
17 | + -- 几乎不用的长文本 | ||
18 | + text1 = "text", --长字符串不索引的类型 | ||
19 | + -- 五个不同类型的数字 基本上满足数量要求 尽量从低到高用 | ||
20 | + short1 = "short", | ||
21 | + int1 = "integer", | ||
22 | + int2 = "integer", | ||
23 | + long1 = "long", | ||
24 | + float1 = "float", | ||
25 | + } | ||
26 | +} | ||
20 | 27 | ||
21 | --- 所有的日志都包括的部分 role 里面的信息 | ||
22 | -local commonField = { | ||
23 | - name = "string", | ||
24 | - id = "number", | ||
25 | - uid = "string", | ||
26 | - sId = "number", | ||
27 | - device = "string", | ||
28 | - ctime = "number", | ||
29 | - ltime = "number", | ||
30 | - level = "number", | ||
31 | - rmbC = "number", | 28 | +-- 所有的日志都包括的部分 role 里面的信息 -- mapping 信息在 gamelog-role |
29 | +local commonRoleField = { | ||
30 | + name = "keyword", | ||
31 | + id = "integer", | ||
32 | + uid = "keyword", | ||
33 | + sid = "short", | ||
34 | + device = "keyword", | ||
35 | + ctime = "integer", | ||
36 | + ltime = "integer", | ||
37 | + level = "short", | ||
38 | + rmbC = "integer", | ||
32 | } | 39 | } |
33 | 40 | ||
34 | -local RoleLog = {} | ||
35 | 41 | ||
36 | -function RoleLog.bind(Role) | 42 | +local function checkType(logType, field, value, ctype) |
43 | + local typecheckfunc = { | ||
44 | + keyword = function() | ||
45 | + --长度不超过256 | ||
46 | + if type(value) ~= "string" then | ||
47 | + value = tostring(value) | ||
48 | + print(string.format("LOG ERROR: logType [%s] field [%s] isn't [keyword]", logType, field)) | ||
49 | + else | ||
50 | + if #value > 256 then | ||
51 | + print(string.format("LOG ERROR: logType [%s] field [%s] [keyword] type to long.", logType, field)) | ||
52 | + end | ||
53 | + end | ||
54 | + return value | ||
55 | + end, | ||
56 | + text = function() | ||
57 | + if type(value) ~= "string" then | ||
58 | + value = tostring(value) | ||
59 | + print(string.format("LOG ERROR: logType [%s] field [%s] isn't [text]", logType, field)) | ||
60 | + end | ||
61 | + return value | ||
62 | + end, | ||
63 | + integer = function() | ||
64 | + if type(value) ~= "number" then | ||
65 | + value = tonumber(value) | ||
66 | + print(string.format("LOG ERROR: logType [%s] field [%s] isn't [integer]", logType, field)) | ||
67 | + end | ||
68 | + if value then | ||
69 | + if math.type(value) ~= "integer" then | ||
70 | + local oldValue = value | ||
71 | + value = math.floor(value) | ||
72 | + if value ~= oldValue then | ||
73 | + print(string.format("LOG ERROR: logType [%s] field [%s] isn't [integer], is float", logType, field)) | ||
74 | + end | ||
75 | + end | ||
76 | + if -2147483648 > value or value > 2147483647 then | ||
77 | + value = nil | ||
78 | + print(string.format("LOG ERROR: logType [%s] field [%s] isn't [integer], too big", logType, field)) | ||
79 | + end | ||
80 | + end | ||
81 | + return value | ||
82 | + end, | ||
83 | + short = function() | ||
84 | + if type(value) ~= "number" then | ||
85 | + value = tonumber(value) | ||
86 | + print(string.format("LOG ERROR: logType [%s] field [%s] isn't [short]", logType, field)) | ||
87 | + end | ||
88 | + if value then | ||
89 | + if math.type(value) ~= "integer" then | ||
90 | + local oldValue = value | ||
91 | + value = math.floor(value) | ||
92 | + if value ~= oldValue then | ||
93 | + print(string.format("LOG ERROR: logType [%s] field [%s] isn't [short], is float", logType, field)) | ||
94 | + end | ||
95 | + end | ||
96 | + | ||
97 | + if -32768 > value or value > 32768 then | ||
98 | + value = nil | ||
99 | + print(string.format("LOG ERROR: logType [%s] field [%s] isn't [short], too big", logType, field)) | ||
100 | + end | ||
101 | + end | ||
102 | + return value | ||
103 | + end, | ||
104 | + long = function() | ||
105 | + if type(value) ~= "number" then | ||
106 | + value = tonumber(value) | ||
107 | + print(string.format("LOG ERROR: logType [%s] field [%s] isn't [long]", logType, field)) | ||
108 | + end | ||
109 | + if value then | ||
110 | + if math.type(value) ~= "integer" then | ||
111 | + local oldValue = value | ||
112 | + value = math.floor(value) | ||
113 | + if type(value) ~= "integer" then | ||
114 | + value = nil | ||
115 | + print(string.format("LOG ERROR: logType [%s] field [%s] isn't [long], too big", logType, field)) | ||
116 | + elseif value ~= oldValue then | ||
117 | + print(string.format("LOG ERROR: logType [%s] field [%s] isn't [long], is float", logType, field)) | ||
118 | + end | ||
119 | + end | ||
120 | + end | ||
121 | + return value | ||
122 | + end, | ||
123 | + float = function() | ||
124 | + if type(value) ~= "number" then | ||
125 | + value = tonumber(value) | ||
126 | + print(string.format("LOG ERROR: logType [%s] field [%s] isn't [float]", logType, field)) | ||
127 | + end | ||
128 | + return value | ||
129 | + end, | ||
130 | + } | ||
131 | + | ||
132 | + if typecheckfunc[ctype] then | ||
133 | + return typecheckfunc[ctype]() | ||
134 | + else | ||
135 | + print(string.format("LOG ERROR: logType [%s] field [%s] have a new type [%s] need add check.", logType, field, ctype)) | ||
136 | + return nil | ||
137 | + end | ||
138 | +end | ||
37 | 139 | ||
140 | +local RoleLog = {} | ||
141 | +function RoleLog.bind(Role) | ||
38 | function Role:log(logType, contents) | 142 | function Role:log(logType, contents) |
39 | - | ||
40 | - local _logType = LogType[logType] | ||
41 | - if not _logType then return end | ||
42 | - | ||
43 | - | ||
44 | - if not logd then return end | ||
45 | - | ||
46 | contents = contents or {} | 143 | contents = contents or {} |
144 | + local _logType = LogType[logType] | ||
145 | + if not _logType then | ||
146 | + print(string.format("LOG ERROR: new logType [%s] need Add Maping.", logType)) | ||
147 | + return | ||
148 | + end | ||
47 | local doc = {} | 149 | local doc = {} |
48 | - for field, _typ in pairs(commonField) do | ||
49 | - doc[field] = self:getProperty(field) | 150 | + for field, ctype in pairs(commonRoleField) do |
151 | + if contents[field] then | ||
152 | + print(string.format("LOG ERROR: logType [%s] had field [%s] overwrite default.", logType, field)) | ||
153 | + end | ||
154 | + doc[field] = checkType("commonRoleField", field, self:getProperty(field), ctype) | ||
50 | end | 155 | end |
156 | + | ||
157 | + local mapping = Mapping[_logType] | ||
158 | + | ||
51 | for field, value in pairs(contents) do | 159 | for field, value in pairs(contents) do |
52 | - if _logType[field] then | ||
53 | - doc[field] = value | 160 | + local ftype = mapping[field] |
161 | + if ftype then | ||
162 | + doc[field] = checkType(logType, field, value, ftype) | ||
54 | else | 163 | else |
55 | - print(string.format("LOG ERROR: %s had new field %s no type.", logType, field)) | 164 | + print(string.format("LOG ERROR: logType [%s] have new field [%s] no type in mapping.", logType, field)) |
56 | end | 165 | end |
57 | end | 166 | end |
58 | - | ||
59 | - doc.mid = doc.uid:sub(-2, -1) | ||
60 | - pcall(skynet.send, logd, "lua", "log", logType, doc) | 167 | + if not logd then return end |
168 | + pcall(skynet.send, logd, "lua", "log", logType, doc, _logType) | ||
61 | end | 169 | end |
62 | - | ||
63 | end | 170 | end |
64 | - | ||
65 | return RoleLog | 171 | return RoleLog |
66 | \ No newline at end of file | 172 | \ No newline at end of file |
src/services/logd.lua
1 | local skynet = require "skynet" | 1 | local skynet = require "skynet" |
2 | -local mongo = require "mongo" | ||
3 | local queue = require "skynet.queue" | 2 | local queue = require "skynet.queue" |
4 | local bson = require "bson" | 3 | local bson = require "bson" |
5 | -local socketdriver = require "socketdriver" | 4 | +local socketdriver = require "skynet.socketdriver" |
6 | 5 | ||
7 | local serverId = tonumber(skynet.getenv("servId")) | 6 | local serverId = tonumber(skynet.getenv("servId")) |
8 | 7 | ||
@@ -14,105 +13,102 @@ local pairs = pairs | @@ -14,105 +13,102 @@ local pairs = pairs | ||
14 | local ipairs = ipairs | 13 | local ipairs = ipairs |
15 | local string_format = string.format | 14 | local string_format = string.format |
16 | 15 | ||
17 | -local CMD, cache, client, cs = {}, {} | ||
18 | -local auto = {} | ||
19 | - | ||
20 | -local rsyslog_fd | ||
21 | - | ||
22 | --- function getNextSequence(collect) | ||
23 | --- local index = auto[collect] | ||
24 | --- if not index then | ||
25 | --- local res = client.counters:findAndModify({ | ||
26 | --- query = {}, | ||
27 | --- update = {["$inc"] = {[collect] = 1}}, | ||
28 | --- upsert = true, | ||
29 | --- new = true, | ||
30 | --- }) | ||
31 | --- index = res.value[collect] | ||
32 | --- else | ||
33 | --- index = index + 1 | ||
34 | --- end | ||
35 | --- auto[collect] = index | ||
36 | --- return index | ||
37 | --- end | ||
38 | - | ||
39 | -local dateTypes = {"timestamp", "createTime", "lastLoginTime"} | ||
40 | -local stringTypes = {"s1", "s2", "s3"} | ||
41 | -local intTypes = {"int1", "int2", "int3", "int4"} | ||
42 | -function CMD.log(collect, doc) | ||
43 | - -- write to rsyslog | ||
44 | - local now = skynet.timex() | ||
45 | - doc["timestamp"] = now | ||
46 | - doc["server"] = serverId | 16 | +local CMD, cs = {} |
17 | +local log_fd, connecting = nil , false | ||
18 | + | ||
19 | + | ||
20 | +local socket_message = {} | ||
21 | +-- read skynet_socket.h for these macro | ||
22 | +-- SKYNET_SOCKET_TYPE_DATA = 1 | ||
23 | +socket_message[1] = function(id, size, data) | ||
24 | + skynet.error(string.format("LOG SOCKET: data: ", skynet.tostring(data, size))) | ||
25 | + socketdriver.drop(data, size) | ||
26 | +end | ||
27 | + | ||
28 | +-- SKYNET_SOCKET_TYPE_CONNECT = 2 | ||
29 | +socket_message[2] = function(id, _ , addr) | ||
30 | + skynet.error("LOG SOCKET: connect: ", addr) | ||
31 | + connecting = false | ||
32 | +end | ||
33 | + | ||
34 | +-- SKYNET_SOCKET_TYPE_CLOSE = 3 | ||
35 | +socket_message[3] = function(id) | ||
36 | + skynet.error("LOG SOCKET: closed") | ||
37 | + connecting = false | ||
38 | +end | ||
39 | + | ||
40 | +-- SKYNET_SOCKET_TYPE_ERROR = 5 | ||
41 | +socket_message[5] = function(id, _, err) | ||
42 | + skynet.error("LOG SOCKET: error: ", err) | ||
43 | + connecting = false | ||
44 | +end | ||
47 | 45 | ||
48 | - for _, field in pairs(stringTypes) do | ||
49 | - if doc[field] then | ||
50 | - doc[field] = tostring(doc[field]) | 46 | + |
47 | +skynet.register_protocol { | ||
48 | + name = "socket", | ||
49 | + id = skynet.PTYPE_SOCKET, -- PTYPE_SOCKET = 6 | ||
50 | + unpack = socketdriver.unpack, | ||
51 | + dispatch = function (_, _, t, ...) | ||
52 | + if socket_message[t] then | ||
53 | + socket_message[t](...) | ||
51 | end | 54 | end |
52 | end | 55 | end |
53 | - for _, field in pairs(intTypes) do | ||
54 | - if doc[field] then | ||
55 | - doc[field] = tonumber(doc[field]) | 56 | +} |
57 | + | ||
58 | + | ||
59 | + | ||
60 | +-- 日志 index 不包含 日期的 index_suffix | ||
61 | +local IndexNoDate = { | ||
62 | + online = true, | ||
63 | +} | ||
64 | +-- 不走 role log 的日志都要自行注意 mapping 设置【重要】 | ||
65 | +-- index_suffix index 后缀 默认为 common | ||
66 | +function CMD.log(logType, doc, index_suffix) | ||
67 | + index_suffix = index_suffix or "common" | ||
68 | + if index_suffix == "common" then | ||
69 | + doc["@type"] = logType | ||
70 | + else | ||
71 | + if logType ~= index_suffix then -- 定制后缀 不一定有type 不相等时才有type | ||
72 | + doc["@type"] = logType | ||
56 | end | 73 | end |
57 | end | 74 | end |
58 | - for _, field in pairs(dateTypes) do | ||
59 | - if doc[field] then | ||
60 | - doc[field .. "_t"] = tonumber(os.date("%Y%m%d%H%M%S", doc[field])) | 75 | + |
76 | + local now = skynet.timex() | ||
77 | + doc["timestamp"] = now | ||
78 | + doc["@timestamp"] = os.date("%Y-%m-%d %H:%M:%S", now) | ||
79 | + doc["server"] = serverId | ||
80 | + | ||
81 | + -- 自己加好 index | ||
82 | + if IndexNoDate[index_suffix] then | ||
83 | + doc["@index"] = string.format("gamelog-%s", index_suffix) | ||
84 | + else | ||
85 | + doc["@index"] = string.format("gamelog-%s-%s", os.date("%Y%m%d", now), index_suffix) | ||
86 | + end | ||
87 | + if not socketdriver.send(log_fd, json.encode(doc) .. "\n") then | ||
88 | + if not connecting then | ||
89 | + CMD.open() -- 连一下试试 | ||
61 | end | 90 | end |
62 | end | 91 | end |
63 | - -- Local-6 + Info | ||
64 | - socketdriver.send(rsyslog_fd, string.format("<182>%s: %s\n", collect, json.encode(doc))) | ||
65 | - | ||
66 | - -- if not cache[collect] then cache[collect] = {} end | ||
67 | - -- doc["timestamp"] = now | ||
68 | - -- doc["_id"] = getNextSequence(collect) | ||
69 | - -- table_insert(cache[collect], doc) | ||
70 | end | 92 | end |
71 | 93 | ||
72 | -function CMD.open(conf) | ||
73 | - rsyslog_fd = socketdriver.connect("127.0.0.1", 514) | ||
74 | - socketdriver.start(rsyslog_fd) | ||
75 | - | ||
76 | - -- local db = mongo.client { | ||
77 | - -- host = conf.mongohost, | ||
78 | - -- port = conf.mongoport or 27017, | ||
79 | - -- username = conf.mongouser, | ||
80 | - -- password = conf.mongopswd, | ||
81 | - -- } | ||
82 | - | ||
83 | - -- assert(db, "mongo connect error") | ||
84 | - | ||
85 | - -- local servId = skynet.getenv "servId" | ||
86 | - -- client = db["s"..servId] | 94 | +function CMD.open() |
95 | + log_fd = socketdriver.connect("127.0.0.1", 5170) | ||
96 | + connecting = true | ||
87 | end | 97 | end |
88 | 98 | ||
89 | --- local function __loop__() | ||
90 | --- while true do | ||
91 | --- cs(function () | ||
92 | --- for col, docs in pairs(cache) do | ||
93 | --- client[col]:batch_insert(docs) | ||
94 | --- client.counters:update({}, {["$set"]={[col]=auto[col]}}) | ||
95 | --- end | ||
96 | --- cache = {} | ||
97 | --- end) | ||
98 | --- skynet.sleep(200) | ||
99 | --- end | ||
100 | --- end | ||
101 | - | ||
102 | local function __init__() | 99 | local function __init__() |
103 | - skynet.dispatch("lua", function (_, _, command, ...) | 100 | + skynet.dispatch("lua", function (session, address, command, ...) |
104 | local f = CMD[command] | 101 | local f = CMD[command] |
105 | if command == "open" then | 102 | if command == "open" then |
106 | skynet.ret(skynet.pack(f(...))) | 103 | skynet.ret(skynet.pack(f(...))) |
107 | else | 104 | else |
108 | - local collect, doc = ... | ||
109 | - cs(function() f(collect, doc) end) | 105 | + local logType, doc, index_suffix = ... |
106 | + cs(function() f(logType, doc, index_suffix) end) | ||
110 | end | 107 | end |
111 | end) | 108 | end) |
112 | cs = queue() | 109 | cs = queue() |
113 | 110 | ||
114 | - -- skynet.fork(__loop__) | ||
115 | - skynet.register "LOGD" | 111 | + skynet.register(".LOGD") |
116 | end | 112 | end |
117 | 113 | ||
118 | skynet.start(__init__) | 114 | skynet.start(__init__) |