whatap 0.5.10 → 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.
@@ -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;
@@ -57,17 +57,29 @@ PrismaObserver.prototype.inject = function(mod, moduleName) {
57
57
  if (mod.PrismaClient) {
58
58
  // 직접 PrismaClient 생성자 후킹
59
59
  shimmer.wrap(mod, 'PrismaClient', function(originalConstructor) {
60
- return function() {
61
- // 원래 생성자 호출
62
- const instance = new originalConstructor(...arguments);
60
+ return function(...args) {
61
+ const originalInstance = new originalConstructor(...args);
62
+ const instance = Object.create(originalInstance);
63
+
64
+ const prismaServicePrototype = Object.getPrototypeOf(this);
65
+ Object.getOwnPropertyNames(prismaServicePrototype).forEach((method) => {
66
+ if (typeof prismaServicePrototype[method] === "function" && !instance[method]) {
67
+ instance[method] = prismaServicePrototype[method].bind(instance);
68
+ }
69
+ });
70
+
71
+ Object.getOwnPropertyNames(originalInstance).forEach((prop) => {
72
+ if (!instance.hasOwnProperty(prop)) {
73
+ instance[prop] = originalInstance[prop];
74
+ }
75
+ });
63
76
 
64
- // 이 시점에서 instance가 생성된 Prisma 클라이언트 인스턴스
65
77
  self.patchPrismaInstance(instance);
66
78
 
67
- if(!instance[prisma_read_func_name]){
68
- instance[prisma_read_func_name] = function() {
79
+ if (!instance[prisma_read_func_name]) {
80
+ instance[prisma_read_func_name] = function () {
69
81
  return this;
70
- }
82
+ };
71
83
  }
72
84
 
73
85
  return instance;
@@ -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
- var trace_background_socket_enabled = conf.getProperty('trace_background_socket_enabled', true);
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
- var trace_sampling_enabled = conf.getProperty('trace_sampling_enabled', true);
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
- var trace_sampling_tps = conf.getProperty('trace_sampling_tps', 1000);
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
- var socket_count = {
43
- count: 0,
44
- start_time: null
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
- shimmer.wrap(socket, 'emit', function (origEmit) {
61
- return function (emitEvent, ...args) {
62
- if (trace_background_socket_enabled) {
63
- self.__handleSocketEmitEvent(socket, emitEvent, args);
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
- if (trace_sampling_enabled) {
81
- var now = Date.now();
82
- // if ((now - socket_count.start_time) > 1000) {
83
- // socket_count.start_time = now;
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
- TraceContextManager._asyncLocalStorage.run(initCtx(socket, args), () => {
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
- ctx.footprint('Socket Emit Event: ' + event);
104
-
105
- var host;
106
- if (socket.conn && socket.conn.remoteAddress && socket.conn.remoteAddress.includes(':')) {
107
- host = socket.conn.remoteAddress.substring(socket.conn.remoteAddress.lastIndexOf(':') + 1);
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
- ctx.socket_connecting = true;
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..', e, false);
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
- setTimeout(function () {
155
- DataProfileAgent.sendProfile(ctx, profile, false);
156
- TraceContextManager.end(ctx._id);
157
- ctx = null;
158
- }, 100);
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..', e, false);
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
- function initCtx(socket, args) {
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
- const address = socket.conn.remoteAddress;
173
- if(address && address.includes(':')){
174
- remote_addr = address.substring(address.lastIndexOf(':')+1);
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
- ctx.start_malloc = ResourceProfile.getUsedHeapSize();
178
- ctx.start_cpu = ResourceProfile.getCPUTime();
310
+ // 리소스 측정은 샘플링된 경우에만 수행
311
+ ctx.shouldProfileResource = shouldProfileResource;
312
+ if (shouldProfileResource) {
313
+ ctx.start_malloc = ResourceProfile.getUsedHeapSize();
314
+ ctx.start_cpu = ResourceProfile.getCPUTime();
315
+ }
179
316
 
180
- remote_addr=IPUtil.checkIp4(remote_addr);
181
- ctx.remoteIp = IPUtil.stringToInt(remote_addr);
182
- ctx.userid = Long.fromNumber(ctx.remoteIp);
183
- MeterUsers.add(ctx.userid);
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
- var MeterSocketio = require('../counter/meter/meter-socket.io'),
8
- TraceContextManager = require('../trace/trace-context-manager'),
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("whatap/lib/trace/serviceurl-pattern-detector");
14
- const HashUtil = require("whatap/lib/util/hashutil");
15
- const DataTextAgent = require("whatap/lib/data/datatext-agent");
16
- const ResourceProfile = require("whatap/lib/util/resourceprofile");
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("whatap/lib/step/message-step");
23
- const MeterUsers = require("whatap/lib/counter/meter/meter-users");
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
- var trace_sampling_enabled = conf.getProperty('trace_sampling_enabled', true);
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
- var trace_sampling_tps = conf.getProperty('trace_sampling_tps', 1000);
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
- var socket_count = {
46
- count: 0,
47
- start_time: null
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
- if ((now - socket_count.start_time) > 5000) {
72
- socket_count.start_time = now;
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
- TraceContextManager._asyncLocalStorage.run(initCtx(obj, args), () => {
87
- try{
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
- var host;
94
- if (remoteAddress && remoteAddress.includes(':')) {
95
- host = remoteAddress.substring(remoteAddress.lastIndexOf(':') + 1);
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 = Buffer.from(IPUtil.stringToBytes(host));
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
- ctx.footprint('Websocket Connecting Done');
122
-
205
+ // 트랜잭션 종료 처리 최적화
123
206
  self.__endTransaction(null, ctx);
124
207
  return;
125
- }catch (e) {
126
- Logger.printError('WHATAP-616', 'Websocket transaction error..', e, false);
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 = wtx.endTime;
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(wtx.service, wtx.elapsed,
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
- setTimeout(function () {
162
- DataProfileAgent.sendProfile(ctx, profile, false);
163
- TraceContextManager.end(ctx._id);
164
- ctx = null;
165
- }, 100);
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..', e, false);
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
- function initCtx(socket, args) {
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
- ctx.start_malloc = ResourceProfile.getUsedHeapSize();
185
- ctx.start_cpu = ResourceProfile.getCPUTime();
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);
@@ -0,0 +1,8 @@
1
+ exports.AP_JAVA = 1;
2
+ exports.AP_NODE = 2;
3
+ exports.AP_PYTHON = 3;
4
+ exports.AP_PHP = 4;
5
+ exports.AP_URL_ADM = 5;
6
+ exports.BSM = 6;
7
+ exports.WEB_CHECK = 9;
8
+ exports.OTEL = 10;
@@ -0,0 +1,7 @@
1
+ exports.AP = 0x0001;
2
+ exports.SM = 0x0002;
3
+ exports.DB = 0x0004;
4
+ exports.VR = 0x0008;
5
+ exports.CL = 0x0010;
6
+ exports.BSM = 0x0011;
7
+ exports.WC = 0x0012;
@@ -4,28 +4,29 @@
4
4
  * can be found in the LICENSE file.
5
5
  */
6
6
 
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')
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
- 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
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 , value) {
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 , value) {
74
- this.tags.putInt(name , value);
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 , value) {
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 , value) {
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 , value) {
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 , value) {
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 , value) {
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 , value) {
104
+ TagCountPack.prototype.put = function (name, value) {
104
105
  this.fields.put(name, value);
105
106
  }
106
107
 
107
- TagCountPack.prototype.putInt = function(name , value) {
108
+ TagCountPack.prototype.putInt = function (name, value) {
108
109
  this.fields.putInt(name, value);
109
110
  }
110
111
 
111
- TagCountPack.prototype.putLog = function(name , value) {
112
+ TagCountPack.prototype.putLog = function (name, value) {
112
113
  this.fields.putLong(name, value);
113
114
  }
114
115
 
115
- TagCountPack.prototype.putString = function(name , value) {
116
+ TagCountPack.prototype.putString = function (name, value) {
116
117
  this.fields.putString(name, value);
117
118
  }
118
119
 
119
- TagCountPack.prototype.putFloat = function(name , value) {
120
+ TagCountPack.prototype.putFloat = function (name, value) {
120
121
  this.fields.putFloat(name, value);
121
122
  }
122
123
 
123
- TagCountPack.prototype.putDouble = function(name , value) {
124
+ TagCountPack.prototype.putDouble = function (name, value) {
124
125
  this.fields.putDouble(name, value);
125
126
  }
126
127
 
127
- TagCountPack.prototype.putBoolean = function(name , value) {
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 = ( map, info, din) => {
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 = ( b ) => {
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);
@@ -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
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "whatap",
3
3
  "homepage": "http://www.whatap.io",
4
- "version": "0.5.10",
5
- "releaseDate": "20250310",
4
+ "version": "0.5.12",
5
+ "releaseDate": "20250324",
6
6
  "description": "Monitoring and Profiling Service",
7
7
  "main": "index.js",
8
8
  "scripts": {},