whatap 0.5.26 → 1.0.0

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.
Files changed (196) hide show
  1. package/agent/darwin/arm64/whatap_nodejs +0 -0
  2. package/agent/linux/amd64/whatap_nodejs +0 -0
  3. package/agent/linux/arm64/whatap_nodejs +0 -0
  4. package/build.txt +4 -0
  5. package/lib/conf/config-default.js +3 -10
  6. package/lib/conf/configure.js +349 -369
  7. package/lib/conf/license.js +1 -1
  8. package/lib/control/packagectr-helper.js +3 -34
  9. package/lib/core/agent.js +882 -176
  10. package/lib/core/interceptor.js +6 -6
  11. package/lib/core/shimmer.js +36 -82
  12. package/lib/counter/counter-manager.js +8 -79
  13. package/lib/counter/task/activetransaction.js +17 -68
  14. package/lib/io/data-inputx.js +3 -13
  15. package/lib/io/data-outputx.js +206 -268
  16. package/lib/logger.js +6 -6
  17. package/lib/net/security-master.js +20 -139
  18. package/lib/observers/apollo-server-observer.js +27 -33
  19. package/lib/observers/global-observer.js +80 -155
  20. package/lib/observers/http-observer.js +236 -666
  21. package/lib/observers/ioredis-observer.js +294 -0
  22. package/lib/observers/maria-observer.js +362 -204
  23. package/lib/observers/mongodb-observer.js +226 -169
  24. package/lib/observers/mongoose-observer.js +323 -518
  25. package/lib/observers/mssql-observer.js +418 -177
  26. package/lib/observers/mysql-observer.js +449 -342
  27. package/lib/observers/mysql2-observer.js +358 -396
  28. package/lib/observers/oracle-observer.js +384 -559
  29. package/lib/observers/pgsql-observer.js +489 -231
  30. package/lib/observers/prisma-observer.js +92 -303
  31. package/lib/observers/process-observer.js +35 -79
  32. package/lib/observers/redis-observer.js +331 -166
  33. package/lib/observers/socket.io-observer.js +187 -226
  34. package/lib/observers/websocket-observer.js +301 -175
  35. package/lib/pack/counter-pack.js +0 -3
  36. package/lib/pack/log-sink-pack.js +52 -14
  37. package/lib/pack/tagcount-pack.js +4 -4
  38. package/lib/{counter/task → system}/gc-action.js +74 -27
  39. package/lib/trace/trace-context-manager.js +25 -113
  40. package/lib/trace/trace-context.js +7 -21
  41. package/lib/trace/trace-httpc.js +11 -17
  42. package/lib/trace/trace-sql.js +21 -29
  43. package/lib/udp/async_sender.js +119 -0
  44. package/lib/udp/index.js +17 -0
  45. package/lib/udp/packet_enum.js +52 -0
  46. package/lib/udp/packet_queue.js +69 -0
  47. package/lib/udp/packet_type_enum.js +33 -0
  48. package/lib/udp/param_def.js +72 -0
  49. package/lib/udp/udp_session.js +336 -0
  50. package/lib/util/escape-literal-sql.js +5 -5
  51. package/lib/util/hashutil.js +18 -18
  52. package/lib/util/keygen.js +3 -0
  53. package/lib/util/linkedset.js +2 -1
  54. package/lib/util/nodeutil.js +1 -2
  55. package/lib/util/sql-util.js +178 -0
  56. package/lib/util/trace-helper.js +91 -0
  57. package/lib/util/transfer.js +58 -0
  58. package/lib/value/map-value.js +2 -3
  59. package/package.json +5 -10
  60. package/lib/conf/conf-sys-mon.js +0 -101
  61. package/lib/control/cmd-config.js +0 -24
  62. package/lib/control/control-handler.js +0 -367
  63. package/lib/core/request-agent.js +0 -27
  64. package/lib/counter/meter/meter-activex.js +0 -67
  65. package/lib/counter/meter/meter-httpc.js +0 -57
  66. package/lib/counter/meter/meter-resource.js +0 -9
  67. package/lib/counter/meter/meter-service.js +0 -168
  68. package/lib/counter/meter/meter-socket.io.js +0 -51
  69. package/lib/counter/meter/meter-sql.js +0 -71
  70. package/lib/counter/meter/meter-users.js +0 -58
  71. package/lib/counter/meter.js +0 -183
  72. package/lib/counter/task/agentinfo.js +0 -107
  73. package/lib/counter/task/gcstat.js +0 -34
  74. package/lib/counter/task/heapmem.js +0 -25
  75. package/lib/counter/task/httpc.js +0 -76
  76. package/lib/counter/task/metering-info.js +0 -125
  77. package/lib/counter/task/proc-cpu.js +0 -29
  78. package/lib/counter/task/realtimeuser.js +0 -31
  79. package/lib/counter/task/res/systemECSTask.js +0 -39
  80. package/lib/counter/task/res/systemKubeTask.js +0 -53
  81. package/lib/counter/task/res/util/awsEcsClientThread.js +0 -218
  82. package/lib/counter/task/res/util/linuxProcStatUtil.js +0 -14
  83. package/lib/counter/task/res-sys-cpu.js +0 -62
  84. package/lib/counter/task/service.js +0 -202
  85. package/lib/counter/task/socketio.js +0 -30
  86. package/lib/counter/task/sql.js +0 -105
  87. package/lib/counter/task/systemperf.js +0 -43
  88. package/lib/data/datapack-sender.js +0 -289
  89. package/lib/data/dataprofile-agent.js +0 -162
  90. package/lib/data/datatext-agent.js +0 -135
  91. package/lib/data/event-level.js +0 -15
  92. package/lib/data/test.js +0 -49
  93. package/lib/data/zipprofile.js +0 -197
  94. package/lib/env/constants.js +0 -21
  95. package/lib/error/error-handler.js +0 -437
  96. package/lib/kube/kube-client.js +0 -144
  97. package/lib/lang/text-types.js +0 -58
  98. package/lib/logsink/line-log-util.js +0 -87
  99. package/lib/logsink/line-log.js +0 -12
  100. package/lib/logsink/log-sender.js +0 -78
  101. package/lib/logsink/log-tracer.js +0 -40
  102. package/lib/logsink/sender-util.js +0 -56
  103. package/lib/logsink/zip/zip-send.js +0 -177
  104. package/lib/net/netflag.js +0 -55
  105. package/lib/net/receiver.js +0 -66
  106. package/lib/net/sender.js +0 -141
  107. package/lib/net/tcp-return.js +0 -18
  108. package/lib/net/tcp-session.js +0 -286
  109. package/lib/net/tcpreq-client-proxy.js +0 -70
  110. package/lib/net/tcprequest-mgr.js +0 -58
  111. package/lib/observers/cluster-observer.js +0 -22
  112. package/lib/observers/express-observer.js +0 -215
  113. package/lib/observers/file-observer.js +0 -184
  114. package/lib/observers/grpc-observer.js +0 -336
  115. package/lib/observers/memcached-observer.js +0 -56
  116. package/lib/observers/mongo-observer.js +0 -317
  117. package/lib/observers/net-observer.js +0 -77
  118. package/lib/observers/promise-observer.js +0 -31
  119. package/lib/observers/schedule-observer.js +0 -67
  120. package/lib/observers/stream-observer.js +0 -19
  121. package/lib/observers/thrift-observer.js +0 -197
  122. package/lib/pack/activestack-pack.js +0 -55
  123. package/lib/pack/apenum.js +0 -8
  124. package/lib/pack/errorsnap-pack.js +0 -69
  125. package/lib/pack/event-pack.js +0 -54
  126. package/lib/pack/hitmap-pack.js +0 -63
  127. package/lib/pack/hitmap-pack1.js +0 -152
  128. package/lib/pack/netstat.js +0 -15
  129. package/lib/pack/otype.js +0 -7
  130. package/lib/pack/profile-pack.js +0 -49
  131. package/lib/pack/realtimeuser-pack.js +0 -41
  132. package/lib/pack/stat-general-pack.js +0 -96
  133. package/lib/pack/staterror-pack.js +0 -120
  134. package/lib/pack/stathttpc-pack.js +0 -66
  135. package/lib/pack/stathttpc-rec.js +0 -78
  136. package/lib/pack/statremote-pack.js +0 -46
  137. package/lib/pack/statservice-pack.js +0 -63
  138. package/lib/pack/statservice-pack1.js +0 -88
  139. package/lib/pack/statservice-rec.js +0 -292
  140. package/lib/pack/statservice-rec_dep.js +0 -151
  141. package/lib/pack/statsql-pack.js +0 -69
  142. package/lib/pack/statsql-rec.js +0 -100
  143. package/lib/pack/statuseragent-pack.js +0 -44
  144. package/lib/pack/tagctr.js +0 -15
  145. package/lib/pack/text-pack.js +0 -50
  146. package/lib/pack/time-count.js +0 -25
  147. package/lib/pack/websocket.js +0 -15
  148. package/lib/pack/zip-pack.js +0 -70
  149. package/lib/pii/pii-item.js +0 -31
  150. package/lib/pii/pii-mask.js +0 -174
  151. package/lib/plugin/plugin-loadermanager.js +0 -57
  152. package/lib/plugin/plugin.js +0 -75
  153. package/lib/service/tx-record.js +0 -332
  154. package/lib/stat/stat-error.js +0 -116
  155. package/lib/stat/stat-httpc.js +0 -98
  156. package/lib/stat/stat-remote-ip.js +0 -46
  157. package/lib/stat/stat-remote-ipurl.js +0 -88
  158. package/lib/stat/stat-sql.js +0 -113
  159. package/lib/stat/stat-tranx.js +0 -58
  160. package/lib/stat/stat-tx-caller.js +0 -160
  161. package/lib/stat/stat-tx-domain.js +0 -111
  162. package/lib/stat/stat-tx-referer.js +0 -112
  163. package/lib/stat/stat-useragent.js +0 -48
  164. package/lib/stat/timingsender.js +0 -76
  165. package/lib/step/activestack-step.js +0 -38
  166. package/lib/step/dbc-step.js +0 -36
  167. package/lib/step/http-stepx.js +0 -67
  168. package/lib/step/message-step.js +0 -40
  169. package/lib/step/method-stepx.js +0 -45
  170. package/lib/step/resultset-step.js +0 -40
  171. package/lib/step/securemsg-step.js +0 -44
  172. package/lib/step/socket-step.js +0 -46
  173. package/lib/step/sql-stepx.js +0 -68
  174. package/lib/step/sqlxtype.js +0 -16
  175. package/lib/step/step.js +0 -66
  176. package/lib/step/stepenum.js +0 -54
  177. package/lib/topology/link.js +0 -63
  178. package/lib/topology/nodeinfo.js +0 -123
  179. package/lib/topology/status-detector.js +0 -111
  180. package/lib/util/anylist.js +0 -103
  181. package/lib/util/cardinality/hyperloglog.js +0 -106
  182. package/lib/util/cardinality/murmurhash.js +0 -31
  183. package/lib/util/cardinality/registerset.js +0 -75
  184. package/lib/util/errordata.js +0 -21
  185. package/lib/util/iputil_x.js +0 -527
  186. package/lib/util/kube-util.js +0 -73
  187. package/lib/util/paramsecurity.js +0 -80
  188. package/lib/util/pre-process.js +0 -13
  189. package/lib/util/process-seq.js +0 -166
  190. package/lib/util/property-util.js +0 -36
  191. package/lib/util/request-queue.js +0 -70
  192. package/lib/util/requestdouble-queue.js +0 -72
  193. package/lib/util/resourceprofile.js +0 -157
  194. package/lib/util/stop-watch.js +0 -30
  195. package/lib/util/system-util.js +0 -10
  196. package/lib/util/userid-util.js +0 -57
