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.
- package/agent/darwin/arm64/whatap_nodejs +0 -0
- package/agent/linux/amd64/whatap_nodejs +0 -0
- package/agent/linux/arm64/whatap_nodejs +0 -0
- package/build.txt +4 -0
- package/lib/conf/config-default.js +3 -10
- package/lib/conf/configure.js +349 -369
- package/lib/conf/license.js +1 -1
- package/lib/control/packagectr-helper.js +3 -34
- package/lib/core/agent.js +882 -176
- package/lib/core/interceptor.js +6 -6
- package/lib/core/shimmer.js +36 -82
- package/lib/counter/counter-manager.js +8 -79
- package/lib/counter/task/activetransaction.js +17 -68
- package/lib/io/data-inputx.js +3 -13
- package/lib/io/data-outputx.js +206 -268
- package/lib/logger.js +6 -6
- package/lib/net/security-master.js +20 -139
- package/lib/observers/apollo-server-observer.js +27 -33
- package/lib/observers/global-observer.js +80 -155
- package/lib/observers/http-observer.js +236 -666
- package/lib/observers/ioredis-observer.js +294 -0
- package/lib/observers/maria-observer.js +362 -204
- package/lib/observers/mongodb-observer.js +226 -169
- package/lib/observers/mongoose-observer.js +323 -518
- package/lib/observers/mssql-observer.js +418 -177
- package/lib/observers/mysql-observer.js +449 -342
- package/lib/observers/mysql2-observer.js +358 -396
- package/lib/observers/oracle-observer.js +384 -559
- package/lib/observers/pgsql-observer.js +489 -231
- package/lib/observers/prisma-observer.js +92 -303
- package/lib/observers/process-observer.js +35 -79
- package/lib/observers/redis-observer.js +331 -166
- package/lib/observers/socket.io-observer.js +187 -226
- package/lib/observers/websocket-observer.js +301 -175
- package/lib/pack/counter-pack.js +0 -3
- package/lib/pack/log-sink-pack.js +52 -14
- package/lib/pack/tagcount-pack.js +4 -4
- package/lib/{counter/task → system}/gc-action.js +74 -27
- package/lib/trace/trace-context-manager.js +25 -113
- package/lib/trace/trace-context.js +7 -21
- package/lib/trace/trace-httpc.js +11 -17
- package/lib/trace/trace-sql.js +21 -29
- package/lib/udp/async_sender.js +119 -0
- package/lib/udp/index.js +17 -0
- package/lib/udp/packet_enum.js +52 -0
- package/lib/udp/packet_queue.js +69 -0
- package/lib/udp/packet_type_enum.js +33 -0
- package/lib/udp/param_def.js +72 -0
- package/lib/udp/udp_session.js +336 -0
- package/lib/util/escape-literal-sql.js +5 -5
- package/lib/util/hashutil.js +18 -18
- package/lib/util/keygen.js +3 -0
- package/lib/util/linkedset.js +2 -1
- package/lib/util/nodeutil.js +1 -2
- package/lib/util/sql-util.js +178 -0
- package/lib/util/trace-helper.js +91 -0
- package/lib/util/transfer.js +58 -0
- package/lib/value/map-value.js +2 -3
- package/package.json +5 -10
- package/lib/conf/conf-sys-mon.js +0 -101
- package/lib/control/cmd-config.js +0 -24
- package/lib/control/control-handler.js +0 -367
- package/lib/core/request-agent.js +0 -27
- package/lib/counter/meter/meter-activex.js +0 -67
- package/lib/counter/meter/meter-httpc.js +0 -57
- package/lib/counter/meter/meter-resource.js +0 -9
- package/lib/counter/meter/meter-service.js +0 -168
- package/lib/counter/meter/meter-socket.io.js +0 -51
- package/lib/counter/meter/meter-sql.js +0 -71
- package/lib/counter/meter/meter-users.js +0 -58
- package/lib/counter/meter.js +0 -183
- package/lib/counter/task/agentinfo.js +0 -107
- package/lib/counter/task/gcstat.js +0 -34
- package/lib/counter/task/heapmem.js +0 -25
- package/lib/counter/task/httpc.js +0 -76
- package/lib/counter/task/metering-info.js +0 -125
- package/lib/counter/task/proc-cpu.js +0 -29
- package/lib/counter/task/realtimeuser.js +0 -31
- package/lib/counter/task/res/systemECSTask.js +0 -39
- package/lib/counter/task/res/systemKubeTask.js +0 -53
- package/lib/counter/task/res/util/awsEcsClientThread.js +0 -218
- package/lib/counter/task/res/util/linuxProcStatUtil.js +0 -14
- package/lib/counter/task/res-sys-cpu.js +0 -62
- package/lib/counter/task/service.js +0 -202
- package/lib/counter/task/socketio.js +0 -30
- package/lib/counter/task/sql.js +0 -105
- package/lib/counter/task/systemperf.js +0 -43
- package/lib/data/datapack-sender.js +0 -289
- package/lib/data/dataprofile-agent.js +0 -162
- package/lib/data/datatext-agent.js +0 -135
- package/lib/data/event-level.js +0 -15
- package/lib/data/test.js +0 -49
- package/lib/data/zipprofile.js +0 -197
- package/lib/env/constants.js +0 -21
- package/lib/error/error-handler.js +0 -437
- package/lib/kube/kube-client.js +0 -144
- package/lib/lang/text-types.js +0 -58
- package/lib/logsink/line-log-util.js +0 -87
- package/lib/logsink/line-log.js +0 -12
- package/lib/logsink/log-sender.js +0 -78
- package/lib/logsink/log-tracer.js +0 -40
- package/lib/logsink/sender-util.js +0 -56
- package/lib/logsink/zip/zip-send.js +0 -177
- package/lib/net/netflag.js +0 -55
- package/lib/net/receiver.js +0 -66
- package/lib/net/sender.js +0 -141
- package/lib/net/tcp-return.js +0 -18
- package/lib/net/tcp-session.js +0 -286
- package/lib/net/tcpreq-client-proxy.js +0 -70
- package/lib/net/tcprequest-mgr.js +0 -58
- package/lib/observers/cluster-observer.js +0 -22
- package/lib/observers/express-observer.js +0 -215
- package/lib/observers/file-observer.js +0 -184
- package/lib/observers/grpc-observer.js +0 -336
- package/lib/observers/memcached-observer.js +0 -56
- package/lib/observers/mongo-observer.js +0 -317
- package/lib/observers/net-observer.js +0 -77
- package/lib/observers/promise-observer.js +0 -31
- package/lib/observers/schedule-observer.js +0 -67
- package/lib/observers/stream-observer.js +0 -19
- package/lib/observers/thrift-observer.js +0 -197
- package/lib/pack/activestack-pack.js +0 -55
- package/lib/pack/apenum.js +0 -8
- package/lib/pack/errorsnap-pack.js +0 -69
- package/lib/pack/event-pack.js +0 -54
- package/lib/pack/hitmap-pack.js +0 -63
- package/lib/pack/hitmap-pack1.js +0 -152
- package/lib/pack/netstat.js +0 -15
- package/lib/pack/otype.js +0 -7
- package/lib/pack/profile-pack.js +0 -49
- package/lib/pack/realtimeuser-pack.js +0 -41
- package/lib/pack/stat-general-pack.js +0 -96
- package/lib/pack/staterror-pack.js +0 -120
- package/lib/pack/stathttpc-pack.js +0 -66
- package/lib/pack/stathttpc-rec.js +0 -78
- package/lib/pack/statremote-pack.js +0 -46
- package/lib/pack/statservice-pack.js +0 -63
- package/lib/pack/statservice-pack1.js +0 -88
- package/lib/pack/statservice-rec.js +0 -292
- package/lib/pack/statservice-rec_dep.js +0 -151
- package/lib/pack/statsql-pack.js +0 -69
- package/lib/pack/statsql-rec.js +0 -100
- package/lib/pack/statuseragent-pack.js +0 -44
- package/lib/pack/tagctr.js +0 -15
- package/lib/pack/text-pack.js +0 -50
- package/lib/pack/time-count.js +0 -25
- package/lib/pack/websocket.js +0 -15
- package/lib/pack/zip-pack.js +0 -70
- package/lib/pii/pii-item.js +0 -31
- package/lib/pii/pii-mask.js +0 -174
- package/lib/plugin/plugin-loadermanager.js +0 -57
- package/lib/plugin/plugin.js +0 -75
- package/lib/service/tx-record.js +0 -332
- package/lib/stat/stat-error.js +0 -116
- package/lib/stat/stat-httpc.js +0 -98
- package/lib/stat/stat-remote-ip.js +0 -46
- package/lib/stat/stat-remote-ipurl.js +0 -88
- package/lib/stat/stat-sql.js +0 -113
- package/lib/stat/stat-tranx.js +0 -58
- package/lib/stat/stat-tx-caller.js +0 -160
- package/lib/stat/stat-tx-domain.js +0 -111
- package/lib/stat/stat-tx-referer.js +0 -112
- package/lib/stat/stat-useragent.js +0 -48
- package/lib/stat/timingsender.js +0 -76
- package/lib/step/activestack-step.js +0 -38
- package/lib/step/dbc-step.js +0 -36
- package/lib/step/http-stepx.js +0 -67
- package/lib/step/message-step.js +0 -40
- package/lib/step/method-stepx.js +0 -45
- package/lib/step/resultset-step.js +0 -40
- package/lib/step/securemsg-step.js +0 -44
- package/lib/step/socket-step.js +0 -46
- package/lib/step/sql-stepx.js +0 -68
- package/lib/step/sqlxtype.js +0 -16
- package/lib/step/step.js +0 -66
- package/lib/step/stepenum.js +0 -54
- package/lib/topology/link.js +0 -63
- package/lib/topology/nodeinfo.js +0 -123
- package/lib/topology/status-detector.js +0 -111
- package/lib/util/anylist.js +0 -103
- package/lib/util/cardinality/hyperloglog.js +0 -106
- package/lib/util/cardinality/murmurhash.js +0 -31
- package/lib/util/cardinality/registerset.js +0 -75
- package/lib/util/errordata.js +0 -21
- package/lib/util/iputil_x.js +0 -527
- package/lib/util/kube-util.js +0 -73
- package/lib/util/paramsecurity.js +0 -80
- package/lib/util/pre-process.js +0 -13
- package/lib/util/process-seq.js +0 -166
- package/lib/util/property-util.js +0 -36
- package/lib/util/request-queue.js +0 -70
- package/lib/util/requestdouble-queue.js +0 -72
- package/lib/util/resourceprofile.js +0 -157
- package/lib/util/stop-watch.js +0 -30
- package/lib/util/system-util.js +0 -10
- 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
|
-
|
|
26
|
-
|
|
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
|
-
|
|
60
|
-
require('../
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
116
|
-
self.starttime = Date.now();
|
|
169
|
+
};
|
|
117
170
|
|
|
118
|
-
|
|
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
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
197
|
+
// Modified isGoAgentRunning to check PID +-1 range
|
|
198
|
+
NodeAgent.prototype.isGoAgentRunning = function(pid) {
|
|
199
|
+
try {
|
|
200
|
+
if (!pid) return false;
|
|
126
201
|
|
|
127
|
-
|
|
202
|
+
// 원래 PID와 +1, -1된 PID 모두 확인
|
|
203
|
+
const pidsToCheck = [
|
|
204
|
+
parseInt(pid),
|
|
205
|
+
parseInt(pid) + 1,
|
|
206
|
+
parseInt(pid) - 1
|
|
207
|
+
];
|
|
128
208
|
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
|
|
134
|
-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
163
|
-
|
|
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.
|
|
167
|
-
|
|
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
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
540
|
+
const identifier = crypto
|
|
541
|
+
.createHash('md5')
|
|
542
|
+
.update(`${appRoot}:${appName}`)
|
|
543
|
+
.digest('hex')
|
|
544
|
+
.substr(0, 8);
|
|
206
545
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
244
|
-
return
|
|
861
|
+
if (self._initialized) {
|
|
862
|
+
return self;
|
|
245
863
|
}
|
|
864
|
+
self._initialized = true;
|
|
865
|
+
self.starttime = Date.now();
|
|
246
866
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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(
|
|
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.
|
|
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
|
-
|
|
328
|
-
|
|
985
|
+
// Initialize the async sender
|
|
986
|
+
AsyncSender.startWhatapThread();
|
|
987
|
+
|
|
988
|
+
Logger.print('WHATAP-UDP', 'UDP connection initialized', false);
|
|
989
|
+
};
|
|
329
990
|
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
334
|
-
|
|
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
|
-
|
|
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();
|