whatap 1.0.1 → 1.0.2
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/README.md +32 -78
- package/lib/conf/conf-sys-mon.js +101 -0
- package/lib/conf/config-default.js +10 -3
- package/lib/conf/configure.js +369 -349
- package/lib/conf/license.js +1 -1
- package/lib/control/cmd-config.js +24 -0
- package/lib/control/control-handler.js +367 -0
- package/lib/control/packagectr-helper.js +34 -3
- package/lib/core/agent.js +176 -882
- package/lib/core/interceptor.js +6 -6
- package/lib/core/request-agent.js +27 -0
- package/lib/core/shimmer.js +82 -36
- package/lib/counter/counter-manager.js +79 -8
- package/lib/counter/meter/meter-activex.js +67 -0
- package/lib/counter/meter/meter-httpc.js +57 -0
- package/lib/counter/meter/meter-resource.js +9 -0
- package/lib/counter/meter/meter-service.js +168 -0
- package/lib/counter/meter/meter-socket.io.js +51 -0
- package/lib/counter/meter/meter-sql.js +71 -0
- package/lib/counter/meter/meter-users.js +58 -0
- package/lib/counter/meter.js +183 -0
- package/lib/counter/task/activetransaction.js +68 -17
- package/lib/counter/task/agentinfo.js +107 -0
- package/lib/{system → counter/task}/gc-action.js +27 -74
- package/lib/counter/task/gcstat.js +34 -0
- package/lib/counter/task/heapmem.js +25 -0
- package/lib/counter/task/httpc.js +76 -0
- package/lib/counter/task/metering-info.js +125 -0
- package/lib/counter/task/proc-cpu.js +29 -0
- package/lib/counter/task/realtimeuser.js +31 -0
- package/lib/counter/task/res/systemECSTask.js +39 -0
- package/lib/counter/task/res/systemKubeTask.js +53 -0
- package/lib/counter/task/res/util/awsEcsClientThread.js +218 -0
- package/lib/counter/task/res/util/linuxProcStatUtil.js +14 -0
- package/lib/counter/task/res-sys-cpu.js +62 -0
- package/lib/counter/task/service.js +202 -0
- package/lib/counter/task/socketio.js +30 -0
- package/lib/counter/task/sql.js +105 -0
- package/lib/counter/task/systemperf.js +43 -0
- package/lib/data/datapack-sender.js +289 -0
- package/lib/data/dataprofile-agent.js +162 -0
- package/lib/data/datatext-agent.js +135 -0
- package/lib/data/event-level.js +15 -0
- package/lib/data/test.js +49 -0
- package/lib/data/zipprofile.js +197 -0
- package/lib/env/constants.js +21 -0
- package/lib/error/error-handler.js +437 -0
- package/lib/io/data-inputx.js +13 -3
- package/lib/io/data-outputx.js +268 -206
- package/lib/kube/kube-client.js +144 -0
- package/lib/lang/text-types.js +58 -0
- package/lib/logger.js +6 -6
- package/lib/logsink/line-log-util.js +87 -0
- package/lib/logsink/line-log.js +12 -0
- package/lib/logsink/log-sender.js +78 -0
- package/lib/logsink/log-tracer.js +40 -0
- package/lib/logsink/sender-util.js +56 -0
- package/lib/logsink/zip/zip-send.js +177 -0
- package/lib/net/netflag.js +55 -0
- package/lib/net/receiver.js +66 -0
- package/lib/net/security-master.js +139 -20
- package/lib/net/sender.js +141 -0
- package/lib/net/tcp-return.js +18 -0
- package/lib/net/tcp-session.js +286 -0
- package/lib/net/tcpreq-client-proxy.js +70 -0
- package/lib/net/tcprequest-mgr.js +58 -0
- package/lib/observers/apollo-server-observer.js +33 -27
- package/lib/observers/cluster-observer.js +22 -0
- package/lib/observers/express-observer.js +215 -0
- package/lib/observers/file-observer.js +184 -0
- package/lib/observers/global-observer.js +155 -80
- package/lib/observers/grpc-observer.js +336 -0
- package/lib/observers/http-observer.js +666 -236
- package/lib/observers/maria-observer.js +204 -362
- package/lib/observers/memcached-observer.js +56 -0
- package/lib/observers/mongo-observer.js +317 -0
- package/lib/observers/mongodb-observer.js +169 -226
- package/lib/observers/mongoose-observer.js +518 -323
- package/lib/observers/mssql-observer.js +177 -418
- package/lib/observers/mysql-observer.js +342 -449
- package/lib/observers/mysql2-observer.js +396 -358
- package/lib/observers/net-observer.js +77 -0
- package/lib/observers/oracle-observer.js +559 -384
- package/lib/observers/pgsql-observer.js +231 -489
- package/lib/observers/prisma-observer.js +303 -92
- package/lib/observers/process-observer.js +79 -35
- package/lib/observers/promise-observer.js +31 -0
- package/lib/observers/redis-observer.js +166 -331
- package/lib/observers/schedule-observer.js +67 -0
- package/lib/observers/socket.io-observer.js +226 -187
- package/lib/observers/stream-observer.js +19 -0
- package/lib/observers/thrift-observer.js +197 -0
- package/lib/observers/websocket-observer.js +175 -301
- package/lib/pack/activestack-pack.js +55 -0
- package/lib/pack/apenum.js +8 -0
- package/lib/pack/counter-pack.js +3 -0
- package/lib/pack/errorsnap-pack.js +69 -0
- package/lib/pack/event-pack.js +54 -0
- package/lib/pack/hitmap-pack.js +63 -0
- package/lib/pack/hitmap-pack1.js +152 -0
- package/lib/pack/log-sink-pack.js +14 -52
- package/lib/pack/netstat.js +15 -0
- package/lib/pack/otype.js +7 -0
- package/lib/pack/profile-pack.js +49 -0
- package/lib/pack/realtimeuser-pack.js +41 -0
- package/lib/pack/stat-general-pack.js +96 -0
- package/lib/pack/staterror-pack.js +120 -0
- package/lib/pack/stathttpc-pack.js +66 -0
- package/lib/pack/stathttpc-rec.js +78 -0
- package/lib/pack/statremote-pack.js +46 -0
- package/lib/pack/statservice-pack.js +63 -0
- package/lib/pack/statservice-pack1.js +88 -0
- package/lib/pack/statservice-rec.js +292 -0
- package/lib/pack/statservice-rec_dep.js +151 -0
- package/lib/pack/statsql-pack.js +69 -0
- package/lib/pack/statsql-rec.js +100 -0
- package/lib/pack/statuseragent-pack.js +44 -0
- package/lib/pack/tagcount-pack.js +4 -4
- package/lib/pack/tagctr.js +15 -0
- package/lib/pack/text-pack.js +50 -0
- package/lib/pack/time-count.js +25 -0
- package/lib/pack/websocket.js +15 -0
- package/lib/pack/zip-pack.js +70 -0
- package/lib/pii/pii-item.js +31 -0
- package/lib/pii/pii-mask.js +174 -0
- package/lib/plugin/plugin-loadermanager.js +57 -0
- package/lib/plugin/plugin.js +75 -0
- package/lib/service/tx-record.js +332 -0
- package/lib/stat/stat-error.js +116 -0
- package/lib/stat/stat-httpc.js +98 -0
- package/lib/stat/stat-remote-ip.js +46 -0
- package/lib/stat/stat-remote-ipurl.js +88 -0
- package/lib/stat/stat-sql.js +113 -0
- package/lib/stat/stat-tranx.js +58 -0
- package/lib/stat/stat-tx-caller.js +160 -0
- package/lib/stat/stat-tx-domain.js +111 -0
- package/lib/stat/stat-tx-referer.js +112 -0
- package/lib/stat/stat-useragent.js +48 -0
- package/lib/stat/timingsender.js +76 -0
- package/lib/step/activestack-step.js +38 -0
- package/lib/step/dbc-step.js +36 -0
- package/lib/step/http-stepx.js +67 -0
- package/lib/step/message-step.js +40 -0
- package/lib/step/method-stepx.js +45 -0
- package/lib/step/resultset-step.js +40 -0
- package/lib/step/securemsg-step.js +44 -0
- package/lib/step/socket-step.js +46 -0
- package/lib/step/sql-stepx.js +68 -0
- package/lib/step/sqlxtype.js +16 -0
- package/lib/step/step.js +66 -0
- package/lib/step/stepenum.js +54 -0
- package/lib/topology/link.js +63 -0
- package/lib/topology/nodeinfo.js +123 -0
- package/lib/topology/status-detector.js +111 -0
- package/lib/trace/trace-context-manager.js +113 -25
- package/lib/trace/trace-context.js +21 -7
- package/lib/trace/trace-httpc.js +17 -11
- package/lib/trace/trace-sql.js +29 -21
- package/lib/util/anylist.js +103 -0
- package/lib/util/cardinality/hyperloglog.js +106 -0
- package/lib/util/cardinality/murmurhash.js +31 -0
- package/lib/util/cardinality/registerset.js +75 -0
- package/lib/util/errordata.js +21 -0
- package/lib/util/escape-literal-sql.js +5 -5
- package/lib/util/hashutil.js +18 -18
- package/lib/util/iputil_x.js +527 -0
- package/lib/util/keygen.js +0 -3
- package/lib/util/kube-util.js +73 -0
- package/lib/util/linkedset.js +1 -2
- package/lib/util/nodeutil.js +2 -1
- package/lib/util/paramsecurity.js +80 -0
- package/lib/util/pre-process.js +13 -0
- package/lib/util/process-seq.js +166 -0
- package/lib/util/property-util.js +36 -0
- package/lib/util/request-queue.js +70 -0
- package/lib/util/requestdouble-queue.js +72 -0
- package/lib/util/resourceprofile.js +157 -0
- package/lib/util/stop-watch.js +30 -0
- package/lib/util/system-util.js +10 -0
- package/lib/util/userid-util.js +57 -0
- package/lib/value/map-value.js +3 -2
- package/package.json +9 -4
- package/whatap.conf +1 -4
- 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 +0 -4
- package/lib/observers/ioredis-observer.js +0 -294
- package/lib/udp/async_sender.js +0 -119
- package/lib/udp/index.js +0 -17
- package/lib/udp/packet_enum.js +0 -52
- package/lib/udp/packet_queue.js +0 -69
- package/lib/udp/packet_type_enum.js +0 -33
- package/lib/udp/param_def.js +0 -72
- package/lib/udp/udp_session.js +0 -336
- package/lib/util/sql-util.js +0 -178
- package/lib/util/trace-helper.js +0 -91
- package/lib/util/transfer.js +0 -58
|
@@ -5,12 +5,31 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const TraceContextManager = require("../trace/trace-context-manager");
|
|
8
|
+
const ParsedSql = require("../trace/parsed-sql");
|
|
9
|
+
const SqlStepX = require("../step/sql-stepx");
|
|
10
|
+
const DBCStep = require("../step/dbc-step");
|
|
11
|
+
const ResultSetStep = require("../step/resultset-step");
|
|
12
|
+
const DataTextAgent = require("../data/datatext-agent");
|
|
13
|
+
const StatSql = require("../stat/stat-sql");
|
|
14
|
+
const MeterSql = require("../counter/meter/meter-sql");
|
|
8
15
|
const conf = require("../conf/configure");
|
|
16
|
+
const IntKeyMap = require("../util/intkey-map");
|
|
17
|
+
const EscapeLiteralSQL = require("../util/escape-literal-sql");
|
|
9
18
|
const HashUtil = require("../util/hashutil");
|
|
19
|
+
const StatError = require("../stat/stat-error");
|
|
20
|
+
const TextTypes = require("../lang/text-types");
|
|
21
|
+
const ParamSecurity = require("../util/paramsecurity");
|
|
10
22
|
const Logger = require("../logger");
|
|
23
|
+
const Buffer = require("buffer").Buffer;
|
|
24
|
+
const DateUtil = require("../util/dateutil");
|
|
25
|
+
const TraceSQL = require("../trace/trace-sql");
|
|
11
26
|
const shimmer = require("../core/shimmer");
|
|
12
|
-
|
|
13
|
-
|
|
27
|
+
|
|
28
|
+
var prisma_read_func_name = conf.getProperty('prisma_read_func_name', 'read');
|
|
29
|
+
var prisma_database_url_name = conf.getProperty('prisma_database_url_name', 'DATABASE_URL')
|
|
30
|
+
conf.on('prisma_database_url_name', function (newProps) {
|
|
31
|
+
prisma_database_url_name = newProps;
|
|
32
|
+
})
|
|
14
33
|
|
|
15
34
|
var PrismaObserver = function(agent) {
|
|
16
35
|
this.agent = agent;
|
|
@@ -36,20 +55,40 @@ PrismaObserver.prototype.inject = function(mod, moduleName) {
|
|
|
36
55
|
|
|
37
56
|
// Prisma Client 초기화 메서드 후킹
|
|
38
57
|
if (mod.PrismaClient) {
|
|
58
|
+
// 직접 PrismaClient 생성자 후킹
|
|
39
59
|
shimmer.wrap(mod, 'PrismaClient', function(originalConstructor) {
|
|
40
60
|
return function(...args) {
|
|
41
|
-
|
|
42
|
-
const instance =
|
|
61
|
+
const originalInstance = new originalConstructor(...args);
|
|
62
|
+
const instance = Object.create(originalInstance);
|
|
63
|
+
|
|
64
|
+
const prismaServicePrototype = Object.getPrototypeOf(this);
|
|
65
|
+
Object.getOwnPropertyNames(prismaServicePrototype).forEach((method) => {
|
|
66
|
+
if (typeof prismaServicePrototype[method] === "function" && !instance[method]) {
|
|
67
|
+
instance[method] = prismaServicePrototype[method].bind(instance);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
Object.getOwnPropertyNames(originalInstance).forEach((prop) => {
|
|
72
|
+
if (!instance.hasOwnProperty(prop)) {
|
|
73
|
+
instance[prop] = originalInstance[prop];
|
|
74
|
+
}
|
|
75
|
+
});
|
|
43
76
|
|
|
44
|
-
// 패치 적용
|
|
45
77
|
self.patchPrismaInstance(instance);
|
|
46
78
|
|
|
79
|
+
if (!instance[prisma_read_func_name]) {
|
|
80
|
+
instance[prisma_read_func_name] = function () {
|
|
81
|
+
return this;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
47
85
|
return instance;
|
|
48
86
|
};
|
|
49
87
|
});
|
|
50
88
|
}
|
|
51
89
|
};
|
|
52
90
|
|
|
91
|
+
// 각 Prisma 인스턴스에 패치 적용
|
|
53
92
|
PrismaObserver.prototype.patchPrismaInstance = function(prismaInstance) {
|
|
54
93
|
if (prismaInstance.__whatap_observe__) {
|
|
55
94
|
return;
|
|
@@ -57,56 +96,71 @@ PrismaObserver.prototype.patchPrismaInstance = function(prismaInstance) {
|
|
|
57
96
|
prismaInstance.__whatap_observe__ = true;
|
|
58
97
|
|
|
59
98
|
this.setupConnectionInfo(prismaInstance);
|
|
99
|
+
|
|
60
100
|
this.hookUseMiddleware(prismaInstance);
|
|
61
101
|
};
|
|
62
102
|
|
|
103
|
+
// 연결 정보 설정
|
|
63
104
|
PrismaObserver.prototype.setupConnectionInfo = function(prismaInstance) {
|
|
64
105
|
try {
|
|
65
106
|
// 연결 정보 가져오기 시도
|
|
66
107
|
const url = prismaInstance._engineConfig?.overrideDatasources?.db?.url ||
|
|
67
|
-
prismaInstance.
|
|
68
|
-
|
|
69
|
-
process.env.DATABASE_URL ||
|
|
108
|
+
prismaInstance.env?.DATABASE_URL ||
|
|
109
|
+
process.env[prisma_database_url_name] ||
|
|
70
110
|
'prisma:unknown';
|
|
71
111
|
|
|
72
112
|
if (url && url !== "prisma:unknown" && !dbc_hash) {
|
|
73
113
|
const dbUrl = new URL(url);
|
|
74
114
|
const protocol = dbUrl.protocol.replace(':', '');
|
|
75
115
|
|
|
116
|
+
// 프로토콜에 따라 접두사 설정 (postgresql -> pgsql로 변환할 수도 있음)
|
|
117
|
+
const dbProtocol = protocol === 'pgsql' ? 'postgresql' : protocol;
|
|
118
|
+
|
|
76
119
|
// MySQL 관찰자와 동일한 형식으로 구성
|
|
77
|
-
dbc = `${
|
|
120
|
+
dbc = `${dbProtocol}://`;
|
|
78
121
|
dbc += dbUrl.username || '';
|
|
79
122
|
dbc += "@";
|
|
80
123
|
dbc += dbUrl.hostname || '';
|
|
81
124
|
dbc += '/';
|
|
82
125
|
dbc += dbUrl.pathname.replace('/', '') || '';
|
|
83
126
|
dbc_hash = HashUtil.hashFromString(dbc);
|
|
127
|
+
|
|
128
|
+
DataTextAgent.DBC.add(dbc_hash, dbc);
|
|
129
|
+
DataTextAgent.METHOD.add(dbc_hash, dbc);
|
|
130
|
+
DataTextAgent.ERROR.add(dbc_hash, dbc);
|
|
84
131
|
}
|
|
85
132
|
|
|
86
133
|
} catch (e) {
|
|
87
|
-
Logger.printError("WHATAP-
|
|
134
|
+
Logger.printError("WHATAP-301", "Failed to extract connection info", e, false);
|
|
88
135
|
dbc = "prisma:unknown";
|
|
89
136
|
dbc_hash = HashUtil.hashFromString(dbc);
|
|
90
137
|
}
|
|
91
138
|
};
|
|
92
139
|
|
|
93
140
|
PrismaObserver.prototype.hookUseMiddleware = function(prismaInstance) {
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
if (typeof prismaInstance.$use === 'function') {
|
|
141
|
+
const originalUse = prismaInstance.$use;
|
|
142
|
+
if (typeof originalUse === 'function') {
|
|
97
143
|
prismaInstance.$use(async (params, next) => {
|
|
144
|
+
var result;
|
|
98
145
|
const ctx = TraceContextManager.getCurrentContext();
|
|
99
146
|
if (!ctx) {
|
|
100
147
|
return next(params);
|
|
101
148
|
}
|
|
102
149
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
150
|
+
var dbc_step = new DBCStep();
|
|
151
|
+
dbc_hash = HashUtil.hashFromString(dbc);
|
|
152
|
+
DataTextAgent.DBC.add(dbc_hash, dbc);
|
|
153
|
+
DataTextAgent.METHOD.add(dbc_hash, dbc);
|
|
154
|
+
DataTextAgent.ERROR.add(dbc_hash, dbc)
|
|
155
|
+
|
|
156
|
+
dbc_step.hash = dbc_hash;
|
|
157
|
+
dbc_step.start_time = ctx.getElapsedTime();
|
|
158
|
+
ctx.profile.push(dbc_step);
|
|
107
159
|
|
|
108
|
-
|
|
109
|
-
|
|
160
|
+
const sql_step = new SqlStepX();
|
|
161
|
+
sql_step.start_time = ctx.getElapsedTime();
|
|
162
|
+
sql_step.elapsed = 0;
|
|
163
|
+
ctx.profile.push(sql_step);
|
|
110
164
|
|
|
111
165
|
const modelName = params.model || 'unknown';
|
|
112
166
|
const action = params.action || 'unknown';
|
|
@@ -114,21 +168,161 @@ PrismaObserver.prototype.hookUseMiddleware = function(prismaInstance) {
|
|
|
114
168
|
ctx.footprint(`Prisma ${modelName}.${action} Start`);
|
|
115
169
|
ctx.sql_count = (ctx.sql_count || 0) + 1;
|
|
116
170
|
|
|
117
|
-
// 쿼리 정보
|
|
171
|
+
// Prisma 쿼리 정보 직접 사용 (SQL 변환 없이)
|
|
118
172
|
const queryInfo = `Prisma ${modelName}.${action}`;
|
|
173
|
+
const queryHash = HashUtil.hashFromString(queryInfo);
|
|
174
|
+
|
|
175
|
+
DataTextAgent.SQL.add(queryHash, queryInfo);
|
|
176
|
+
sql_step.hash = queryHash;
|
|
177
|
+
sql_step.dbc = dbc_hash;
|
|
178
|
+
|
|
179
|
+
ctx.active_sqlhash = sql_step.hash;
|
|
180
|
+
ctx.active_dbc = sql_step.dbc;
|
|
181
|
+
|
|
182
|
+
// 쿼리 파라미터 정보 추출
|
|
183
|
+
if (conf.profile_sql_param_enabled) {
|
|
184
|
+
const paramsString = JSON.stringify(params.args || {});
|
|
185
|
+
sql_step.setTrue(1);
|
|
186
|
+
var crc = { value: 0 };
|
|
187
|
+
sql_step.p1 = toParamBytes(paramsString, crc);
|
|
188
|
+
sql_step.pcrc = crc.value;
|
|
189
|
+
}
|
|
119
190
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
191
|
+
if (action === "queryRaw" || action === "executeRaw" || action === "queryRawUnsafe" || action === "executeRawUnsafe") {
|
|
192
|
+
// SQL 문자열 추출
|
|
193
|
+
let sqlString = "";
|
|
194
|
+
if (params.args && params.args.length > 0) {
|
|
195
|
+
if (params.args[0] && params.args[0].strings && Array.isArray(params.args[0].strings)) {
|
|
196
|
+
sqlString = params.args[0].strings.join('').trim();
|
|
197
|
+
} else if (params.args[0] && params.args[0] && Array.isArray(params.args[0])){
|
|
198
|
+
sqlString = params.args[0].join('').trim();
|
|
199
|
+
} else if (typeof params.args[0] === 'string') {
|
|
200
|
+
sqlString = params.args[0];
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// SQL 문자열이 있으면 처리
|
|
205
|
+
if (sqlString && sqlString.length > 0) {
|
|
206
|
+
try {
|
|
207
|
+
var psql = escapeLiteral(sqlString);
|
|
208
|
+
if (psql != null) {
|
|
209
|
+
sql_step.hash = psql.sql;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// 추가 SQL 정보 처리
|
|
213
|
+
var els = new EscapeLiteralSQL(sqlString);
|
|
214
|
+
els.process();
|
|
215
|
+
|
|
216
|
+
// SQL 파라미터 처리
|
|
217
|
+
if (conf.profile_sql_param_enabled) {
|
|
218
|
+
var params = params.args.slice(1);
|
|
219
|
+
sql_step.setTrue(1);
|
|
220
|
+
var crc = { value: 0 };
|
|
221
|
+
sql_step.p1 = toParamBytes(psql.param, crc);
|
|
222
|
+
|
|
223
|
+
if (params && params.length > 0) {
|
|
224
|
+
const result = params.map((param) => {
|
|
225
|
+
if (typeof param === 'string') {
|
|
226
|
+
return `'${param}'`;
|
|
227
|
+
}
|
|
228
|
+
return param;
|
|
229
|
+
}).toString();
|
|
230
|
+
sql_step.p2 = toParamBytes(result, crc);
|
|
231
|
+
}
|
|
232
|
+
sql_step.pcrc = crc.value;
|
|
233
|
+
}
|
|
234
|
+
} catch (e) {
|
|
235
|
+
Logger.printError("WHATAP-305", "escapeLiteral error", e, false);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
123
239
|
try {
|
|
124
|
-
|
|
240
|
+
result = await next(params);
|
|
241
|
+
|
|
242
|
+
// Raw 쿼리 처리
|
|
243
|
+
if (action === "queryRaw" || action === "executeRaw" || action === "queryRawUnsafe" || action === "executeRawUnsafe") {
|
|
244
|
+
// Raw 쿼리 결과셋 처리
|
|
245
|
+
if ((action === "queryRaw" || action === "queryRawUnsafe") && result) {
|
|
246
|
+
let recordCount = 0;
|
|
247
|
+
|
|
248
|
+
if (Array.isArray(result)) {
|
|
249
|
+
recordCount = result.length;
|
|
250
|
+
} else if (result && typeof result === "object") {
|
|
251
|
+
recordCount = 1;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if(psql && psql.sql){
|
|
255
|
+
const sqlHashToUse = psql.sql;
|
|
256
|
+
|
|
257
|
+
var result_step = new ResultSetStep();
|
|
258
|
+
result_step.start_time = ctx.getElapsedTime();
|
|
259
|
+
result_step.elapsed = 0;
|
|
260
|
+
result_step.fetch = recordCount;
|
|
261
|
+
result_step.sqlhash = sqlHashToUse;
|
|
262
|
+
result_step.dbc = dbc_hash;
|
|
263
|
+
ctx.profile.push(result_step);
|
|
264
|
+
|
|
265
|
+
ctx.rs_count = ctx.rs_count ? ctx.rs_count + recordCount : recordCount;
|
|
266
|
+
ctx.rs_time = ctx.rs_time ? ctx.rs_time + sql_step.elapsed : sql_step.elapsed;
|
|
267
|
+
|
|
268
|
+
MeterSql.addFetch(result_step.dbc, result_step.fetch, 0);
|
|
269
|
+
StatSql.addFetch(result_step.dbc, result_step.sqlhash, result_step.fetch, 0);
|
|
270
|
+
|
|
271
|
+
TraceSQL.isTooManyRecords(sql_step, result_step.fetch, ctx);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// 결과셋 처리 (findMany, findFirst, findUnique 등)
|
|
277
|
+
if (["findMany", "findFirst", "findUnique"].includes(action)) {
|
|
278
|
+
let recordCount = 0;
|
|
279
|
+
|
|
280
|
+
if (Array.isArray(result)) {
|
|
281
|
+
recordCount = result.length;
|
|
282
|
+
} else if (result && typeof result === "object") {
|
|
283
|
+
recordCount = 1;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
var result_step = new ResultSetStep();
|
|
287
|
+
result_step.start_time = ctx.getElapsedTime();
|
|
288
|
+
result_step.elapsed = 0;
|
|
289
|
+
result_step.fetch = recordCount;
|
|
290
|
+
result_step.sqlhash = queryHash;
|
|
291
|
+
result_step.dbc = dbc_hash;
|
|
292
|
+
ctx.profile.push(result_step);
|
|
293
|
+
|
|
294
|
+
ctx.rs_count = ctx.rs_count ? ctx.rs_count + recordCount : recordCount;
|
|
295
|
+
ctx.rs_time = ctx.rs_time ? ctx.rs_time + sql_step.elapsed : sql_step.elapsed;
|
|
296
|
+
|
|
297
|
+
MeterSql.addFetch(result_step.dbc, result_step.fetch, 0);
|
|
298
|
+
StatSql.addFetch(result_step.dbc, result_step.sqlhash, result_step.fetch, 0);
|
|
299
|
+
|
|
300
|
+
TraceSQL.isTooManyRecords(sql_step, result_step.fetch, ctx);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// 수정된 레코드 수 처리 (create, update, delete 등)
|
|
304
|
+
if (["create", "createMany", "update", "updateMany", "delete", "deleteMany"].includes(action)) {
|
|
305
|
+
if (result && result.count !== undefined) {
|
|
306
|
+
sql_step.updated = result.count;
|
|
307
|
+
} else if (result && typeof result === "object") {
|
|
308
|
+
sql_step.updated = 1;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// UPSERT 처리 추가
|
|
313
|
+
if (action === "upsert") {
|
|
314
|
+
sql_step.updated = 1; // upsert는 항상 1개의 레코드에 영향을 미침
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
this._finishQuery(ctx, sql_step);
|
|
125
318
|
|
|
126
|
-
self._finishQuery(ctx, sql_start_time, result, queryInfo);
|
|
127
319
|
ctx.footprint(`Prisma ${modelName}.${action} Done`);
|
|
128
320
|
|
|
129
321
|
return result;
|
|
130
322
|
} catch (err) {
|
|
131
|
-
|
|
323
|
+
Logger.printError("WHATAP-308", `Middleware error in ${modelName}.${action}: ${err.message}`, err, false);
|
|
324
|
+
|
|
325
|
+
this._handleError(ctx, sql_step, err);
|
|
132
326
|
ctx.footprint(`Prisma ${modelName}.${action} Error`);
|
|
133
327
|
throw err;
|
|
134
328
|
}
|
|
@@ -136,89 +330,106 @@ PrismaObserver.prototype.hookUseMiddleware = function(prismaInstance) {
|
|
|
136
330
|
}
|
|
137
331
|
};
|
|
138
332
|
|
|
139
|
-
PrismaObserver.prototype._finishQuery = function(ctx,
|
|
140
|
-
|
|
141
|
-
ctx.sql_time = (ctx.sql_time || 0) +
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
let resultCount = 0;
|
|
145
|
-
if (result) {
|
|
146
|
-
if (Array.isArray(result)) {
|
|
147
|
-
resultCount = result.length;
|
|
148
|
-
} else if (result && typeof result === "object" && result.count !== undefined) {
|
|
149
|
-
resultCount = result.count;
|
|
150
|
-
} else if (result && typeof result === "object") {
|
|
151
|
-
resultCount = 1;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
333
|
+
PrismaObserver.prototype._finishQuery = function(ctx, sql_step) {
|
|
334
|
+
sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
|
|
335
|
+
ctx.sql_time = (ctx.sql_time || 0) + sql_step.elapsed;
|
|
336
|
+
|
|
337
|
+
TraceSQL.isSlowSQL(ctx);
|
|
154
338
|
|
|
155
|
-
|
|
156
|
-
ctx.elapsed
|
|
157
|
-
|
|
158
|
-
|
|
339
|
+
MeterSql.add(dbc_hash, sql_step.elapsed, false);
|
|
340
|
+
StatSql.addSqlTime(ctx.service_hash, sql_step.dbc, sql_step.hash, sql_step.elapsed, false, 0);
|
|
341
|
+
|
|
342
|
+
ctx.footprint("Prisma Query Done");
|
|
159
343
|
};
|
|
160
344
|
|
|
161
|
-
PrismaObserver.prototype._handleError = function(ctx,
|
|
162
|
-
|
|
163
|
-
ctx.sql_time = (ctx.sql_time || 0) +
|
|
345
|
+
PrismaObserver.prototype._handleError = function(ctx, sql_step, err) {
|
|
346
|
+
sql_step.elapsed = ctx.getElapsedTime() - sql_step.start_time;
|
|
347
|
+
ctx.sql_time = (ctx.sql_time || 0) + sql_step.elapsed;
|
|
348
|
+
|
|
349
|
+
TraceSQL.isSlowSQL(ctx);
|
|
350
|
+
|
|
351
|
+
MeterSql.add(dbc_hash, sql_step.elapsed, true);
|
|
352
|
+
StatSql.addSqlTime(ctx.service_hash, sql_step.dbc, sql_step.hash, sql_step.elapsed, true, 0);
|
|
164
353
|
|
|
165
354
|
try {
|
|
166
355
|
const errorClassName = err.name || err.constructor?.name || "UnknownError";
|
|
167
|
-
const errorMessage = err.message || 'Prisma error';
|
|
168
|
-
let errorStack = '';
|
|
169
|
-
|
|
170
356
|
ctx.error_class = errorClassName;
|
|
171
|
-
ctx.error_message =
|
|
172
|
-
|
|
173
|
-
// 스택 트레이스 처리
|
|
174
|
-
if (conf.trace_sql_error_stack && conf.trace_sql_error_depth && err.stack) {
|
|
175
|
-
var traceDepth = conf.trace_sql_error_depth;
|
|
176
|
-
var stackLines = err.stack.split("\n");
|
|
357
|
+
ctx.error_message = err.message;
|
|
358
|
+
sql_step.error = StatError.addError(errorClassName, err.message, ctx.service_hash, TextTypes.SQL, sql_step.hash);
|
|
177
359
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
errorStack = stackLines.join("\n");
|
|
182
|
-
ctx.error_message = errorStack;
|
|
360
|
+
if (ctx.error.isZero()) {
|
|
361
|
+
ctx.error = sql_step.error;
|
|
183
362
|
}
|
|
184
363
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
errorClassName.indexOf(conf.trace_ignore_err_cls_contains) < 0) {
|
|
189
|
-
shouldAddError = true;
|
|
190
|
-
} else if (conf._is_trace_ignore_err_msg_contains === true && errorMessage &&
|
|
191
|
-
errorMessage.indexOf(conf.trace_ignore_err_msg_contains) < 0) {
|
|
192
|
-
shouldAddError = true;
|
|
193
|
-
} else if (conf._is_trace_ignore_err_cls_contains === false &&
|
|
194
|
-
conf._is_trace_ignore_err_msg_contains === false) {
|
|
195
|
-
shouldAddError = true;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (shouldAddError) {
|
|
199
|
-
if(!ctx.error) ctx.error = 1;
|
|
200
|
-
ctx.status = 500;
|
|
201
|
-
ctx.errClass = errorClassName;
|
|
202
|
-
ctx.errMessage = errorMessage;
|
|
364
|
+
if (conf.trace_sql_error_stack && conf.trace_sql_error_depth) {
|
|
365
|
+
var traceDepth = conf.trace_sql_error_depth;
|
|
366
|
+
var errorStack = err.stack.split("\n");
|
|
203
367
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
errors.push(errorMessage);
|
|
207
|
-
}
|
|
208
|
-
if (errorStack || err.stack) {
|
|
209
|
-
errors.push(errorStack || err.stack);
|
|
368
|
+
if (errorStack.length > traceDepth) {
|
|
369
|
+
errorStack = errorStack.slice(0, traceDepth + 1);
|
|
210
370
|
}
|
|
211
|
-
|
|
371
|
+
ctx.error_message = errorStack.join("\n");
|
|
212
372
|
}
|
|
373
|
+
} catch (e) {
|
|
374
|
+
Logger.printError("WHATAP-309", "Error handling failed", e, false);
|
|
375
|
+
}
|
|
213
376
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
ctx.active_sqlhash = false;
|
|
217
|
-
AsyncSender.send_packet(PacketTypeEnum.TX_SQL, ctx, [dbc, queryInfo, "0"]);
|
|
377
|
+
ctx.footprint("Prisma Query Error");
|
|
378
|
+
};
|
|
218
379
|
|
|
380
|
+
var toParamBytes = function(p, crc) {
|
|
381
|
+
if (p == null || p.length === 0) {
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
try {
|
|
385
|
+
return ParamSecurity.encrypt(Buffer.from(p, "utf8"), crc);
|
|
219
386
|
} catch (e) {
|
|
220
|
-
|
|
387
|
+
return null;
|
|
221
388
|
}
|
|
222
389
|
};
|
|
223
390
|
|
|
391
|
+
var checkedSql = new IntKeyMap(2000).setMax(2000);
|
|
392
|
+
var nonLiteSql = new IntKeyMap(5000).setMax(5000);
|
|
393
|
+
var date = DateUtil.yyyymmdd();
|
|
394
|
+
|
|
395
|
+
function escapeLiteral(sql) {
|
|
396
|
+
if (sql == null) {
|
|
397
|
+
sql = "";
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (date !== DateUtil.yyyymmdd()) {
|
|
401
|
+
checkedSql.clear();
|
|
402
|
+
nonLiteSql.clear();
|
|
403
|
+
date = DateUtil.yyyymmdd();
|
|
404
|
+
Logger.print("WHATAP-SQL-CLEAR", "PrismaObserver CLEAR OK!!!!!!!!!!!!!!!!", false);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
var sqlHash = HashUtil.hashFromString(sql);
|
|
408
|
+
var psql = nonLiteSql.get(sqlHash);
|
|
409
|
+
|
|
410
|
+
if (psql != null) {
|
|
411
|
+
return psql;
|
|
412
|
+
}
|
|
413
|
+
psql = checkedSql.get(sqlHash);
|
|
414
|
+
|
|
415
|
+
if (psql != null) {
|
|
416
|
+
return psql;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
var els = new EscapeLiteralSQL(sql);
|
|
420
|
+
els.process();
|
|
421
|
+
|
|
422
|
+
var hash = HashUtil.hashFromString(els.getParsedSql());
|
|
423
|
+
DataTextAgent.SQL.add(hash, els.getParsedSql());
|
|
424
|
+
|
|
425
|
+
if (hash === sqlHash) {
|
|
426
|
+
psql = new ParsedSql(els.sqlType, hash, null);
|
|
427
|
+
nonLiteSql.put(sqlHash, psql);
|
|
428
|
+
} else {
|
|
429
|
+
psql = new ParsedSql(els.sqlType, hash, els.getParameter());
|
|
430
|
+
checkedSql.put(sqlHash, psql);
|
|
431
|
+
}
|
|
432
|
+
return psql;
|
|
433
|
+
}
|
|
434
|
+
|
|
224
435
|
exports.PrismaObserver = PrismaObserver;
|
|
@@ -6,26 +6,28 @@
|
|
|
6
6
|
|
|
7
7
|
const TraceContextManager = require('../trace/trace-context-manager');
|
|
8
8
|
const conf = require('../conf/configure');
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const SecurityMaster = require('../net/security-master');
|
|
12
|
-
const LogSinkPack = require('../pack/log-sink-pack');
|
|
9
|
+
const LogTracer = require('../logsink/log-tracer');
|
|
10
|
+
const Logger = require('../logger');
|
|
13
11
|
|
|
14
12
|
let logsink_enabled = conf.getProperty('logsink_enabled', false);
|
|
13
|
+
let logTracer = logsink_enabled ? new LogTracer() : null;
|
|
15
14
|
|
|
16
|
-
conf.on('logsink_enabled', function(newProperty) {
|
|
15
|
+
conf.on('logsink_enabled', function (newProperty) {
|
|
17
16
|
logsink_enabled = newProperty;
|
|
17
|
+
logTracer = logsink_enabled ? new LogTracer() : null;
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
const ProcessObserver = function (agent) {
|
|
21
21
|
this.agent = agent;
|
|
22
22
|
this.packages = ['process'];
|
|
23
|
+
this.exitHandlersRegistered = false;
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
ProcessObserver.prototype.inject = function (mod, moduleName) {
|
|
26
27
|
this._hookNextTick(mod);
|
|
27
28
|
this._hookStdOutWrite();
|
|
28
29
|
this._hookStdErrWrite();
|
|
30
|
+
this._hookProcessExit(mod);
|
|
29
31
|
};
|
|
30
32
|
|
|
31
33
|
ProcessObserver.prototype._hookNextTick = function (mod) {
|
|
@@ -39,26 +41,6 @@ ProcessObserver.prototype._hookNextTick = function (mod) {
|
|
|
39
41
|
});
|
|
40
42
|
};
|
|
41
43
|
|
|
42
|
-
ProcessObserver.prototype._sendLogPack = function (content, category) {
|
|
43
|
-
const ctx = TraceContextManager.getCurrentContext();
|
|
44
|
-
const tags = ctx && ctx.id ? {'@txid': ctx.id.toString()} : {};
|
|
45
|
-
|
|
46
|
-
const fields = {"filename": null};
|
|
47
|
-
|
|
48
|
-
const p = new LogSinkPack();
|
|
49
|
-
p.Category = category;
|
|
50
|
-
p.time = Date.now();
|
|
51
|
-
p.line = Date.now();
|
|
52
|
-
p.content = content;
|
|
53
|
-
|
|
54
|
-
p.pcode = SecurityMaster.PCODE;
|
|
55
|
-
const bout = new DataOuputX();
|
|
56
|
-
bout.writePack(p, null);
|
|
57
|
-
const packbytes = bout.toByteArray();
|
|
58
|
-
|
|
59
|
-
AsyncSender.send_relaypack(packbytes);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
44
|
ProcessObserver.prototype._hookStdOutWrite = function () {
|
|
63
45
|
this.agent.aop.after(process.stdout, 'write', (obj, args) => {
|
|
64
46
|
if (conf.getProperty('logsink_enabled', false) && args[0]) {
|
|
@@ -67,12 +49,9 @@ ProcessObserver.prototype._hookStdOutWrite = function () {
|
|
|
67
49
|
const parsedContent = JSON.parse(content);
|
|
68
50
|
content = parsedContent.message ? parsedContent.message : JSON.stringify(parsedContent);
|
|
69
51
|
} catch (e) {
|
|
70
|
-
// JSON 파싱 실패 시 원본 content 사용
|
|
71
52
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const category = conf.getProperty('logsink_category_stdout', 'AppStdOut');
|
|
75
|
-
this._sendLogPack(content, category);
|
|
53
|
+
if (logTracer && content) {
|
|
54
|
+
logTracer.addStdWrite(content, conf.logsink_category_stdout);
|
|
76
55
|
}
|
|
77
56
|
}
|
|
78
57
|
});
|
|
@@ -86,15 +65,80 @@ ProcessObserver.prototype._hookStdErrWrite = function () {
|
|
|
86
65
|
const parsedContent = JSON.parse(content);
|
|
87
66
|
content = parsedContent.message ? parsedContent.message : JSON.stringify(parsedContent);
|
|
88
67
|
} catch (e) {
|
|
89
|
-
// JSON 파싱 실패 시 원본 content 사용
|
|
90
68
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const category = conf.getProperty('logsink_category_stderr', 'AppStdErr');
|
|
94
|
-
this._sendLogPack(content, category);
|
|
69
|
+
if (logTracer && content) {
|
|
70
|
+
logTracer.addStdWrite(content, conf.logsink_category_stderr);
|
|
95
71
|
}
|
|
96
72
|
}
|
|
97
73
|
});
|
|
98
74
|
};
|
|
99
75
|
|
|
76
|
+
ProcessObserver.prototype._hookProcessExit = function (mod) {
|
|
77
|
+
if (this.exitHandlersRegistered) {
|
|
78
|
+
Logger.print("WHATAP-901", "[ProcessObserver] Exit handlers already registered, skipping...", false)
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const self = this;
|
|
82
|
+
|
|
83
|
+
// SIGTERM (graceful shutdown)
|
|
84
|
+
process.on('SIGTERM', function () {
|
|
85
|
+
const message = 'Process termination requested by system or process manager (SIGTERM) - Usually from service stop or container shutdown';
|
|
86
|
+
Logger.print('WHATAP-902', message, false);
|
|
87
|
+
setTimeout(() => process.exit(0), 100);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// SIGINT (Ctrl+C)
|
|
91
|
+
process.on('SIGINT', function () {
|
|
92
|
+
const message = 'Process interrupted by user (SIGINT) - Typically Ctrl+C or kill command';
|
|
93
|
+
Logger.print('WHATAP-903', message, false);
|
|
94
|
+
setTimeout(() => process.exit(0), 100);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// SIGHUP (hang up)
|
|
98
|
+
process.on('SIGHUP', function () {
|
|
99
|
+
const message = 'Process hangup signal received (SIGHUP) - Terminal disconnection or parent process terminated';
|
|
100
|
+
Logger.print('WHATAP-904', message, false);
|
|
101
|
+
setTimeout(() => process.exit(0), 100);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// SIGUSR1 (user-defined signal 1)
|
|
105
|
+
process.on('SIGUSR1', function () {
|
|
106
|
+
const message = 'User-defined signal 1 received (SIGUSR1) - Custom application signal';
|
|
107
|
+
Logger.print('WHATAP-905', message, false);
|
|
108
|
+
setTimeout(() => process.exit(0), 100);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// SIGUSR2 (user-defined signal 2)
|
|
112
|
+
process.on('SIGUSR2', function () {
|
|
113
|
+
const message = 'User-defined signal 2 received (SIGUSR2) - Custom application signal';
|
|
114
|
+
Logger.print('WHATAP-906', message, false);
|
|
115
|
+
setTimeout(() => process.exit(0), 100);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// 처리되지 않은 예외
|
|
119
|
+
// process.on('uncaughtException', function (err) {
|
|
120
|
+
// const message = `Unhandled exception caused process crash - Error: ${err.message}, File: ${err.stack ? err.stack.split('\n')[1] || 'unknown' : 'unknown'}`;
|
|
121
|
+
// Logger.print('WHATAP-907', message, err, false);
|
|
122
|
+
//
|
|
123
|
+
// // 로그 남긴 후 프로세스 종료
|
|
124
|
+
// setTimeout(() => {
|
|
125
|
+
// process.exit(1);
|
|
126
|
+
// }, 100);
|
|
127
|
+
// });
|
|
128
|
+
//
|
|
129
|
+
// // 처리되지 않은 Promise rejection
|
|
130
|
+
// process.on('unhandledRejection', function (reason, promise) {
|
|
131
|
+
// const message = `Unhandled Promise rejection may cause process termination - Reason: ${reason instanceof Error ? reason.message : String(reason)}`;
|
|
132
|
+
// const err = reason instanceof Error ? reason : new Error(String(reason));
|
|
133
|
+
// Logger.print('WHATAP-908', message, err, false);
|
|
134
|
+
//
|
|
135
|
+
// // 로그 남긴 후 프로세스 종료
|
|
136
|
+
// setTimeout(() => {
|
|
137
|
+
// process.exit(1);
|
|
138
|
+
// }, 100);
|
|
139
|
+
// });
|
|
140
|
+
|
|
141
|
+
this.exitHandlersRegistered = true;
|
|
142
|
+
};
|
|
143
|
+
|
|
100
144
|
module.exports.ProcessObserver = ProcessObserver;
|