whatap 0.5.11 → 0.5.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/conf/config-default.js +2 -0
- package/lib/counter/counter-manager.js +4 -0
- package/lib/counter/task/metering-info.js +118 -0
- package/lib/observers/socket.io-observer.js +213 -74
- package/lib/observers/websocket-observer.js +164 -67
- package/lib/pack/apenum.js +8 -0
- package/lib/pack/otype.js +7 -0
- package/lib/pack/tagcount-pack.js +51 -50
- package/lib/util/dateutil.js +33 -1
- package/package.json +2 -2
|
@@ -275,6 +275,8 @@ var ConfigDefault = {
|
|
|
275
275
|
|
|
276
276
|
"prisma_read_func_name": str("prisma_read_func_name", "read"),
|
|
277
277
|
"prisma_database_url_name": str("prisma_database_url_name", "DATABASE_URL"),
|
|
278
|
+
|
|
279
|
+
"metering_tagcount_enabled": bool("metering_tagcount_enabled", false)
|
|
278
280
|
};
|
|
279
281
|
|
|
280
282
|
ConfigDefault._hook_method_ignore_prefix = ConfigDefault.hook_method_ignore_prefixes.split(',');
|
|
@@ -17,6 +17,7 @@ var CounterPack = require('../pack/counter-pack'),
|
|
|
17
17
|
GCStat = require('./task/gcstat'),
|
|
18
18
|
Sql = require('./task/sql'),
|
|
19
19
|
HttpC = require('./task/httpc'),
|
|
20
|
+
// MeteringInfo = require('./task/metering-info'),
|
|
20
21
|
StatError = require('../stat/stat-error'),
|
|
21
22
|
TagCounterPack = require('../pack/tagcount-pack'),
|
|
22
23
|
TraceContextManager = require('../trace/trace-context-manager'),
|
|
@@ -52,6 +53,9 @@ CounterManager.prototype.run = function () {
|
|
|
52
53
|
tasks.push(new GCStat());
|
|
53
54
|
tasks.push(new Sql());
|
|
54
55
|
tasks.push(new HttpC());
|
|
56
|
+
|
|
57
|
+
// metering
|
|
58
|
+
// tasks.push(new MeteringInfo());
|
|
55
59
|
self.intervalIndex = setInterval(function(){
|
|
56
60
|
self.process(tasks);
|
|
57
61
|
},5000);
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2025 the WHATAP project authors. All rights reserved.
|
|
3
|
+
* Use of this source code is governed by a license that
|
|
4
|
+
* can be found in the LICENSE file.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
var CounterTask = require('./counter-task'),
|
|
8
|
+
DataPackSender = require('../../data/datapack-sender'),
|
|
9
|
+
conf = require('../../conf/configure'),
|
|
10
|
+
DateUtil = require('../../util/dateutil'),
|
|
11
|
+
Logger = require('../../logger'),
|
|
12
|
+
fs = require('fs'),
|
|
13
|
+
TagCountPack = require('../../pack/tagcount-pack'),
|
|
14
|
+
OType = require('../../pack/otype'),
|
|
15
|
+
APEnum = require('../../pack/apenum')
|
|
16
|
+
|
|
17
|
+
function MeteringInfo() {
|
|
18
|
+
CounterTask.call(this);
|
|
19
|
+
this.first_connected = true;
|
|
20
|
+
this.lastTime = DateUtil.getFiveMinUnit(DateUtil.currentTime());
|
|
21
|
+
this.hostUUID = null;
|
|
22
|
+
this.csp = null;
|
|
23
|
+
this.hostUUIDFileName = '/sys/class/dmi/id/product_uuid';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
MeteringInfo.prototype = new CounterTask();
|
|
27
|
+
MeteringInfo.prototype.constructor = MeteringInfo;
|
|
28
|
+
|
|
29
|
+
MeteringInfo.prototype.process = function (p) {
|
|
30
|
+
if (!conf.getProperty('metering_tagcount_enabled', false)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (this.first_connected) {
|
|
35
|
+
this.first_connected = false;
|
|
36
|
+
this.doProcess(p);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const now = DateUtil.getFiveMinUnit(DateUtil.currentTime());
|
|
41
|
+
if (this.lastTime >= now) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this.lastTime = now;
|
|
46
|
+
this.doProcess(p);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
MeteringInfo.prototype.doProcess = function (p) {
|
|
50
|
+
const pk = new TagCountPack();
|
|
51
|
+
pk.category = 'metering';
|
|
52
|
+
pk.time = DateUtil.currentTime() / 300000 * 300000;
|
|
53
|
+
|
|
54
|
+
pk.putTagInt('otype', OType.AP);
|
|
55
|
+
pk.putTagInt('subtype', APEnum.AP_NODE);
|
|
56
|
+
pk.putTagInt('ip', p.host_ip);
|
|
57
|
+
|
|
58
|
+
const host_uuid = this.getHostUUID(this.hostUUIDFileName);
|
|
59
|
+
if (host_uuid) {
|
|
60
|
+
pk.putTagString('host_uuid', host_uuid);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const csp = this.getCsp();
|
|
64
|
+
if (csp) {
|
|
65
|
+
pk.putTagString('csp', csp)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
pk.putTagFloat(p.metering)
|
|
69
|
+
|
|
70
|
+
DataPackSender.sendTagCounterPack(pk);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
MeteringInfo.prototype.hasHostUUID = function (fileName) {
|
|
74
|
+
try{
|
|
75
|
+
return fs.existsSync(fileName);
|
|
76
|
+
}catch (e) {
|
|
77
|
+
Logger.printError("WHATAP-081", "hasHostUUID error", e, false);
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
MeteringInfo.prototype.getHostUUID = function (fileName) {
|
|
83
|
+
const hostUUID = this.getHostUUIDValue();
|
|
84
|
+
if (hostUUID) {
|
|
85
|
+
return hostUUID;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const hasHostUUIDFile = this.hasHostUUID(fileName);
|
|
89
|
+
if (hasHostUUIDFile) {
|
|
90
|
+
try {
|
|
91
|
+
const data = fs.readFileSync(fileName);
|
|
92
|
+
|
|
93
|
+
if (data && data.length > 0) {
|
|
94
|
+
this.hostUUID = data.toString().trim();
|
|
95
|
+
return this.hostUUID;
|
|
96
|
+
} else {
|
|
97
|
+
Logger.print('WHATAP-082', 'cannot read /sys/class/dmi/id/product_uuid', false);
|
|
98
|
+
}
|
|
99
|
+
} catch (error) {
|
|
100
|
+
Logger.printError('WHATAP-083', 'error reading /sys/class/dmi/id/product_uuid ', error, false);
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
Logger.print('WHATAP-084', 'cannot find /sys/class/dmi/id/product_uuid', false);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return null;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
MeteringInfo.prototype.getHostUUIDValue = function () {
|
|
110
|
+
return this.hostUUID;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
MeteringInfo.prototype.getCsp = function () {
|
|
114
|
+
|
|
115
|
+
return null;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
module.exports = MeteringInfo;
|
|
@@ -21,27 +21,121 @@ const MeterUsers = require("../counter/meter/meter-users");
|
|
|
21
21
|
const MeterService = require('../counter/meter/meter-service').MeterService;
|
|
22
22
|
const shimmer = require('../core/shimmer');
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
// 설정을 객체로 통합하여 관리 (참조 성능 향상)
|
|
25
|
+
var config = {
|
|
26
|
+
trace_background_socket_enabled: conf.getProperty('trace_background_socket_enabled', true),
|
|
27
|
+
trace_sampling_enabled: conf.getProperty('trace_sampling_enabled', true),
|
|
28
|
+
trace_sampling_tps: conf.getProperty('trace_sampling_tps', 1000),
|
|
29
|
+
resource_sampling_rate: 0.1, // 리소스 프로파일링을 10%만 수행
|
|
30
|
+
profile_batch_size: 20, // 프로파일 배치 처리 크기
|
|
31
|
+
flush_interval: 500 // 프로파일 플러시 간격 (ms)
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// 설정 변경 감지를 단일 리스너로 통합
|
|
25
35
|
conf.on('trace_background_socket_enabled', function (newProps) {
|
|
26
|
-
trace_background_socket_enabled = newProps;
|
|
27
|
-
})
|
|
28
|
-
|
|
36
|
+
config.trace_background_socket_enabled = newProps;
|
|
37
|
+
});
|
|
38
|
+
|
|
29
39
|
conf.on('trace_sampling_enabled', function (newProps) {
|
|
30
|
-
trace_sampling_enabled = newProps;
|
|
31
|
-
})
|
|
32
|
-
|
|
40
|
+
config.trace_sampling_enabled = newProps;
|
|
41
|
+
});
|
|
42
|
+
|
|
33
43
|
conf.on('trace_sampling_tps', function (newProps) {
|
|
34
|
-
trace_sampling_tps = newProps;
|
|
35
|
-
})
|
|
44
|
+
config.trace_sampling_tps = newProps;
|
|
45
|
+
});
|
|
36
46
|
|
|
37
47
|
var SocketIOObserver = function(agent){
|
|
38
48
|
this.agent = agent;
|
|
39
49
|
this.packages = ['socket.io'];
|
|
50
|
+
|
|
51
|
+
this.socketCounter = {
|
|
52
|
+
count: 0,
|
|
53
|
+
start_time: Date.now(),
|
|
54
|
+
window_size: 5000,
|
|
55
|
+
|
|
56
|
+
checkSampling: function() {
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
if ((now - this.start_time) >= this.window_size) {
|
|
59
|
+
this.start_time = now;
|
|
60
|
+
this.count = 0;
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.count++;
|
|
65
|
+
return this.count <= config.trace_sampling_tps;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// 프로파일 데이터 버퍼링 (배치 처리)
|
|
70
|
+
this.profileBuffer = [];
|
|
71
|
+
|
|
72
|
+
// 주기적 플러시 설정
|
|
73
|
+
setInterval(() => this.flushProfileBuffer(), config.flush_interval);
|
|
74
|
+
|
|
75
|
+
// IP 주소 캐싱 (성능 최적화)
|
|
76
|
+
this.ipCache = new Map();
|
|
40
77
|
};
|
|
41
78
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
79
|
+
SocketIOObserver.prototype.flushProfileBuffer = function() {
|
|
80
|
+
if (this.profileBuffer.length === 0) return;
|
|
81
|
+
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
// 100ms 이상 지난 항목만 처리
|
|
84
|
+
const bufferToFlush = this.profileBuffer.filter(item => (now - item.timestamp) >= 100);
|
|
85
|
+
if (bufferToFlush.length === 0) return;
|
|
86
|
+
|
|
87
|
+
// 최대 batch_size까지만 처리
|
|
88
|
+
let batchToProcess;
|
|
89
|
+
if(bufferToFlush.length > config.profile_batch_size)
|
|
90
|
+
batchToProcess = bufferToFlush.slice(0, config.profile_batch_size);
|
|
91
|
+
else
|
|
92
|
+
batchToProcess = bufferToFlush;
|
|
93
|
+
|
|
94
|
+
// 버퍼에서 처리할 항목 제거
|
|
95
|
+
this.profileBuffer = this.profileBuffer.filter(item =>
|
|
96
|
+
!batchToProcess.some(batch => batch.ctx._id === item.ctx._id)
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// 배치 처리
|
|
100
|
+
batchToProcess.forEach(item => {
|
|
101
|
+
try {
|
|
102
|
+
DataProfileAgent.sendProfile(item.ctx, item.profile, false);
|
|
103
|
+
// 컨텍스트 정리 (메모리 누수 방지)
|
|
104
|
+
item.ctx = null;
|
|
105
|
+
} catch (e) {
|
|
106
|
+
Logger.printError('WHATAP-615', 'Socket.io buffer flush error', e, false);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// IP 주소 처리 최적화 함수
|
|
112
|
+
SocketIOObserver.prototype.getProcessedIp = function(address) {
|
|
113
|
+
if (!address) return null;
|
|
114
|
+
|
|
115
|
+
if (this.ipCache.has(address)) {
|
|
116
|
+
return this.ipCache.get(address);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let host = address;
|
|
120
|
+
if (address.includes(':')) {
|
|
121
|
+
host = address.substring(address.lastIndexOf(':') + 1);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
host = IPUtil.checkIp4(host);
|
|
125
|
+
const ipInt = IPUtil.stringToInt(host);
|
|
126
|
+
const ipBytes = Buffer.from(IPUtil.stringToBytes(host));
|
|
127
|
+
|
|
128
|
+
const result = { host, ipInt, ipBytes };
|
|
129
|
+
this.ipCache.set(address, result);
|
|
130
|
+
|
|
131
|
+
// 캐시 크기 관리 (메모리 누수 방지)
|
|
132
|
+
if (this.ipCache.size > 10000) {
|
|
133
|
+
// 오래된 항목부터 20% 제거
|
|
134
|
+
const keysToDelete = Array.from(this.ipCache.keys()).slice(0, Math.floor(this.ipCache.size * 0.2));
|
|
135
|
+
keysToDelete.forEach(key => this.ipCache.delete(key));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return result;
|
|
45
139
|
};
|
|
46
140
|
|
|
47
141
|
SocketIOObserver.prototype.inject = function (mod, moduleName) {
|
|
@@ -57,15 +151,34 @@ SocketIOObserver.prototype.inject = function (mod, moduleName) {
|
|
|
57
151
|
return function (event, listener) {
|
|
58
152
|
if (event === 'connection') {
|
|
59
153
|
return original.call(this, event, function (socket) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
154
|
+
// 효율적인 이벤트 핸들러 래핑
|
|
155
|
+
const wrappedEmit = function (origEmit) {
|
|
156
|
+
// 클로저 최소화 (메모리 사용 감소)
|
|
157
|
+
const emitFn = function (emitEvent, ...args) {
|
|
158
|
+
// 빠른 조건 검사 (조기 반환)
|
|
159
|
+
if (!config.trace_background_socket_enabled) {
|
|
160
|
+
return origEmit.apply(this, [emitEvent, ...args]);
|
|
64
161
|
}
|
|
65
162
|
|
|
163
|
+
self.__handleSocketEmitEvent(socket, emitEvent, args);
|
|
66
164
|
return origEmit.apply(this, [emitEvent, ...args]);
|
|
67
165
|
};
|
|
68
|
-
|
|
166
|
+
|
|
167
|
+
// 원본 함수 속성 유지
|
|
168
|
+
Object.defineProperties(emitFn, {
|
|
169
|
+
length: { value: origEmit.length },
|
|
170
|
+
name: { value: origEmit.name }
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
return emitFn;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// emit 함수에만 래핑 적용 (불필요한 래핑 제거)
|
|
177
|
+
if (typeof socket.emit === 'function' && !socket.emit.__wrapped__) {
|
|
178
|
+
const original = socket.emit;
|
|
179
|
+
socket.emit = wrappedEmit(original);
|
|
180
|
+
socket.emit.__wrapped__ = true;
|
|
181
|
+
}
|
|
69
182
|
|
|
70
183
|
return listener.apply(this, [socket]);
|
|
71
184
|
});
|
|
@@ -76,59 +189,57 @@ SocketIOObserver.prototype.inject = function (mod, moduleName) {
|
|
|
76
189
|
});
|
|
77
190
|
};
|
|
78
191
|
|
|
192
|
+
// 소켓 이벤트 처리 최적화 (CPU 및 메모리 사용 감소)
|
|
79
193
|
SocketIOObserver.prototype.__handleSocketEmitEvent = function(socket, event, args) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// socket_count.count = 0;
|
|
85
|
-
// }
|
|
86
|
-
if (!socket_count.start_time || (now - socket_count.start_time) >= 1000) {
|
|
87
|
-
socket_count.start_time = now;
|
|
88
|
-
socket_count.count = 0;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
socket_count.count++;
|
|
92
|
-
if (socket_count.count > trace_sampling_tps) {
|
|
93
|
-
MeterService.add(0, 1, 0, SecurityMaster.PCODE, SecurityMaster.OKIND, SecurityMaster.OID);
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
194
|
+
// 빠른 샘플링 체크 (조기 반환으로 성능 향상)
|
|
195
|
+
if (config.trace_sampling_enabled && !this.socketCounter.checkSampling()) {
|
|
196
|
+
MeterService.add(0, 1, 0, SecurityMaster.PCODE, SecurityMaster.OKIND, SecurityMaster.OID);
|
|
197
|
+
return;
|
|
96
198
|
}
|
|
97
199
|
|
|
98
|
-
|
|
200
|
+
// 리소스 프로파일링 샘플링 (전체 이벤트의 일부만 측정)
|
|
201
|
+
const shouldProfileResource = Math.random() < config.resource_sampling_rate;
|
|
202
|
+
|
|
203
|
+
// 컨텍스트 생성 및 실행
|
|
204
|
+
TraceContextManager._asyncLocalStorage.run(this.__initCtx(socket, args, shouldProfileResource), () => {
|
|
99
205
|
try {
|
|
100
206
|
var ctx = TraceContextManager._asyncLocalStorage.getStore();
|
|
101
207
|
if (!ctx) return;
|
|
102
208
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
209
|
+
// IP 주소 처리 최적화
|
|
210
|
+
var host = null;
|
|
211
|
+
if (socket.conn && socket.conn.remoteAddress) {
|
|
212
|
+
const ipInfo = this.getProcessedIp(socket.conn.remoteAddress);
|
|
213
|
+
if (ipInfo) {
|
|
214
|
+
host = ipInfo.host;
|
|
215
|
+
// 이미 처리된 IP 바이트 사용
|
|
216
|
+
var step = new SocketStep();
|
|
217
|
+
step.start_time = ctx.getElapsedTime();
|
|
218
|
+
step.ipaddr = ipInfo.ipBytes;
|
|
219
|
+
step.elapsed = 0; // 즉시 종료 (측정 최소화)
|
|
220
|
+
ctx.profile.push(step);
|
|
221
|
+
}
|
|
108
222
|
}
|
|
109
223
|
|
|
110
|
-
|
|
111
|
-
ctx.footprint('Socket Connecting: ' + host);
|
|
112
|
-
|
|
113
|
-
var step = new SocketStep();
|
|
114
|
-
step.start_time = ctx.getElapsedTime();
|
|
115
|
-
step.ipaddr = Buffer.from(IPUtil.stringToBytes(host));
|
|
116
|
-
// step.port = port;
|
|
117
|
-
|
|
118
|
-
ctx.socket_connecting = false;
|
|
119
|
-
step.elapsed = ctx.getElapsedTime() - step.start_time;
|
|
120
|
-
ctx.profile.push(step)
|
|
121
|
-
|
|
224
|
+
// 트랜잭션 종료 및 데이터 전송
|
|
122
225
|
this.__endTransaction(null, ctx);
|
|
123
226
|
|
|
124
227
|
} catch (e) {
|
|
125
|
-
Logger.printError('WHATAP-616', 'socket.io emit transaction error
|
|
228
|
+
Logger.printError('WHATAP-616', 'socket.io emit transaction error', e, false);
|
|
229
|
+
|
|
230
|
+
// 에러 시 컨텍스트 정리 (메모리 누수 방지)
|
|
231
|
+
var ctx = TraceContextManager._asyncLocalStorage.getStore();
|
|
232
|
+
if (ctx) {
|
|
233
|
+
TraceContextManager.end(ctx._id);
|
|
234
|
+
}
|
|
126
235
|
}
|
|
127
236
|
});
|
|
128
237
|
};
|
|
129
238
|
|
|
239
|
+
// 트랜잭션 종료 최적화 (배치 처리 및 메모리 사용 감소)
|
|
130
240
|
SocketIOObserver.prototype.__endTransaction = function(error, ctx) {
|
|
131
241
|
try {
|
|
242
|
+
// 기본 성능 데이터 수집
|
|
132
243
|
var profile = new ProfilePack();
|
|
133
244
|
var wtx = new TxRecord();
|
|
134
245
|
wtx.endTime = DateUtil.currentTime();
|
|
@@ -139,50 +250,78 @@ SocketIOObserver.prototype.__endTransaction = function(error, ctx) {
|
|
|
139
250
|
|
|
140
251
|
wtx.seq = ctx.txid;
|
|
141
252
|
wtx.service = ctx.service_hash;
|
|
142
|
-
wtx.cpuTime = ResourceProfile.getCPUTime() - ctx.start_cpu;
|
|
143
|
-
wtx.malloc = ResourceProfile.getUsedHeapSize()-ctx.start_malloc;
|
|
144
|
-
if(wtx.malloc < 0) { wtx.malloc = 0; }
|
|
145
|
-
wtx.status = 2;
|
|
146
253
|
|
|
254
|
+
// 리소스 프로파일링은 샘플링된 경우에만 수행
|
|
255
|
+
if (ctx.shouldProfileResource) {
|
|
256
|
+
wtx.cpuTime = ResourceProfile.getCPUTime() - ctx.start_cpu;
|
|
257
|
+
wtx.malloc = ResourceProfile.getUsedHeapSize() - ctx.start_malloc;
|
|
258
|
+
if(wtx.malloc < 0) { wtx.malloc = 0; }
|
|
259
|
+
} else {
|
|
260
|
+
wtx.cpuTime = 0;
|
|
261
|
+
wtx.malloc = 0;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
wtx.status = 2;
|
|
147
265
|
wtx.ipaddr = ctx.remoteIp;
|
|
148
266
|
|
|
267
|
+
// 기본 측정 데이터 기록
|
|
149
268
|
MeterService.add(0, wtx.elapsed, 0, SecurityMaster.PCODE, SecurityMaster.OKIND, SecurityMaster.OID);
|
|
150
269
|
|
|
151
270
|
profile.oid = SecurityMaster.OID;
|
|
152
271
|
profile.service = wtx;
|
|
153
272
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
273
|
+
// 컨텍스트 ID 보존 후 컨텍스트 종료 (메모리 누수 방지)
|
|
274
|
+
const ctxId = ctx._id;
|
|
275
|
+
TraceContextManager.end(ctxId);
|
|
276
|
+
|
|
277
|
+
// 프로파일 데이터 버퍼에 추가 (비동기 처리 최적화)
|
|
278
|
+
this.profileBuffer.push({
|
|
279
|
+
ctx: ctx,
|
|
280
|
+
profile: profile,
|
|
281
|
+
timestamp: Date.now()
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
if (this.profileBuffer.length > 1000) {
|
|
285
|
+
// 가장 오래된 항목부터 제거
|
|
286
|
+
this.profileBuffer = this.profileBuffer.slice(-500);
|
|
287
|
+
}
|
|
288
|
+
|
|
159
289
|
} catch (e) {
|
|
160
|
-
Logger.printError('WHATAP-615', 'Socket.io end transaction error
|
|
290
|
+
Logger.printError('WHATAP-615', 'Socket.io end transaction error', e, false);
|
|
291
|
+
// 에러 시에도 컨텍스트 정리
|
|
161
292
|
TraceContextManager.end(ctx._id);
|
|
162
293
|
ctx = null;
|
|
163
294
|
}
|
|
164
|
-
|
|
165
295
|
};
|
|
166
296
|
|
|
167
|
-
|
|
297
|
+
// 컨텍스트 초기화 최적화
|
|
298
|
+
SocketIOObserver.prototype.__initCtx = function(socket, args, shouldProfileResource) {
|
|
168
299
|
const ctx = TraceContextManager.start();
|
|
169
300
|
if (!ctx) {return;}
|
|
170
301
|
|
|
171
302
|
var remote_addr;
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
303
|
+
if (socket.conn && socket.conn.remoteAddress) {
|
|
304
|
+
const address = socket.conn.remoteAddress;
|
|
305
|
+
if(address && address.includes(':')){
|
|
306
|
+
remote_addr = address.substring(address.lastIndexOf(':')+1);
|
|
307
|
+
}
|
|
175
308
|
}
|
|
176
309
|
|
|
177
|
-
|
|
178
|
-
ctx.
|
|
310
|
+
// 리소스 측정은 샘플링된 경우에만 수행
|
|
311
|
+
ctx.shouldProfileResource = shouldProfileResource;
|
|
312
|
+
if (shouldProfileResource) {
|
|
313
|
+
ctx.start_malloc = ResourceProfile.getUsedHeapSize();
|
|
314
|
+
ctx.start_cpu = ResourceProfile.getCPUTime();
|
|
315
|
+
}
|
|
179
316
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
317
|
+
if (remote_addr) {
|
|
318
|
+
remote_addr = IPUtil.checkIp4(remote_addr);
|
|
319
|
+
ctx.remoteIp = IPUtil.stringToInt(remote_addr);
|
|
320
|
+
ctx.userid = Long.fromNumber(ctx.remoteIp);
|
|
321
|
+
MeterUsers.add(ctx.userid);
|
|
322
|
+
}
|
|
184
323
|
|
|
185
324
|
return ctx;
|
|
186
|
-
}
|
|
325
|
+
};
|
|
187
326
|
|
|
188
327
|
exports.SocketIOObserver = SocketIOObserver;
|
|
@@ -4,47 +4,113 @@
|
|
|
4
4
|
* can be found in the LICENSE file.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
|
|
8
|
+
var TraceContextManager = require('../trace/trace-context-manager'),
|
|
9
9
|
SocketStep = require('../step/socket-step'),
|
|
10
10
|
conf = require('../conf/configure'),
|
|
11
11
|
IPUtil = require('../util/iputil'),
|
|
12
12
|
Logger = require('../logger');
|
|
13
|
-
const {Detector: URLPatternDetector} = require("
|
|
14
|
-
const HashUtil = require("
|
|
15
|
-
const DataTextAgent = require("
|
|
16
|
-
const ResourceProfile = require("
|
|
13
|
+
const {Detector: URLPatternDetector} = require("../trace/serviceurl-pattern-detector");
|
|
14
|
+
const HashUtil = require("../util/hashutil");
|
|
15
|
+
const DataTextAgent = require("../data/datatext-agent");
|
|
16
|
+
const ResourceProfile = require("../util/resourceprofile");
|
|
17
17
|
const ProfilePack = require('../pack/profile-pack');
|
|
18
18
|
const TxRecord = require('../service/tx-record');
|
|
19
19
|
const DateUtil = require('../util/dateutil');
|
|
20
20
|
const SecurityMaster = require('../net/security-master');
|
|
21
21
|
const DataProfileAgent = require('../data/dataprofile-agent');
|
|
22
|
-
const MessageStep = require("
|
|
23
|
-
const MeterUsers = require("
|
|
24
|
-
const DataPackSender = require("whatap/lib/data/datapack-sender");
|
|
22
|
+
const MessageStep = require("../step/message-step");
|
|
23
|
+
const MeterUsers = require("../counter/meter/meter-users");
|
|
25
24
|
const MeterService = require('../counter/meter/meter-service').MeterService;
|
|
25
|
+
const shimmer = require('../core/shimmer');
|
|
26
|
+
|
|
27
|
+
// 설정을 객체로 통합하여 관리 (참조 성능 향상)
|
|
28
|
+
var config = {
|
|
29
|
+
trace_background_socket_enabled: conf.getProperty('trace_background_socket_enabled', true),
|
|
30
|
+
trace_sampling_enabled: conf.getProperty('trace_sampling_enabled', true),
|
|
31
|
+
trace_sampling_tps: conf.getProperty('trace_sampling_tps', 1000),
|
|
32
|
+
resource_sampling_rate: 0.1,
|
|
33
|
+
profile_batch_size: 20,
|
|
34
|
+
flush_interval: 500
|
|
35
|
+
};
|
|
26
36
|
|
|
27
|
-
var trace_background_socket_enabled = conf.getProperty('trace_background_socket_enabled', true);
|
|
28
37
|
conf.on('trace_background_socket_enabled', function (newProps) {
|
|
29
|
-
trace_background_socket_enabled = newProps;
|
|
30
|
-
})
|
|
31
|
-
|
|
38
|
+
config.trace_background_socket_enabled = newProps;
|
|
39
|
+
});
|
|
40
|
+
|
|
32
41
|
conf.on('trace_sampling_enabled', function (newProps) {
|
|
33
|
-
trace_sampling_enabled = newProps;
|
|
34
|
-
})
|
|
35
|
-
|
|
42
|
+
config.trace_sampling_enabled = newProps;
|
|
43
|
+
});
|
|
44
|
+
|
|
36
45
|
conf.on('trace_sampling_tps', function (newProps) {
|
|
37
|
-
trace_sampling_tps = newProps;
|
|
38
|
-
})
|
|
46
|
+
config.trace_sampling_tps = newProps;
|
|
47
|
+
});
|
|
39
48
|
|
|
40
49
|
var WebsocketObserver = function(agent){
|
|
41
50
|
this.agent = agent;
|
|
42
51
|
this.packages = ['websocket'];
|
|
52
|
+
|
|
53
|
+
this.socketCounter = {
|
|
54
|
+
count: 0,
|
|
55
|
+
start_time: Date.now(),
|
|
56
|
+
window_size: 5000,
|
|
57
|
+
|
|
58
|
+
checkSampling: function() {
|
|
59
|
+
const now = Date.now();
|
|
60
|
+
if ((now - this.start_time) >= this.window_size) {
|
|
61
|
+
this.start_time = now;
|
|
62
|
+
this.count = 0;
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this.count++;
|
|
67
|
+
return this.count <= config.trace_sampling_tps;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
this.profileBuffer = [];
|
|
72
|
+
|
|
73
|
+
// 주기적 플러시 설정
|
|
74
|
+
setInterval(() => this.flushProfileBuffer(), config.flush_interval);
|
|
75
|
+
|
|
76
|
+
this.ipCache = new Map();
|
|
43
77
|
};
|
|
44
78
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
79
|
+
WebsocketObserver.prototype.flushProfileBuffer = function() {
|
|
80
|
+
if (this.profileBuffer.length === 0) return;
|
|
81
|
+
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
// 100ms 이상 지난 항목만 처리
|
|
84
|
+
const bufferToFlush = this.profileBuffer.filter(item => (now - item.timestamp) >= 100);
|
|
85
|
+
if (bufferToFlush.length === 0) return;
|
|
86
|
+
|
|
87
|
+
// 최대 batch_size까지만 처리
|
|
88
|
+
let batchToProcess;
|
|
89
|
+
if(bufferToFlush.length > config.profile_batch_size)
|
|
90
|
+
batchToProcess = bufferToFlush.slice(0, config.profile_batch_size);
|
|
91
|
+
else
|
|
92
|
+
batchToProcess = bufferToFlush;
|
|
93
|
+
|
|
94
|
+
// 버퍼에서 처리할 항목 제거
|
|
95
|
+
this.profileBuffer = this.profileBuffer.filter(item =>
|
|
96
|
+
!batchToProcess.some(batch => batch.ctx._id === item.ctx._id)
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// 배치 처리
|
|
100
|
+
batchToProcess.forEach(item => {
|
|
101
|
+
try {
|
|
102
|
+
DataProfileAgent.sendProfile(item.ctx, item.profile, false);
|
|
103
|
+
// 컨텍스트 정리 (메모리 누수 방지)
|
|
104
|
+
item.ctx = null;
|
|
105
|
+
} catch (e) {
|
|
106
|
+
Logger.printError('WHATAP-615', 'Websocket buffer flush error', e, false);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
if (this.profileBuffer.length > 1000) {
|
|
111
|
+
// 가장 오래된 항목부터 제거
|
|
112
|
+
this.profileBuffer = this.profileBuffer.slice(-500);
|
|
113
|
+
}
|
|
48
114
|
};
|
|
49
115
|
|
|
50
116
|
WebsocketObserver.prototype.inject = function (mod, moduleName) {
|
|
@@ -57,24 +123,39 @@ WebsocketObserver.prototype.inject = function (mod, moduleName) {
|
|
|
57
123
|
var self = this;
|
|
58
124
|
var aop = self.agent.aop;
|
|
59
125
|
|
|
126
|
+
// IP 주소 변환 캐시 (동일한 IP 변환 작업 최소화)
|
|
127
|
+
const ipCache = new Map();
|
|
128
|
+
|
|
129
|
+
function getProcessedIp(remoteAddress) {
|
|
130
|
+
if (!remoteAddress) return null;
|
|
131
|
+
|
|
132
|
+
if (ipCache.has(remoteAddress)) {
|
|
133
|
+
return ipCache.get(remoteAddress);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let host = remoteAddress;
|
|
137
|
+
if (remoteAddress.includes(':')) {
|
|
138
|
+
host = remoteAddress.substring(remoteAddress.lastIndexOf(':') + 1);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
host = IPUtil.checkIp4(host);
|
|
142
|
+
const ipInt = IPUtil.stringToInt(host);
|
|
143
|
+
const ipBytes = Buffer.from(IPUtil.stringToBytes(host));
|
|
144
|
+
|
|
145
|
+
const result = { host, ipInt, ipBytes };
|
|
146
|
+
ipCache.set(remoteAddress, result);
|
|
147
|
+
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
|
|
60
151
|
aop.after(mod.server.prototype, 'on', function (obj, args, ret, lctx){
|
|
61
152
|
aop.after(mod.connection.prototype, ['send', 'sendUTF', 'sendBytes'], function (obj, args, ret, lctx) {
|
|
62
|
-
if(!trace_background_socket_enabled) {
|
|
153
|
+
if(!config.trace_background_socket_enabled) {
|
|
63
154
|
return;
|
|
64
155
|
}
|
|
65
|
-
if(trace_sampling_enabled){
|
|
66
|
-
var now = Date.now();
|
|
67
|
-
if(!socket_count.start_time) {
|
|
68
|
-
socket_count.start_time = now;
|
|
69
|
-
}
|
|
70
156
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
socket_count.count = 0;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
socket_count.count++;
|
|
77
|
-
if(socket_count.count > trace_sampling_tps) {
|
|
157
|
+
if(config.trace_sampling_enabled) {
|
|
158
|
+
if(!self.socketCounter.checkSampling()) {
|
|
78
159
|
MeterService.add(0, 1, 0, 0, 0, 0);
|
|
79
160
|
return;
|
|
80
161
|
}
|
|
@@ -83,21 +164,24 @@ WebsocketObserver.prototype.inject = function (mod, moduleName) {
|
|
|
83
164
|
var protocol = obj.protocol;
|
|
84
165
|
var remoteAddress = obj.remoteAddress;
|
|
85
166
|
|
|
86
|
-
|
|
87
|
-
|
|
167
|
+
// 리소스 프로파일링 최적화 - 모든 트랜잭션 대신 샘플링
|
|
168
|
+
const shouldProfileResource = Math.random() < config.resource_sampling_rate; // 10%만 리소스 프로파일링
|
|
169
|
+
|
|
170
|
+
TraceContextManager._asyncLocalStorage.run(initCtx(obj, args, shouldProfileResource), () => {
|
|
171
|
+
try {
|
|
88
172
|
var ctx = TraceContextManager._asyncLocalStorage.getStore();
|
|
89
173
|
if (!ctx) {
|
|
90
174
|
return;
|
|
91
175
|
}
|
|
92
176
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
177
|
+
// IP 주소 처리 최적화
|
|
178
|
+
const ipInfo = getProcessedIp(remoteAddress);
|
|
179
|
+
if (!ipInfo) return;
|
|
97
180
|
|
|
181
|
+
// 불필요한 footprint 로깅 제거 (메모리 사용 감소)
|
|
98
182
|
ctx.socket_connecting = true;
|
|
99
|
-
ctx.footprint('Websocket Connecting: ' + host);
|
|
100
183
|
|
|
184
|
+
// 프로토콜 정보 수집 최적화
|
|
101
185
|
if (protocol) {
|
|
102
186
|
var protocol_hash = HashUtil.hashFromString('Protocol');
|
|
103
187
|
var step = new MessageStep();
|
|
@@ -109,26 +193,25 @@ WebsocketObserver.prototype.inject = function (mod, moduleName) {
|
|
|
109
193
|
ctx.profile.add(step);
|
|
110
194
|
}
|
|
111
195
|
|
|
196
|
+
// 소켓 단계 기록 (메모리 사용 최적화)
|
|
112
197
|
var step = new SocketStep();
|
|
113
198
|
step.start_time = ctx.getElapsedTime();
|
|
114
|
-
step.ipaddr =
|
|
115
|
-
// step.port = port;
|
|
199
|
+
step.ipaddr = ipInfo.ipBytes;
|
|
116
200
|
|
|
117
201
|
ctx.socket_connecting = false;
|
|
118
202
|
step.elapsed = ctx.getElapsedTime() - step.start_time;
|
|
119
203
|
ctx.profile.push(step);
|
|
120
204
|
|
|
121
|
-
|
|
122
|
-
|
|
205
|
+
// 트랜잭션 종료 처리 최적화
|
|
123
206
|
self.__endTransaction(null, ctx);
|
|
124
207
|
return;
|
|
125
|
-
}catch (e) {
|
|
126
|
-
Logger.printError('WHATAP-616', 'Websocket transaction error
|
|
208
|
+
} catch (e) {
|
|
209
|
+
Logger.printError('WHATAP-616', 'Websocket transaction error', e, false);
|
|
127
210
|
return;
|
|
128
211
|
}
|
|
129
212
|
});
|
|
130
|
-
})
|
|
131
|
-
})
|
|
213
|
+
});
|
|
214
|
+
});
|
|
132
215
|
};
|
|
133
216
|
|
|
134
217
|
WebsocketObserver.prototype.__endTransaction = function(error, ctx) {
|
|
@@ -136,42 +219,52 @@ WebsocketObserver.prototype.__endTransaction = function(error, ctx) {
|
|
|
136
219
|
var profile = new ProfilePack();
|
|
137
220
|
var wtx = new TxRecord();
|
|
138
221
|
wtx.endTime = DateUtil.currentTime();
|
|
139
|
-
profile.time
|
|
222
|
+
profile.time = wtx.endTime;
|
|
140
223
|
wtx.elapsed = ctx.getElapsedTime();
|
|
141
224
|
|
|
142
225
|
DataTextAgent.SERVICE.add(ctx.service_hash, ctx.service_name);
|
|
143
226
|
|
|
144
227
|
wtx.seq = ctx.txid;
|
|
145
228
|
wtx.service = ctx.service_hash;
|
|
146
|
-
wtx.cpuTime = ResourceProfile.getCPUTime() - ctx.start_cpu;
|
|
147
|
-
wtx.malloc = ResourceProfile.getUsedHeapSize()-ctx.start_malloc;
|
|
148
|
-
if(wtx.malloc < 0) { wtx.malloc = 0; }
|
|
149
|
-
wtx.status = 2;
|
|
150
229
|
|
|
230
|
+
// CPU와 메모리 측정은 샘플링된 트랜잭션에만 수행
|
|
231
|
+
if (ctx.shouldProfileResource) {
|
|
232
|
+
wtx.cpuTime = ResourceProfile.getCPUTime() - ctx.start_cpu;
|
|
233
|
+
wtx.malloc = ResourceProfile.getUsedHeapSize() - ctx.start_malloc;
|
|
234
|
+
if(wtx.malloc < 0) { wtx.malloc = 0; }
|
|
235
|
+
} else {
|
|
236
|
+
wtx.cpuTime = 0;
|
|
237
|
+
wtx.malloc = 0;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
wtx.status = 2;
|
|
151
241
|
wtx.ipaddr = ctx.remoteIp;
|
|
152
242
|
|
|
153
|
-
MeterService.add(
|
|
154
|
-
wtx.errorLevel, ctx.mcaller_pcode, ctx.mcaller_okind, ctx.mcaller_oid);
|
|
243
|
+
MeterService.add(0, wtx.elapsed, 0, SecurityMaster.PCODE, SecurityMaster.OKIND, SecurityMaster.OID);
|
|
155
244
|
|
|
156
245
|
profile.oid = SecurityMaster.OID;
|
|
157
246
|
profile.service = wtx;
|
|
158
247
|
|
|
248
|
+
// 트랜잭션 컨텍스트 종료 (메모리 누수 방지)
|
|
159
249
|
TraceContextManager.end(ctx._id);
|
|
160
250
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
251
|
+
// 프로파일 데이터 버퍼에 추가 (비동기 처리 최적화)
|
|
252
|
+
this.profileBuffer.push({
|
|
253
|
+
ctx: ctx,
|
|
254
|
+
profile: profile,
|
|
255
|
+
timestamp: Date.now()
|
|
256
|
+
});
|
|
257
|
+
|
|
166
258
|
} catch (e) {
|
|
167
|
-
Logger.printError('WHATAP-615', 'Websocket end transaction error
|
|
259
|
+
Logger.printError('WHATAP-615', 'Websocket end transaction error', e, false);
|
|
260
|
+
// 에러 발생 시에도 컨텍스트 정리
|
|
168
261
|
TraceContextManager.end(ctx._id);
|
|
169
262
|
ctx = null;
|
|
170
263
|
}
|
|
171
|
-
|
|
172
264
|
};
|
|
173
265
|
|
|
174
|
-
|
|
266
|
+
// 컨텍스트 초기화 최적화
|
|
267
|
+
function initCtx(socket, args, shouldProfileResource) {
|
|
175
268
|
const ctx = TraceContextManager.start();
|
|
176
269
|
if (!ctx) {return;}
|
|
177
270
|
|
|
@@ -181,10 +274,14 @@ function initCtx(socket, args) {
|
|
|
181
274
|
remote_addr = address.substring(address.lastIndexOf(':')+1);
|
|
182
275
|
}
|
|
183
276
|
|
|
184
|
-
|
|
185
|
-
ctx.
|
|
277
|
+
// 리소스 측정은 샘플링된 트랜잭션에만 수행 (CPU 사용 감소)
|
|
278
|
+
ctx.shouldProfileResource = shouldProfileResource;
|
|
279
|
+
if (shouldProfileResource) {
|
|
280
|
+
ctx.start_malloc = ResourceProfile.getUsedHeapSize();
|
|
281
|
+
ctx.start_cpu = ResourceProfile.getCPUTime();
|
|
282
|
+
}
|
|
186
283
|
|
|
187
|
-
remote_addr=IPUtil.checkIp4(remote_addr);
|
|
284
|
+
remote_addr = IPUtil.checkIp4(remote_addr);
|
|
188
285
|
ctx.remoteIp = IPUtil.stringToInt(remote_addr);
|
|
189
286
|
ctx.userid = Long.fromNumber(ctx.remoteIp);
|
|
190
287
|
MeterUsers.add(ctx.userid);
|
|
@@ -4,28 +4,29 @@
|
|
|
4
4
|
* can be found in the LICENSE file.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
var PackEnum = require('./packenum'),
|
|
8
|
+
Pack = require('./pack'),
|
|
9
|
+
Long = require('long'),
|
|
10
|
+
ValueEnum = require('../value/valueenum'),
|
|
11
|
+
SummaryValue = require('../value/summary-value'),
|
|
12
|
+
DataOutPutX = require('../io/data-outputx'),
|
|
13
|
+
DataInputX = require('../io/data-inputx'),
|
|
14
|
+
HashUtil = require('../util/hashutil'),
|
|
15
|
+
MapValue = require('../value/map-value')
|
|
16
16
|
|
|
17
17
|
function TagCountPack() {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
Pack.call(this);
|
|
19
|
+
this.category = ''
|
|
20
|
+
this.tagHash = 0
|
|
21
|
+
this.originOid = 0
|
|
22
|
+
this.tags = new MapValue()
|
|
23
|
+
this.fields = new MapValue()
|
|
24
|
+
this.read_size
|
|
25
|
+
this.fieldPos
|
|
26
|
+
this.tagPos
|
|
27
|
+
this.madeByPlugIn
|
|
28
28
|
}
|
|
29
|
+
|
|
29
30
|
TagCountPack.prototype = new Pack();
|
|
30
31
|
TagCountPack.prototype.constructor = TagCountPack;
|
|
31
32
|
|
|
@@ -44,99 +45,99 @@ TagCountPack.prototype.resetOid = function () {
|
|
|
44
45
|
}
|
|
45
46
|
};
|
|
46
47
|
|
|
47
|
-
TagCountPack.prototype.getTagString = function(name) {
|
|
48
|
+
TagCountPack.prototype.getTagString = function (name) {
|
|
48
49
|
return this.tags.getText(name);
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
TagCountPack.prototype.getTagNotNull = function(name) {
|
|
52
|
+
TagCountPack.prototype.getTagNotNull = function (name) {
|
|
52
53
|
var v = this.tags.getText(name);
|
|
53
54
|
return v == null ? "" : v;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
TagCountPack.prototype.getTag = function(name) {
|
|
57
|
+
TagCountPack.prototype.getTag = function (name) {
|
|
57
58
|
return this.tags.get(name);
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
TagCountPack.prototype.getTagInt = function(name) {
|
|
61
|
+
TagCountPack.prototype.getTagInt = function (name) {
|
|
61
62
|
return this.tags.getInt(name);
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
TagCountPack.prototype.getTagLong = function(name) {
|
|
65
|
+
TagCountPack.prototype.getTagLong = function (name) {
|
|
65
66
|
return this.tags.getLong(name);
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
TagCountPack.prototype.putTag = function(name
|
|
69
|
+
TagCountPack.prototype.putTag = function (name, value) {
|
|
69
70
|
this.tags.put(name, value);
|
|
70
71
|
return this;
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
TagCountPack.prototype.putTagInt = function(name
|
|
74
|
-
this.tags.putInt(name
|
|
74
|
+
TagCountPack.prototype.putTagInt = function (name, value) {
|
|
75
|
+
this.tags.putInt(name, value);
|
|
75
76
|
return this;
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
TagCountPack.prototype.putTagString = function(name
|
|
79
|
+
TagCountPack.prototype.putTagString = function (name, value) {
|
|
79
80
|
this.tags.putString(name, value);
|
|
80
81
|
return this;
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
TagCountPack.prototype.putTagLong = function(name
|
|
84
|
+
TagCountPack.prototype.putTagLong = function (name, value) {
|
|
84
85
|
this.tags.putLong(name, value);
|
|
85
86
|
return this;
|
|
86
87
|
}
|
|
87
88
|
|
|
88
|
-
TagCountPack.prototype.putTagFloat = function(name
|
|
89
|
+
TagCountPack.prototype.putTagFloat = function (name, value) {
|
|
89
90
|
this.tags.putFloat(name, value);
|
|
90
91
|
return this;
|
|
91
92
|
}
|
|
92
93
|
|
|
93
|
-
TagCountPack.prototype.putTagDouble = function(name
|
|
94
|
+
TagCountPack.prototype.putTagDouble = function (name, value) {
|
|
94
95
|
this.tags.putDouble(name, value);
|
|
95
96
|
return this;
|
|
96
97
|
}
|
|
97
98
|
|
|
98
|
-
TagCountPack.prototype.putTagBoolean= function(name
|
|
99
|
+
TagCountPack.prototype.putTagBoolean = function (name, value) {
|
|
99
100
|
this.tags.putBoolean(name, value);
|
|
100
101
|
return this;
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
TagCountPack.prototype.put = function(name
|
|
104
|
+
TagCountPack.prototype.put = function (name, value) {
|
|
104
105
|
this.fields.put(name, value);
|
|
105
106
|
}
|
|
106
107
|
|
|
107
|
-
TagCountPack.prototype.putInt = function(name
|
|
108
|
+
TagCountPack.prototype.putInt = function (name, value) {
|
|
108
109
|
this.fields.putInt(name, value);
|
|
109
110
|
}
|
|
110
111
|
|
|
111
|
-
TagCountPack.prototype.putLog = function(name
|
|
112
|
+
TagCountPack.prototype.putLog = function (name, value) {
|
|
112
113
|
this.fields.putLong(name, value);
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
TagCountPack.prototype.putString = function(name
|
|
116
|
+
TagCountPack.prototype.putString = function (name, value) {
|
|
116
117
|
this.fields.putString(name, value);
|
|
117
118
|
}
|
|
118
119
|
|
|
119
|
-
TagCountPack.prototype.putFloat = function(name
|
|
120
|
+
TagCountPack.prototype.putFloat = function (name, value) {
|
|
120
121
|
this.fields.putFloat(name, value);
|
|
121
122
|
}
|
|
122
123
|
|
|
123
|
-
TagCountPack.prototype.putDouble = function(name
|
|
124
|
+
TagCountPack.prototype.putDouble = function (name, value) {
|
|
124
125
|
this.fields.putDouble(name, value);
|
|
125
126
|
}
|
|
126
127
|
|
|
127
|
-
TagCountPack.prototype.putBoolean = function(name
|
|
128
|
+
TagCountPack.prototype.putBoolean = function (name, value) {
|
|
128
129
|
this.fields.putBoolean(name, value);
|
|
129
130
|
}
|
|
130
131
|
|
|
131
|
-
TagCountPack.prototype.get = function(name) {
|
|
132
|
+
TagCountPack.prototype.get = function (name) {
|
|
132
133
|
return this.fields.get(name);
|
|
133
134
|
}
|
|
134
135
|
|
|
135
|
-
TagCountPack.prototype.getField = function(key) {
|
|
136
|
+
TagCountPack.prototype.getField = function (key) {
|
|
136
137
|
return get(key);
|
|
137
138
|
}
|
|
138
139
|
|
|
139
|
-
TagCountPack.prototype.getFloat = function(name) {
|
|
140
|
+
TagCountPack.prototype.getFloat = function (name) {
|
|
140
141
|
var val = this.fields.get(name);
|
|
141
142
|
if (val == null)
|
|
142
143
|
return 0;
|
|
@@ -154,7 +155,7 @@ TagCountPack.prototype.getFloat = function(name) {
|
|
|
154
155
|
return 0;
|
|
155
156
|
}
|
|
156
157
|
|
|
157
|
-
TagCountPack.prototype.getLong = function(name) {
|
|
158
|
+
TagCountPack.prototype.getLong = function (name) {
|
|
158
159
|
var val = this.fields.get(name);
|
|
159
160
|
if (val == null)
|
|
160
161
|
return 0;
|
|
@@ -172,7 +173,7 @@ TagCountPack.prototype.getLong = function(name) {
|
|
|
172
173
|
return 0;
|
|
173
174
|
}
|
|
174
175
|
|
|
175
|
-
TagCountPack.prototype.write = function(dout) {
|
|
176
|
+
TagCountPack.prototype.write = function (dout) {
|
|
176
177
|
Pack.prototype.write.call(this, dout);
|
|
177
178
|
dout.writeByte(1);
|
|
178
179
|
dout.writeText(this.category);
|
|
@@ -199,7 +200,7 @@ TagCountPack.prototype.write = function(dout) {
|
|
|
199
200
|
}
|
|
200
201
|
}
|
|
201
202
|
|
|
202
|
-
TagCountPack.prototype.resetTagHash = function() {
|
|
203
|
+
TagCountPack.prototype.resetTagHash = function () {
|
|
203
204
|
var tagIO = new DataOutPutX();
|
|
204
205
|
tagIO.writeValue(this.tags);
|
|
205
206
|
var tagBytes = tagIO.toByteArray();
|
|
@@ -207,7 +208,7 @@ TagCountPack.prototype.resetTagHash = function() {
|
|
|
207
208
|
return tagBytes;
|
|
208
209
|
}
|
|
209
210
|
|
|
210
|
-
TagCountPack.prototype.read = function(din) {
|
|
211
|
+
TagCountPack.prototype.read = function (din) {
|
|
211
212
|
var offset1 = din.getOffset();
|
|
212
213
|
Pack.prototype.read.call(this, din);
|
|
213
214
|
var ver = din.readByte();
|
|
@@ -264,7 +265,7 @@ TagCountPack.prototype.writeTag = (info) => {
|
|
|
264
265
|
return dout.toByteArray();
|
|
265
266
|
}
|
|
266
267
|
|
|
267
|
-
TagCountPack.prototype._readMap = (
|
|
268
|
+
TagCountPack.prototype._readMap = (map, info, din) => {
|
|
268
269
|
while (din.readBoolean()) {
|
|
269
270
|
var idx = din.readDecimal();
|
|
270
271
|
var value = din.readValue();
|
|
@@ -286,7 +287,7 @@ TagCountPack.prototype._writeMap = (fields, info, dout) => {
|
|
|
286
287
|
dout.writeBoolean(false);
|
|
287
288
|
}
|
|
288
289
|
|
|
289
|
-
TagCountPack.prototype.readHead = (
|
|
290
|
+
TagCountPack.prototype.readHead = (b) => {
|
|
290
291
|
var din = new DataInputX(b);
|
|
291
292
|
this.time = din.readDecimal();
|
|
292
293
|
this.oid = din.readDecimal();
|
|
@@ -327,7 +328,7 @@ TagCountPack.prototype.getCategory = () => {
|
|
|
327
328
|
TagCountPack.prototype.hasAnyField = (fields) => {
|
|
328
329
|
if (fields != null) {
|
|
329
330
|
fields.array.forEach(element => {
|
|
330
|
-
if(this.fields.get(element) != null) {
|
|
331
|
+
if (this.fields.get(element) != null) {
|
|
331
332
|
return true;
|
|
332
333
|
}
|
|
333
334
|
});
|
|
@@ -339,7 +340,7 @@ TagCountPack.prototype.createTagHash = (tags) => {
|
|
|
339
340
|
if (tags === null || tags.length === 0) return 0;
|
|
340
341
|
|
|
341
342
|
var tagIO = new DataOutPutX();
|
|
342
|
-
for(var k in tags){
|
|
343
|
+
for (var k in tags) {
|
|
343
344
|
let v = this.tags.get(t);
|
|
344
345
|
if (v != null) {
|
|
345
346
|
tagIO.writeValue(v);
|
package/lib/util/dateutil.js
CHANGED
|
@@ -56,6 +56,34 @@ var getMonth = function (time, delta) {
|
|
|
56
56
|
return this.helper.getMonth(time, delta);
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
+
var getFiveMinUnit = function(time) {
|
|
60
|
+
if(time == null || isNaN(time)) {
|
|
61
|
+
time = this.currentTime();
|
|
62
|
+
}
|
|
63
|
+
return time - (time % MILLIS_PER_FIVE_MINUTE);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
var getTenMinUnit = function(time) {
|
|
67
|
+
if(time == null || isNaN(time)) {
|
|
68
|
+
time = this.currentTime();
|
|
69
|
+
}
|
|
70
|
+
return time - (time % MILLIS_PER_TEN_MINUTE);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
var getHourUnit = function(time) {
|
|
74
|
+
if(time == null || isNaN(time)) {
|
|
75
|
+
time = this.currentTime();
|
|
76
|
+
}
|
|
77
|
+
return time - (time % MILLIS_PER_HOUR);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
var getMinUnit = function(time) {
|
|
81
|
+
if(time == null || isNaN(time)) {
|
|
82
|
+
time = this.currentTime();
|
|
83
|
+
}
|
|
84
|
+
return time - (time % MILLIS_PER_MINUTE);
|
|
85
|
+
};
|
|
86
|
+
|
|
59
87
|
function timeListen(crons, loop, cb){
|
|
60
88
|
if(!loop) loop = false;
|
|
61
89
|
|
|
@@ -97,7 +125,11 @@ var DateUtil = {
|
|
|
97
125
|
yyyymmddhh : yyyymmddhh,
|
|
98
126
|
getDateUnit : getDateUnit,
|
|
99
127
|
timeListen: timeListen,
|
|
100
|
-
getMonth : getMonth
|
|
128
|
+
getMonth : getMonth,
|
|
129
|
+
getFiveMinUnit: getFiveMinUnit,
|
|
130
|
+
getTenMinUnit: getTenMinUnit,
|
|
131
|
+
getHourUnit: getHourUnit,
|
|
132
|
+
getMinUnit: getMinUnit,
|
|
101
133
|
};
|
|
102
134
|
|
|
103
135
|
module.exports = DateUtil;
|
package/package.json
CHANGED