whatap 1.0.8 → 1.0.9
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 +2 -2
- package/lib/core/agent.js +266 -81
- package/lib/logger.js +1 -1
- package/lib/udp/udp_session.js +11 -10
- package/package.json +2 -2
- package/.claude/settings.local.json +0 -10
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/build.txt
CHANGED
package/lib/core/agent.js
CHANGED
|
@@ -121,7 +121,7 @@ NodeAgent.prototype.isNodejsAgentProcess = function(pid) {
|
|
|
121
121
|
if (fs.existsSync(cmdlineFile)) {
|
|
122
122
|
processCommand = fs.readFileSync(cmdlineFile, 'utf8');
|
|
123
123
|
// Log for debugging
|
|
124
|
-
Logger.print("WHATAP-001", `Process ${pid} command line: ${processCommand}`, false);
|
|
124
|
+
// Logger.print("WHATAP-001", `Process ${pid} command line: ${processCommand}`, false);
|
|
125
125
|
}
|
|
126
126
|
} catch (e) {
|
|
127
127
|
Logger.printError("WHATAP-101", "Error reading Linux process info", e, false);
|
|
@@ -131,7 +131,7 @@ NodeAgent.prototype.isNodejsAgentProcess = function(pid) {
|
|
|
131
131
|
// On macOS, use ps command
|
|
132
132
|
try {
|
|
133
133
|
processCommand = child_process.execSync(`ps -p ${pid} -o command=`, { encoding: 'utf8' }).trim();
|
|
134
|
-
Logger.print("WHATAP-001", `Process ${pid} command line: ${processCommand}`, false);
|
|
134
|
+
// Logger.print("WHATAP-001", `Process ${pid} command line: ${processCommand}`, false);
|
|
135
135
|
} catch (e) {
|
|
136
136
|
Logger.printError("WHATAP-102", "Error reading MacOS process info", e, false);
|
|
137
137
|
return false;
|
|
@@ -144,7 +144,7 @@ NodeAgent.prototype.isNodejsAgentProcess = function(pid) {
|
|
|
144
144
|
const match = output.match(/"([^"]+)"/);
|
|
145
145
|
if (match && match[1]) {
|
|
146
146
|
processCommand = match[1];
|
|
147
|
-
Logger.print("WHATAP-001", `Process ${pid} command line: ${processCommand}`, false);
|
|
147
|
+
// Logger.print("WHATAP-001", `Process ${pid} command line: ${processCommand}`, false);
|
|
148
148
|
}
|
|
149
149
|
} catch (e) {
|
|
150
150
|
Logger.printError("WHATAP-103", "Error reading Windows process info", e, false);
|
|
@@ -163,7 +163,7 @@ NodeAgent.prototype.isNodejsAgentProcess = function(pid) {
|
|
|
163
163
|
|
|
164
164
|
// Check if the command contains the agent name
|
|
165
165
|
const isAgentProcess = processCommand.indexOf(AGENT_NAME) >= 0;
|
|
166
|
-
Logger.print("WHATAP-002", `Process ${pid} is ${isAgentProcess ? '' : 'not '}a WhaTap agent`, false);
|
|
166
|
+
// Logger.print("WHATAP-002", `Process ${pid} is ${isAgentProcess ? '' : 'not '}a WhaTap agent`, false);
|
|
167
167
|
return isAgentProcess;
|
|
168
168
|
} catch (e) {
|
|
169
169
|
Logger.printError("WHATAP-003", "Error in isNodejsAgentProcess", e, false);
|
|
@@ -209,7 +209,7 @@ NodeAgent.prototype.isGoAgentRunning = function(pid) {
|
|
|
209
209
|
parseInt(pid) - 1
|
|
210
210
|
];
|
|
211
211
|
|
|
212
|
-
Logger.print("WHATAP-007", `Checking PIDs: ${pidsToCheck.join(', ')} (original: ${pid})`, false);
|
|
212
|
+
// Logger.print("WHATAP-007", `Checking PIDs: ${pidsToCheck.join(', ')} (original: ${pid})`, false);
|
|
213
213
|
|
|
214
214
|
for (const pidToCheck of pidsToCheck) {
|
|
215
215
|
try {
|
|
@@ -218,9 +218,9 @@ NodeAgent.prototype.isGoAgentRunning = function(pid) {
|
|
|
218
218
|
|
|
219
219
|
// whatap_nodejs 프로세스인지 확인
|
|
220
220
|
if (this.isNodejsAgentProcess(pidToCheck)) {
|
|
221
|
-
Logger.print("WHATAP-008",
|
|
222
|
-
|
|
223
|
-
|
|
221
|
+
// Logger.print("WHATAP-008",
|
|
222
|
+
// `Found agent process at PID ${pidToCheck} (original PID in file: ${pid})`,
|
|
223
|
+
// false);
|
|
224
224
|
return true;
|
|
225
225
|
}
|
|
226
226
|
} catch (e) {
|
|
@@ -682,9 +682,9 @@ NodeAgent.prototype.getApplicationIdentifier = function() {
|
|
|
682
682
|
.digest('hex')
|
|
683
683
|
.substr(0, 8);
|
|
684
684
|
|
|
685
|
-
Logger.print("WHATAP-026",
|
|
686
|
-
|
|
687
|
-
|
|
685
|
+
// Logger.print("WHATAP-026",
|
|
686
|
+
// `Application identifier: ${appName} at ${appRoot}${isPM2Cluster ? ' (PM2 cluster mode)' : (process.env.pm_id !== undefined ? ' (PM2 instance: ' + process.env.pm_id + ')' : '')} => ${identifier}`,
|
|
687
|
+
// false);
|
|
688
688
|
|
|
689
689
|
return identifier;
|
|
690
690
|
};
|
|
@@ -718,6 +718,164 @@ NodeAgent.prototype.getApplicationName = function() {
|
|
|
718
718
|
return path.basename(process.argv[1], '.js');
|
|
719
719
|
};
|
|
720
720
|
|
|
721
|
+
// Restart tracking variables
|
|
722
|
+
NodeAgent.prototype._restartCount = 0;
|
|
723
|
+
NodeAgent.prototype._lastRestartTime = 0;
|
|
724
|
+
NodeAgent.prototype._maxRestarts = 5;
|
|
725
|
+
NodeAgent.prototype._restartInterval = 10000; // 10초
|
|
726
|
+
NodeAgent.prototype._healthCheckInterval = null;
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* Start health check for whatap_nodejs process
|
|
730
|
+
* Periodically checks if the agent is running and restarts if needed
|
|
731
|
+
*/
|
|
732
|
+
NodeAgent.prototype.startHealthCheck = function() {
|
|
733
|
+
const self = this;
|
|
734
|
+
const whatapHome = process.env.WHATAP_HOME;
|
|
735
|
+
|
|
736
|
+
if (!whatapHome) {
|
|
737
|
+
Logger.print("WHATAP-206", "Cannot start health check: WHATAP_HOME not set", false);
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// Clear existing health check if any
|
|
742
|
+
if (self._healthCheckInterval) {
|
|
743
|
+
clearInterval(self._healthCheckInterval);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// PM2 cluster 모드에서는 첫 번째 인스턴스만 health check 수행
|
|
747
|
+
const isPM2Cluster = self.isPM2ClusterMode();
|
|
748
|
+
if (isPM2Cluster && process.env.pm_id !== '0') {
|
|
749
|
+
// Logger.print("WHATAP-207",
|
|
750
|
+
// `PM2 cluster mode: Instance ${process.env.pm_id} will not run health check (only instance 0)`,
|
|
751
|
+
// false);
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
Logger.print("WHATAP-208", "Starting agent health check (interval: 30s)", false);
|
|
756
|
+
|
|
757
|
+
self._healthCheckInterval = setInterval(() => {
|
|
758
|
+
try {
|
|
759
|
+
const appIdentifier = self.getApplicationIdentifier();
|
|
760
|
+
const sharedPidFile = path.join(whatapHome, `agent-${appIdentifier}.pid`);
|
|
761
|
+
|
|
762
|
+
// PID 파일이 없으면 에이전트가 시작되지 않은 것
|
|
763
|
+
if (!fs.existsSync(sharedPidFile)) {
|
|
764
|
+
Logger.print("WHATAP-209", "Agent PID file not found, attempting to start...", false);
|
|
765
|
+
self.startGoAgent();
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// PID 읽기
|
|
770
|
+
const pid = fs.readFileSync(sharedPidFile, 'utf8').trim();
|
|
771
|
+
if (!pid) {
|
|
772
|
+
Logger.print("WHATAP-210", "Agent PID file is empty, attempting to start...", false);
|
|
773
|
+
self.startGoAgent();
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// 프로세스가 실행 중인지 확인
|
|
778
|
+
if (!self.isGoAgentRunning(pid)) {
|
|
779
|
+
Logger.printError("WHATAP-211",
|
|
780
|
+
`Agent process (PID: ${pid}) is not running. Attempting restart...`,
|
|
781
|
+
null, false);
|
|
782
|
+
self.restartGoAgent();
|
|
783
|
+
}
|
|
784
|
+
} catch (e) {
|
|
785
|
+
Logger.printError("WHATAP-212", "Error during health check", e, false);
|
|
786
|
+
}
|
|
787
|
+
}, 30000); // 30초마다 체크
|
|
788
|
+
|
|
789
|
+
// Node.js 프로세스 종료 시 health check도 정리
|
|
790
|
+
process.on('exit', () => {
|
|
791
|
+
if (self._healthCheckInterval) {
|
|
792
|
+
clearInterval(self._healthCheckInterval);
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
};
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Stop health check
|
|
799
|
+
*/
|
|
800
|
+
NodeAgent.prototype.stopHealthCheck = function() {
|
|
801
|
+
if (this._healthCheckInterval) {
|
|
802
|
+
clearInterval(this._healthCheckInterval);
|
|
803
|
+
this._healthCheckInterval = null;
|
|
804
|
+
Logger.print("WHATAP-213", "Stopped agent health check", false);
|
|
805
|
+
}
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* Restart the Go agent
|
|
810
|
+
* @param {object} opts - Options for agent
|
|
811
|
+
* @returns {boolean} - True if restart attempted, false if blocked
|
|
812
|
+
*/
|
|
813
|
+
NodeAgent.prototype.restartGoAgent = function(opts = {}) {
|
|
814
|
+
const self = this;
|
|
815
|
+
const now = Date.now();
|
|
816
|
+
const timeSinceLastRestart = now - self._lastRestartTime;
|
|
817
|
+
|
|
818
|
+
// Reset restart count if enough time has passed (5 minutes)
|
|
819
|
+
if (timeSinceLastRestart > 300000) {
|
|
820
|
+
self._restartCount = 0;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// Check if we've exceeded max restarts
|
|
824
|
+
if (self._restartCount >= self._maxRestarts) {
|
|
825
|
+
Logger.printError("WHATAP-200",
|
|
826
|
+
`Max restart attempts (${self._maxRestarts}) reached. Agent will not restart automatically.`,
|
|
827
|
+
null, false);
|
|
828
|
+
return false;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// Check if restart interval has passed
|
|
832
|
+
if (timeSinceLastRestart < self._restartInterval) {
|
|
833
|
+
Logger.print("WHATAP-201",
|
|
834
|
+
`Restart interval not reached. Waiting ${(self._restartInterval - timeSinceLastRestart) / 1000}s`,
|
|
835
|
+
false);
|
|
836
|
+
return false;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
self._restartCount++;
|
|
840
|
+
self._lastRestartTime = now;
|
|
841
|
+
|
|
842
|
+
Logger.print("WHATAP-202",
|
|
843
|
+
`Attempting to restart agent (attempt ${self._restartCount}/${self._maxRestarts})`,
|
|
844
|
+
false);
|
|
845
|
+
|
|
846
|
+
// Clean up lock and PID files before restart
|
|
847
|
+
const whatapHome = process.env.WHATAP_HOME;
|
|
848
|
+
if (whatapHome) {
|
|
849
|
+
const appIdentifier = self.getApplicationIdentifier();
|
|
850
|
+
const sharedLockFile = path.join(whatapHome, `agent-${appIdentifier}.lock`);
|
|
851
|
+
const sharedPidFile = path.join(whatapHome, `agent-${appIdentifier}.pid`);
|
|
852
|
+
|
|
853
|
+
try {
|
|
854
|
+
if (fs.existsSync(sharedLockFile)) fs.unlinkSync(sharedLockFile);
|
|
855
|
+
if (fs.existsSync(sharedPidFile)) fs.unlinkSync(sharedPidFile);
|
|
856
|
+
Logger.print("WHATAP-203", "Cleaned up lock and PID files", false);
|
|
857
|
+
} catch (e) {
|
|
858
|
+
Logger.printError("WHATAP-204", "Error cleaning up files before restart", e, false);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// Start the agent again
|
|
863
|
+
self.startGoAgent(opts);
|
|
864
|
+
|
|
865
|
+
// UDP 재초기화 (새 포트로 연결)
|
|
866
|
+
// whatap_nodejs가 daemon으로 전환되는 시간을 고려하여 지연 후 재초기화
|
|
867
|
+
setTimeout(() => {
|
|
868
|
+
try {
|
|
869
|
+
Logger.print("WHATAP-214", "Reinitializing UDP connection after agent restart...", false);
|
|
870
|
+
self.initUdp();
|
|
871
|
+
} catch (e) {
|
|
872
|
+
Logger.printError("WHATAP-215", "Error reinitializing UDP after restart", e, false);
|
|
873
|
+
}
|
|
874
|
+
}, 2000); // 2초 대기 (daemon 전환 시간 고려)
|
|
875
|
+
|
|
876
|
+
return true;
|
|
877
|
+
};
|
|
878
|
+
|
|
721
879
|
// startGoAgent with improved PID handling
|
|
722
880
|
NodeAgent.prototype.startGoAgent = function(opts = {}) {
|
|
723
881
|
const self = this;
|
|
@@ -830,23 +988,40 @@ NodeAgent.prototype.startGoAgent = function(opts = {}) {
|
|
|
830
988
|
const architecture = ARCH[process.arch] || process.arch;
|
|
831
989
|
const sourcePath = path.join(__dirname, '..', '..', 'agent', platform, architecture, AGENT_NAME);
|
|
832
990
|
|
|
833
|
-
//
|
|
991
|
+
// sourcePath 존재 여부 확인
|
|
992
|
+
if (!fs.existsSync(sourcePath)) {
|
|
993
|
+
throw new Error(`Agent binary not found at ${sourcePath}. Platform: ${platform}, Architecture: ${architecture}`);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// 심볼릭 링크 또는 복사
|
|
834
997
|
if (!fs.existsSync(agentPath)) {
|
|
835
998
|
try {
|
|
836
999
|
fs.symlinkSync(sourcePath, agentPath);
|
|
837
1000
|
Logger.print("WHATAP-036", `Created symbolic link for ${AGENT_NAME}`, false);
|
|
838
1001
|
} catch (e) {
|
|
839
1002
|
if (e.code !== 'EEXIST') {
|
|
840
|
-
|
|
841
|
-
|
|
1003
|
+
// Symlink 실패 시 모든 플랫폼에서 복사 시도 (컨테이너 환경 대응)
|
|
1004
|
+
Logger.print("WHATAP-037", `Symlink failed (${e.code}), copying binary instead`, false);
|
|
1005
|
+
try {
|
|
842
1006
|
fs.copyFileSync(sourcePath, agentPath);
|
|
843
|
-
|
|
844
|
-
|
|
1007
|
+
// 실행 권한 부여
|
|
1008
|
+
fs.chmodSync(agentPath, 0o755);
|
|
1009
|
+
Logger.print("WHATAP-221", `Binary copied and made executable at ${agentPath}`, false);
|
|
1010
|
+
} catch (copyErr) {
|
|
1011
|
+
throw new Error(`Failed to copy agent binary: ${copyErr.message}`);
|
|
845
1012
|
}
|
|
846
1013
|
}
|
|
847
1014
|
}
|
|
848
1015
|
}
|
|
849
1016
|
|
|
1017
|
+
// agentPath 실행 권한 확인
|
|
1018
|
+
try {
|
|
1019
|
+
fs.accessSync(agentPath, fs.constants.X_OK);
|
|
1020
|
+
} catch (e) {
|
|
1021
|
+
Logger.print("WHATAP-222", `Agent binary not executable, adding execute permission`, false);
|
|
1022
|
+
fs.chmodSync(agentPath, 0o755);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
850
1025
|
// run 디렉토리 생성
|
|
851
1026
|
const sockfilePath = path.join(whatapHome, 'run');
|
|
852
1027
|
if (!fs.existsSync(sockfilePath)) {
|
|
@@ -900,80 +1075,77 @@ NodeAgent.prototype.startGoAgent = function(opts = {}) {
|
|
|
900
1075
|
cwd: whatapHome,
|
|
901
1076
|
env: newEnv,
|
|
902
1077
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
903
|
-
detached:
|
|
1078
|
+
detached: false
|
|
904
1079
|
}
|
|
905
1080
|
);
|
|
906
1081
|
|
|
1082
|
+
const MAX_BUFFER_SIZE = 50000; // 50KB - prevent memory leak
|
|
907
1083
|
let stdout = '';
|
|
908
1084
|
let stderr = '';
|
|
909
1085
|
|
|
910
1086
|
agentProcess.stdout.on('data', (data) => {
|
|
911
1087
|
stdout += data.toString();
|
|
1088
|
+
if (stdout.length > MAX_BUFFER_SIZE) {
|
|
1089
|
+
stdout = ''; // Clear buffer when limit exceeded
|
|
1090
|
+
}
|
|
912
1091
|
});
|
|
913
1092
|
|
|
914
1093
|
agentProcess.stderr.on('data', (data) => {
|
|
915
1094
|
stderr += data.toString();
|
|
1095
|
+
if (stderr.length > MAX_BUFFER_SIZE) {
|
|
1096
|
+
stderr = ''; // Clear buffer when limit exceeded
|
|
1097
|
+
}
|
|
916
1098
|
});
|
|
917
1099
|
|
|
918
|
-
// PID
|
|
1100
|
+
// PID 추적 (detached: false이므로 agentProcess.pid가 실제 PID)
|
|
919
1101
|
agentProcess.on('spawn', () => {
|
|
920
|
-
|
|
1102
|
+
const actualPid = agentProcess.pid;
|
|
1103
|
+
Logger.print("WHATAP-039", `Agent process spawned with PID: ${actualPid}`, false);
|
|
921
1104
|
|
|
922
|
-
//
|
|
923
|
-
|
|
1105
|
+
// PID 파일에 기록
|
|
1106
|
+
try {
|
|
1107
|
+
fs.writeFileSync(sharedPidFile, actualPid.toString());
|
|
1108
|
+
this.writeToFile(home, pidFileName, actualPid.toString());
|
|
1109
|
+
Logger.print("WHATAP-040", `PID ${actualPid} written to PID files`, false);
|
|
1110
|
+
|
|
1111
|
+
// Lock 파일 해제 (spawn 완료 후 안전하게 삭제)
|
|
924
1112
|
try {
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
if (match) foundPid = match[1];
|
|
940
|
-
} else {
|
|
941
|
-
// Unix format: uid pid ppid ...
|
|
942
|
-
const parts = line.split(/\s+/);
|
|
943
|
-
if (parts.length > 1) foundPid = parts[1];
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
if (foundPid) {
|
|
947
|
-
const pidNum = parseInt(foundPid);
|
|
948
|
-
// spawn된 PID와 비슷한 범위인지 확인
|
|
949
|
-
if (Math.abs(pidNum - agentProcess.pid) <= 2) {
|
|
950
|
-
Logger.print("WHATAP-040",
|
|
951
|
-
`Found actual agent PID: ${foundPid} (spawn returned: ${agentProcess.pid})`,
|
|
952
|
-
false);
|
|
953
|
-
|
|
954
|
-
// 실제 PID로 파일 업데이트
|
|
955
|
-
fs.writeFileSync(sharedPidFile, foundPid);
|
|
956
|
-
this.writeToFile(home, pidFileName, foundPid);
|
|
957
|
-
return;
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
}
|
|
1113
|
+
properLock.unlockSync(sharedLockFile);
|
|
1114
|
+
fs.unlinkSync(sharedLockFile);
|
|
1115
|
+
Logger.print("WHATAP-041", "Lock file released after spawn", false);
|
|
1116
|
+
} catch (e) {}
|
|
1117
|
+
|
|
1118
|
+
// AGENT UP 메시지 (spawn 성공 후)
|
|
1119
|
+
const agentMsg = isPM2Cluster
|
|
1120
|
+
? `AGENT UP! (process name: ${AGENT_NAME}, app: ${appIdentifier}, PM2 cluster - shared by ${process.env.instances} instances)`
|
|
1121
|
+
: `AGENT UP! (process name: ${AGENT_NAME}, app: ${appIdentifier})`;
|
|
1122
|
+
Logger.print("WHATAP-047", agentMsg, false);
|
|
1123
|
+
} catch (e) {
|
|
1124
|
+
Logger.printError("WHATAP-042", "Error writing PID to files", e, false);
|
|
1125
|
+
}
|
|
1126
|
+
});
|
|
961
1127
|
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
false);
|
|
1128
|
+
// spawn 에러 핸들러 (파일 없음, 권한 없음 등)
|
|
1129
|
+
agentProcess.on('error', (err) => {
|
|
1130
|
+
Logger.printError("WHATAP-216", `Failed to spawn agent process: ${err.message}`, err, false);
|
|
966
1131
|
|
|
967
|
-
|
|
968
|
-
|
|
1132
|
+
// 파일 정리
|
|
1133
|
+
try { fs.unlinkSync(sharedLockFile); } catch (e) {}
|
|
1134
|
+
try { fs.unlinkSync(sharedPidFile); } catch (e) {}
|
|
969
1135
|
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
}
|
|
1136
|
+
// PM2 cluster 모드에서는 instance 0만 재시작 시도
|
|
1137
|
+
if (isPM2Cluster && process.env.pm_id !== '0') {
|
|
1138
|
+
Logger.print("WHATAP-217",
|
|
1139
|
+
`Spawn failed but instance ${process.env.pm_id} will not restart (only instance 0)`,
|
|
1140
|
+
false);
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
// 자동 재시작 시도
|
|
1145
|
+
Logger.print("WHATAP-218", "Attempting to restart agent after spawn failure...", false);
|
|
1146
|
+
setTimeout(() => {
|
|
1147
|
+
self.restartGoAgent(opts);
|
|
1148
|
+
}, 5000); // 5초 후 재시작 (spawn 실패는 더 긴 대기)
|
|
977
1149
|
});
|
|
978
1150
|
|
|
979
1151
|
agentProcess.on('close', (code) => {
|
|
@@ -984,26 +1156,36 @@ NodeAgent.prototype.startGoAgent = function(opts = {}) {
|
|
|
984
1156
|
// 에이전트가 비정상 종료되면 파일 제거
|
|
985
1157
|
try { fs.unlinkSync(sharedLockFile); } catch (e) {}
|
|
986
1158
|
try { fs.unlinkSync(sharedPidFile); } catch (e) {}
|
|
1159
|
+
|
|
1160
|
+
// PM2 cluster 모드에서는 instance 0만 재시작 시도
|
|
1161
|
+
if (isPM2Cluster && process.env.pm_id !== '0') {
|
|
1162
|
+
Logger.print("WHATAP-219",
|
|
1163
|
+
`Agent crashed but instance ${process.env.pm_id} will not restart (only instance 0)`,
|
|
1164
|
+
false);
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
// 자동 재시작 시도
|
|
1169
|
+
Logger.print("WHATAP-220", "Attempting to restart agent after abnormal exit...", false);
|
|
1170
|
+
setTimeout(() => {
|
|
1171
|
+
self.restartGoAgent(opts);
|
|
1172
|
+
}, 2000); // 2초 후 재시작
|
|
987
1173
|
} else {
|
|
988
1174
|
Logger.print("WHATAP-046", "Agent started successfully", false);
|
|
989
1175
|
}
|
|
990
1176
|
});
|
|
991
1177
|
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
const agentMsg = isPM2Cluster
|
|
995
|
-
? `AGENT UP! (process name: ${AGENT_NAME}, app: ${appIdentifier}, PM2 cluster - shared by ${process.env.instances} instances)`
|
|
996
|
-
: `AGENT UP! (process name: ${AGENT_NAME}, app: ${appIdentifier})`;
|
|
997
|
-
Logger.print("WHATAP-047", agentMsg, false);
|
|
998
|
-
|
|
999
|
-
// 잠금 해제
|
|
1000
|
-
try { fs.unlinkSync(sharedLockFile); } catch (e) {}
|
|
1178
|
+
// detached: false이므로 unref() 불필요 (부모 프로세스와 함께 종료됨)
|
|
1179
|
+
// "AGENT UP!" 메시지는 spawn 이벤트 핸들러에서 출력됨 (line 1118-1122)
|
|
1001
1180
|
|
|
1002
1181
|
} catch (e) {
|
|
1003
1182
|
Logger.printError("WHATAP-048", "Error starting agent", e, false);
|
|
1004
1183
|
// 에러 발생 시 파일 정리
|
|
1005
|
-
try {
|
|
1006
|
-
|
|
1184
|
+
try {
|
|
1185
|
+
properLock.unlockSync(sharedLockFile);
|
|
1186
|
+
fs.unlinkSync(sharedLockFile);
|
|
1187
|
+
} catch (unlockErr) {}
|
|
1188
|
+
try { fs.unlinkSync(sharedPidFile); } catch (pidErr) {}
|
|
1007
1189
|
}
|
|
1008
1190
|
};
|
|
1009
1191
|
|
|
@@ -1106,6 +1288,9 @@ NodeAgent.prototype.init = function(cb) {
|
|
|
1106
1288
|
TraceContextManager.initialized = true;
|
|
1107
1289
|
|
|
1108
1290
|
self.initUdp();
|
|
1291
|
+
|
|
1292
|
+
// Start health check to monitor agent process
|
|
1293
|
+
self.startHealthCheck();
|
|
1109
1294
|
});
|
|
1110
1295
|
|
|
1111
1296
|
self.loadObserves();
|
package/lib/logger.js
CHANGED
package/lib/udp/udp_session.js
CHANGED
|
@@ -16,6 +16,7 @@ const PackageCtrHelper = require('../../lib/control/packagectr-helper');
|
|
|
16
16
|
const ActiveTransaction = require('../counter/task/activetransaction');
|
|
17
17
|
const GCAction = require('../system/gc-action');
|
|
18
18
|
const DataOuputX = require('../../lib/io/data-outputx');
|
|
19
|
+
const Logger = require('../logger');
|
|
19
20
|
|
|
20
21
|
class UdpSession {
|
|
21
22
|
constructor() {
|
|
@@ -40,7 +41,7 @@ class UdpSession {
|
|
|
40
41
|
try {
|
|
41
42
|
self.socket.close();
|
|
42
43
|
} catch (e) {
|
|
43
|
-
|
|
44
|
+
Logger.printError("WHATAP-UDP-002", "Error closing UDP socket", e, false);
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
|
|
@@ -53,7 +54,7 @@ class UdpSession {
|
|
|
53
54
|
if (conf.unix_socket_enabled === true && whatapHome) {
|
|
54
55
|
// Unix socket not directly supported in Node.js dgram
|
|
55
56
|
// We'll use the regular UDP socket with the configured server and port
|
|
56
|
-
|
|
57
|
+
Logger.print("WHATAP-UDP-003", "Unix socket not supported in Node.js, using UDP instead", false);
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
// Configure server info
|
|
@@ -72,7 +73,7 @@ class UdpSession {
|
|
|
72
73
|
|
|
73
74
|
// Set up error handling
|
|
74
75
|
self.socket.on('error', function (err) {
|
|
75
|
-
|
|
76
|
+
Logger.printError("WHATAP-UDP-001", "UDP socket error", err, false);
|
|
76
77
|
});
|
|
77
78
|
self.socket.on('close', () => { self.isConnected = false; });
|
|
78
79
|
|
|
@@ -100,7 +101,7 @@ class UdpSession {
|
|
|
100
101
|
self.handle(paramId, request, extra);
|
|
101
102
|
}
|
|
102
103
|
} catch (e) {
|
|
103
|
-
|
|
104
|
+
Logger.printError("WHATAP-UDP-004", "Error handling UDP message", e, false);
|
|
104
105
|
}
|
|
105
106
|
}
|
|
106
107
|
|
|
@@ -152,7 +153,7 @@ class UdpSession {
|
|
|
152
153
|
}
|
|
153
154
|
return;
|
|
154
155
|
} catch (e) {
|
|
155
|
-
|
|
156
|
+
Logger.printError("WHATAP-UDP-005", "Error in handle method", e, false);
|
|
156
157
|
}
|
|
157
158
|
}
|
|
158
159
|
|
|
@@ -217,7 +218,7 @@ class UdpSession {
|
|
|
217
218
|
|
|
218
219
|
if (diff < 0) {
|
|
219
220
|
data = ' ';
|
|
220
|
-
|
|
221
|
+
Logger.print("WHATAP-UDP-006", "UDP message too long, truncated", false);
|
|
221
222
|
}
|
|
222
223
|
|
|
223
224
|
dout.writeUTF(data);
|
|
@@ -241,7 +242,7 @@ class UdpSession {
|
|
|
241
242
|
}
|
|
242
243
|
}
|
|
243
244
|
} catch (e) {
|
|
244
|
-
|
|
245
|
+
Logger.printError("WHATAP-UDP-007", "Error sending packet", e, false);
|
|
245
246
|
}
|
|
246
247
|
}
|
|
247
248
|
|
|
@@ -262,7 +263,7 @@ class UdpSession {
|
|
|
262
263
|
// Send via UDP
|
|
263
264
|
const cb = (err) => {
|
|
264
265
|
if (err) {
|
|
265
|
-
|
|
266
|
+
Logger.printError("WHATAP-UDP-008", "Failed to send UDP packet", err, false);
|
|
266
267
|
// Consider reinitializing UDP connection on error
|
|
267
268
|
self.udp();
|
|
268
269
|
}
|
|
@@ -276,7 +277,7 @@ class UdpSession {
|
|
|
276
277
|
self.socket.send(sendbuf, 0, sendbuf.length, self.serverPort, self.serverHost, cb);
|
|
277
278
|
}
|
|
278
279
|
} catch (e) {
|
|
279
|
-
|
|
280
|
+
Logger.printError("WHATAP-UDP-009", "Error in send method", e, false);
|
|
280
281
|
} finally {
|
|
281
282
|
// Clear buffer array
|
|
282
283
|
self.bufferArr = [];
|
|
@@ -327,7 +328,7 @@ class UdpSession {
|
|
|
327
328
|
self.threadLock = false;
|
|
328
329
|
}
|
|
329
330
|
} catch (e) {
|
|
330
|
-
|
|
331
|
+
Logger.printError("WHATAP-UDP-010", "Error sending relay pack", e, false);
|
|
331
332
|
}
|
|
332
333
|
}
|
|
333
334
|
}
|
package/package.json
CHANGED