whatap 0.5.19 → 0.5.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/conf/config-default.js +7 -0
- package/lib/control/control-handler.js +21 -12
- package/lib/core/agent.js +2 -0
- package/lib/error/error-handler.js +437 -0
- package/lib/observers/apollo-server-observer.js +10 -2
- package/lib/observers/global-observer.js +17 -7
- package/lib/observers/grpc-observer.js +9 -9
- package/lib/observers/http-observer.js +145 -15
- package/lib/observers/maria-observer.js +0 -1
- package/lib/observers/mongodb-observer.js +4 -1
- package/lib/observers/mongoose-observer.js +414 -114
- package/lib/observers/mssql-observer.js +2 -0
- package/lib/observers/mysql-observer.js +5 -2
- package/lib/observers/mysql2-observer.js +703 -0
- package/lib/observers/oracle-observer.js +4 -1
- package/lib/observers/pgsql-observer.js +2 -0
- package/lib/observers/prisma-observer.js +3 -2
- package/lib/service/tx-record.js +92 -54
- package/lib/trace/trace-context.js +2 -0
- package/package.json +2 -2
|
@@ -278,6 +278,13 @@ var ConfigDefault = {
|
|
|
278
278
|
|
|
279
279
|
"metering_tagcount_enabled": bool("metering_tagcount_enabled", false),
|
|
280
280
|
"profile_redis_param_enabled": bool("profile_redis_param_enabled", false),
|
|
281
|
+
"profile_mongodb_param_enabled": bool("profile_mongodb_param_enabled", false),
|
|
282
|
+
|
|
283
|
+
"trace_mtrace_traceparent_key": str("trace_mtrace_traceparent_key", "traceparent"),
|
|
284
|
+
|
|
285
|
+
"txtext_txname_enabled": bool("txtext_txname_enabled", true),
|
|
286
|
+
|
|
287
|
+
"ignore_env_variable_set": str("ignore_env_variable_set", "")
|
|
281
288
|
|
|
282
289
|
};
|
|
283
290
|
|
|
@@ -17,7 +17,8 @@ var PackageCtrHelper = require('./packagectr-helper'),
|
|
|
17
17
|
TraceContextManager = require('../trace/trace-context-manager'),
|
|
18
18
|
Logger = require('../logger'),
|
|
19
19
|
StatusDetector = require('../topology/status-detector'),
|
|
20
|
-
BlobValue = require('../value/blob-value')
|
|
20
|
+
BlobValue = require('../value/blob-value'),
|
|
21
|
+
conf = require('../conf/configure');
|
|
21
22
|
|
|
22
23
|
var ControlHandler = function () {
|
|
23
24
|
this.queue = new RequestQueue(100);
|
|
@@ -136,10 +137,14 @@ ControlHandler.prototype.handle = async function (p) {
|
|
|
136
137
|
self._dataPackSender.sendResponseHide(p.getResponse());
|
|
137
138
|
break;
|
|
138
139
|
case ParamDef.GET_ENV:
|
|
139
|
-
|
|
140
140
|
try {
|
|
141
141
|
var mv = new MapValue();
|
|
142
|
+
let ignore_env_variable = conf.get('ignore_env_variable_set', '');
|
|
143
|
+
let ignore_env_variable_set = ignore_env_variable ? new Set(ignore_env_variable.split(',').map(item => item.trim())) : new Set();
|
|
142
144
|
for (var k in process.env) {
|
|
145
|
+
if(ignore_env_variable_set.has(k)) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
143
148
|
var v = process.env[k];
|
|
144
149
|
mv.putString(k, v);
|
|
145
150
|
}
|
|
@@ -152,23 +157,27 @@ ControlHandler.prototype.handle = async function (p) {
|
|
|
152
157
|
|
|
153
158
|
case ParamDef.SYSTEM_GC:
|
|
154
159
|
try {
|
|
155
|
-
var
|
|
156
|
-
var
|
|
157
|
-
|
|
160
|
+
var mem1 = process.memoryUsage();
|
|
161
|
+
var total1 = mem1.heapTotal;
|
|
162
|
+
var used1 = mem1.heapUsed;
|
|
163
|
+
|
|
164
|
+
if (global && global.gc && typeof global.gc === 'function') {
|
|
158
165
|
global.gc();
|
|
159
166
|
} else {
|
|
160
167
|
Logger.printError('WHATAP-177', 'Garbage collection unavailable. Pass --expose-gc '
|
|
161
168
|
+ 'when launching node to enable forced garbage collection.', new Error());
|
|
162
169
|
}
|
|
163
|
-
|
|
164
|
-
var
|
|
170
|
+
|
|
171
|
+
var mem2 = process.memoryUsage();
|
|
172
|
+
var total2 = mem2.heapTotal;
|
|
173
|
+
var used2 = mem2.heapUsed;
|
|
165
174
|
|
|
166
175
|
var mv = new MapValue();
|
|
167
|
-
mv.
|
|
168
|
-
mv.
|
|
169
|
-
mv.
|
|
170
|
-
mv.
|
|
171
|
-
p.putValue("gc",
|
|
176
|
+
mv.putLong("before-t", total1);
|
|
177
|
+
mv.putLong("before-u", used1);
|
|
178
|
+
mv.putLong("after-t", total2);
|
|
179
|
+
mv.putLong("after-u", used2);
|
|
180
|
+
p.putValue("gc", mv);
|
|
172
181
|
} catch (e) {
|
|
173
182
|
p.putValue("error", e.toString());
|
|
174
183
|
}
|
package/lib/core/agent.js
CHANGED
|
@@ -17,6 +17,7 @@ var Interceptor = require('./interceptor').Interceptor,
|
|
|
17
17
|
ExpressObserver = require('../observers/express-observer').ExpressObserver,
|
|
18
18
|
GlobalObserver = require('../observers/global-observer').GlobalObserver,
|
|
19
19
|
MysqlObserver = require('../observers/mysql-observer').MysqlObserver,
|
|
20
|
+
Mysql2Observer = require('../observers/mysql2-observer').Mysql2Observer,
|
|
20
21
|
MariaObserver = require('../observers/maria-observer').MariaObserver,
|
|
21
22
|
SocketioObserver = require('../observers/socket.io-observer').SocketIOObserver,
|
|
22
23
|
WebsocketObserver = require('../observers/websocket-observer').WebsocketObserver,
|
|
@@ -265,6 +266,7 @@ NodeAgent.prototype.loadObserves = function() {
|
|
|
265
266
|
observes.push(NetObserver);
|
|
266
267
|
observes.push(ClusterObserver);
|
|
267
268
|
observes.push(MysqlObserver);
|
|
269
|
+
observes.push(Mysql2Observer);
|
|
268
270
|
observes.push(MariaObserver);
|
|
269
271
|
observes.push(SocketioObserver);
|
|
270
272
|
observes.push(WebsocketObserver);
|
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 메시지/알림 오류 처리
|
|
3
|
+
* @param {Object} ctx - Trace Context
|
|
4
|
+
* @param {Object} step - Message Step
|
|
5
|
+
* @param {Error} error - 오류 객체
|
|
6
|
+
*/
|
|
7
|
+
function handleMessageError(ctx, step, error) {
|
|
8
|
+
setCommonErrorInfo(ctx, step, error, {
|
|
9
|
+
prefix: 'msg-',
|
|
10
|
+
textType: TextTypes.MESSAGE,
|
|
11
|
+
defaultMessage: 'Message processing error',
|
|
12
|
+
includeStack: false,
|
|
13
|
+
logPrefix: 'MESSAGE-ERROR'
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 오류 정보를 Context에만 설정 (StatError 생성 없이)
|
|
19
|
+
* @param {Object} ctx - Trace Context
|
|
20
|
+
* @param {Error} error - 오류 객체
|
|
21
|
+
* @param {string} prefix - 오류 접두사
|
|
22
|
+
*/
|
|
23
|
+
function setContextErrorOnly(ctx, error, prefix = '') {
|
|
24
|
+
if (!ctx || !error) return;
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// 오류 클래스 정보 설정
|
|
28
|
+
ctx.error_class = error.name || (error.constructor && error.constructor.name) || 'UnknownError';
|
|
29
|
+
|
|
30
|
+
// 오류 메시지 설정
|
|
31
|
+
ctx.error_message = error.message || error.stack || 'Unknown error';
|
|
32
|
+
|
|
33
|
+
// 상태 코드 설정 (있는 경우)
|
|
34
|
+
if (error.status || error.statusCode || error.code) {
|
|
35
|
+
ctx.statusCode = error.status || error.statusCode || error.code;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
} catch (e) {
|
|
39
|
+
Logger.printError(
|
|
40
|
+
'WHATAP-CONTEXT-ERROR',
|
|
41
|
+
'Error setting context error info',
|
|
42
|
+
e,
|
|
43
|
+
false
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}/**
|
|
47
|
+
* Step 타입에 따라 TextType 자동 추론
|
|
48
|
+
* @param {Object} step - Step 객체
|
|
49
|
+
* @returns {number} - TextTypes 상수
|
|
50
|
+
*/
|
|
51
|
+
function inferTextTypeFromStep(step) {
|
|
52
|
+
if (!step) return TextTypes.MESSAGE;
|
|
53
|
+
|
|
54
|
+
// Step 클래스명 또는 타입으로 판단
|
|
55
|
+
const stepName = step.constructor?.name || '';
|
|
56
|
+
|
|
57
|
+
if (stepName.includes('Http') || step.url !== undefined) {
|
|
58
|
+
return TextTypes.HTTPC_URL;
|
|
59
|
+
}
|
|
60
|
+
if (stepName.includes('Socket') || stepName.includes('Websocket')) {
|
|
61
|
+
return TextTypes.MESSAGE;
|
|
62
|
+
}
|
|
63
|
+
if (stepName.includes('Message') || step.desc !== undefined) {
|
|
64
|
+
return TextTypes.MESSAGE;
|
|
65
|
+
}
|
|
66
|
+
if (stepName.includes('Sql') || step.hash !== undefined) {
|
|
67
|
+
return TextTypes.SQL;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 기본값
|
|
71
|
+
return TextTypes.MESSAGE;
|
|
72
|
+
}/**
|
|
73
|
+
* 공통 오류 처리 유틸리티
|
|
74
|
+
* 모든 Observer에서 사용할 수 있는 통합 오류 처리 함수들
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
const StatError = require('../stat/stat-error');
|
|
78
|
+
const TextTypes = require('../lang/text-types');
|
|
79
|
+
const Logger = require('../logger');
|
|
80
|
+
const conf = require('../conf/configure');
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 공통 오류 정보 설정 함수 (범용)
|
|
84
|
+
* @param {Object} ctx - Trace Context
|
|
85
|
+
* @param {Object} step - Step 객체 (SQL, HTTP, Message 등)
|
|
86
|
+
* @param {Error} error - 오류 객체
|
|
87
|
+
* @param {Object} options - 추가 옵션
|
|
88
|
+
* @param {string} options.prefix - 오류 코드 접두사 (예: 'mysql-', 'httpc-', 'redis-')
|
|
89
|
+
* @param {number} options.textType - TextTypes 상수 (기본값: null, step 타입에 따라 자동 결정)
|
|
90
|
+
* @param {boolean} options.includeStack - 스택 트레이스 포함 여부 (기본값: true)
|
|
91
|
+
* @param {string} options.defaultMessage - 기본 오류 메시지
|
|
92
|
+
* @param {boolean} options.setStatError - StatError 생성 여부 (기본값: true)
|
|
93
|
+
* @param {string} options.logPrefix - 로그 접두사 (기본값: 'COMMON-ERROR')
|
|
94
|
+
*/
|
|
95
|
+
function setCommonErrorInfo(ctx, step, error, options = {}) {
|
|
96
|
+
if (!ctx || !error) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const {
|
|
101
|
+
prefix = '',
|
|
102
|
+
textType = null,
|
|
103
|
+
includeStack = true,
|
|
104
|
+
defaultMessage = 'Error',
|
|
105
|
+
setStatError = true,
|
|
106
|
+
logPrefix = 'COMMON-ERROR'
|
|
107
|
+
} = options;
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
// 오류 클래스 정보 설정
|
|
111
|
+
const errorClass = error.name || (error.constructor && error.constructor.name) || 'UnknownError';
|
|
112
|
+
ctx.error_class = errorClass;
|
|
113
|
+
|
|
114
|
+
// 오류 코드 생성
|
|
115
|
+
const errorCode = error.code || error.errno || error.errorNum || 'UNKNOWN';
|
|
116
|
+
const fullErrorCode = prefix ? `${prefix}${errorCode}` : errorCode;
|
|
117
|
+
|
|
118
|
+
// 오류 메시지 설정
|
|
119
|
+
const errorMessage = error.message || error.sqlMessage || defaultMessage;
|
|
120
|
+
|
|
121
|
+
// 스택 트레이스 처리
|
|
122
|
+
if (includeStack && conf.trace_sql_error_stack && conf.trace_sql_error_depth && error.stack) {
|
|
123
|
+
const traceDepth = conf.trace_sql_error_depth;
|
|
124
|
+
const errorStack = error.stack.split("\n");
|
|
125
|
+
|
|
126
|
+
if (errorStack.length > traceDepth) {
|
|
127
|
+
ctx.error_message = errorStack.slice(0, traceDepth + 1).join("\n");
|
|
128
|
+
} else {
|
|
129
|
+
ctx.error_message = error.stack;
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
ctx.error_message = errorMessage;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// StatError 생성 (step이 있고 설정이 활성화된 경우)
|
|
136
|
+
if (step && setStatError) {
|
|
137
|
+
const shouldAddError = shouldIgnoreError(errorCode, errorMessage);
|
|
138
|
+
|
|
139
|
+
if (shouldAddError) {
|
|
140
|
+
// textType이 null이면 step 타입에 따라 자동 결정
|
|
141
|
+
const finalTextType = textType || inferTextTypeFromStep(step);
|
|
142
|
+
|
|
143
|
+
step.error = StatError.addError(
|
|
144
|
+
fullErrorCode,
|
|
145
|
+
errorMessage,
|
|
146
|
+
ctx.service_hash,
|
|
147
|
+
finalTextType,
|
|
148
|
+
step.hash || step.url || null
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// Context 오류가 아직 설정되지 않았다면 설정
|
|
152
|
+
if (ctx.error && ctx.error.isZero && ctx.error.isZero()) {
|
|
153
|
+
ctx.error = step.error;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
Logger.printError(
|
|
159
|
+
`WHATAP-${logPrefix.toUpperCase()}`,
|
|
160
|
+
`${prefix || 'SYSTEM'} Error: ${fullErrorCode} - ${errorMessage}`,
|
|
161
|
+
error,
|
|
162
|
+
false
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
} catch (e) {
|
|
166
|
+
Logger.printError(
|
|
167
|
+
'WHATAP-ERROR-HANDLER',
|
|
168
|
+
'Error in common error handler',
|
|
169
|
+
e,
|
|
170
|
+
false
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 오류 무시 여부 확인
|
|
177
|
+
* @param {string} errorCode - 오류 코드
|
|
178
|
+
* @param {string} errorMessage - 오류 메시지
|
|
179
|
+
* @returns {boolean} - 오류를 처리할지 여부
|
|
180
|
+
*/
|
|
181
|
+
function shouldIgnoreError(errorCode, errorMessage) {
|
|
182
|
+
try {
|
|
183
|
+
// 오류 클래스 무시 설정 확인
|
|
184
|
+
if (conf._is_trace_ignore_err_cls_contains === true &&
|
|
185
|
+
conf.trace_ignore_err_cls_contains &&
|
|
186
|
+
errorCode &&
|
|
187
|
+
errorCode.indexOf(conf.trace_ignore_err_cls_contains) >= 0) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// 오류 메시지 무시 설정 확인
|
|
192
|
+
if (conf._is_trace_ignore_err_msg_contains === true &&
|
|
193
|
+
conf.trace_ignore_err_msg_contains &&
|
|
194
|
+
errorMessage &&
|
|
195
|
+
errorMessage.indexOf(conf.trace_ignore_err_msg_contains) >= 0) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// 기본적으로 오류 처리
|
|
200
|
+
return true;
|
|
201
|
+
} catch (e) {
|
|
202
|
+
Logger.printError(
|
|
203
|
+
'WHATAP-ERROR-IGNORE-CHECK',
|
|
204
|
+
'Error checking ignore conditions',
|
|
205
|
+
e,
|
|
206
|
+
false
|
|
207
|
+
);
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* MySQL 전용 오류 처리
|
|
214
|
+
* @param {Object} ctx - Trace Context
|
|
215
|
+
* @param {Object} step - SQL Step
|
|
216
|
+
* @param {Error} error - MySQL 오류 객체
|
|
217
|
+
*/
|
|
218
|
+
function handleMysqlError(ctx, step, error) {
|
|
219
|
+
setCommonErrorInfo(ctx, step, error, {
|
|
220
|
+
prefix: 'mysql-',
|
|
221
|
+
textType: TextTypes.SQL,
|
|
222
|
+
defaultMessage: 'mysql error'
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* PostgreSQL 전용 오류 처리
|
|
228
|
+
* @param {Object} ctx - Trace Context
|
|
229
|
+
* @param {Object} step - SQL Step
|
|
230
|
+
* @param {Error} error - PostgreSQL 오류 객체
|
|
231
|
+
*/
|
|
232
|
+
function handlePgsqlError(ctx, step, error) {
|
|
233
|
+
setCommonErrorInfo(ctx, step, error, {
|
|
234
|
+
prefix: 'pgsql-',
|
|
235
|
+
textType: TextTypes.SQL,
|
|
236
|
+
defaultMessage: 'postgresql error'
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Oracle 전용 오류 처리
|
|
242
|
+
* @param {Object} ctx - Trace Context
|
|
243
|
+
* @param {Object} step - SQL Step
|
|
244
|
+
* @param {Error} error - Oracle 오류 객체
|
|
245
|
+
*/
|
|
246
|
+
function handleOracleError(ctx, step, error) {
|
|
247
|
+
setCommonErrorInfo(ctx, step, error, {
|
|
248
|
+
prefix: 'oracle-',
|
|
249
|
+
textType: TextTypes.SQL,
|
|
250
|
+
defaultMessage: 'Oracle error'
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* MongoDB 전용 오류 처리
|
|
256
|
+
* @param {Object} ctx - Trace Context
|
|
257
|
+
* @param {Object} step - SQL Step
|
|
258
|
+
* @param {Error} error - MongoDB 오류 객체
|
|
259
|
+
*/
|
|
260
|
+
function handleMongoError(ctx, step, error) {
|
|
261
|
+
setCommonErrorInfo(ctx, step, error, {
|
|
262
|
+
prefix: 'mongodb-',
|
|
263
|
+
textType: TextTypes.SQL,
|
|
264
|
+
defaultMessage: 'MongoDB error'
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Redis 전용 오류 처리
|
|
270
|
+
* @param {Object} ctx - Trace Context
|
|
271
|
+
* @param {Object} step - SQL Step
|
|
272
|
+
* @param {Error} error - Redis 오류 객체
|
|
273
|
+
*/
|
|
274
|
+
function handleRedisError(ctx, step, error) {
|
|
275
|
+
setCommonErrorInfo(ctx, step, error, {
|
|
276
|
+
prefix: 'redis-',
|
|
277
|
+
textType: TextTypes.SQL,
|
|
278
|
+
defaultMessage: 'Redis error'
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* HTTP 클라이언트 오류 처리
|
|
284
|
+
* @param {Object} ctx - Trace Context
|
|
285
|
+
* @param {Object} step - HTTP Step
|
|
286
|
+
* @param {Error} error - HTTP 오류 객체
|
|
287
|
+
*/
|
|
288
|
+
function handleHttpcError(ctx, step, error) {
|
|
289
|
+
setCommonErrorInfo(ctx, step, error, {
|
|
290
|
+
prefix: 'httpc-',
|
|
291
|
+
textType: TextTypes.HTTPC_URL,
|
|
292
|
+
defaultMessage: 'HTTP client error',
|
|
293
|
+
includeStack: false,
|
|
294
|
+
logPrefix: 'HTTPC-ERROR'
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* 일반적인 시스템 오류 처리
|
|
300
|
+
* @param {Object} ctx - Trace Context
|
|
301
|
+
* @param {Object} step - Step 객체 (선택사항)
|
|
302
|
+
* @param {Error} error - 오류 객체
|
|
303
|
+
* @param {string} prefix - 오류 접두사
|
|
304
|
+
*/
|
|
305
|
+
function handleSystemError(ctx, step, error, prefix = 'system') {
|
|
306
|
+
setCommonErrorInfo(ctx, step, error, {
|
|
307
|
+
prefix: `${prefix}-`,
|
|
308
|
+
textType: null, // 자동 추론
|
|
309
|
+
defaultMessage: 'System error',
|
|
310
|
+
includeStack: true,
|
|
311
|
+
logPrefix: 'SYSTEM-ERROR'
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* gRPC 전용 오류 처리
|
|
317
|
+
* @param {Object} ctx - Trace Context
|
|
318
|
+
* @param {Object} step - gRPC Step (MessageStep)
|
|
319
|
+
* @param {Error} error - gRPC 오류 객체
|
|
320
|
+
*/
|
|
321
|
+
function handleGrpcError(ctx, step, error) {
|
|
322
|
+
setCommonErrorInfo(ctx, step, error, {
|
|
323
|
+
prefix: 'grpc-',
|
|
324
|
+
textType: TextTypes.MESSAGE,
|
|
325
|
+
defaultMessage: 'gRPC error',
|
|
326
|
+
includeStack: true,
|
|
327
|
+
logPrefix: 'GRPC-ERROR'
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* WebSocket 전용 오류 처리
|
|
333
|
+
* @param {Object} ctx - Trace Context
|
|
334
|
+
* @param {Object} step - WebSocket Step (SocketStep)
|
|
335
|
+
* @param {Error} error - WebSocket 오류 객체
|
|
336
|
+
*/
|
|
337
|
+
function handleWebsocketError(ctx, step, error) {
|
|
338
|
+
setCommonErrorInfo(ctx, step, error, {
|
|
339
|
+
prefix: 'websocket-',
|
|
340
|
+
textType: TextTypes.MESSAGE,
|
|
341
|
+
defaultMessage: 'WebSocket error',
|
|
342
|
+
includeStack: false,
|
|
343
|
+
logPrefix: 'WEBSOCKET-ERROR'
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Socket.IO 전용 오류 처리
|
|
349
|
+
* @param {Object} ctx - Trace Context
|
|
350
|
+
* @param {Object} step - Socket.IO Step (SocketStep)
|
|
351
|
+
* @param {Error} error - Socket.IO 오류 객체
|
|
352
|
+
*/
|
|
353
|
+
function handleSocketIOError(ctx, step, error) {
|
|
354
|
+
setCommonErrorInfo(ctx, step, error, {
|
|
355
|
+
prefix: 'socketio-',
|
|
356
|
+
textType: TextTypes.MESSAGE,
|
|
357
|
+
defaultMessage: 'Socket.IO error',
|
|
358
|
+
includeStack: false,
|
|
359
|
+
logPrefix: 'SOCKETIO-ERROR'
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Global Fetch API 오류 처리
|
|
365
|
+
* @param {Object} ctx - Trace Context
|
|
366
|
+
* @param {Object} step - HTTP Step (HttpStepX)
|
|
367
|
+
* @param {Error|Response} error - Fetch 오류 객체 또는 Response 객체
|
|
368
|
+
*/
|
|
369
|
+
function handleFetchError(ctx, step, error) {
|
|
370
|
+
// Response 객체인 경우와 Error 객체인 경우 구분
|
|
371
|
+
const isResponse = error && typeof error.status === 'number';
|
|
372
|
+
|
|
373
|
+
setCommonErrorInfo(ctx, step, error, {
|
|
374
|
+
prefix: 'fetch-',
|
|
375
|
+
textType: TextTypes.HTTPC_URL,
|
|
376
|
+
defaultMessage: isResponse ? 'Fetch HTTP error' : 'Fetch network error',
|
|
377
|
+
includeStack: !isResponse, // Response 객체는 스택 없음
|
|
378
|
+
logPrefix: 'FETCH-ERROR'
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Process 관련 오류 처리 (stdout/stderr)
|
|
384
|
+
* @param {Object} ctx - Trace Context
|
|
385
|
+
* @param {Object} step - Process Step
|
|
386
|
+
* @param {Error} error - Process 오류 객체
|
|
387
|
+
*/
|
|
388
|
+
function handleProcessError(ctx, step, error) {
|
|
389
|
+
setCommonErrorInfo(ctx, step, error, {
|
|
390
|
+
prefix: 'process-',
|
|
391
|
+
textType: TextTypes.MESSAGE,
|
|
392
|
+
defaultMessage: 'Process error',
|
|
393
|
+
includeStack: true,
|
|
394
|
+
logPrefix: 'PROCESS-ERROR'
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Prisma 전용 오류 처리
|
|
400
|
+
* @param {Object} ctx - Trace Context
|
|
401
|
+
* @param {Object} step - SQL Step
|
|
402
|
+
* @param {Error} error - Prisma 오류 객체
|
|
403
|
+
*/
|
|
404
|
+
function handlePrismaError(ctx, step, error) {
|
|
405
|
+
setCommonErrorInfo(ctx, step, error, {
|
|
406
|
+
prefix: 'prisma-',
|
|
407
|
+
textType: TextTypes.SQL,
|
|
408
|
+
defaultMessage: 'Prisma error'
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
module.exports = {
|
|
413
|
+
setCommonErrorInfo,
|
|
414
|
+
shouldIgnoreError,
|
|
415
|
+
inferTextTypeFromStep,
|
|
416
|
+
setContextErrorOnly,
|
|
417
|
+
|
|
418
|
+
// 데이터베이스 관련
|
|
419
|
+
handleMysqlError,
|
|
420
|
+
handlePgsqlError,
|
|
421
|
+
handleOracleError,
|
|
422
|
+
handleMongoError,
|
|
423
|
+
handleRedisError,
|
|
424
|
+
handlePrismaError,
|
|
425
|
+
|
|
426
|
+
// 네트워크/통신 관련
|
|
427
|
+
handleHttpcError,
|
|
428
|
+
handleFetchError,
|
|
429
|
+
handleGrpcError,
|
|
430
|
+
handleWebsocketError,
|
|
431
|
+
handleSocketIOError,
|
|
432
|
+
|
|
433
|
+
// 시스템 관련
|
|
434
|
+
handleSystemError,
|
|
435
|
+
handleProcessError,
|
|
436
|
+
handleMessageError
|
|
437
|
+
};
|
|
@@ -11,6 +11,8 @@ const HashUtil = require('../util/hashutil');
|
|
|
11
11
|
const DataTextAgent = require('../data/datatext-agent');
|
|
12
12
|
const MessageStep = require('../step/message-step');
|
|
13
13
|
const shimmer = require('../core/shimmer');
|
|
14
|
+
const StatError = require("whatap/lib/stat/stat-error");
|
|
15
|
+
const TextTypes = require("whatap/lib/lang/text-types");
|
|
14
16
|
|
|
15
17
|
var profile_graphql_enabled = conf.getProperty('profile_graphql_enabled', true);
|
|
16
18
|
var profile_graphql_variable_enabled = conf.getProperty('profile_graphql_variable_enabled', false);
|
|
@@ -114,8 +116,13 @@ function wrapExecuteHTTPGraphQLRequest(original) {
|
|
|
114
116
|
const result = JSON.parse(response.body.string);
|
|
115
117
|
if (result.errors && result.errors.length > 0) {
|
|
116
118
|
const errorMessages = result.errors.map(error => error.message).join('\n');
|
|
117
|
-
ctx.statusCode =
|
|
119
|
+
ctx.statusCode = response.status;
|
|
118
120
|
ctx.error_message = errorMessages;
|
|
121
|
+
ctx.error_class = 'GraphQLError';
|
|
122
|
+
|
|
123
|
+
if(ctx.error.isZero()){
|
|
124
|
+
ctx.error = StatError.addError(response.status, errorMessages, ctx.service_hash, TextTypes.SQL, HashUtil.hashFromString(ctx.service_name));
|
|
125
|
+
}
|
|
119
126
|
}
|
|
120
127
|
} catch (e) {
|
|
121
128
|
Logger.printError('WHATAP-802', 'GraphQL error parsing failed', e, false);
|
|
@@ -128,7 +135,8 @@ function wrapExecuteHTTPGraphQLRequest(original) {
|
|
|
128
135
|
Logger.printError('WHATAP-801', 'GraphQL executeHTTPGraphQLRequest error: ' + err, false);
|
|
129
136
|
if (ctx) {
|
|
130
137
|
ctx.statusCode = 500;
|
|
131
|
-
ctx.error_message = err.
|
|
138
|
+
ctx.error_message = err.message;
|
|
139
|
+
ctx.error_class = err.name || err.constructor?.name || 'UnknownError'
|
|
132
140
|
}
|
|
133
141
|
return original.apply(this, arguments);
|
|
134
142
|
}
|
|
@@ -101,8 +101,10 @@ function handleResponse(ctx, args, response) {
|
|
|
101
101
|
ctx.profile.push(step);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
function handleError(ctx, args,
|
|
104
|
+
function handleError(ctx, args, err) {
|
|
105
105
|
if (!ctx || !args[0]) return;
|
|
106
|
+
ctx.error_class = err.name || err.constructor?.name || 'HttpError';
|
|
107
|
+
ctx.error_message = err.message;
|
|
106
108
|
|
|
107
109
|
const url = new URL(args[0]);
|
|
108
110
|
setupContext(ctx, url);
|
|
@@ -110,7 +112,7 @@ function handleError(ctx, args, error) {
|
|
|
110
112
|
var step = createStep(ctx, url);
|
|
111
113
|
|
|
112
114
|
if (transaction_status_error_enable) {
|
|
113
|
-
recordError(ctx, step,
|
|
115
|
+
recordError(ctx, step, err);
|
|
114
116
|
}
|
|
115
117
|
|
|
116
118
|
ctx.profile.push(step);
|
|
@@ -135,13 +137,21 @@ function createStep(ctx, url) {
|
|
|
135
137
|
|
|
136
138
|
function recordError(ctx, step, response) {
|
|
137
139
|
if (step.error.isZero()) {
|
|
138
|
-
|
|
139
|
-
ctx.service_name = URLPatternDetector.normalize(cleanUrl);
|
|
140
|
-
ctx.service_hash = HashUtil.hashFromString(ctx.service_name);
|
|
141
|
-
|
|
142
|
-
const errorMessage = response.statusText || (response.cause ? response.cause.message : '') || response.message || 'Unknown error';
|
|
140
|
+
const errorMessage = ctx.error_message = response.statusText || (response.cause ? response.cause.message : '') || response.message || 'Unknown error';
|
|
143
141
|
step.error = StatError.addError(response.status || null, errorMessage, ctx.service_hash, TextTypes.HTTPC_URL, step.url);
|
|
144
142
|
|
|
143
|
+
if (response.status) {
|
|
144
|
+
if (response.status >= 400 && response.status < 500) {
|
|
145
|
+
ctx.error_class = `HttpClientError`;
|
|
146
|
+
} else if (response.status >= 500) {
|
|
147
|
+
ctx.error_class = `HttpServerError`;
|
|
148
|
+
} else {
|
|
149
|
+
ctx.error_class = `HttpError`;
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
ctx.error_class = 'HttpError';
|
|
153
|
+
}
|
|
154
|
+
|
|
145
155
|
if (ctx.error.isZero()) {
|
|
146
156
|
ctx.error = step.error;
|
|
147
157
|
ctx.statusCode = response.status || null;
|
|
@@ -20,6 +20,7 @@ const MessageStep = require("../step/message-step");
|
|
|
20
20
|
const MeterService = require('../counter/meter/meter-service').MeterService;
|
|
21
21
|
|
|
22
22
|
const shimmer = require('../core/shimmer');
|
|
23
|
+
const StatError = require("../stat/stat-error");
|
|
23
24
|
|
|
24
25
|
var grpc_profile_enabled = conf.getProperty('grpc_profile_enabled', true);
|
|
25
26
|
var grpc_profile_stream_client_enabled = conf.getProperty('grpc_profile_stream_client_enabled', true);
|
|
@@ -88,8 +89,8 @@ function wrapHandler(handler, methodName, type) {
|
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
TraceContextManager._asyncLocalStorage.run(initCtx(), () => {
|
|
92
|
+
var ctx = TraceContextManager._asyncLocalStorage.getStore();
|
|
91
93
|
try {
|
|
92
|
-
var ctx = TraceContextManager._asyncLocalStorage.getStore();
|
|
93
94
|
if (!ctx) {
|
|
94
95
|
return handler.call(this, call, callback);
|
|
95
96
|
}
|
|
@@ -120,8 +121,6 @@ function wrapHandler(handler, methodName, type) {
|
|
|
120
121
|
ctx.profile.push(step_param);
|
|
121
122
|
}
|
|
122
123
|
|
|
123
|
-
console.log(call.pendingStatus)
|
|
124
|
-
|
|
125
124
|
ctx.grpc_method = method_name;
|
|
126
125
|
|
|
127
126
|
function wrappedCallback(err, response, ctx) {
|
|
@@ -129,6 +128,8 @@ function wrapHandler(handler, methodName, type) {
|
|
|
129
128
|
if (err) {
|
|
130
129
|
ctx.error = err.stack;
|
|
131
130
|
ctx.statusCode = 500;
|
|
131
|
+
ctx.error_message = err.message;
|
|
132
|
+
ctx.error_class = err.name || err.constructor?.name || 'GrpcError';
|
|
132
133
|
|
|
133
134
|
var step_error = new MessageStep();
|
|
134
135
|
step_error.hash = HashUtil.hashFromString("EXCEPTION");
|
|
@@ -146,15 +147,14 @@ function wrapHandler(handler, methodName, type) {
|
|
|
146
147
|
}
|
|
147
148
|
|
|
148
149
|
return handler.call(this, call, (err, response) => wrappedCallback(err, response, ctx));
|
|
149
|
-
// return handler.call(this, call, (err, response) => {
|
|
150
|
-
// if (!err) {
|
|
151
|
-
// err = new Error('Exception error occured');
|
|
152
|
-
// }
|
|
153
|
-
// wrappedCallback(err, response, ctx);
|
|
154
150
|
} catch (err) {
|
|
155
151
|
Logger.printError('WHATAP-701', 'gRPC wrapHandler error: ' + err, false);
|
|
156
152
|
|
|
157
|
-
ctx.error
|
|
153
|
+
if(ctx.error.isZero()) {
|
|
154
|
+
ctx.error = StatError.addError(err.code, err.message, ctx.service_hash);
|
|
155
|
+
}
|
|
156
|
+
ctx.error_message = err.message;
|
|
157
|
+
ctx.error_class = err.name || err.constructor?.name || 'GrpcError';
|
|
158
158
|
|
|
159
159
|
var step_error = new MessageStep();
|
|
160
160
|
step_error.hash = HashUtil.hashFromString("EXCEPTION");
|