package/lib/core/agent.js CHANGED
@@ -6,63 +6,60 @@
6
6
 
7
7
  var os = require('os'),
8
8
  fs = require('fs'),
9
- path = require('path');
9
+ path = require('path'),
10
+ child_process = require('child_process'),
11
+ properLock = require('proper-lockfile'),
12
+ crypto = require('crypto');
10
13
 
11
14
  const RequestLog = require('../requestlog');
12
15
 
13
16
  var Interceptor = require('./interceptor').Interceptor,
14
17
  HttpObserver = require('../observers/http-observer').HttpObserver,
15
- NetObserver = require('../observers/net-observer').NetObserver,
16
- ClusterObserver = require('../observers/cluster-observer').ClusterObserver,
17
- ExpressObserver = require('../observers/express-observer').ExpressObserver,
18
18
  GlobalObserver = require('../observers/global-observer').GlobalObserver,
19
19
  MysqlObserver = require('../observers/mysql-observer').MysqlObserver,
20
20
  Mysql2Observer = require('../observers/mysql2-observer').Mysql2Observer,
21
21
  MariaObserver = require('../observers/maria-observer').MariaObserver,
22
22
  SocketioObserver = require('../observers/socket.io-observer').SocketIOObserver,
23
23
  WebsocketObserver = require('../observers/websocket-observer').WebsocketObserver,
24
+ // WsObserver = require('../observers/websocket-observer').WsObserver,
24
25
  ProcessObserver = require('../observers/process-observer').ProcessObserver,
25
- FileObserver = require('../observers/file-observer').FileObserver,
26
- MongoObserver = require('../observers/mongo-observer').MongoObserver,
27
- MongooseObserver = require('../observers/mongoose-observer').MongooseObserver,
26
+ MongoObserver = require('../observers/mongodb-observer').MongoObserver,
27
+ // MongooseObserver = require('../observers/mongoose-observer').MongooseObserver,
28
28
  RedisObserver = require('../observers/redis-observer').RedisObserver,
29
+ IORedisObserver = require('../observers/ioredis-observer').IORedisObserver,
29
30
  MssqlObserver = require('../observers/mssql-observer').MssqlObserver,
30
- ThriftObserver = require('../observers/thrift-observer').ThriftObserver,
31
- PromiseObserver = require('../observers/promise-observer').PromiseObserver,
32
31
  PgSqlObserver = require('../observers/pgsql-observer').PgSqlObserver,
33
- ScheduleObserver = require('../observers/schedule-observer').ScheduleObserver,
34
32
  // GRpcObserver = require('../observers/grpc-observer').GRpcObserver,
35
33
  ApolloObserver = require('../observers/apollo-server-observer').ApolloServerObserver,
36
34
  PrismaObserver = require('../observers/prisma-observer').PrismaObserver,
37
35
  OracleObserver = require('../observers/oracle-observer').OracleObserver;
38
36
 
39
37
 
40
-
41
38
  var Configuration = require('./../conf/configure'),
42
39
  SecurityMaster = require('./../net/security-master'),
43
- TcpSession = require('./../net/tcp-session'),
44
- PackageCtrHelper = require('./../control/packagectr-helper'),
45
- DataPackSender = require('../data/datapack-sender'),
46
40
  CounterManager = require('./../counter/counter-manager'),
47
41
  NodeUtil = require('./../util/nodeutil'),
48
42
  DateUtil = require('./../util/dateutil'),
49
43
  WhatapUtil = require('./../util'),
50
- ParamPack = require('../pack/param-pack').ParamPack,
51
- PluginLoaderManager = require('../plugin/plugin-loadermanager'),
52
44
  TraceContextManager = require('../trace/trace-context-manager'),
53
- DataTextAgent = require('../data/datatext-agent').agent,
54
- TextTypes = require('../lang/text-types'),
55
45
  HashUtil = require('../util/hashutil'),
56
- MessageStep = require('../step/message-step'),
57
46
  Logger = require('../logger');
58
47
 
