腾讯tlog模拟实现

说明

基于腾讯tlog接口实现具体代码
接入腾讯的项目都要接入tlog事件日志收集系统,但是很多项目后期才会接入
做了这个符合tlog接口的功能,这样游戏前期就可以有自己的event-log用了
数据配置规则参考

接入层代码(伪)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// tlog_interface.h
namespace tlog
{
class tlog_category_interface;
class tlog_ctx_interface;
};

typedef tlog::tlog_ctx_interface* LPTLOGCTX;
typedef tlog::tlog_category_interface* LPLOGCATEGORYINST;

LPTLOGCTX tlog_init_from_file(const char*);
LPLOGCATEGORYINST tlog_get_category(LPTLOGCTX, const char*);

void tlog_info(LPLOGCATEGORYINST, size_t, size_t, const char*);

int tlog_fini_ctx(LPTLOGCTX*);

// tlog_base.h
namespace tlog{
class tlog_category_interface
{
public:
~tlog_category_interface(){}
virtual void exec(const char*) = 0;
virtual int release() = 0;
};

class tlog_ctx_interface
{
public:
~tlog_ctx_interface(){}
virtual tlog_category_interface* get_category(const char*) = 0;
virtual int release();
};

enum EN_DRVICE_TYPE
{
en_device_file,
en_device_net,
};

struct stDevice
{
EN_DRVICE_TYPE _enType;
stDevice(EN_DRVICE_TYPE enType)
:_enType(enType)
{}
virtual EN_DRVICE_TYPE get_type() { return _enType; }

virtual bool init(pugi::xml_node node) = 0;
virtual int release() = 0;
virtual void exec(const char* pszData) = 0;
};

struct stDeviceFile : public stDevice
{
std::string _pattern; // 路径
uint32_t _bufferSize; // 大小
std::string _data; // 内容
std::ofstream _out; // 句柄

stDeviceFile()
: stDevice(en_device_file)
{}

virtual bool init(pugi::xml_node node)
{
// 初始化数据成员
// 打开要写的文件
}
virtual int release()
{
// 如果 _data 里面还有内容,写入到文件
// 关闭文件
}
virtual void exec(const char* pStr)
{
_data.append(pStr);
_data.append("\n");
if (_data.size() >= _bufferSize)
{
_out << _data;
_data.clear();
}
}
};
struct stDeviceNet : public stDevice
{
char _protocol[32];
char _ip[64];
char _port[8];
int _sock_c;

stDeviceNet()
: stDevice(en_device_net)
{}

virtual bool init(pugi::xml_node node)
{
// 初始化数据成员
// 链接到UDPServer
}
virtual int release()
{

}
virtual void exec(const char* pStr)
{
sockaddr_in ser;
bzero(&ser, sizeof(ser));
ser.sin_family = AF_INET;
ser.sin_port = htons(atoi(_port));
ser.sin_addr.s_addr = inet_addr(_ip);
int nlen = sizeof(ser);

sendto(_sock_c, pStr, strlen(pStr) + 1, 0, (sockaddr*)&ser, nlen);
}
};

class tlog_category : public tlog_category_interface
{
private:
std::string _name;
std::vector<stDevice*> _vecDevice;
public:
bool init(pugi::xml_node node)
{
// 解析配置,初始化数据成员
}

virtual int release()
{
// vec data release;
// destroy vec;
}

virtual void exec(const char* pstr)
{
// for (auto& itr : _vec)
// if (itr) itr->exec(pstr);
}
};

class tlog_ctx : public tlog_ctx_interface
{
private:
std::unordered_map<std::string, tlog_category*> _mapCate;
public:
bool init(const char* pszPath)
{
// 初始化 _mapCate
}

virtual int release()
{
// _mapCate data release
// destroy _mapCate
}

virtual tlog_category_interface* get_category(const char* name)
{
return _mapCate[name];
}
};

};

// tlog_base.cpp

LPTLOGCTX tlog_init_from_file(const char* pszPath)
{
tlog_ctx* pCtx = new tlog_ctx;
pCtx->init(pszPath);
return pCtx;
}

LPLOGCATEGORYINST tlog_get_category(LPTLOGCTX pstCtx, const char* pszName)
{
if (pstCtx)
{
return pstCtx->get_category(pszName);
}
return NULL;
}

void tlog_info(LPLOGCATEGORYINST pCat, size_t id, size_t, cls, const char* pszFmt, ...)
{
if (!pCat) return;
va_list vArg;
va_start(vArg, pszFmt);
char buffer[MAX_SIZE] = {0};
// char* pbuffer = buffer;
// int len = _vsnprintf(pszFmt, vArg);
// if (len < 0)
// {
// va_end(vArg);
// return;
// }
// if (len >= MAX_SIZE)
// pbuffer = (char*)malloc(len + 1);
vsnprintf(buffer, MAX_SIZE, pszFmt, vArg);

pCat->exec(buffer);

va_end(vArg);
}

int tlog_fini_ctx(LPTLOGCTX* ppstCtx)
{
if (ppstCtx && *ppstCtx)
{
(*ppstCtx)->release();
*ppstCtx = NULL;
return 0;
}
return -1;
}

解析xml工具代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// tlog_sql.h

struct stEntry
{
std::string _name;
std::string _oldname;
std::string _type;
uint32_t _size;
std::string _desc;
bool _index;
};

struct stStruct
{
std::string _name;
std::string _oldname;
std::string _desc;
std::unordered_map<std::string, stEntry> _mapEntry;
};

/*
解析xml到三个文件:.sql .h .cpp
.sql
procedure
-- alter table name
IF EXIST (select * from information_schema.TABLES) WHERE table_name='...') THEN ALTER TABLE ... RENAME TO ... END IF;
-- new table
IF NOT EXIST (select * from information_schema.TABLES) WHERE table_name='...') THEN CREATE TABLE .... END IF;
-- alter column name
IF EXIST (select * from information_schema.COLUMNS) WHERE table_name='...' and column_name='...') THEN ALTER TABLE ... END IF;
-- add column
IF NOT EXIST (select * from information_schema.COLUMNS) WHERE table_name='...' and column_name='...') THEN ALTER TABLE ... END IF;
所有字段都这么组织一下sql语句,没有oldname的话就没有相关sql语句
.h interface
class data_basic
{
public:
virtual bool ss_split(const char* str) = 0;
virtual const char* get_sql() = 0;
};

template<class data_basic>
data_basic* getTlogData()
{
static data_base Inst;
return &Inst;
}
每一个struct都继承data_basic, name作为类名,entry内容作为数据成员
ss_split: 解析传入的 name|...|...|... 格式字符串到数据成员,组织DB INSERT语句
get_sql: 返回DB INSERT语句
.cpp
data_base* getTLogData(const char* name)
{
if (strcmp(name, "...") == 0)
return getTLogData<...>();
// .... else if () ....
return NULL;
}
... 是struct的名字,
*/

接收端

接收端大概是收到udp消息,用xml解析工具生成的 .h .cpp函数,插入到数据库
当然也可以基于es + kafka做一个更简单的服务器

备忘

** MySQL 的”utf8mb4”才是真正的”UTF-8” **

------ 本文结束 ------
------ 版权声明:转载请注明出处 ------