log-collector-async 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +58 -0
- package/README.md +866 -0
- package/__tests__/client.test.js +418 -0
- package/example_express.js +99 -0
- package/example_global_error_handler.js +77 -0
- package/example_user_context.js +305 -0
- package/jest.config.js +13 -0
- package/package.json +60 -0
- package/src/browser-client.js +382 -0
- package/src/browser-worker.js +118 -0
- package/src/index.js +29 -0
- package/src/node-client.js +561 -0
- package/src/node-worker.js +127 -0
- package/test-manual.js +29 -0
- package/test_local.js +45 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 사용자 컨텍스트 자동 수집 예제 (JavaScript/Node.js)
|
|
3
|
+
*
|
|
4
|
+
* 실행 방법:
|
|
5
|
+
* node example_user_context.js
|
|
6
|
+
*
|
|
7
|
+
* 테스트 시나리오:
|
|
8
|
+
* 1. runWithUserContext 방식 (권장)
|
|
9
|
+
* 2. Promise와 함께 사용
|
|
10
|
+
* 3. 중첩 컨텍스트
|
|
11
|
+
* 4. 분산 추적 (trace_id)
|
|
12
|
+
*/
|
|
13
|
+
import { WorkerThreadsLogClient } from './src/node-client.js';
|
|
14
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
15
|
+
|
|
16
|
+
// 로그 클라이언트 초기화
|
|
17
|
+
const logger = new WorkerThreadsLogClient('http://localhost:8000', {
|
|
18
|
+
service: 'user-context-example-js',
|
|
19
|
+
environment: 'development'
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 예제 1: runWithUserContext 방식 (권장)
|
|
24
|
+
*/
|
|
25
|
+
function example1_runWithUserContext() {
|
|
26
|
+
console.log('\n=== 예제 1: runWithUserContext 방식 ===');
|
|
27
|
+
|
|
28
|
+
// 컨텍스트 없이 로그
|
|
29
|
+
logger.info('Operation without context');
|
|
30
|
+
// → user_id 없음
|
|
31
|
+
|
|
32
|
+
// 컨텍스트 있이 로그
|
|
33
|
+
WorkerThreadsLogClient.runWithUserContext({
|
|
34
|
+
user_id: 'user_123',
|
|
35
|
+
trace_id: 'trace_xyz',
|
|
36
|
+
session_id: 'sess_abc'
|
|
37
|
+
}, () => {
|
|
38
|
+
logger.info('User logged in');
|
|
39
|
+
// → user_id, trace_id, session_id 자동 포함!
|
|
40
|
+
|
|
41
|
+
logger.info('User viewing dashboard');
|
|
42
|
+
// → 같은 컨텍스트 정보 포함
|
|
43
|
+
|
|
44
|
+
simulatePayment();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// 블록 벗어나면 컨텍스트 자동 초기화
|
|
48
|
+
logger.info('Operation after context cleared');
|
|
49
|
+
// → user_id 없음
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 결제 시뮬레이션 (컨텍스트 자동 전파)
|
|
54
|
+
*/
|
|
55
|
+
function simulatePayment() {
|
|
56
|
+
logger.info('Processing payment');
|
|
57
|
+
// → 상위 컨텍스트의 user_id, trace_id 자동 포함!
|
|
58
|
+
|
|
59
|
+
setTimeout(() => {
|
|
60
|
+
logger.info('Payment completed', { amount: 99.99, currency: 'USD' });
|
|
61
|
+
// → user_id, trace_id + 추가 필드
|
|
62
|
+
}, 100);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 예제 2: Promise와 함께 사용
|
|
67
|
+
*/
|
|
68
|
+
async function example2_withPromises() {
|
|
69
|
+
console.log('\n=== 예제 2: Promise와 함께 사용 ===');
|
|
70
|
+
|
|
71
|
+
await WorkerThreadsLogClient.runWithUserContext({
|
|
72
|
+
user_id: 'user_456',
|
|
73
|
+
trace_id: 'trace_async_123'
|
|
74
|
+
}, async () => {
|
|
75
|
+
logger.info('Async operation started');
|
|
76
|
+
// → user_id, trace_id 자동 포함
|
|
77
|
+
|
|
78
|
+
await fetchUserData();
|
|
79
|
+
await processData();
|
|
80
|
+
|
|
81
|
+
logger.info('Async operation completed');
|
|
82
|
+
// → 같은 컨텍스트 유지됨
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 사용자 데이터 조회 (비동기)
|
|
88
|
+
*/
|
|
89
|
+
async function fetchUserData() {
|
|
90
|
+
logger.info('Fetching user data from database');
|
|
91
|
+
// → 상위 컨텍스트의 user_id, trace_id 자동 포함!
|
|
92
|
+
|
|
93
|
+
return new Promise(resolve => {
|
|
94
|
+
setTimeout(() => {
|
|
95
|
+
logger.info('User data fetched successfully');
|
|
96
|
+
resolve({ name: 'John Doe', email: 'john@example.com' });
|
|
97
|
+
}, 50);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 데이터 처리 (비동기)
|
|
103
|
+
*/
|
|
104
|
+
async function processData() {
|
|
105
|
+
logger.info('Processing user data');
|
|
106
|
+
|
|
107
|
+
return new Promise(resolve => {
|
|
108
|
+
setTimeout(() => {
|
|
109
|
+
logger.info('Data processed successfully');
|
|
110
|
+
resolve();
|
|
111
|
+
}, 80);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 예제 3: 중첩 컨텍스트
|
|
117
|
+
*/
|
|
118
|
+
function example3_nestedContext() {
|
|
119
|
+
console.log('\n=== 예제 3: 중첩 컨텍스트 ===');
|
|
120
|
+
|
|
121
|
+
// 외부 컨텍스트: tenant_id
|
|
122
|
+
WorkerThreadsLogClient.runWithUserContext({ tenant_id: 'tenant_acme' }, () => {
|
|
123
|
+
logger.info('Tenant operation started');
|
|
124
|
+
// → tenant_id="tenant_acme"
|
|
125
|
+
|
|
126
|
+
// 내부 컨텍스트: user_id 추가
|
|
127
|
+
WorkerThreadsLogClient.runWithUserContext({ user_id: 'user_789' }, () => {
|
|
128
|
+
logger.info('User operation in tenant');
|
|
129
|
+
// → tenant_id="tenant_acme", user_id="user_789" 둘 다 포함!
|
|
130
|
+
|
|
131
|
+
logger.info('Fetching tenant data for user');
|
|
132
|
+
// → 같은 컨텍스트
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
logger.info('Back to tenant-only context');
|
|
136
|
+
// → tenant_id="tenant_acme" (user_id 없음)
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 예제 4: 분산 추적 (trace_id)
|
|
142
|
+
*/
|
|
143
|
+
function example4_distributedTracing() {
|
|
144
|
+
console.log('\n=== 예제 4: 분산 추적 ===');
|
|
145
|
+
|
|
146
|
+
// 요청마다 고유한 trace_id 생성 (32자, 대시 제거)
|
|
147
|
+
const traceId = uuidv4().replace(/-/g, '');
|
|
148
|
+
|
|
149
|
+
WorkerThreadsLogClient.runWithUserContext({
|
|
150
|
+
trace_id: traceId,
|
|
151
|
+
user_id: 'user_999'
|
|
152
|
+
}, () => {
|
|
153
|
+
logger.info('Request received');
|
|
154
|
+
// → trace_id, user_id 포함
|
|
155
|
+
|
|
156
|
+
// 여러 서비스 호출 (시뮬레이션)
|
|
157
|
+
callServiceA();
|
|
158
|
+
callServiceB();
|
|
159
|
+
callServiceC();
|
|
160
|
+
|
|
161
|
+
logger.info('Request completed');
|
|
162
|
+
// → 같은 trace_id로 전체 흐름 추적 가능!
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Service A 호출 시뮬레이션
|
|
168
|
+
*/
|
|
169
|
+
function callServiceA() {
|
|
170
|
+
logger.info('Calling Service A');
|
|
171
|
+
// → 상위 컨텍스트의 trace_id 자동 포함!
|
|
172
|
+
setTimeout(() => {
|
|
173
|
+
logger.info('Service A responded');
|
|
174
|
+
}, 50);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Service B 호출 시뮬레이션
|
|
179
|
+
*/
|
|
180
|
+
function callServiceB() {
|
|
181
|
+
logger.info('Calling Service B');
|
|
182
|
+
setTimeout(() => {
|
|
183
|
+
logger.info('Service B responded');
|
|
184
|
+
}, 80);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Service C 호출 시뮬레이션
|
|
189
|
+
*/
|
|
190
|
+
function callServiceC() {
|
|
191
|
+
logger.info('Calling Service C');
|
|
192
|
+
setTimeout(() => {
|
|
193
|
+
logger.info('Service C responded');
|
|
194
|
+
}, 60);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 예제 5: 에러 추적 with Context
|
|
199
|
+
*/
|
|
200
|
+
function example5_errorTracking() {
|
|
201
|
+
console.log('\n=== 예제 5: 에러 추적 ===');
|
|
202
|
+
|
|
203
|
+
WorkerThreadsLogClient.runWithUserContext({
|
|
204
|
+
user_id: 'user_error_test',
|
|
205
|
+
trace_id: 'trace_error_123'
|
|
206
|
+
}, () => {
|
|
207
|
+
logger.info('Starting risky operation');
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
// 의도적으로 에러 발생
|
|
211
|
+
throw new Error('Simulated error for testing');
|
|
212
|
+
|
|
213
|
+
} catch (err) {
|
|
214
|
+
logger.errorWithTrace('Operation failed', err);
|
|
215
|
+
// → user_id, trace_id, stack_trace, error_type 모두 포함!
|
|
216
|
+
// 어떤 사용자의 작업이 실패했는지 즉시 파악 가능
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 예제 6: Set/Clear 방식 (주의사항 있음)
|
|
223
|
+
*/
|
|
224
|
+
function example6_setCleanStyle() {
|
|
225
|
+
console.log('\n=== 예제 6: Set/Clear 방식 (주의) ===');
|
|
226
|
+
|
|
227
|
+
// ⚠️ 주의: 동기 코드에서만 안전
|
|
228
|
+
WorkerThreadsLogClient.setUserContext({
|
|
229
|
+
user_id: 'user_set_clear',
|
|
230
|
+
session_id: 'sess_123'
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
logger.info('User logged in (set/clear style)');
|
|
234
|
+
// → user_id, session_id 포함
|
|
235
|
+
|
|
236
|
+
logger.info('Browsing products');
|
|
237
|
+
// → 계속 포함됨
|
|
238
|
+
|
|
239
|
+
// 컨텍스트 초기화
|
|
240
|
+
WorkerThreadsLogClient.clearUserContext();
|
|
241
|
+
|
|
242
|
+
logger.info('User logged out');
|
|
243
|
+
// → user_id 없음
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* 모든 예제 실행
|
|
248
|
+
*/
|
|
249
|
+
async function main() {
|
|
250
|
+
console.log('='.repeat(60));
|
|
251
|
+
console.log('사용자 컨텍스트 자동 수집 예제 (JavaScript)');
|
|
252
|
+
console.log('='.repeat(60));
|
|
253
|
+
|
|
254
|
+
example1_runWithUserContext();
|
|
255
|
+
await sleep(500);
|
|
256
|
+
|
|
257
|
+
await example2_withPromises();
|
|
258
|
+
await sleep(500);
|
|
259
|
+
|
|
260
|
+
example3_nestedContext();
|
|
261
|
+
await sleep(500);
|
|
262
|
+
|
|
263
|
+
example4_distributedTracing();
|
|
264
|
+
await sleep(500);
|
|
265
|
+
|
|
266
|
+
example5_errorTracking();
|
|
267
|
+
await sleep(500);
|
|
268
|
+
|
|
269
|
+
example6_setCleanStyle();
|
|
270
|
+
await sleep(500);
|
|
271
|
+
|
|
272
|
+
// Flush 대기
|
|
273
|
+
console.log('\n=== Flushing logs... ===');
|
|
274
|
+
logger.flush();
|
|
275
|
+
await sleep(2000);
|
|
276
|
+
|
|
277
|
+
console.log('\n=== 완료! ===');
|
|
278
|
+
console.log('\nPostgreSQL에서 확인:');
|
|
279
|
+
console.log(' SELECT');
|
|
280
|
+
console.log(" created_at,");
|
|
281
|
+
console.log(" metadata->>'user_id' as user_id,");
|
|
282
|
+
console.log(" metadata->>'trace_id' as trace_id,");
|
|
283
|
+
console.log(" message");
|
|
284
|
+
console.log(" FROM logs");
|
|
285
|
+
console.log(" WHERE service = 'user-context-example-js'");
|
|
286
|
+
console.log(" ORDER BY created_at DESC");
|
|
287
|
+
console.log(" LIMIT 20;");
|
|
288
|
+
|
|
289
|
+
// 종료
|
|
290
|
+
await logger.close();
|
|
291
|
+
process.exit(0);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Sleep 헬퍼
|
|
296
|
+
*/
|
|
297
|
+
function sleep(ms) {
|
|
298
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// 실행
|
|
302
|
+
main().catch(err => {
|
|
303
|
+
console.error('Error:', err);
|
|
304
|
+
process.exit(1);
|
|
305
|
+
});
|
package/jest.config.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "log-collector-async",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "비동기 로그 수집 클라이언트 (Browser + Node.js)",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
9
|
+
"lint": "eslint src/**/*.js",
|
|
10
|
+
"build": "rollup -c",
|
|
11
|
+
"prepublishOnly": "npm test && npm run lint"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"logging",
|
|
15
|
+
"async",
|
|
16
|
+
"web-worker",
|
|
17
|
+
"worker-threads",
|
|
18
|
+
"performance",
|
|
19
|
+
"log-collector",
|
|
20
|
+
"distributed-tracing",
|
|
21
|
+
"trace-id",
|
|
22
|
+
"context-propagation"
|
|
23
|
+
],
|
|
24
|
+
"author": "Log Analysis System Team <jack1087902@gmail.com>",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/yourusername/log-analysis-system.git",
|
|
29
|
+
"directory": "clients/javascript"
|
|
30
|
+
},
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/yourusername/log-analysis-system/issues"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/yourusername/log-analysis-system/tree/main/clients/javascript#readme",
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=12.0.0"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"node-fetch": "^3.0.0"
|
|
40
|
+
},
|
|
41
|
+
"peerDependenciesMeta": {
|
|
42
|
+
"node-fetch": {
|
|
43
|
+
"optional": true
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@rollup/plugin-node-resolve": "^15.0.0",
|
|
48
|
+
"eslint": "^8.0.0",
|
|
49
|
+
"jest": "^29.0.0",
|
|
50
|
+
"rollup": "^3.0.0"
|
|
51
|
+
},
|
|
52
|
+
"browser": {
|
|
53
|
+
"worker_threads": false,
|
|
54
|
+
"path": false,
|
|
55
|
+
"zlib": false
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"uuid": "^13.0.0"
|
|
59
|
+
}
|
|
60
|
+
}
|