59
- require('../stat/stat-tranx');
60
- require('../stat/stat-sql');
61
- require('../stat/stat-httpc');
62
- require('../stat/stat-remote-ip');
63
- require('../stat/stat-remote-ipurl');
64
- require('../stat/stat-useragent');
65
- require('../stat/timingsender');
48
+ // Import UDP module
49
+ const UdpModule = require('../udp');
50
+ const UdpSession = UdpModule.UdpSession;
51
+ const AsyncSender = UdpModule.AsyncSender;
52
+ const PacketTypeEnum = UdpModule.PacketTypeEnum;
53
+
54
+ const ARCH = {
55
+ 'x64': 'amd64',
56
+ 'ia32': '386',
57
+ 'arm': 'arm',
58
+ 'arm64': 'arm64'
59
+ };
60
+
61
+ // Agent binary name
62
+ const AGENT_NAME = 'whatap_nodejs';
66
63
 
67
64
  var NodeAgent = function(opt) {
68
65
  this._userOpt = opt;
@@ -70,7 +67,6 @@ var NodeAgent = function(opt) {
70
67
  this.aop = new Interceptor(this);
71
68
  this._conf = Configuration;
72
69
  this._securityMaster = SecurityMaster;
73
- this._tcpsession = null;
74
70
  this._counterManager = null;
75
71
  this.setLoopTime = 5000;
76
72
  this.connectCount = 0;
@@ -107,179 +103,837 @@ NodeAgent.prototype.findRoot = function () {
107
103
  root = path.join(root, '..');
108
104
  }
109
105
  };
110
- NodeAgent.prototype.init = function(cb) {
111
- var self = this;
112
- if(self._initialized) {
113
- return;
106
+
107
+ // Cross-platform function to check if a process is running and is whatap_nodejs
108
+ NodeAgent.prototype.isNodejsAgentProcess = function(pid) {
109
+ try {
110
+ pid = parseInt(pid);
111
+ const platform = process.platform;
112
+ let processCommand = '';
113
+
114
+ if (platform === 'linux') {
115
+ // On Linux, use /proc filesystem - same approach as Python
116
+ try {
117
+ const cmdlineFile = path.join('/proc', pid.toString(), 'cmdline');
118
+ if (fs.existsSync(cmdlineFile)) {
119
+ processCommand = fs.readFileSync(cmdlineFile, 'utf8');
120
+ // Log for debugging
121
+ Logger.print("WHATAP-PROCESS", `Process ${pid} command line: ${processCommand}`, false);
122
+ }
123
+ } catch (e) {
124
+ Logger.printError("WHATAP-101", "Error reading Linux process info", e, false);
125
+ return false;
126
+ }
127
+ } else if (platform === 'darwin') {
128
+ // On macOS, use ps command
129
+ try {
130
+ processCommand = child_process.execSync(`ps -p ${pid} -o command=`, { encoding: 'utf8' }).trim();
131
+ Logger.print("WHATAP-PROCESS", `Process ${pid} command line: ${processCommand}`, false);
132
+ } catch (e) {
133
+ Logger.printError("WHATAP-102", "Error reading MacOS process info", e, false);
134
+ return false;
135
+ }
136
+ } else if (platform === 'win32') {
137
+ // On Windows, use tasklist command
138
+ try {
139
+ const output = child_process.execSync(`tasklist /FI "PID eq ${pid}" /FO CSV /NH`, { encoding: 'utf8' });
140
+ // Windows output has quotes, e.g.: "whatap_nodejs.exe","1234","Console","1","12,345 K"
141
+ const match = output.match(/"([^"]+)"/);
142
+ if (match && match[1]) {
143
+ processCommand = match[1];
144
+ Logger.print("WHATAP-PROCESS", `Process ${pid} command line: ${processCommand}`, false);
145
+ }
146
+ } catch (e) {
147
+ Logger.printError("WHATAP-103", "Error reading Windows process info", e, false);
148
+ return false;
149
+ }
150
+ } else {
151
+ // On other platforms, try generic ps command
152
+ try {
153
+ processCommand = child_process.execSync(`ps -p ${pid} -o command=`, { encoding: 'utf8' }).trim();
154
+ Logger.print("WHATAP-PROCESS", `Process ${pid} command line: ${processCommand}`, false);
155
+ } catch (e) {
156
+ Logger.printError("WHATAP-104", "Error reading other os process info", e, false);
157
+ return false;
158
+ }
159
+ }
160
+
161
+ // Check if the command contains the agent name (equivalent to Python's find('whatap_python') >= 0)
162
+ const isAgentProcess = processCommand.indexOf(AGENT_NAME) >= 0;
163
+ Logger.print("WHATAP-PROCESS", `Process ${pid} is ${isAgentProcess ? '' : 'not '}a WhaTap agent`, false);
164
+ return isAgentProcess;
165
+ } catch (e) {
166
+ Logger.printError("WHATAP-AGENT", "Error in isNodejsAgentProcess", e, false);
167
+ return false;
114
168
  }
115
- self._initialized = true;
116
- self.starttime = Date.now();
169
+ };
117
170
 
118
- self.findRoot();
171
+ // Function to read PID from file
172
+ NodeAgent.prototype.readPidFile = function(home, fileName) {
173
+ try {
174
+ const filePath = path.join(process.env[home], fileName);
175
+ if (fs.existsSync(filePath)) {
176
+ return fs.readFileSync(filePath, 'utf8').trim();
177
+ }
178
+ } catch (e) {
179
+ Logger.printError("WHATAP-PID", "Error reading PID file", e, false);
180
+ }
181
+ return '';
182
+ };
119
183
 
120
- Logger.initializer.process();
184
+ // Function to write to file
185
+ NodeAgent.prototype.writeToFile = function(home, fileName, value) {
186
+ try {
187
+ const filePath = path.join(process.env[home], fileName);
188
+ fs.writeFileSync(filePath, value);
189
+ return true;
190
+ } catch (e) {
191
+ Logger.printError("WHATAP-FILE", `Error writing to file ${fileName}`, e, false);
192
+ Logger.print("WHATAP-FILE", `Try to execute command: \`sudo chmod -R 777 $WHATAP_HOME\``, false);
193
+ return false;
194
+ }
195
+ };
121
196
 
122
- // 최초 1회 실행 1시간마다 실행
123
- const ONE_HOUR = 60 * 60 * 1000;
124
- Logger.clearOldLog();
125
- setInterval(Logger.clearOldLog, ONE_HOUR);
197
+ // Modified isGoAgentRunning to check PID +-1 range
198
+ NodeAgent.prototype.isGoAgentRunning = function(pid) {
199
+ try {
200
+ if (!pid) return false;
126
201
 
127
- Logger.print('WHATAP-001', 'Start initialize WhaTap Agent... Root[' + self._conf['app.root'] + ']', true);
202
+ // 원래 PID와 +1, -1된 PID 모두 확인
203
+ const pidsToCheck = [
204
+ parseInt(pid),
205
+ parseInt(pid) + 1,
206
+ parseInt(pid) - 1
207
+ ];
128
208
 
129
- if(self._conf['app.root'] == null || self._conf['app.root'].length == 0) {
130
- return Logger.print("WHATAP-001", "Can not find application root directory", true);
209
+ Logger.print("WHATAP-AGENT", `Checking PIDs: ${pidsToCheck.join(', ')} (original: ${pid})`, false);
210
+
211
+ for (const pidToCheck of pidsToCheck) {
212
+ try {
213
+ // 프로세스 존재 여부 확인
214
+ process.kill(pidToCheck, 0);
215
+
216
+ // whatap_nodejs 프로세스인지 확인
217
+ if (this.isNodejsAgentProcess(pidToCheck)) {
218
+ Logger.print("WHATAP-AGENT",
219
+ `Found agent process at PID ${pidToCheck} (original PID in file: ${pid})`,
220
+ false);
221
+ return true;
222
+ }
223
+ } catch (e) {
224
+ // ESRCH error means process doesn't exist
225
+ if (e.code !== 'ESRCH') {
226
+ throw e;
227
+ }
228
+ }
229
+ }
230
+
231
+ Logger.print("WHATAP-AGENT", `No agent process found for PID ${pid} or adjacent PIDs`, false);
232
+ return false;
233
+ } catch (e) {
234
+ Logger.printError("WHATAP-AGENT", "Error checking if agent is running", e, false);
235
+ return false;
131
236
  }
237
+ };
132
238
 
133
- NodeUtil.getPackageJson();
134
- PackageCtrHelper.dynamicHook();
239
+ /**
240
+ * Read content from a file - similar to Python's read_file
241
+ * @param {string} home - Environment variable name for home directory
242
+ * @param {string} fileName - Name of the file to read
243
+ * @returns {string} - Content of the file, or empty string on error
244
+ */
245
+ NodeAgent.prototype.readFile = function(home, fileName) {
246
+ try {
247
+ const filePath = path.join(process.env[home], fileName);
248
+ if (fs.existsSync(filePath)) {
249
+ return fs.readFileSync(filePath, 'utf8').toString().trim();
250
+ }
251
+ } catch (e) {
252
+ Logger.printError("WHATAP-FILE", `Error reading file ${fileName}`, e, false);
253
+ }
254
+ return '';
255
+ };
135
256
 
136
- self._conf.init(this._userOpt , function(e) {
137
- if(e) {
138
- Logger.printError("WHATAP-002", "Configuataion initialize error. " + e.message,
139
- e, true);
140
- return;
257
+ /**
258
+ * Write content to a file - similar to Python's write_file
259
+ * @param {string} home - Environment variable name for home directory
260
+ * @param {string} fileName - Name of the file to write
261
+ * @param {string} value - Content to write to the file
262
+ * @returns {boolean} - True if successful, false otherwise
263
+ */
264
+ NodeAgent.prototype.writeFile = function(home, fileName, value) {
265
+ try {
266
+ const filePath = path.join(process.env[home], fileName);
267
+ fs.writeFileSync(filePath, value);
268
+ return true;
269
+ } catch (e) {
270
+ Logger.printError("WHATAP-FILE", `Error writing to file ${fileName}`, e, false);
271
+ Logger.print("WHATAP-FILE", `Try to execute command: \`sudo chmod -R 777 $WHATAP_HOME\``, false);
272
+ return false;
273
+ }
274
+ };
275
+
276
+ /**
277
+ * Find whatap.conf file by searching directories - similar to Python's find_whatap_conf
278
+ * @returns {string|null} - Path to whatap.conf or null if not found
279
+ */
280
+ NodeAgent.prototype.findWhatapConf = function() {
281
+ // 1. Check current directory
282
+ const scriptDir = path.dirname(__filename);
283
+ const parentDir = path.dirname(scriptDir);
284
+ let confPath = path.join(parentDir, 'whatap.conf');
285
+
286
+ if (fs.existsSync(confPath)) {
287
+ process.env.WHATAP_HOME = parentDir;
288
+ return confPath;
289
+ }
290
+
291
+ // 2. Check parent directories
292
+ let current = parentDir;
293
+ while (current !== '/' && current !== path.parse(current).root) {
294
+ confPath = path.join(current, 'whatap.conf');
295
+ if (fs.existsSync(confPath)) {
296
+ process.env.WHATAP_HOME = current;
297
+ return confPath;
141
298
  }
142
- Logger.print('WHATAP-101', 'Finish initialize configuration... ' + Boolean(self._conf['reqlog_enabled']) , false);
143
- if(Boolean(self._conf['reqlog_enabled']) == true ) {
144
- Logger.print("WHATAP-REQLOG" , "ReqLog Init Start !!!! " , false);
145
- RequestLog.initializer.process();
299
+ current = path.dirname(current);
300
+ }
301
+
302
+ return null;
303
+ };
304
+
305
+ /**
306
+ * Open and lock the port file - similar to Python's openPortFile
307
+ * @param {string} filepath - Path to the lock file
308
+ * @returns {object|null} - File descriptor or null if failed
309
+ */
310
+ NodeAgent.prototype.openPortFile = function(filepath) {
311
+ filepath = filepath || process.env.WHATAP_LOCK_FILE || '/tmp/whatap-nodejs.lock';
312
+
313
+ let fileHandle = null;
314
+ let attempts = 0;
315
+
316
+ // Try to open the file, similar to Python's while loop
317
+ while (!fileHandle && attempts < 100) {
318
+ try {
319
+ // Try to open file for reading and writing
320
+ if (fs.existsSync(filepath)) {
321
+ fileHandle = fs.openSync(filepath, 'r+');
322
+ } else {
323
+ // If file doesn't exist, create the directory if needed
324
+ const dirPath = path.dirname(filepath);
325
+ if (!fs.existsSync(dirPath)) {
326
+ fs.mkdirSync(dirPath, { recursive: true });
327
+ }
328
+ // Create the file
329
+ fileHandle = fs.openSync(filepath, 'w+');
330
+ }
331
+ } catch (e) {
332
+ Logger.printError("WHATAP-PORT", `Error opening port file (attempt ${attempts})`, e, false);
146
333
  }
147
334
 
148
- self._securityMaster.run( function (err) {
149
- if(err) {
150
- Logger.printError('WHATAP-104', 'Failed to connect to whatap server', err, false);
151
- if(self._counterManager) self._counterManager.stop();
152
- return;
335
+ // Similar to Python, add a delay after 50 attempts
336
+ if (attempts > 50) {
337
+ setTimeout(() => {}, 100); // 0.1 second delay
338
+ }
339
+
340
+ attempts++;
341
+ }
342
+
343
+ if (fileHandle) {
344
+ try {
345
+ // Use proper-lock with the filepath instead of file descriptor
346
+ properLock.lockSync(filepath);
347
+ return { fileHandle, filepath };
348
+ } catch (e) {
349
+ Logger.printError("WHATAP-PORT", "Failed to lock port file", e, false);
350
+ try {
351
+ fs.closeSync(fileHandle);
352
+ } catch (closeErr) {}
353
+ return null;
354
+ }
355
+ }
356
+ return null;
357
+ };
358
+
359
+ /**
360
+ * Get available port number for the agent - similar to Python's get_port_number
361
+ * @param {number} defaultPort - Default port to start with
362
+ * @param {string} home - WHATAP_HOME directory
363
+ * @returns {number|null} - Allocated port number or null if failed
364
+ */
365
+ NodeAgent.prototype.getPortNumber = function(port, home) {
366
+ port = port || 6600;
367
+ home = home || process.env.WHATAP_HOME;
368
+
369
+ // Return null if home is not defined
370
+ if (!home) {
371
+ return null;
372
+ }
373
+
374
+ // Open the port file
375
+ const fileInfo = this.openPortFile();
376
+ if (!fileInfo) {
377
+ return port; // Return default port if we couldn't open the file
378
+ }
379
+
380
+ const { fileHandle, filepath } = fileInfo;
381
+
382
+ try {
383
+ // Get file size
384
+ const stats = fs.fstatSync(fileHandle);
385
+ let content = '';
386
+
387
+ // Only read if file is not empty
388
+ if (stats.size > 0) {
389
+ const buffer = Buffer.alloc(stats.size);
390
+ fs.readSync(fileHandle, buffer, 0, buffer.length, 0);
391
+ content = buffer.toString().trim();
392
+ }
393
+
394
+ let lastPortFound = null;
395
+
396
+ // Process each line
397
+ if (content && content.length > 0) {
398
+ const lines = content.split('\n');
399
+ for (const line of lines) {
400
+ const trimmedLine = line.trim();
401
+ if (!trimmedLine) continue;
402
+
403
+ try {
404
+ // Parse the line "port home"
405
+ const parts = trimmedLine.split(/\s+/, 2);
406
+ if (parts.length < 2) continue;
407
+
408
+ const portStr = parts[0];
409
+ const portHome = parts[1];
410
+ const currentPort = parseInt(portStr, 10);
411
+
412
+ if (isNaN(currentPort)) continue;
413
+
414
+ // If home matches, return the port
415
+ if (home === portHome) {
416
+ // Clean up before returning
417
+ properLock.unlockSync(filepath);
418
+ fs.closeSync(fileHandle);
419
+ return currentPort;
420
+ }
421
+
422
+ // Track the highest port number
423
+ if (!lastPortFound || lastPortFound < currentPort) {
424
+ lastPortFound = currentPort;
425
+ }
426
+ } catch (e) {
427
+ continue; // Skip lines that can't be parsed
428
+ }
153
429
  }
430
+ }
431
+
432
+ // Determine the new port number
433
+ const newPort = lastPortFound ? lastPortFound + 1 : port;
434
+
435
+ // Clear the file completely
436
+ fs.ftruncateSync(fileHandle, 0);
154
437
 
155
- self.connect(function (err) {
156
- if(!err) {
157
- self.initOK(cb);
438
+ // Write the new entry directly
439
+ fs.writeSync(fileHandle, `${newPort}\t${home}\n`);
440
+
441
+ // Unlock and close the file
442
+ properLock.unlockSync(filepath);
443
+ fs.closeSync(fileHandle);
444
+
445
+ return newPort;
446
+ } catch (e) {
447
+ Logger.printError("WHATAP-PORT", "Error processing port file", e, false);
448
+
449
+ try {
450
+ properLock.unlockSync(filepath);
451
+ fs.closeSync(fileHandle);
452
+ } catch (closeErr) {
453
+ }
454
+
455
+ return port;
456
+ }
457
+ };
458
+
459
+ /**
460
+ * Configure the port for the agent and update the configuration
461
+ * @returns {number|null} - The configured port or null if failed
462
+ */
463
+ NodeAgent.prototype.configurePort = function() {
464
+ const port = this.getPortNumber();
465
+ if (port) {
466
+ this.updateConfig('WHATAP_HOME', 'net_udp_port', port.toString());
467
+ return port;
468
+ }
469
+ return null;
470
+ };
471
+
472
+ /**
473
+ * Update configuration file with new option value - similar to Python's update_config
474
+ * @param {string} home - Environment variable name for WhaTap home
475
+ * @param {string} optKey - Option key to update
476
+ * @param {string} optValue - Option value to set
477
+ */
478
+ NodeAgent.prototype.updateConfig = function(home, optKey, optValue) {
479
+ const homePath = process.env[home];
480
+ if (!homePath) {
481
+ Logger.print("WHATAP-CONFIG", `${home} environment variable not set`, true);
482
+ return;
483
+ }
484
+
485
+ const configFile = path.join(homePath, 'whatap.conf');
486
+
487
+ try {
488
+ let content = '';
489
+ let isUpdated = false;
490
+
491
+ if (fs.existsSync(configFile)) {
492
+ const lines = fs.readFileSync(configFile, 'utf8').split('\n');
493
+
494
+ // Look for the option key and update if found
495
+ for (let line of lines) {
496
+ if (line.trim()) {
497
+ const parts = line.split('=');
498
+ const key = parts[0].trim();
499
+
500
+ if (key === optKey) {
501
+ line = `${key}=${optValue}`;
502
+ isUpdated = true;
503
+ }
158
504
  }
159
- });
160
- });
161
- });
162
- self.loadObserves();
163
- return this;
505
+ content += line + '\n';
506
+ }
507
+
508
+ // If the option wasn't found, add it
509
+ if (!isUpdated) {
510
+ content += `\n${optKey}=${optValue}\n`;
511
+ }
512
+ } else {
513
+ // If config file doesn't exist, create with just this option
514
+ content = `${optKey}=${optValue}\n`;
515
+ }
516
+
517
+ // Write updated content back to file
518
+ fs.writeFileSync(configFile, content);
519
+ Logger.print("WHATAP-CONFIG", `Updated configuration: ${optKey}=${optValue}`, false);
520
+ } catch (e) {
521
+ Logger.printError("WHATAP-CONFIG", `Error updating configuration: ${optKey}=${optValue}`, e, false);
522
+ }
164
523
  };
165
524
 
166
- NodeAgent.prototype.initOK = function(cb) {
167
- var self = this;
525
+ NodeAgent.prototype.getApplicationIdentifier = function() {
526
+ // 1. 명시적 그룹 지정 (환경 변수)
527
+ if (process.env.WHATAP_APP_GROUP) {
528
+ return process.env.WHATAP_APP_GROUP;
529
+ }
168
530
 
169
- Logger.ONAME = self._securityMaster.ONAME;
170
- Logger.initializer.reset();
171
-
172
- TraceContextManager.initialized = true;
173
- DataTextAgent.reset();
174
-
175
- var param = new ParamPack();
176
- param.putString("whatap.version", NodeUtil.getVersion()+' '+ NodeUtil.getReleaseDate());
177
- param.putString("os.name", os.platform());
178
- param.putString("os.arch", os.arch());
179
- param.putString('os.release', os.release());
180
- param.putString('node.version', process.version);
181
- param.putString('node.uptime', (new Date()).toString());
182
- param.putString('node.name', NodeUtil.getName());
183
- param.putString('user.timezone', NodeUtil.getTimeZone());
184
- param.putString('user.home', os.homedir());
185
- param.putString('user.hostname', os.hostname());
186
- // CLOUD PLATFORM INFO
187
- param.putString('CLOUD_PLATFORM', WhatapUtil.cloudPlatformCheck());
188
- DataPackSender.sendBoot(param);
189
-
190
- if(self._counterManager === null) {
191
- self._counterManager = new CounterManager(self);
192
- self._counterManager.run();
193
- PluginLoaderManager.start();
194
- }
195
-
196
- WhatapUtil.printWhatap();
197
- self.connectCount = 0;
198
- self.setLoopTime = 5000;
199
- if(cb) cb();
200
- }
531
+ // 2. 설정 파일에서 그룹 지정
532
+ if (this._conf['app_group']) {
533
+ return this._conf['app_group'];
534
+ }
201
535
 
536
+ // 3. 자동 생성
537
+ const appRoot = this._conf['app.root'] || process.cwd();
538
+ const appName = this.getApplicationName();
202
539
 
203
- NodeAgent.prototype.context = function(cb) {
204
- return TraceContextManager.getCurrentContext();
205
- }
540
+ const identifier = crypto
541
+ .createHash('md5')
542
+ .update(`${appRoot}:${appName}`)
543
+ .digest('hex')
544
+ .substr(0, 8);
206
545
 
207
- NodeAgent.prototype.connect = function (cb) {
208
- var self = this;
209
- if(self.connectCount >0 && self.connectCount % 10 == 0 ) {
210
- Logger.print("WHATAP-171",
211
- "[WhaTap Agent] Can not find server port.\n" +
212
- "Did you add requre('whatap') as the first line of the app's main module?\n" +
213
- "https://service.whatap.io/project/" + self._securityMaster.PCODE + "/install",
214
- true);
215
- if(self.setLoopTime == 5000) {
216
- self.setLoopTime += 25000;
217
- Logger.print("WHATAP-150", "[WhaTap Agent] setLoopTime Up -> " + self.setLoopTime , false)
218
- }else if(self.setLoopTime == 30000) {
219
- self.setLoopTime += 30000;
220
- Logger.print("WHATAP-150", "[WhaTap Agent] setLoopTime Up -> " + self.setLoopTime , false)
221
- } else if(self.setLoopTime == 60000) {
222
- self.setLoopTime += 300000;
223
- Logger.print("WHATAP-150", "[WhaTap Agent] setLoopTime Up -> " + self.setLoopTime , false)
546
+ Logger.print("WHATAP-AGENT",
547
+ `Application identifier: ${appName} at ${appRoot} => ${identifier}`,
548
+ false);
549
+
550
+ return identifier;
551
+ };
552
+
553
+ NodeAgent.prototype.getApplicationName = function() {
554
+ // 1. PM2 실행
555
+ if (process.env.PM2_APP_NAME || process.env.name) {
556
+ return process.env.PM2_APP_NAME || process.env.name;
557
+ }
558
+
559
+ // 2. 설정 파일에서
560
+ if (this._conf['app_name']) {
561
+ return this._conf['app_name'];
562
+ }
563
+
564
+ // 3. 명령행 인수에서 --name 옵션 찾기
565
+ const nameArgIndex = process.argv.indexOf('--name');
566
+ if (nameArgIndex !== -1 && nameArgIndex < process.argv.length - 1) {
567
+ return process.argv[nameArgIndex + 1];
568
+ }
569
+
570
+ // 4. 환경 변수로 직접 설정
571
+ if (process.env.APP_NAME) {
572
+ return process.env.APP_NAME;
573
+ }
574
+
575
+ // 5. 기본값: 실행 파일명
576
+ return path.basename(process.argv[1], '.js');
577
+ };
578
+
579
+ // startGoAgent with improved PID handling
580
+ NodeAgent.prototype.startGoAgent = function(opts = {}) {
581
+ const self = this;
582
+ const home = 'WHATAP_HOME';
583
+ const pidFileName = `${AGENT_NAME}.pid`;
584
+ const whatapHome = process.env[home];
585
+
586
+ if (!whatapHome) {
587
+ Logger.printError("WHATAP-105", "WHATAP_HOME environment variable is not set", true);
588
+ return;
589
+ }
590
+
591
+ // 1. 애플리케이션 식별자 생성 (포트 제외)
592
+ const appIdentifier = this.getApplicationIdentifier();
593
+ const sharedLockFile = path.join(whatapHome, `agent-${appIdentifier}.lock`);
594
+ const sharedPidFile = path.join(whatapHome, `agent-${appIdentifier}.pid`);
595
+
596
+ // 2. 공유 잠금 파일을 사용하여 중복 실행 방지
597
+ try {
598
+ if (fs.existsSync(sharedPidFile)) {
599
+ // PID 파일에서 기존 PID 읽기
600
+ const existingPid = fs.readFileSync(sharedPidFile, 'utf8').trim();
601
+ if (existingPid && this.isGoAgentRunning(existingPid)) {
602
+ Logger.print("WHATAP-AGENT", `Go agent already running for application ${appIdentifier} (PID: ${existingPid})`, false);
603
+ return;
604
+ } else {
605
+ // 프로세스가 죽었다면 파일 제거
606
+ Logger.print("WHATAP-AGENT", `Previous agent process ${existingPid} not found, cleaning up`, false);
607
+ try { fs.unlinkSync(sharedLockFile); } catch (e) {}
608
+ try { fs.unlinkSync(sharedPidFile); } catch (e) {}
609
+ }
224
610
  }
611
+ } catch (e) {
612
+ Logger.printError("WHATAP-AGENT", "Error checking shared lock file", e, false);
225
613
  }
226
- Logger.print("WHATAP-170", "[WhaTap Agent] now waiting for starting......", false)
227
- self._tcpsession = TcpSession;
228
- setTimeout(function () {
229
- Logger.print("WHATAP-011" , "Connecton Retry....")
230
- self.connectCount++;
231
- self._tcpsession.open(function(err) {
232
- if(err) {
233
- Logger.printError("WHTAP-11", "TCP SESSION OPEN ERROR", err ,false);
234
- return self.connect(cb);
235
- };
236
- if(cb) cb();
614
+
615
+ // 잠금 획득 시도
616
+ let lockAcquired = false;
617
+ try {
618
+ if (!fs.existsSync(sharedLockFile)) {
619
+ fs.writeFileSync(sharedLockFile, process.pid.toString());
620
+ lockAcquired = true;
621
+ }
622
+ } catch (e) {
623
+ Logger.printError("WHATAP-AGENT", "Error acquiring lock", e, false);
624
+ }
625
+
626
+ if (!lockAcquired) {
627
+ Logger.print("WHATAP-AGENT", "Another process is starting the agent, waiting...", false);
628
+ return;
629
+ }
630
+
631
+ // 기존 whatap_nodejs.pid 파일 처리 (호환성 유지)
632
+ const pid = this.readPidFile(home, pidFileName);
633
+ if (pid) {
634
+ try {
635
+ if (this.isGoAgentRunning(pid)) {
636
+ Logger.print("WHATAP-106", `Found existing agent with PID ${pid}, terminating...`, false);
637
+ try {
638
+ process.kill(parseInt(pid), 'SIGKILL');
639
+ Logger.print("WHATAP-107", `Successfully terminated existing agent with PID ${pid}`, false);
640
+ } catch (killError) {
641
+ Logger.printError("WHATAP-108", `Error terminating process with PID ${pid}`, killError, false);
642
+ }
643
+ }
644
+ } catch (e) {
645
+ Logger.printError("WHATAP-109", `Error checking process with PID ${pid}`, e, false);
646
+ }
647
+ }
648
+
649
+ // 포트 설정 및 에이전트 시작
650
+ const port = this.configurePort();
651
+ if (!port) {
652
+ Logger.printError("WHATAP-AGENT", "Failed to configure UDP port for agent", null, true);
653
+ try { fs.unlinkSync(sharedLockFile); } catch (e) {}
654
+ return;
655
+ }
656
+ Logger.print("WHATAP-AGENT", `Configured agent to use UDP port: ${port}`, false);
657
+
658
+ try {
659
+ // 바이너리 경로 설정
660
+ const agentPath = path.join(whatapHome, AGENT_NAME);
661
+ const platform = process.platform;
662
+ const architecture = ARCH[process.arch] || process.arch;
663
+ const sourcePath = path.join(__dirname, '..', '..', 'agent', platform, architecture, AGENT_NAME);
664
+
665
+ // 심볼릭 링크 생성
666
+ if (!fs.existsSync(agentPath)) {
667
+ try {
668
+ fs.symlinkSync(sourcePath, agentPath);
669
+ Logger.print("WHATAP-AGENT", `Created symbolic link for ${AGENT_NAME}`, false);
670
+ } catch (e) {
671
+ if (e.code !== 'EEXIST') {
672
+ if (platform === 'win32') {
673
+ Logger.print("WHATAP-AGENT", "Symlink failed, copying binary instead", false);
674
+ fs.copyFileSync(sourcePath, agentPath);
675
+ } else {
676
+ throw e;
677
+ }
678
+ }
679
+ }
680
+ }
681
+
682
+ // run 디렉토리 생성
683
+ const sockfilePath = path.join(whatapHome, 'run');
684
+ if (!fs.existsSync(sockfilePath)) {
685
+ fs.mkdirSync(sockfilePath, { recursive: true });
686
+ }
687
+
688
+ // 환경 변수 설정
689
+ const newEnv = Object.assign({}, process.env);
690
+ newEnv['whatap.start'] = Date.now().toString();
691
+ newEnv['node.uptime'] = new Date().toString();
692
+ newEnv['node.version'] = process.version;
693
+ newEnv['node.tzname'] = new Date().toLocaleString('en', {timeZoneName: 'short'}).split(' ').pop();
694
+ newEnv['os.release'] = os.release();
695
+ newEnv['whatap.enabled'] = 'True';
696
+ newEnv['WHATAP_PID_FILE'] = sharedPidFile; // 공유 PID 파일 사용
697
+ newEnv['NODEJS_PARENT_APP_PID'] = process.pid.toString();
698
+ newEnv['net_udp_port'] = port.toString();
699
+ newEnv['APP_IDENTIFIER'] = appIdentifier;
700
+ newEnv['APP_NAME'] = this.getApplicationName();
701
+
702
+ // PM2 정보 추가
703
+ const isPM2 = typeof process.env.PM2_HOME !== 'undefined' ||
704
+ typeof process.env.pm_id !== 'undefined' ||
705
+ typeof process.env.PM2_JSON_PROCESSING !== 'undefined';
706
+
707
+ if (isPM2) {
708
+ newEnv['PM2_APP_NAME'] = process.env.name || '';
709
+ newEnv['PM2_ID'] = process.env.pm_id || '';
710
+ }
711
+
712
+ Object.assign(newEnv, opts);
713
+
714
+ // 에이전트 프로세스 시작
715
+ const agentProcess = child_process.spawn(
716
+ agentPath,
717
+ ['-t', '2', '-d', '1'],
718
+ {
719
+ cwd: whatapHome,
720
+ env: newEnv,
721
+ stdio: ['pipe', 'pipe', 'pipe'],
722
+ detached: true
723
+ }
724
+ );
725
+
726
+ let stdout = '';
727
+ let stderr = '';
728
+
729
+ agentProcess.stdout.on('data', (data) => {
730
+ stdout += data.toString();
731
+ });
732
+
733
+ agentProcess.stderr.on('data', (data) => {
734
+ stderr += data.toString();
237
735
  });
238
- }, self.setLoopTime);
736
+
737
+ // PID 추적을 위한 추가 로직
738
+ agentProcess.on('spawn', () => {
739
+ Logger.print("WHATAP-AGENT", `Agent process spawned with PID: ${agentProcess.pid}`, false);
740
+
741
+ // detached 프로세스에서 실제 PID가 다를 수 있으므로 잠시 대기 후 확인
742
+ setTimeout(() => {
743
+ try {
744
+ // ps 명령으로 실제 PID 찾기
745
+ const psCommand = process.platform === 'win32'
746
+ ? `tasklist /FI "IMAGENAME eq ${AGENT_NAME}*" /FO CSV /NH`
747
+ : `ps -ef | grep ${AGENT_NAME} | grep -v grep`;
748
+
749
+ const psOutput = child_process.execSync(psCommand, { encoding: 'utf8' });
750
+ const lines = psOutput.trim().split('\n');
751
+
752
+ for (const line of lines) {
753
+ let foundPid = null;
754
+
755
+ if (process.platform === 'win32') {
756
+ // Windows CSV format: "image","pid","session","#","mem"
757
+ const match = line.match(/"[^"]+","(\d+)"/);
758
+ if (match) foundPid = match[1];
759
+ } else {
760
+ // Unix format: uid pid ppid ...
761
+ const parts = line.split(/\s+/);
762
+ if (parts.length > 1) foundPid = parts[1];
763
+ }
764
+
765
+ if (foundPid) {
766
+ const pidNum = parseInt(foundPid);
767
+ // spawn된 PID와 비슷한 범위인지 확인
768
+ if (Math.abs(pidNum - agentProcess.pid) <= 2) {
769
+ Logger.print("WHATAP-AGENT",
770
+ `Found actual agent PID: ${foundPid} (spawn returned: ${agentProcess.pid})`,
771
+ false);
772
+
773
+ // 실제 PID로 파일 업데이트
774
+ fs.writeFileSync(sharedPidFile, foundPid);
775
+ this.writeToFile(home, pidFileName, foundPid);
776
+ return;
777
+ }
778
+ }
779
+ }
780
+
781
+ // 실제 PID를 찾지 못한 경우 spawn이 반환한 PID 사용
782
+ Logger.print("WHATAP-AGENT",
783
+ `Using spawn PID: ${agentProcess.pid} (actual PID not found)`,
784
+ false);
785
+
786
+ fs.writeFileSync(sharedPidFile, agentProcess.pid.toString());
787
+ this.writeToFile(home, pidFileName, agentProcess.pid.toString());
788
+
789
+ } catch (e) {
790
+ Logger.printError("WHATAP-AGENT", "Error finding actual PID", e, false);
791
+ // 에러 발생 시 spawn이 반환한 PID 사용
792
+ fs.writeFileSync(sharedPidFile, agentProcess.pid.toString());
793
+ this.writeToFile(home, pidFileName, agentProcess.pid.toString());
794
+ }
795
+ }, 1000); // 1초 대기
796
+ });
797
+
798
+ agentProcess.on('close', (code) => {
799
+ if (code !== 0) {
800
+ Logger.printError("WHATAP-AGENT", `Agent process exited with code ${code}`, null, true);
801
+ Logger.print("WHATAP-AGENT", `STDOUT: ${stdout}`, false);
802
+ Logger.print("WHATAP-AGENT", `STDERR: ${stderr}`, false);
803
+ // 에이전트가 비정상 종료되면 파일 제거
804
+ try { fs.unlinkSync(sharedLockFile); } catch (e) {}
805
+ try { fs.unlinkSync(sharedPidFile); } catch (e) {}
806
+ } else {
807
+ Logger.print("WHATAP-AGENT", "Agent started successfully", false);
808
+ }
809
+ });
810
+
811
+ agentProcess.unref();
812
+
813
+ Logger.print("WHATAP-AGENT",
814
+ `AGENT UP! (process name: ${AGENT_NAME}, using port: ${port}, app: ${appIdentifier})`,
815
+ false);
816
+
817
+ // 잠금 해제
818
+ try { fs.unlinkSync(sharedLockFile); } catch (e) {}
819
+
820
+ } catch (e) {
821
+ Logger.printError("WHATAP-AGENT", "Error starting agent", e, true);
822
+ // 에러 발생 시 파일 정리
823
+ try { fs.unlinkSync(sharedLockFile); } catch (e) {}
824
+ try { fs.unlinkSync(sharedPidFile); } catch (e) {}
825
+ }
239
826
  };
240
827
 
241
- NodeAgent.prototype.connectCB = function (cb) {
828
+ // 프로세스 종료 정리 작업 개선
829
+ process.on('exit', () => {
830
+ if (!process.env.WHATAP_HOME || !NodeAgent.prototype.getApplicationIdentifier) {
831
+ return;
832
+ }
833
+
834
+ try {
835
+ const appIdentifier = NodeAgent.prototype.getApplicationIdentifier.call(NodeAgent);
836
+ const sharedPidFile = path.join(process.env.WHATAP_HOME, `agent-${appIdentifier}.pid`);
837
+
838
+ if (fs.existsSync(sharedPidFile)) {
839
+ const pid = fs.readFileSync(sharedPidFile, 'utf8').trim();
840
+ // 현재 프로세스가 기록된 PID의 부모 프로세스인 경우에만 제거
841
+ if (pid === process.env.NODEJS_PARENT_APP_PID) {
842
+ fs.unlinkSync(sharedPidFile);
843
+ }
844
+ }
845
+ } catch (e) {
846
+ // 에러 무시
847
+ }
848
+ });
849
+
850
+ // graceful shutdown 처리 추가
851
+ ['SIGTERM', 'SIGINT'].forEach((signal) => {
852
+ process.on(signal, () => {
853
+ Logger.print("WHATAP-AGENT", `Received ${signal}, cleaning up...`, false);
854
+ process.exit(0);
855
+ });
856
+ });
857
+
858
+ // Updated init method to incorporate configuration setup similar to Python agent
859
+ NodeAgent.prototype.init = function(cb) {
242
860
  var self = this;
243
- if(self.connectCount >0 && self.connectCount % 5 == 0 ) {
244
- return cb(new Error());
861
+ if (self._initialized) {
862
+ return self;
245
863
  }
864
+ self._initialized = true;
865
+ self.starttime = Date.now();
246
866
 
247
- Logger.print("WHATAP-970", "[WhaTap Agent] now waiting for starting......", true)
248
- self._tcpsession = TcpSession;
249
- setTimeout(function () {
250
- self._tcpsession.open(function(err) {
251
- if(err) {
252
- Logger.printError("WHTAP-911", "TCP SESSION OPEN ERROR", err ,true);
253
- self.connectCB(cb);
254
- return;
255
- };
256
- if(cb) cb();
257
- });
258
- }, self.setLoopTime);
867
+ self.findRoot();
868
+
869
+ Logger.initializer.process();
870
+ Logger.print('WHATAP-110', 'Start initialize WhaTap Agent... Root[' + self._conf['app.root'] + ']', true);
871
+
872
+ if (self._conf['app.root'] == null || self._conf['app.root'].length == 0) {
873
+ return Logger.print("WHATAP-111", "Can not find application root directory", true);
874
+ }
875
+
876
+ // Initialize configuration with proper home directory
877
+ if (!self.initConfig('WHATAP_HOME')) {
878
+ Logger.printError("WHATAP-CONFIG", "Failed to initialize configuration", null, true);
879
+ return self;
880
+ }
881
+
882
+ NodeUtil.getPackageJson();
883
+
884
+ self._conf.init(this._userOpt, function(e) {
885
+ if (e) {
886
+ Logger.printError("WHATAP-112", "Configuration initialize error. " + e.message,
887
+ e, true);
888
+ return;
889
+ }
890
+ Logger.print('WHATAP-113', 'Finish initialize configuration... ' + Boolean(self._conf['reqlog_enabled']), false);
891
+ if (Boolean(self._conf['reqlog_enabled']) == true) {
892
+ Logger.print("WHATAP-REQLOG", "ReqLog Init Start !!!!", false);
893
+ RequestLog.initializer.process();
894
+ }
895
+
896
+ if(!self._counterManager){
897
+ self._counterManager = new CounterManager();
898
+ self._counterManager.run();
899
+ }
900
+
901
+ // Set Oname,Oid
902
+ self._securityMaster.decideAgentOnameOid();
903
+
904
+ // Start Node.js agent with proper port configuration
905
+ WhatapUtil.printWhatap();
906
+ self.startGoAgent();
907
+ TraceContextManager.initialized = true;
908
+
909
+ self.initUdp();
910
+ });
911
+
912
+ self.loadObserves();
913
+ return self;
259
914
  };
260
915
 
916
+ NodeAgent.prototype.context = function(cb) {
917
+ return TraceContextManager.getCurrentContext();
918
+ }
919
+
261
920
  NodeAgent.prototype.loadObserves = function() {
262
921
  var agent = this;
263
922
  var observes = [];
264
923
 
265
924
  observes.push(HttpObserver);
266
- observes.push(NetObserver);
267
- observes.push(ClusterObserver);
268
925
  observes.push(MysqlObserver);
269
926
  observes.push(Mysql2Observer);
270
927
  observes.push(MariaObserver);
271
928
  observes.push(SocketioObserver);
272
929
  observes.push(WebsocketObserver);
273
- observes.push(ExpressObserver);
274
- observes.push(FileObserver);
930
+ // observes.push(WsObserver);
275
931
  observes.push(MongoObserver);
276
- observes.push(MongooseObserver);
932
+ // observes.push(MongooseObserver);
277
933
  observes.push(RedisObserver);
934
+ observes.push(IORedisObserver);
278
935
  observes.push(MssqlObserver);
279
- observes.push(ThriftObserver);
280
- observes.push(PromiseObserver);
281
936
  observes.push(PgSqlObserver);
282
- observes.push(ScheduleObserver);
283
937
  // observes.push(GRpcObserver);
284
938
  observes.push(ApolloObserver);
285
939
  observes.push(PrismaObserver);
@@ -321,31 +975,83 @@ NodeAgent.prototype.setServicePort = function (port) {
321
975
  Configuration['whatap.port'] = port || 0;
322
976
  }
323
977
 
324
- NodeAgent.prototype.profile = function(desc, value){
978
+ NodeAgent.prototype.initUdp = function() {
325
979
  var self = this;
980
+ Logger.print('WHATAP-UDP', 'Initializing UDP connection...', false);
981
+
982
+ // Initialize the UDP session
983
+ UdpSession.udp(self._conf);
326
984
 
327
- var ctx = TraceContextManager.getCurrentContext();
328
- if(ctx == null) { return; }
985
+ // Initialize the async sender
986
+ AsyncSender.startWhatapThread();
987
+
988
+ Logger.print('WHATAP-UDP', 'UDP connection initialized', false);
989
+ };
329
990
 
330
- if( self.userProfileHash === 0){
331
- self.userProfileHash = HashUtil.hashFromString('CustomProfileStep');
991
+ /**
992
+ * Check if WHATAP_HOME is set - similar to Python's check_whatap_home
993
+ * @param {string} target - Environment variable name to check
994
+ * @returns {string|null} - Value of the environment variable or null
995
+ */
996
+ NodeAgent.prototype.checkWhatapHome = function(target) {
997
+ let whatapHome = process.env[target];
998
+ if (!whatapHome) {
999
+ whatapHome = this.findWhatapConf();
332
1000
  }
333
- DataTextAgent.add(TextTypes.MESSAGE,self.userProfileHash, 'CustomProfileStep');
334
- var step = new MessageStep();
335
- step.hash = self.userProfileHash;
336
- step.start_time = ctx.getElapsedTime();
337
- step.desc = desc;
338
- if(value){
339
- step.value=false;
1001
+ if (!whatapHome) {
1002
+ Logger.print("WHATAP-HOME", `${target} is empty`, true);
340
1003
  }
341
- ctx.profile.add(step);
1004
+ return whatapHome;
1005
+ };
1006
+
1007
+ /**
1008
+ * Initialize configuration with default values - similar to Python's init_config
1009
+ * @param {string} home - Environment variable name for home directory
1010
+ * @returns {boolean} - True if successful, false otherwise
1011
+ */
1012
+ NodeAgent.prototype.initConfig = function(home) {
1013
+ let whatapHome = process.env[home];
1014
+ if (!whatapHome) {
1015
+ whatapHome = this.findWhatapConf();
1016
+ }
1017
+ if (!whatapHome) {
1018
+ whatapHome = this.readFile(home, home.toLowerCase());
1019
+ if (!whatapHome) {
1020
+ whatapHome = process.cwd();
1021
+ process.env[home] = whatapHome;
1022
+
1023
+ Logger.print("WHATAP-HOME", "WHATAP_HOME is empty", true);
1024
+ Logger.print("WHATAP-HOME", "WHATAP_HOME set default CURRENT_WORKING_DIRECTORY value", true);
1025
+ Logger.print("WHATAP-HOME", `CURRENT_WORKING_DIRECTORY is ${whatapHome}`, true);
1026
+ }
1027
+ }
1028
+
1029
+ if (!this.writeFile(home, home.toLowerCase(), whatapHome)) {
1030
+ return false;
1031
+ }
1032
+
1033
+ process.env[home] = whatapHome;
1034
+ const configFile = path.join(process.env[home], 'whatap.conf');
1035
+
1036
+ if (!fs.existsSync(configFile)) {
1037
+ try {
1038
+ // Copy default config from module directory
1039
+ const defaultConfigPath = path.join(path.dirname(__dirname), 'whatap.conf');
1040
+ if (fs.existsSync(defaultConfigPath)) {
1041
+ const content = fs.readFileSync(defaultConfigPath, 'utf8');
1042
+ fs.writeFileSync(configFile, content);
1043
+ } else {
1044
+ // Create empty config file
1045
+ fs.writeFileSync(configFile, '# WhaTap Node.js Agent Configuration\n');
1046
+ }
1047
+ } catch (e) {
1048
+ Logger.printError("WHATAP-CONFIG", "Permission error creating config file", e, true);
1049
+ Logger.print("WHATAP-CONFIG", 'Try to execute command: `sudo chmod -R 777 $WHATAP_HOME`', true);
1050
+ return false;
1051
+ }
1052
+ }
1053
+
1054
+ return true;
1055
+ };
342
1056
 
343
- }
344
- // var plugins={};
345
- // NodeAgent.prototype.plugin = function(name, func){
346
- // if(func){
347
- // plugins[name]=func;
348
- // }
349
- // return plugins[name];
350
- // }
351
1057
  exports.NodeAgent = new NodeAgent();