whatap 0.5.14 → 0.5.15
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/core/shimmer.js +82 -36
- package/lib/counter/counter-manager.js +2 -2
- package/lib/counter/task/metering-info.js +22 -15
- package/lib/data/datapack-sender.js +12 -2
- package/lib/observers/pgsql-observer.js +25 -16
- package/lib/observers/prisma-observer.js +15 -404
- package/lib/observers/redis-observer.js +179 -175
- package/package.json +2 -2
package/lib/core/shimmer.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
function isFunction
|
|
3
|
+
function isFunction(funktion) {
|
|
4
4
|
return typeof funktion === 'function'
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
function defineProperty
|
|
7
|
+
function defineProperty(obj, name, value) {
|
|
8
8
|
var enumerable = !!obj[name] && obj.propertyIsEnumerable(name)
|
|
9
9
|
Object.defineProperty(obj, name, {
|
|
10
10
|
configurable: true,
|
|
@@ -14,10 +14,28 @@ function defineProperty (obj, name, value) {
|
|
|
14
14
|
})
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
var shimmer = function
|
|
18
|
-
|
|
17
|
+
var shimmer = function() {}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Wrap a single method or multiple methods with the provided wrapper function
|
|
21
|
+
*
|
|
22
|
+
* @param {Object} nodule - The object containing methods to wrap
|
|
23
|
+
* @param {string|Array<string>} name - Method name or array of method names to wrap
|
|
24
|
+
* @param {Function} wrapper - The wrapper function
|
|
25
|
+
* @returns {Function|Array<Function>} - The wrapped function(s)
|
|
26
|
+
*/
|
|
27
|
+
var wrap = function(nodule, name, wrapper) {
|
|
28
|
+
// If name is an array, recursively call wrap for each name in the array
|
|
29
|
+
if (Array.isArray(name)) {
|
|
30
|
+
var wrapped = [];
|
|
31
|
+
name.forEach(function(methodName) {
|
|
32
|
+
var result = wrap(nodule, methodName, wrapper);
|
|
33
|
+
if (result) wrapped.push(result);
|
|
34
|
+
});
|
|
35
|
+
return wrapped.length > 0 ? wrapped : undefined;
|
|
36
|
+
}
|
|
19
37
|
|
|
20
|
-
|
|
38
|
+
// Standard single method wrap logic
|
|
21
39
|
if (!nodule || !nodule[name]) {
|
|
22
40
|
return;
|
|
23
41
|
}
|
|
@@ -27,67 +45,95 @@ var wrap = function (nodule, name, wrapper) {
|
|
|
27
45
|
}
|
|
28
46
|
|
|
29
47
|
if (!isFunction(nodule[name]) || !isFunction(wrapper)) {
|
|
30
|
-
return
|
|
48
|
+
return;
|
|
31
49
|
}
|
|
32
50
|
|
|
33
|
-
var original = nodule[name]
|
|
34
|
-
var wrapped = wrapper(original, name)
|
|
51
|
+
var original = nodule[name];
|
|
52
|
+
var wrapped = wrapper(original, name);
|
|
35
53
|
|
|
36
|
-
defineProperty(wrapped, '__original', original)
|
|
37
|
-
defineProperty(wrapped, '__unwrap', function
|
|
38
|
-
if (nodule[name] === wrapped) defineProperty(nodule, name, original)
|
|
39
|
-
})
|
|
40
|
-
defineProperty(wrapped, '__wrapped', true)
|
|
54
|
+
defineProperty(wrapped, '__original', original);
|
|
55
|
+
defineProperty(wrapped, '__unwrap', function() {
|
|
56
|
+
if (nodule[name] === wrapped) defineProperty(nodule, name, original);
|
|
57
|
+
});
|
|
58
|
+
defineProperty(wrapped, '__wrapped', true);
|
|
41
59
|
|
|
42
|
-
defineProperty(nodule, name, wrapped)
|
|
43
|
-
return wrapped
|
|
60
|
+
defineProperty(nodule, name, wrapped);
|
|
61
|
+
return wrapped;
|
|
44
62
|
}
|
|
45
63
|
|
|
46
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Unwrap a single method or multiple methods
|
|
66
|
+
*
|
|
67
|
+
* @param {Object} nodule - The object containing wrapped methods
|
|
68
|
+
* @param {string|Array<string>} name - Method name or array of method names to unwrap
|
|
69
|
+
*/
|
|
70
|
+
function unwrap(nodule, name) {
|
|
71
|
+
// If name is an array, recursively call unwrap for each name in the array
|
|
72
|
+
if (Array.isArray(name)) {
|
|
73
|
+
name.forEach(function(methodName) {
|
|
74
|
+
unwrap(nodule, methodName);
|
|
75
|
+
});
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
47
79
|
if (!nodule || !nodule[name]) {
|
|
48
|
-
return
|
|
80
|
+
return;
|
|
49
81
|
}
|
|
50
82
|
|
|
51
83
|
if (!nodule[name].__unwrap) {
|
|
84
|
+
// Method is not wrapped
|
|
52
85
|
} else {
|
|
53
|
-
return nodule[name].__unwrap()
|
|
86
|
+
return nodule[name].__unwrap();
|
|
54
87
|
}
|
|
55
88
|
}
|
|
56
89
|
|
|
57
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Wrap multiple methods of multiple modules with the same wrapper
|
|
92
|
+
*
|
|
93
|
+
* @param {Object|Array<Object>} nodules - Module or array of modules with methods to wrap
|
|
94
|
+
* @param {Array<string>} names - Array of method names to wrap
|
|
95
|
+
* @param {Function} wrapper - The wrapper function to apply
|
|
96
|
+
*/
|
|
97
|
+
function massWrap(nodules, names, wrapper) {
|
|
58
98
|
if (!nodules) {
|
|
59
|
-
return
|
|
99
|
+
return;
|
|
60
100
|
} else if (!Array.isArray(nodules)) {
|
|
61
|
-
nodules = [nodules]
|
|
101
|
+
nodules = [nodules];
|
|
62
102
|
}
|
|
63
103
|
|
|
64
104
|
if (!(names && Array.isArray(names))) {
|
|
65
|
-
return
|
|
105
|
+
return;
|
|
66
106
|
}
|
|
67
107
|
|
|
68
|
-
nodules.forEach(function
|
|
69
|
-
names.forEach(function
|
|
70
|
-
wrap(nodule, name, wrapper)
|
|
71
|
-
})
|
|
72
|
-
})
|
|
108
|
+
nodules.forEach(function(nodule) {
|
|
109
|
+
names.forEach(function(name) {
|
|
110
|
+
wrap(nodule, name, wrapper);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
73
113
|
}
|
|
74
114
|
|
|
75
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Unwrap multiple methods of multiple modules
|
|
117
|
+
*
|
|
118
|
+
* @param {Object|Array<Object>} nodules - Module or array of modules with wrapped methods
|
|
119
|
+
* @param {Array<string>} names - Array of method names to unwrap
|
|
120
|
+
*/
|
|
121
|
+
function massUnwrap(nodules, names) {
|
|
76
122
|
if (!nodules) {
|
|
77
|
-
return
|
|
123
|
+
return;
|
|
78
124
|
} else if (!Array.isArray(nodules)) {
|
|
79
|
-
nodules = [nodules]
|
|
125
|
+
nodules = [nodules];
|
|
80
126
|
}
|
|
81
127
|
|
|
82
128
|
if (!(names && Array.isArray(names))) {
|
|
83
|
-
return
|
|
129
|
+
return;
|
|
84
130
|
}
|
|
85
131
|
|
|
86
|
-
nodules.forEach(function
|
|
87
|
-
names.forEach(function
|
|
88
|
-
unwrap(nodule, name)
|
|
89
|
-
})
|
|
90
|
-
})
|
|
132
|
+
nodules.forEach(function(nodule) {
|
|
133
|
+
names.forEach(function(name) {
|
|
134
|
+
unwrap(nodule, name);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
91
137
|
}
|
|
92
138
|
|
|
93
139
|
shimmer.wrap = wrap;
|
|
@@ -17,7 +17,7 @@ var CounterPack = require('../pack/counter-pack'),
|
|
|
17
17
|
GCStat = require('./task/gcstat'),
|
|
18
18
|
Sql = require('./task/sql'),
|
|
19
19
|
HttpC = require('./task/httpc'),
|
|
20
|
-
|
|
20
|
+
MeteringInfo = require('./task/metering-info'),
|
|
21
21
|
StatError = require('../stat/stat-error'),
|
|
22
22
|
TagCounterPack = require('../pack/tagcount-pack'),
|
|
23
23
|
TraceContextManager = require('../trace/trace-context-manager'),
|
|
@@ -55,7 +55,7 @@ CounterManager.prototype.run = function () {
|
|
|
55
55
|
tasks.push(new HttpC());
|
|
56
56
|
|
|
57
57
|
// metering
|
|
58
|
-
|
|
58
|
+
tasks.push(new MeteringInfo());
|
|
59
59
|
self.intervalIndex = setInterval(function(){
|
|
60
60
|
self.process(tasks);
|
|
61
61
|
},5000);
|
|
@@ -12,7 +12,8 @@ var CounterTask = require('./counter-task'),
|
|
|
12
12
|
fs = require('fs'),
|
|
13
13
|
TagCountPack = require('../../pack/tagcount-pack'),
|
|
14
14
|
OType = require('../../pack/otype'),
|
|
15
|
-
APEnum = require('../../pack/apenum')
|
|
15
|
+
APEnum = require('../../pack/apenum'),
|
|
16
|
+
Util = require('../../util/index');
|
|
16
17
|
|
|
17
18
|
function MeteringInfo() {
|
|
18
19
|
CounterTask.call(this);
|
|
@@ -47,6 +48,10 @@ MeteringInfo.prototype.process = function (p) {
|
|
|
47
48
|
};
|
|
48
49
|
|
|
49
50
|
MeteringInfo.prototype.doProcess = function (p) {
|
|
51
|
+
const host_uuid = this.getHostUUID(this.hostUUIDFileName);
|
|
52
|
+
if(!host_uuid)
|
|
53
|
+
return;
|
|
54
|
+
|
|
50
55
|
const pk = new TagCountPack();
|
|
51
56
|
pk.category = 'metering';
|
|
52
57
|
pk.time = DateUtil.currentTime() / 300000 * 300000;
|
|
@@ -54,18 +59,15 @@ MeteringInfo.prototype.doProcess = function (p) {
|
|
|
54
59
|
pk.putTagInt('otype', OType.AP);
|
|
55
60
|
pk.putTagInt('subtype', APEnum.AP_NODE);
|
|
56
61
|
pk.putTagInt('ip', p.host_ip);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (host_uuid) {
|
|
60
|
-
pk.putTagString('host_uuid', host_uuid);
|
|
61
|
-
}
|
|
62
|
+
pk.putTagString('host_uuid', host_uuid);
|
|
63
|
+
pk.putTagString('_no_5m_hour_', "");
|
|
62
64
|
|
|
63
65
|
const csp = this.getCsp();
|
|
64
66
|
if (csp) {
|
|
65
67
|
pk.putTagString('csp', csp)
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
pk.putTagFloat(p.metering)
|
|
70
|
+
pk.putTagFloat('mcore', p.metering)
|
|
69
71
|
|
|
70
72
|
DataPackSender.sendTagCounterPack(pk);
|
|
71
73
|
};
|
|
@@ -88,21 +90,19 @@ MeteringInfo.prototype.getHostUUID = function (fileName) {
|
|
|
88
90
|
const hasHostUUIDFile = this.hasHostUUID(fileName);
|
|
89
91
|
if (hasHostUUIDFile) {
|
|
90
92
|
try {
|
|
91
|
-
const data = fs.readFileSync(fileName);
|
|
92
|
-
|
|
93
|
+
const data = fs.readFileSync(fileName, 'utf8').trim();
|
|
93
94
|
if (data && data.length > 0) {
|
|
94
|
-
this.hostUUID = data
|
|
95
|
+
this.hostUUID = data;
|
|
95
96
|
return this.hostUUID;
|
|
96
97
|
} else {
|
|
97
|
-
Logger.print('WHATAP-082',
|
|
98
|
+
Logger.print('WHATAP-082', `cannot read ${fileName}`, false);
|
|
98
99
|
}
|
|
99
100
|
} catch (error) {
|
|
100
|
-
Logger.printError('WHATAP-083',
|
|
101
|
+
Logger.printError('WHATAP-083', `error reading ${fileName}`, error, false);
|
|
101
102
|
}
|
|
102
103
|
} else {
|
|
103
|
-
Logger.print('WHATAP-084',
|
|
104
|
+
Logger.print('WHATAP-084', `cannot find ${fileName}`, false);
|
|
104
105
|
}
|
|
105
|
-
|
|
106
106
|
return null;
|
|
107
107
|
};
|
|
108
108
|
|
|
@@ -111,7 +111,14 @@ MeteringInfo.prototype.getHostUUIDValue = function () {
|
|
|
111
111
|
};
|
|
112
112
|
|
|
113
113
|
MeteringInfo.prototype.getCsp = function () {
|
|
114
|
-
|
|
114
|
+
if(process.env["ECS_CONTAINER_METADATA_URI"]) {
|
|
115
|
+
this.csp = "aws";
|
|
116
|
+
return this.csp;
|
|
117
|
+
}
|
|
118
|
+
if(Util.cloudPlatformCheck()) {
|
|
119
|
+
this.csp = "kic";
|
|
120
|
+
return this.csp;
|
|
121
|
+
}
|
|
115
122
|
return null;
|
|
116
123
|
};
|
|
117
124
|
|
|
@@ -44,8 +44,18 @@ var sendTagCounterPack = function(p){
|
|
|
44
44
|
|
|
45
45
|
p.pcode = secuMaster.PCODE;
|
|
46
46
|
p.oid = secuMaster.OID;
|
|
47
|
-
p.
|
|
48
|
-
p.
|
|
47
|
+
p.putTagString("oname", secuMaster.ONAME);
|
|
48
|
+
// p.okind=secuMaster.OKIND;
|
|
49
|
+
// p.onode=secuMaster.ONODE;
|
|
50
|
+
|
|
51
|
+
if(conf.OKIND){
|
|
52
|
+
p.okind = conf.OKIND;
|
|
53
|
+
p.putTagString("okindName", conf.OKIND_NAME);
|
|
54
|
+
}
|
|
55
|
+
if(conf.ONODE){
|
|
56
|
+
p.okind = conf.ONODE;
|
|
57
|
+
p.putTagString("onodeName", conf.ONODE_NAME);
|
|
58
|
+
}
|
|
49
59
|
|
|
50
60
|
switch (conf.encrypt_level || 2) {
|
|
51
61
|
case 1:
|
|
@@ -124,12 +124,10 @@ PgSqlObserver.prototype.inject = function (mod, moduleName) {
|
|
|
124
124
|
dbc_step = null;
|
|
125
125
|
|
|
126
126
|
var sql_step = new SqlStepX();
|
|
127
|
-
sql_step.start_time =
|
|
127
|
+
sql_step.start_time = Date.now();
|
|
128
128
|
ctx.profile.push(sql_step);
|
|
129
129
|
ctx.footprint('PgSql Query Start');
|
|
130
130
|
|
|
131
|
-
ctx.vvv++;
|
|
132
|
-
|
|
133
131
|
let sql = arguments[0];
|
|
134
132
|
let psql = null;
|
|
135
133
|
if (typeof sql === 'string' && sql.length > 0) {
|
|
@@ -178,7 +176,7 @@ PgSqlObserver.prototype.inject = function (mod, moduleName) {
|
|
|
178
176
|
return result.then(res => {
|
|
179
177
|
if(res.command && res.command === 'SELECT'){
|
|
180
178
|
var result_step = new ResultSetStep();
|
|
181
|
-
result_step.start_time =
|
|
179
|
+
result_step.start_time = Date.now();
|
|
182
180
|
result_step.elapsed = 0;
|
|
183
181
|
result_step.fetch = res.rowCount;
|
|
184
182
|
result_step.sqlhash = psql.sql;
|
|
@@ -196,16 +194,6 @@ PgSqlObserver.prototype.inject = function (mod, moduleName) {
|
|
|
196
194
|
return res;
|
|
197
195
|
}).catch(err => {
|
|
198
196
|
self._handleError(ctx, sql_step, err)
|
|
199
|
-
if (conf.trace_sql_error_stack && conf.trace_sql_error_depth) {
|
|
200
|
-
var traceDepth = conf.trace_sql_error_depth;
|
|
201
|
-
|
|
202
|
-
var errorStack = err.stack.split("\n");
|
|
203
|
-
if (errorStack.length > traceDepth) {
|
|
204
|
-
errorStack = errorStack.slice(0, traceDepth + 1);
|
|
205
|
-
}
|
|
206
|
-
ctx.error_message = errorStack.join("\n");
|
|
207
|
-
sql_step.error = ctx.error = StatError.addError('pgsql -' + err.code, err.message, ctx.service_hash, TextTypes.SQL, null);
|
|
208
|
-
}
|
|
209
197
|
throw err;
|
|
210
198
|
});
|
|
211
199
|
} else {
|
|
@@ -231,7 +219,7 @@ PgSqlObserver.prototype.inject = function (mod, moduleName) {
|
|
|
231
219
|
};
|
|
232
220
|
|
|
233
221
|
PgSqlObserver.prototype._finishQuery = function (ctx, sql_step) {
|
|
234
|
-
sql_step.elapsed =
|
|
222
|
+
sql_step.elapsed = Date.now() - sql_step.start_time;
|
|
235
223
|
TraceSQL.isSlowSQL(ctx);
|
|
236
224
|
|
|
237
225
|
MeterSql.add(sql_step.hash, sql_step.elapsed, false);
|
|
@@ -239,7 +227,28 @@ PgSqlObserver.prototype._finishQuery = function (ctx, sql_step) {
|
|
|
239
227
|
};
|
|
240
228
|
|
|
241
229
|
PgSqlObserver.prototype._handleError = function (ctx, sql_step, err) {
|
|
242
|
-
sql_step.elapsed =
|
|
230
|
+
sql_step.elapsed = Date.now() - sql_step.start_time;
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
sql_step.error = StatError.addError("pgsql-" + (err.code || "unknown"), err.message, ctx.service_hash, TextTypes.SQL, sql_step.hash);
|
|
234
|
+
|
|
235
|
+
if (ctx.error.isZero()) {
|
|
236
|
+
ctx.error = sql_step.error;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (conf.trace_sql_error_stack && conf.trace_sql_error_depth) {
|
|
240
|
+
var traceDepth = conf.trace_sql_error_depth;
|
|
241
|
+
var errorStack = err.stack.split("\n");
|
|
242
|
+
|
|
243
|
+
if (errorStack.length > traceDepth) {
|
|
244
|
+
errorStack = errorStack.slice(0, traceDepth + 1);
|
|
245
|
+
}
|
|
246
|
+
ctx.error_message = errorStack.join("\n");
|
|
247
|
+
}
|
|
248
|
+
} catch (e) {
|
|
249
|
+
Logger.printError("WHATAP-309", "Error handling failed", e, false);
|
|
250
|
+
}
|
|
251
|
+
|
|
243
252
|
TraceSQL.isSlowSQL(ctx);
|
|
244
253
|
|
|
245
254
|
MeterSql.add(sql_step.hash, sql_step.elapsed, false);
|
|
@@ -95,20 +95,9 @@ PrismaObserver.prototype.patchPrismaInstance = function(prismaInstance) {
|
|
|
95
95
|
}
|
|
96
96
|
prismaInstance.__whatap_observe__ = true;
|
|
97
97
|
|
|
98
|
-
// 모든 DB 연결 정보 추출 및 초기화
|
|
99
98
|
this.setupConnectionInfo(prismaInstance);
|
|
100
99
|
|
|
101
|
-
// $connect 후킹
|
|
102
|
-
// this.hookConnect(prismaInstance);
|
|
103
|
-
|
|
104
|
-
// Raw 쿼리 메서드 후킹
|
|
105
|
-
// this.hookRawQueryMethods(prismaInstance);
|
|
106
|
-
|
|
107
|
-
// $use 미들웨어 후킹 (모델 메서드 추적)
|
|
108
100
|
this.hookUseMiddleware(prismaInstance);
|
|
109
|
-
|
|
110
|
-
// 각 모델에 대한 직접 메서드 후킹 (더 안정적인 추적을 위해)
|
|
111
|
-
// this.hookModelMethods(prismaInstance);
|
|
112
101
|
};
|
|
113
102
|
|
|
114
103
|
// 연결 정보 설정
|
|
@@ -148,196 +137,9 @@ PrismaObserver.prototype.setupConnectionInfo = function(prismaInstance) {
|
|
|
148
137
|
}
|
|
149
138
|
};
|
|
150
139
|
|
|
151
|
-
// Connect 메서드 후킹
|
|
152
|
-
// PrismaObserver.prototype.hookConnect = function(prismaInstance) {
|
|
153
|
-
// const self = this;
|
|
154
|
-
//
|
|
155
|
-
// shimmer.wrap(prismaInstance, "$connect", function(original) {
|
|
156
|
-
// return async function() {
|
|
157
|
-
// const ctx = TraceContextManager.getCurrentContext();
|
|
158
|
-
// if (!ctx || ctx.db_opening) {
|
|
159
|
-
// return original.apply(this, arguments);
|
|
160
|
-
// }
|
|
161
|
-
//
|
|
162
|
-
// ctx.db_opening = true;
|
|
163
|
-
// ctx.footprint("Prisma Connecting Start");
|
|
164
|
-
//
|
|
165
|
-
// const dbc_step = new DBCStep();
|
|
166
|
-
// dbc_step.start_time = ctx.getElapsedTime();
|
|
167
|
-
//
|
|
168
|
-
// try {
|
|
169
|
-
// const result = await original.apply(this, arguments);
|
|
170
|
-
//
|
|
171
|
-
// dbc_step.hash = dbc_hash;
|
|
172
|
-
// dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
|
|
173
|
-
//
|
|
174
|
-
// ctx.db_opening = false;
|
|
175
|
-
// ctx.footprint("Prisma Connecting Done");
|
|
176
|
-
// ctx.profile.push(dbc_step);
|
|
177
|
-
//
|
|
178
|
-
// return result;
|
|
179
|
-
// } catch (err) {
|
|
180
|
-
// ctx.db_opening = false;
|
|
181
|
-
//
|
|
182
|
-
// dbc_step.hash = dbc_hash;
|
|
183
|
-
// dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
|
|
184
|
-
// dbc_step.error = StatError.addError("prisma-connection", err.message, ctx.service_hash);
|
|
185
|
-
//
|
|
186
|
-
// if (ctx.error.isZero()) {
|
|
187
|
-
// ctx.error = dbc_step.error;
|
|
188
|
-
// }
|
|
189
|
-
//
|
|
190
|
-
// ctx.profile.push(dbc_step);
|
|
191
|
-
// Logger.printError("WHATAP-302", "Connection Error", err);
|
|
192
|
-
// throw err;
|
|
193
|
-
// }
|
|
194
|
-
// };
|
|
195
|
-
// });
|
|
196
|
-
// };
|
|
197
|
-
|
|
198
|
-
// Raw 쿼리 메서드 후킹
|
|
199
|
-
// PrismaObserver.prototype.hookRawQueryMethods = function(prismaInstance) {
|
|
200
|
-
// const self = this;
|
|
201
|
-
// const queryMethods = ["$queryRaw", "$executeRaw", "$queryRawUnsafe", "$executeRawUnsafe"];
|
|
202
|
-
//
|
|
203
|
-
// queryMethods.forEach(method => {
|
|
204
|
-
// if (typeof prismaInstance[method] === 'function') {
|
|
205
|
-
// shimmer.wrap(prismaInstance, method, function(original) {
|
|
206
|
-
// return async function() {
|
|
207
|
-
// const ctx = TraceContextManager.getCurrentContext();
|
|
208
|
-
// if (!ctx) {
|
|
209
|
-
// return original.apply(this, arguments);
|
|
210
|
-
// }
|
|
211
|
-
//
|
|
212
|
-
// const sql_step = new SqlStepX();
|
|
213
|
-
// sql_step.start_time = ctx.getElapsedTime();
|
|
214
|
-
// ctx.profile.push(sql_step);
|
|
215
|
-
// ctx.footprint(`Prisma ${method} Start`);
|
|
216
|
-
//
|
|
217
|
-
// ctx.sql_count = (ctx.sql_count || 0) + 1;
|
|
218
|
-
//
|
|
219
|
-
// // SQL 쿼리 추출
|
|
220
|
-
// let sql = "";
|
|
221
|
-
// let psql = null;
|
|
222
|
-
//
|
|
223
|
-
// if (arguments.length > 0) {
|
|
224
|
-
// if (typeof arguments[0] === "object" && arguments[0].sql) {
|
|
225
|
-
// // Tagged template으로 전달된 경우
|
|
226
|
-
// sql = arguments[0].sql;
|
|
227
|
-
// } else if (typeof arguments[0] === "string") {
|
|
228
|
-
// // Raw string으로 전달된 경우
|
|
229
|
-
// sql = arguments[0];
|
|
230
|
-
// }
|
|
231
|
-
// }
|
|
232
|
-
//
|
|
233
|
-
// // SQL 파싱
|
|
234
|
-
// if (sql && sql.length > 0) {
|
|
235
|
-
// try {
|
|
236
|
-
// psql = escapeLiteral(sql);
|
|
237
|
-
// } catch (e) {
|
|
238
|
-
// Logger.printError("WHATAP-303", "escapeliteral error", e, false);
|
|
239
|
-
// }
|
|
240
|
-
// } else {
|
|
241
|
-
// sql = "";
|
|
242
|
-
// psql = escapeLiteral(sql);
|
|
243
|
-
// }
|
|
244
|
-
//
|
|
245
|
-
// if (psql != null) {
|
|
246
|
-
// sql_step.hash = psql.sql;
|
|
247
|
-
// }
|
|
248
|
-
// sql_step.dbc = dbc_hash;
|
|
249
|
-
//
|
|
250
|
-
// var els = new EscapeLiteralSQL(sql);
|
|
251
|
-
// els.process();
|
|
252
|
-
//
|
|
253
|
-
// ctx.active_sqlhash = sql_step.hash;
|
|
254
|
-
// ctx.active_dbc = sql_step.dbc;
|
|
255
|
-
//
|
|
256
|
-
// // 파라미터 정보 추출
|
|
257
|
-
// if (conf.profile_sql_param_enabled) {
|
|
258
|
-
// var params = Array.from(arguments).slice(1);
|
|
259
|
-
// sql_step.setTrue(1);
|
|
260
|
-
// var crc = { value: 0 };
|
|
261
|
-
// sql_step.p1 = toParamBytes(psql.param, crc);
|
|
262
|
-
//
|
|
263
|
-
// if (params.length > 0) {
|
|
264
|
-
// const result = params.map((param) => {
|
|
265
|
-
// if (typeof param === "string") {
|
|
266
|
-
// return `'${param}'`;
|
|
267
|
-
// }
|
|
268
|
-
// return param;
|
|
269
|
-
// }).toString();
|
|
270
|
-
// sql_step.p2 = toParamBytes(result, crc);
|
|
271
|
-
// }
|
|
272
|
-
//
|
|
273
|
-
// sql_step.pcrc = crc.value;
|
|
274
|
-
// }
|
|
275
|
-
//
|
|
276
|
-
// try {
|
|
277
|
-
// const result = await original.apply(this, arguments);
|
|
278
|
-
//
|
|
279
|
-
// // SELECT 쿼리의 결과셋 처리
|
|
280
|
-
// if (method === "$queryRaw" || method === "$queryRawUnsafe") {
|
|
281
|
-
// if (Array.isArray(result)) {
|
|
282
|
-
// var result_step = new ResultSetStep();
|
|
283
|
-
// result_step.start_time = ctx.getElapsedTime();
|
|
284
|
-
// result_step.elapsed = 0;
|
|
285
|
-
// result_step.fetch = result.length;
|
|
286
|
-
// result_step.sqlhash = psql.sql;
|
|
287
|
-
// result_step.dbc = dbc_hash;
|
|
288
|
-
// ctx.profile.push(result_step);
|
|
289
|
-
//
|
|
290
|
-
// ctx.rs_count = ctx.rs_count ? ctx.rs_count + result.length : result.length;
|
|
291
|
-
// ctx.rs_time = ctx.rs_time ? ctx.rs_time + sql_step.elapsed : sql_step.elapsed;
|
|
292
|
-
//
|
|
293
|
-
// MeterSql.addFetch(result_step.dbc, result_step.fetch, 0);
|
|
294
|
-
// StatSql.addFetch(result_step.dbc, result_step.sqlhash, result_step.fetch, 0);
|
|
295
|
-
//
|
|
296
|
-
// TraceSQL.isTooManyRecords(sql_step, result_step.fetch, ctx);
|
|
297
|
-
// }
|
|
298
|
-
// }
|
|
299
|
-
//
|
|
300
|
-
// // UPDATE/INSERT/DELETE 쿼리 결과 처리
|
|
301
|
-
// if ((method === "$executeRaw" || method === "$executeRawUnsafe") && typeof result === "number") {
|
|
302
|
-
// sql_step.updated = result;
|
|
303
|
-
// }
|
|
304
|
-
//
|
|
305
|
-
// self._finishQuery(ctx, sql_step);
|
|
306
|
-
// return result;
|
|
307
|
-
// } catch (err) {
|
|
308
|
-
// Logger.printError("WHATAP-304", `${method} error: ${err.message}`, err);
|
|
309
|
-
//
|
|
310
|
-
// self._handleError(ctx, sql_step, err);
|
|
311
|
-
//
|
|
312
|
-
// if (conf.trace_sql_error_stack && conf.trace_sql_error_depth) {
|
|
313
|
-
// var traceDepth = conf.trace_sql_error_depth;
|
|
314
|
-
// var errorStack = err.stack.split("\n");
|
|
315
|
-
//
|
|
316
|
-
// if (errorStack.length > traceDepth) {
|
|
317
|
-
// errorStack = errorStack.slice(0, traceDepth + 1);
|
|
318
|
-
// }
|
|
319
|
-
//
|
|
320
|
-
// ctx.error_message = errorStack.join("\n");
|
|
321
|
-
// sql_step.error = ctx.error = StatError.addError("prisma-" + (err.code || "unknown"), err.message, ctx.service_hash, TextTypes.SQL, null);
|
|
322
|
-
// }
|
|
323
|
-
//
|
|
324
|
-
// throw err;
|
|
325
|
-
// }
|
|
326
|
-
// };
|
|
327
|
-
// });
|
|
328
|
-
// }
|
|
329
|
-
// });
|
|
330
|
-
// };
|
|
331
|
-
|
|
332
140
|
PrismaObserver.prototype.hookUseMiddleware = function(prismaInstance) {
|
|
333
|
-
const self = this;
|
|
334
|
-
|
|
335
|
-
// 원본 $use 함수를 저장
|
|
336
141
|
const originalUse = prismaInstance.$use;
|
|
337
|
-
|
|
338
|
-
// 우리만의 미들웨어 추가
|
|
339
142
|
if (typeof originalUse === 'function') {
|
|
340
|
-
// 간단한 미들웨어 추가 (모든 쿼리를 추적)
|
|
341
143
|
prismaInstance.$use(async (params, next) => {
|
|
342
144
|
var result;
|
|
343
145
|
const ctx = TraceContextManager.getCurrentContext();
|
|
@@ -511,13 +313,7 @@ PrismaObserver.prototype.hookUseMiddleware = function(prismaInstance) {
|
|
|
511
313
|
sql_step.updated = 1; // upsert는 항상 1개의 레코드에 영향을 미침
|
|
512
314
|
}
|
|
513
315
|
|
|
514
|
-
|
|
515
|
-
ctx.sql_time = (ctx.sql_time || 0) + sql_step.elapsed;
|
|
516
|
-
|
|
517
|
-
TraceSQL.isSlowSQL(ctx);
|
|
518
|
-
|
|
519
|
-
MeterSql.add(dbc_hash, sql_step.elapsed, false);
|
|
520
|
-
StatSql.addSqlTime(ctx.service_hash, sql_step.dbc, sql_step.hash, sql_step.elapsed, false, 0);
|
|
316
|
+
this._finishQuery(ctx, sql_step);
|
|
521
317
|
|
|
522
318
|
ctx.footprint(`Prisma ${modelName}.${action} Done`);
|
|
523
319
|
|
|
@@ -525,36 +321,7 @@ PrismaObserver.prototype.hookUseMiddleware = function(prismaInstance) {
|
|
|
525
321
|
} catch (err) {
|
|
526
322
|
Logger.printError("WHATAP-308", `Middleware error in ${modelName}.${action}: ${err.message}`, err, false);
|
|
527
323
|
|
|
528
|
-
|
|
529
|
-
ctx.sql_time = (ctx.sql_time || 0) + sql_step.elapsed;
|
|
530
|
-
|
|
531
|
-
TraceSQL.isSlowSQL(ctx);
|
|
532
|
-
|
|
533
|
-
MeterSql.add(dbc_hash, sql_step.elapsed, true);
|
|
534
|
-
StatSql.addSqlTime(ctx.service_hash, sql_step.dbc, sql_step.hash, sql_step.elapsed, true, 0);
|
|
535
|
-
|
|
536
|
-
// 에러 처리
|
|
537
|
-
try {
|
|
538
|
-
sql_step.error = StatError.addError("prisma-" + (err.code || "unknown"), err.message, ctx.service_hash, TextTypes.SQL, sql_step.hash);
|
|
539
|
-
|
|
540
|
-
if (ctx.error.isZero()) {
|
|
541
|
-
ctx.error = sql_step.error;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
if (conf.trace_sql_error_stack && conf.trace_sql_error_depth) {
|
|
545
|
-
var traceDepth = conf.trace_sql_error_depth;
|
|
546
|
-
var errorStack = err.stack.split("\n");
|
|
547
|
-
|
|
548
|
-
if (errorStack.length > traceDepth) {
|
|
549
|
-
errorStack = errorStack.slice(0, traceDepth + 1);
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
ctx.error_message = errorStack.join("\n");
|
|
553
|
-
}
|
|
554
|
-
} catch (e) {
|
|
555
|
-
Logger.printError("WHATAP-309", "Error handling failed", e, false);
|
|
556
|
-
}
|
|
557
|
-
|
|
324
|
+
this._handleError(ctx, sql_step, err);
|
|
558
325
|
ctx.footprint(`Prisma ${modelName}.${action} Error`);
|
|
559
326
|
throw err;
|
|
560
327
|
}
|
|
@@ -562,174 +329,8 @@ PrismaObserver.prototype.hookUseMiddleware = function(prismaInstance) {
|
|
|
562
329
|
}
|
|
563
330
|
};
|
|
564
331
|
|
|
565
|
-
// 모델 메서드 직접 후킹
|
|
566
|
-
// PrismaObserver.prototype.hookModelMethods = function(prismaInstance) {
|
|
567
|
-
// const self = this;
|
|
568
|
-
//
|
|
569
|
-
// // Prisma에서 모든 모델 가져오기
|
|
570
|
-
// let models = [];
|
|
571
|
-
// try {
|
|
572
|
-
// // DMMF를 통해 모델 이름 얻기
|
|
573
|
-
// if (prismaInstance._baseDmmf && prismaInstance._baseDmmf.modelMap) {
|
|
574
|
-
// models = Object.keys(prismaInstance._baseDmmf.modelMap);
|
|
575
|
-
// } else if (prismaInstance._dmmf && prismaInstance._dmmf.modelMap) {
|
|
576
|
-
// models = Object.keys(prismaInstance._dmmf.modelMap);
|
|
577
|
-
// }
|
|
578
|
-
// } catch (e) {
|
|
579
|
-
// Logger.printError("WHATAP-PRISMA", "Failed to get models", e, false);
|
|
580
|
-
// }
|
|
581
|
-
//
|
|
582
|
-
// // 모델 메서드 목록
|
|
583
|
-
// const methods = [
|
|
584
|
-
// "findUnique", "findFirst", "findMany",
|
|
585
|
-
// "create", "createMany",
|
|
586
|
-
// "update", "updateMany",
|
|
587
|
-
// "upsert",
|
|
588
|
-
// "delete", "deleteMany",
|
|
589
|
-
// "count", "aggregate", "groupBy"
|
|
590
|
-
// ];
|
|
591
|
-
//
|
|
592
|
-
// Logger.print("WHATAP-PRISMA", `Found models: ${models.join(', ')}`, false);
|
|
593
|
-
//
|
|
594
|
-
// // 각 모델에 대해 메서드 후킹
|
|
595
|
-
// models.forEach(model => {
|
|
596
|
-
// if (prismaInstance[model]) {
|
|
597
|
-
// methods.forEach(method => {
|
|
598
|
-
// if (typeof prismaInstance[model][method] === 'function') {
|
|
599
|
-
// shimmer.wrap(prismaInstance[model], method, function(original) {
|
|
600
|
-
// return async function() {
|
|
601
|
-
// const ctx = TraceContextManager.getCurrentContext();
|
|
602
|
-
// if (!ctx) {
|
|
603
|
-
// return original.apply(this, arguments);
|
|
604
|
-
// }
|
|
605
|
-
//
|
|
606
|
-
// Logger.print("WHATAP-PRISMA", `Direct model method called: ${model}.${method}`, false);
|
|
607
|
-
//
|
|
608
|
-
// const sql_step = new SqlStepX();
|
|
609
|
-
// sql_step.start_time = ctx.getElapsedTime();
|
|
610
|
-
// ctx.profile.push(sql_step);
|
|
611
|
-
// ctx.footprint(`Prisma ${model}.${method} Start (Direct)`);
|
|
612
|
-
//
|
|
613
|
-
// ctx.sql_count = (ctx.sql_count || 0) + 1;
|
|
614
|
-
//
|
|
615
|
-
// // 쿼리 정보
|
|
616
|
-
// const queryInfo = `${method.toUpperCase()} ${model}`;
|
|
617
|
-
// const queryHash = HashUtil.hashFromString(queryInfo);
|
|
618
|
-
//
|
|
619
|
-
// DataTextAgent.SQL.add(queryHash, queryInfo);
|
|
620
|
-
// sql_step.hash = queryHash;
|
|
621
|
-
// sql_step.dbc = dbc_hash;
|
|
622
|
-
//
|
|
623
|
-
// ctx.active_sqlhash = sql_step.hash;
|
|
624
|
-
// ctx.active_dbc = sql_step.dbc;
|
|
625
|
-
//
|
|
626
|
-
// // 쿼리 파라미터
|
|
627
|
-
// if (conf.profile_sql_param_enabled) {
|
|
628
|
-
// const argsString = JSON.stringify(arguments[0] || {});
|
|
629
|
-
// sql_step.setTrue(1);
|
|
630
|
-
// var crc = { value: 0 };
|
|
631
|
-
// sql_step.p1 = toParamBytes(argsString, crc);
|
|
632
|
-
// sql_step.pcrc = crc.value;
|
|
633
|
-
// }
|
|
634
|
-
//
|
|
635
|
-
// try {
|
|
636
|
-
// const result = await original.apply(this, arguments);
|
|
637
|
-
//
|
|
638
|
-
// // 결과셋 처리
|
|
639
|
-
// if (["findMany", "findFirst", "findUnique"].includes(method)) {
|
|
640
|
-
// let recordCount = 0;
|
|
641
|
-
//
|
|
642
|
-
// if (Array.isArray(result)) {
|
|
643
|
-
// recordCount = result.length;
|
|
644
|
-
// } else if (result && typeof result === "object") {
|
|
645
|
-
// recordCount = 1;
|
|
646
|
-
// }
|
|
647
|
-
//
|
|
648
|
-
// if (recordCount > 0) {
|
|
649
|
-
// var result_step = new ResultSetStep();
|
|
650
|
-
// result_step.start_time = ctx.getElapsedTime();
|
|
651
|
-
// result_step.elapsed = 0;
|
|
652
|
-
// result_step.fetch = recordCount;
|
|
653
|
-
// result_step.sqlhash = queryHash;
|
|
654
|
-
// result_step.dbc = dbc_hash;
|
|
655
|
-
// ctx.profile.push(result_step);
|
|
656
|
-
//
|
|
657
|
-
// ctx.rs_count = ctx.rs_count ? ctx.rs_count + recordCount : recordCount;
|
|
658
|
-
// ctx.rs_time = ctx.rs_time ? ctx.rs_time + sql_step.elapsed : sql_step.elapsed;
|
|
659
|
-
//
|
|
660
|
-
// MeterSql.addFetch(result_step.dbc, result_step.fetch, 0);
|
|
661
|
-
// StatSql.addFetch(result_step.dbc, result_step.sqlhash, result_step.fetch, 0);
|
|
662
|
-
//
|
|
663
|
-
// TraceSQL.isTooManyRecords(sql_step, result_step.fetch, ctx);
|
|
664
|
-
// }
|
|
665
|
-
// }
|
|
666
|
-
//
|
|
667
|
-
// // 수정된 레코드 처리
|
|
668
|
-
// if (["create", "createMany", "update", "updateMany", "delete", "deleteMany"].includes(method)) {
|
|
669
|
-
// if (result && result.count !== undefined) {
|
|
670
|
-
// sql_step.updated = result.count;
|
|
671
|
-
// } else if (result && typeof result === "object") {
|
|
672
|
-
// sql_step.updated = 1;
|
|
673
|
-
// }
|
|
674
|
-
// }
|
|
675
|
-
//
|
|
676
|
-
// sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
|
|
677
|
-
// ctx.sql_time = (ctx.sql_time || 0) + sql_step.elapsed;
|
|
678
|
-
//
|
|
679
|
-
// TraceSQL.isSlowSQL(ctx);
|
|
680
|
-
//
|
|
681
|
-
// MeterSql.add(dbc_hash, sql_step.elapsed, false);
|
|
682
|
-
// StatSql.addSqlTime(ctx.service_hash, sql_step.dbc, sql_step.hash, sql_step.elapsed, false, 0);
|
|
683
|
-
//
|
|
684
|
-
// ctx.footprint(`Prisma ${model}.${method} Done (Direct)`);
|
|
685
|
-
// Logger.print("WHATAP-PRISMA", `Direct model method completed: ${model}.${method}`, false);
|
|
686
|
-
//
|
|
687
|
-
// return result;
|
|
688
|
-
// } catch (err) {
|
|
689
|
-
// Logger.printError("WHATAP-PRISMA", `Direct model method error in ${model}.${method}: ${err.message}`, err);
|
|
690
|
-
//
|
|
691
|
-
// sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
|
|
692
|
-
// ctx.sql_time = (ctx.sql_time || 0) + sql_step.elapsed;
|
|
693
|
-
//
|
|
694
|
-
// TraceSQL.isSlowSQL(ctx);
|
|
695
|
-
//
|
|
696
|
-
// MeterSql.add(dbc_hash, sql_step.elapsed, true);
|
|
697
|
-
// StatSql.addSqlTime(ctx.service_hash, sql_step.dbc, sql_step.hash, sql_step.elapsed, true, 0);
|
|
698
|
-
//
|
|
699
|
-
// try {
|
|
700
|
-
// sql_step.error = StatError.addError("prisma-" + (err.code || "unknown"), err.message, ctx.service_hash, TextTypes.SQL, sql_step.hash);
|
|
701
|
-
//
|
|
702
|
-
// if (ctx.error.isZero()) {
|
|
703
|
-
// ctx.error = sql_step.error;
|
|
704
|
-
// }
|
|
705
|
-
//
|
|
706
|
-
// if (conf.trace_sql_error_stack && conf.trace_sql_error_depth) {
|
|
707
|
-
// var traceDepth = conf.trace_sql_error_depth;
|
|
708
|
-
// var errorStack = err.stack.split("\n");
|
|
709
|
-
//
|
|
710
|
-
// if (errorStack.length > traceDepth) {
|
|
711
|
-
// errorStack = errorStack.slice(0, traceDepth + 1);
|
|
712
|
-
// }
|
|
713
|
-
//
|
|
714
|
-
// ctx.error_message = errorStack.join("\n");
|
|
715
|
-
// }
|
|
716
|
-
// } catch (e) {
|
|
717
|
-
// Logger.printError("WHATAP-PRISMA", "Error handling failed", e, false);
|
|
718
|
-
// }
|
|
719
|
-
//
|
|
720
|
-
// ctx.footprint(`Prisma ${model}.${method} Error (Direct)`);
|
|
721
|
-
// throw err;
|
|
722
|
-
// }
|
|
723
|
-
// };
|
|
724
|
-
// });
|
|
725
|
-
// }
|
|
726
|
-
// });
|
|
727
|
-
// }
|
|
728
|
-
// });
|
|
729
|
-
// };
|
|
730
|
-
|
|
731
332
|
PrismaObserver.prototype._finishQuery = function(ctx, sql_step) {
|
|
732
|
-
sql_step.elapsed =
|
|
333
|
+
sql_step.elapsed = Date.now() - sql_step.start_time;
|
|
733
334
|
ctx.sql_time = (ctx.sql_time || 0) + sql_step.elapsed;
|
|
734
335
|
|
|
735
336
|
TraceSQL.isSlowSQL(ctx);
|
|
@@ -741,7 +342,7 @@ PrismaObserver.prototype._finishQuery = function(ctx, sql_step) {
|
|
|
741
342
|
};
|
|
742
343
|
|
|
743
344
|
PrismaObserver.prototype._handleError = function(ctx, sql_step, err) {
|
|
744
|
-
sql_step.elapsed =
|
|
345
|
+
sql_step.elapsed = Date.now() - sql_step.start_time;
|
|
745
346
|
ctx.sql_time = (ctx.sql_time || 0) + sql_step.elapsed;
|
|
746
347
|
|
|
747
348
|
TraceSQL.isSlowSQL(ctx);
|
|
@@ -755,8 +356,18 @@ PrismaObserver.prototype._handleError = function(ctx, sql_step, err) {
|
|
|
755
356
|
if (ctx.error.isZero()) {
|
|
756
357
|
ctx.error = sql_step.error;
|
|
757
358
|
}
|
|
359
|
+
|
|
360
|
+
if (conf.trace_sql_error_stack && conf.trace_sql_error_depth) {
|
|
361
|
+
var traceDepth = conf.trace_sql_error_depth;
|
|
362
|
+
var errorStack = err.stack.split("\n");
|
|
363
|
+
|
|
364
|
+
if (errorStack.length > traceDepth) {
|
|
365
|
+
errorStack = errorStack.slice(0, traceDepth + 1);
|
|
366
|
+
}
|
|
367
|
+
ctx.error_message = errorStack.join("\n");
|
|
368
|
+
}
|
|
758
369
|
} catch (e) {
|
|
759
|
-
Logger.printError("WHATAP-
|
|
370
|
+
Logger.printError("WHATAP-309", "Error handling failed", e, false);
|
|
760
371
|
}
|
|
761
372
|
|
|
762
373
|
ctx.footprint("Prisma Query Error");
|
|
@@ -2,219 +2,223 @@ var TraceContextManager = require('../trace/trace-context-manager'),
|
|
|
2
2
|
DataTextAgent = require('../data/datatext-agent'),
|
|
3
3
|
SqlStepX = require('../step/sql-stepx'),
|
|
4
4
|
DBCStep = require('../step/dbc-step'),
|
|
5
|
-
TextTypes = require('../lang/text-types'),
|
|
6
5
|
HashUtil = require('../util/hashutil'),
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
Logger = require('../logger'),
|
|
7
|
+
conf = require('../conf/configure'),
|
|
8
|
+
ParamSecurity = require('../util/paramsecurity');
|
|
9
9
|
const shimmer = require('../core/shimmer');
|
|
10
|
+
const {Buffer} = require("buffer");
|
|
10
11
|
|
|
11
12
|
var RedisObserver = function (agent) {
|
|
12
13
|
this.agent = agent;
|
|
13
|
-
this.aop = agent.aop;
|
|
14
14
|
this.packages = ['redis', 'ioredis'];
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
RedisObserver.prototype.inject = function (mod, modName) {
|
|
18
|
-
|
|
19
18
|
if (mod.__whatap_observe__) {
|
|
20
19
|
return;
|
|
21
20
|
}
|
|
22
21
|
mod.__whatap_observe__ = true;
|
|
23
22
|
Logger.initPrint("RedisObserver");
|
|
24
|
-
var self = this;
|
|
25
23
|
|
|
24
|
+
if (modName === 'redis') {
|
|
25
|
+
this._injectRedis(mod);
|
|
26
|
+
} else if (modName === 'ioredis') {
|
|
27
|
+
this._injectIORedis(mod);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
RedisObserver.prototype._injectRedis = function (mod) {
|
|
26
32
|
var dbc_hash = 0;
|
|
27
|
-
var dbc;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
var dbc = '';
|
|
34
|
+
|
|
35
|
+
const selectCommand = new Set(['get', 'hGet', 'hmGet']);
|
|
36
|
+
const insertCommand = new Set(['set', 'hSet', 'hset', 'hmSet', 'hmset', 'zAdd', 'zadd', 'lSet', 'lset']);
|
|
37
|
+
const updateCommand = new Set(['set', 'lSet', 'lset', 'hSet', 'hset', 'zAdd', 'zadd']);
|
|
38
|
+
const deleteCommand = new Set(['del', 'lRem', 'sRem', 'srem', 'hDel', 'hdel', 'zRem', 'zrem']);
|
|
39
|
+
const commands = new Set([...selectCommand, ...insertCommand, ...updateCommand, ...deleteCommand]);
|
|
40
|
+
|
|
41
|
+
shimmer.wrap(mod, 'createClient', function (original) {
|
|
42
|
+
return function wrappedCreateClient() {
|
|
31
43
|
if (dbc_hash === 0) {
|
|
32
|
-
if (
|
|
33
|
-
var info = (
|
|
34
|
-
dbc = '
|
|
35
|
-
dbc += info.host || '';
|
|
36
|
-
dbc += ':';
|
|
37
|
-
dbc += info.port || '';
|
|
44
|
+
if (arguments.length > 0) {
|
|
45
|
+
var info = (arguments[0] || {});
|
|
46
|
+
dbc = info.url || '';
|
|
38
47
|
dbc_hash = HashUtil.hashFromString(dbc);
|
|
48
|
+
|
|
49
|
+
DataTextAgent.DBC.add(dbc_hash, dbc);
|
|
50
|
+
DataTextAgent.METHOD.add(dbc_hash, dbc);
|
|
51
|
+
DataTextAgent.ERROR.add(dbc_hash, dbc);
|
|
39
52
|
}
|
|
40
53
|
}
|
|
41
|
-
},
|
|
42
|
-
function (obj, args, ret, lctx) {
|
|
43
|
-
var selectCommand = new Set(['get', 'hGet', 'hmGet']);
|
|
44
|
-
var insertCommand = new Set(['set', 'hSet', 'hset', 'hmSet', 'hmset', 'zAdd', 'zadd', 'lSet', 'lset']);
|
|
45
|
-
var updateCommand = new Set(['set', 'lSet', 'lset', 'hSet', 'hset', 'zAdd', 'zadd']);
|
|
46
|
-
var deleteCommand = new Set(['del', 'lRem', 'sRem', 'srem', 'hDel', 'hdel', 'zRem', 'zrem']);
|
|
47
|
-
var commands = new Set([...selectCommand, ...insertCommand, ...updateCommand, ...deleteCommand]);
|
|
48
|
-
commands.forEach(function (command) {
|
|
49
|
-
self.aop.after(ret, command,
|
|
50
|
-
function (obj, args, ret, lctx) {
|
|
51
|
-
var ctx = TraceContextManager.getCurrentContext();
|
|
52
|
-
if (ctx == null) {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
var dbc_step = new DBCStep();
|
|
57
|
-
dbc_step.hash = dbc_hash;
|
|
58
|
-
dbc_step.start_time = dbc_time;
|
|
59
|
-
dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
|
|
60
|
-
ctx.profile.push(dbc_step);
|
|
61
|
-
|
|
62
|
-
var sql_step;
|
|
63
|
-
try {
|
|
64
|
-
sql_step = new SqlStepX();
|
|
65
|
-
const key = args[0];
|
|
66
|
-
const values = [];
|
|
67
|
-
for (let i = 1; i < args.length; i++)
|
|
68
|
-
values.push(args[i]);
|
|
69
|
-
|
|
70
|
-
var sql = 'Redis ' + command + ': ' + JSON.stringify([key]);
|
|
71
|
-
// if (values.length > 0)
|
|
72
|
-
// sql += ' ' + JSON.stringify(values);
|
|
73
|
-
sql_step.hash = HashUtil.hashFromString(sql);
|
|
74
|
-
sql_step.start_time = ctx.getElapsedTime();
|
|
75
|
-
//sql_step.crud = 'I'.charCodeAt(0);
|
|
76
|
-
// DataTextAgent.SQL.add(sql_step.hash, sql);
|
|
77
|
-
ctx.profile.push(sql_step);
|
|
78
|
-
} catch (e) {
|
|
79
|
-
Logger.printError("WHATAP-605", "Redis CRUD error", e, false);
|
|
80
|
-
sql_step = null;
|
|
81
|
-
}
|
|
82
|
-
})
|
|
83
|
-
})
|
|
84
54
|
|
|
85
|
-
var
|
|
86
|
-
ctx = lctx.context;
|
|
55
|
+
var client = original.apply(this, arguments);
|
|
87
56
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
57
|
+
const commandsArray = Array.from(commands);
|
|
58
|
+
commandsArray.forEach(cmdName => {
|
|
59
|
+
shimmer.wrap(client, cmdName, function (originalMethod) {
|
|
60
|
+
const wrappedFunction = function wrappedSendCommand() {
|
|
61
|
+
try{
|
|
62
|
+
var ctx = TraceContextManager.getCurrentContext();
|
|
63
|
+
if (!ctx) {
|
|
64
|
+
return originalMethod.apply(this, arguments);
|
|
65
|
+
}
|
|
91
66
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
: "send_command";
|
|
67
|
+
var dbc_step = new DBCStep();
|
|
68
|
+
dbc_step.hash = dbc_hash;
|
|
69
|
+
ctx.profile.push(dbc_step);
|
|
96
70
|
|
|
97
|
-
|
|
98
|
-
address += (client.address || (client.host + ':' + client.port)) || '';
|
|
99
|
-
dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
|
|
100
|
-
dbc_step.hash = HashUtil.hashFromString(address);
|
|
101
|
-
DataTextAgent.DBC.add(dbc_step.hash, address);
|
|
71
|
+
ctx.footprint('Redis Command Start');
|
|
102
72
|
|
|
103
|
-
|
|
73
|
+
var command = wrappedFunction._commandName || 'unknown';
|
|
74
|
+
var sql = `${command.toUpperCase()}`;
|
|
75
|
+
var sql_step = new SqlStepX();
|
|
76
|
+
sql_step.start_time = ctx.getElapsedTime();
|
|
77
|
+
sql_step.hash = HashUtil.hashFromString(sql);
|
|
78
|
+
sql_step.dbc = dbc_hash;
|
|
79
|
+
|
|
80
|
+
if(conf.getProperty('profile_redis_param_enabled', false)){
|
|
81
|
+
var params;
|
|
82
|
+
if (arguments[0] && typeof arguments[0] === 'string')
|
|
83
|
+
sql += ' ' + arguments[0];
|
|
84
|
+
else if (arguments[0] && Array.isArray(arguments[0]))
|
|
85
|
+
sql += ' ' + JSON.stringify(arguments[0]);
|
|
86
|
+
else if (arguments[0] && typeof arguments[0] === 'object'){
|
|
87
|
+
sql += ' ' + JSON.stringify(Object.keys(arguments[0]))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if(arguments[1] && arguments[1].length > 0){
|
|
91
|
+
var args = Array.isArray(arguments[1]) ? arguments[1] : [arguments[1]];
|
|
92
|
+
sql_step.setTrue(1);
|
|
93
|
+
var crc = {value: 0};
|
|
94
|
+
sql_step.p1 = toParamBytes(args.join(","), crc);
|
|
95
|
+
const result = args.map((arg) => {
|
|
96
|
+
if (typeof arg === 'string') {
|
|
97
|
+
return `'${arg}'`
|
|
98
|
+
}
|
|
99
|
+
return arg
|
|
100
|
+
}).toString()
|
|
101
|
+
sql_step.p2 = toParamBytes(result, crc);
|
|
102
|
+
sql_step.pcrc = crc.value;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
104
105
|
|
|
105
|
-
|
|
106
|
-
var command, params;
|
|
107
|
-
try {
|
|
108
|
-
if (typeof (args[0]) === 'string') {
|
|
109
|
-
command = args[0];
|
|
110
|
-
params = args[1];
|
|
111
|
-
} else {
|
|
112
|
-
command = args[0].command;
|
|
113
|
-
params = args[0].args;
|
|
114
|
-
}
|
|
115
|
-
} catch (e) {
|
|
116
|
-
command = '';
|
|
117
|
-
params = '';
|
|
118
|
-
}
|
|
106
|
+
ctx.profile.push(sql_step);
|
|
119
107
|
|
|
120
|
-
|
|
121
|
-
var sql_step = new SqlStepX();
|
|
122
|
-
sql_step.start_time = ctx.getElapsedTime();
|
|
123
|
-
sql_step.hash = HashUtil.hashFromString(sql);
|
|
124
|
-
sql_step.dbc = dbc_step.hash;
|
|
125
|
-
//sql_step.crud = 'S'.charCodeAt(0);
|
|
126
|
-
ctx.profile.push(sql_step);
|
|
127
|
-
|
|
128
|
-
DataTextAgent.SQL.add(sql_step.hash, sql);
|
|
129
|
-
|
|
130
|
-
ctx.footprint('Redis Command Start');
|
|
131
|
-
var hasCallback = self.aop.functionHook(args[1], -1, function (obj, args) {
|
|
132
|
-
|
|
133
|
-
TraceContextManager.resume(ctx._id);
|
|
134
|
-
if (args[0]) {
|
|
135
|
-
//error
|
|
136
|
-
sql_step.error = StatError.addError(
|
|
137
|
-
'redis',
|
|
138
|
-
(args[0].message || 'redis error'),
|
|
139
|
-
ctx.service_hash,
|
|
140
|
-
TextTypes.SQL,
|
|
141
|
-
sql_step.hash);
|
|
142
|
-
if (ctx.error.isZero()) {
|
|
143
|
-
ctx.error = sql_step.error;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
sql_step.elapsed = sql_step.start_time - ctx.getElapsedTime();
|
|
108
|
+
DataTextAgent.SQL.add(sql_step.hash, sql);
|
|
147
109
|
|
|
148
|
-
|
|
110
|
+
var result = originalMethod.apply(this, arguments);
|
|
111
|
+
sql_step.elapsed = ctx.getElapsedTime();
|
|
149
112
|
|
|
113
|
+
ctx.footprint('Redis Command Done');
|
|
114
|
+
return result;
|
|
115
|
+
}catch (e) {
|
|
116
|
+
Logger.printError("WHATAP-605", "Redis CRUD error", e, false);
|
|
117
|
+
return originalMethod.apply(this, arguments);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
wrappedFunction._commandName = cmdName;
|
|
121
|
+
return wrappedFunction;
|
|
150
122
|
});
|
|
151
|
-
|
|
152
|
-
// if(hasCallback === false) {
|
|
153
|
-
// ctx.footprint('Redis Command Done');
|
|
154
|
-
// }
|
|
155
123
|
});
|
|
156
|
-
|
|
124
|
+
return client;
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return this;
|
|
129
|
+
};
|
|
157
130
|
|
|
131
|
+
RedisObserver.prototype._injectIORedis = function (mod) {
|
|
158
132
|
var ioredis_dbc_hash = 0;
|
|
159
|
-
var ioredis_dbc;
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
133
|
+
var ioredis_dbc = '';
|
|
134
|
+
|
|
135
|
+
const selectCommand = new Set(['get', 'hget', 'hmget', 'zrange', 'smembers', 'lrange']);
|
|
136
|
+
const insertCommand = new Set(['set', 'hset', 'hmset', 'zadd', 'lset', 'sadd', 'lpush', 'rpush', 'evalsha']);
|
|
137
|
+
const updateCommand = new Set(['set', 'lset', 'hset', 'zadd']);
|
|
138
|
+
const deleteCommand = new Set(['del', 'lrem', 'srem', 'hdel', 'zrem']);
|
|
139
|
+
const commands = new Set([...selectCommand, ...insertCommand, ...updateCommand, ...deleteCommand]);
|
|
140
|
+
|
|
141
|
+
shimmer.wrap(mod.prototype, 'sendCommand', function (original) {
|
|
142
|
+
return function wrappedSendCommand(command) {
|
|
143
|
+
// Get trace context
|
|
144
|
+
var ctx = TraceContextManager.getCurrentContext();
|
|
145
|
+
if (!ctx) {
|
|
146
|
+
return original.apply(this, arguments);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (ioredis_dbc_hash === 0 && this.options) {
|
|
164
150
|
ioredis_dbc = 'redis://';
|
|
165
|
-
ioredis_dbc +=
|
|
151
|
+
ioredis_dbc += this.options.host || '';
|
|
166
152
|
ioredis_dbc += ':';
|
|
167
|
-
ioredis_dbc +=
|
|
153
|
+
ioredis_dbc += this.options.port || '';
|
|
168
154
|
ioredis_dbc_hash = HashUtil.hashFromString(ioredis_dbc);
|
|
155
|
+
DataTextAgent.DBC.add(ioredis_dbc_hash, ioredis_dbc);
|
|
169
156
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
157
|
+
|
|
158
|
+
if (command && command.name && commands.has(command.name.toLowerCase())) {
|
|
159
|
+
var dbc_step = new DBCStep();
|
|
160
|
+
dbc_step.hash = ioredis_dbc_hash;
|
|
161
|
+
dbc_step.start_time = ctx.getElapsedTime();
|
|
162
|
+
ctx.profile.push(dbc_step);
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
var sql_step = new SqlStepX();
|
|
166
|
+
|
|
167
|
+
var sql = command.name.toUpperCase();
|
|
168
|
+
if(conf.getProperty('profile_redis_param_enabled', false)){
|
|
169
|
+
var params;
|
|
170
|
+
var args = command.args;
|
|
171
|
+
var key = args.shift();
|
|
172
|
+
if (key && typeof key === 'string')
|
|
173
|
+
sql += ' ' + key;
|
|
174
|
+
|
|
175
|
+
if(args && args.length > 0){
|
|
176
|
+
sql_step.setTrue(1);
|
|
177
|
+
var crc = {value: 0};
|
|
178
|
+
sql_step.p1 = toParamBytes(args.join(","), crc);
|
|
179
|
+
const result = args.map((arg) => {
|
|
180
|
+
if (typeof arg === 'string') {
|
|
181
|
+
return `'${arg}'`
|
|
182
|
+
}
|
|
183
|
+
return arg;
|
|
184
|
+
}).toString()
|
|
185
|
+
sql_step.p2 = toParamBytes(result, crc);
|
|
186
|
+
sql_step.pcrc = crc.value;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
sql_step.hash = HashUtil.hashFromString(sql);
|
|
191
|
+
sql_step.start_time = ctx.getElapsedTime();
|
|
192
|
+
DataTextAgent.SQL.add(sql_step.hash, sql);
|
|
193
|
+
ctx.profile.push(sql_step);
|
|
194
|
+
|
|
195
|
+
var originalPromise = original.apply(this, arguments);
|
|
196
|
+
return originalPromise.finally(function () {
|
|
197
|
+
dbc_step.elapsed = ctx.getElapsedTime() - dbc_step.start_time;
|
|
198
|
+
sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
|
|
199
|
+
});
|
|
200
|
+
} catch (e) {
|
|
201
|
+
Logger.printError("WHATAP-605", "ioredis CRUD error", e, false);
|
|
202
|
+
return original.apply(this, arguments);
|
|
203
|
+
}
|
|
215
204
|
}
|
|
216
|
-
|
|
217
|
-
|
|
205
|
+
|
|
206
|
+
return original.apply(this, arguments);
|
|
207
|
+
};
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
return this;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
var toParamBytes = function (p, crc) {
|
|
214
|
+
if (p == null || p.length === 0) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
return ParamSecurity.encrypt(Buffer.from(p, 'utf8'), crc);
|
|
219
|
+
} catch (e) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
218
222
|
};
|
|
219
223
|
|
|
220
224
|
exports.RedisObserver = RedisObserver;
|
package/package.json
CHANGED