@kood/claude-code 0.5.8 → 0.5.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -854,7 +854,7 @@ var init = async (options) => {
|
|
|
854
854
|
|
|
855
855
|
// src/index.ts
|
|
856
856
|
var program = new Command();
|
|
857
|
-
program.name("claude-code").description("Claude Code documentation installer for projects").version("0.5.
|
|
857
|
+
program.name("claude-code").description("Claude Code documentation installer for projects").version("0.5.9");
|
|
858
858
|
program.option(
|
|
859
859
|
"-t, --template <names>",
|
|
860
860
|
"template names (comma-separated: tanstack-start,hono)"
|
package/package.json
CHANGED
|
@@ -0,0 +1,687 @@
|
|
|
1
|
+
# Consola
|
|
2
|
+
|
|
3
|
+
> **Version 3.x** | Elegant Console Logger for Node.js & Browser
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<context>
|
|
8
|
+
|
|
9
|
+
**Purpose:** 타입 안전하고 우아한 콘솔 로깅 라이브러리
|
|
10
|
+
|
|
11
|
+
**Key Features:**
|
|
12
|
+
- 우아한 콘솔 출력 (색상, 아이콘, 포맷팅)
|
|
13
|
+
- 로그 레벨 관리 (0-5, Silent, Verbose)
|
|
14
|
+
- 커스텀 리포터 지원
|
|
15
|
+
- 테스트 환경 Mock 지원
|
|
16
|
+
- 타입스크립트 완벽 지원
|
|
17
|
+
- 작은 번들 크기 (`consola/basic`, `consola/core` - 80% 크기 감소)
|
|
18
|
+
|
|
19
|
+
**Bundle Size:**
|
|
20
|
+
- `consola`: ~8KB (전체 기능)
|
|
21
|
+
- `consola/basic`: ~2KB (기본 로깅)
|
|
22
|
+
- `consola/core`: ~1KB (최소 기능)
|
|
23
|
+
|
|
24
|
+
</context>
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
<forbidden>
|
|
29
|
+
|
|
30
|
+
| 분류 | 금지 |
|
|
31
|
+
|------|------|
|
|
32
|
+
| **로깅** | ❌ `console.log()` 직접 사용 (consola 사용) |
|
|
33
|
+
| **로그 레벨** | ❌ 프로덕션 환경에서 로그 레벨 미설정 |
|
|
34
|
+
| **에러 핸들링** | ❌ 에러를 `console.error()` 직접 출력 |
|
|
35
|
+
| **테스트** | ❌ 테스트에서 실제 콘솔 출력 (Mock 사용) |
|
|
36
|
+
|
|
37
|
+
</forbidden>
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
<required>
|
|
42
|
+
|
|
43
|
+
| 분류 | 필수 |
|
|
44
|
+
|------|------|
|
|
45
|
+
| **환경 변수** | ✅ `CONSOLA_LEVEL` 설정 (프로덕션) |
|
|
46
|
+
| **로그 레벨** | ✅ 프로덕션: 3 (log) 이하, 개발: 4 (debug) 이상 |
|
|
47
|
+
| **에러 처리** | ✅ `consola.error()` 사용 |
|
|
48
|
+
| **테스트** | ✅ `mockTypes()` 또는 `pauseLogs()` 사용 |
|
|
49
|
+
| **Hono** | ✅ Middleware 및 Route Handler에서 consola 사용 |
|
|
50
|
+
|
|
51
|
+
</required>
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
<installation>
|
|
56
|
+
|
|
57
|
+
## 설치
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm install consola@^3.0.0
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Bundle 최적화
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// ✅ 전체 기능 (8KB)
|
|
67
|
+
import { consola } from 'consola'
|
|
68
|
+
|
|
69
|
+
// ✅ 기본 로깅 (2KB, 80% 크기 감소)
|
|
70
|
+
import { consola } from 'consola/basic'
|
|
71
|
+
|
|
72
|
+
// ✅ 최소 기능 (1KB)
|
|
73
|
+
import { consola } from 'consola/core'
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**권장사항:**
|
|
77
|
+
- 서버: `consola` (전체 기능) 또는 `consola/basic` (크기 최적화)
|
|
78
|
+
|
|
79
|
+
</installation>
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
<basic_usage>
|
|
84
|
+
|
|
85
|
+
## 기본 사용법
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { consola } from 'consola'
|
|
89
|
+
|
|
90
|
+
// 기본 로그 레벨 (0-5)
|
|
91
|
+
consola.fatal('Fatal error!') // 0: 치명적 에러
|
|
92
|
+
consola.error('Error occurred!') // 1: 에러
|
|
93
|
+
consola.warn('Warning message') // 2: 경고
|
|
94
|
+
consola.log('Log message') // 3: 일반 로그 (기본값)
|
|
95
|
+
consola.info('Info message') // 4: 정보
|
|
96
|
+
consola.debug('Debug message') // 5: 디버그
|
|
97
|
+
consola.trace('Trace message') // 5: 추적
|
|
98
|
+
consola.verbose('Verbose message') // +999: 상세 로그
|
|
99
|
+
consola.silent('Silent message') // -999: 출력 안 됨
|
|
100
|
+
|
|
101
|
+
// 성공/시작/준비 메시지
|
|
102
|
+
consola.success('Task completed!') // ✅ 성공
|
|
103
|
+
consola.start('Starting task...') // ⏳ 시작
|
|
104
|
+
consola.ready('Server is ready!') // ✅ 준비 완료
|
|
105
|
+
|
|
106
|
+
// 박스 출력
|
|
107
|
+
consola.box('Important Message') // 박스로 감싸서 출력
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## 로거 생성
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { createConsola } from 'consola'
|
|
114
|
+
|
|
115
|
+
// 커스텀 태그
|
|
116
|
+
const logger = createConsola({
|
|
117
|
+
level: 4, // info 레벨까지 출력
|
|
118
|
+
fancy: true, // 색상 및 아이콘 사용
|
|
119
|
+
formatOptions: {
|
|
120
|
+
date: true, // 타임스탬프 표시
|
|
121
|
+
},
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
// 태그 추가
|
|
125
|
+
const apiLogger = logger.withTag('API')
|
|
126
|
+
apiLogger.info('Request received') // [API] Request received
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## 환경 변수
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# 로그 레벨 설정
|
|
133
|
+
CONSOLA_LEVEL=3 # 0: fatal, 1: error, 2: warn, 3: log, 4: info, 5: debug/trace
|
|
134
|
+
|
|
135
|
+
# 프로덕션
|
|
136
|
+
CONSOLA_LEVEL=3 # log까지만 출력
|
|
137
|
+
|
|
138
|
+
# 개발
|
|
139
|
+
CONSOLA_LEVEL=5 # 모든 로그 출력
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
</basic_usage>
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
<log_levels>
|
|
147
|
+
|
|
148
|
+
## 로그 레벨
|
|
149
|
+
|
|
150
|
+
| 레벨 | 값 | 메서드 | 사용 |
|
|
151
|
+
|------|------|--------|------|
|
|
152
|
+
| **fatal** | 0 | `consola.fatal()` | 치명적 에러 (프로세스 종료) |
|
|
153
|
+
| **error** | 1 | `consola.error()` | 에러 발생 |
|
|
154
|
+
| **warn** | 2 | `consola.warn()` | 경고 메시지 |
|
|
155
|
+
| **log** | 3 | `consola.log()` | 일반 로그 (기본값) |
|
|
156
|
+
| **info** | 4 | `consola.info()` | 정보성 메시지 |
|
|
157
|
+
| **debug** | 5 | `consola.debug()` | 디버그 정보 |
|
|
158
|
+
| **trace** | 5 | `consola.trace()` | 추적 정보 |
|
|
159
|
+
| **verbose** | +999 | `consola.verbose()` | 상세 로그 |
|
|
160
|
+
| **silent** | -999 | `consola.silent()` | 출력 안 됨 |
|
|
161
|
+
|
|
162
|
+
### 로그 레벨 설정
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import { consola } from 'consola'
|
|
166
|
+
|
|
167
|
+
// 레벨 설정
|
|
168
|
+
consola.level = 3 // log까지만 출력
|
|
169
|
+
consola.level = 5 // 모든 로그 출력 (debug/trace 포함)
|
|
170
|
+
|
|
171
|
+
// 환경별 설정
|
|
172
|
+
if (process.env.NODE_ENV === 'production') {
|
|
173
|
+
consola.level = 2 // warn, error, fatal만 출력
|
|
174
|
+
} else {
|
|
175
|
+
consola.level = 5 // 개발 환경: 모든 로그 출력
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### 환경 변수로 제어
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
# .env.production
|
|
183
|
+
CONSOLA_LEVEL=2 # warn, error, fatal
|
|
184
|
+
|
|
185
|
+
# .env.development
|
|
186
|
+
CONSOLA_LEVEL=5 # 모든 로그
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
</log_levels>
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
<custom_reporters>
|
|
194
|
+
|
|
195
|
+
## 커스텀 리포터
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
import { consola, createConsola, type ConsolaReporter } from 'consola'
|
|
199
|
+
|
|
200
|
+
// 파일 리포터
|
|
201
|
+
const fileReporter: ConsolaReporter = {
|
|
202
|
+
log(logObj) {
|
|
203
|
+
const message = `[${logObj.date.toISOString()}] ${logObj.type}: ${logObj.args.join(' ')}`
|
|
204
|
+
fs.appendFileSync('app.log', message + '\n')
|
|
205
|
+
},
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Sentry 리포터
|
|
209
|
+
const sentryReporter: ConsolaReporter = {
|
|
210
|
+
log(logObj) {
|
|
211
|
+
if (logObj.level <= 1) { // error, fatal
|
|
212
|
+
Sentry.captureException(new Error(logObj.args.join(' ')))
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// 리포터 추가
|
|
218
|
+
const logger = createConsola({
|
|
219
|
+
reporters: [
|
|
220
|
+
consola.options.reporters[0], // 기본 콘솔 리포터
|
|
221
|
+
fileReporter,
|
|
222
|
+
sentryReporter,
|
|
223
|
+
],
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
logger.error('This will be logged to console, file, and Sentry')
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## 프로덕션 리포터 패턴
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import { createConsola } from 'consola'
|
|
233
|
+
|
|
234
|
+
// 환경별 리포터 설정
|
|
235
|
+
const reporters = [consola.options.reporters[0]] // 기본 콘솔
|
|
236
|
+
|
|
237
|
+
if (process.env.NODE_ENV === 'production') {
|
|
238
|
+
// 프로덕션: 파일 + 외부 로깅 서비스
|
|
239
|
+
reporters.push(fileReporter, sentryReporter)
|
|
240
|
+
} else {
|
|
241
|
+
// 개발: 콘솔만
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export const logger = createConsola({
|
|
245
|
+
level: process.env.NODE_ENV === 'production' ? 2 : 5,
|
|
246
|
+
reporters,
|
|
247
|
+
})
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
</custom_reporters>
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
<testing>
|
|
255
|
+
|
|
256
|
+
## 테스트 통합
|
|
257
|
+
|
|
258
|
+
### Mock 사용
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
import { consola } from 'consola'
|
|
262
|
+
|
|
263
|
+
describe('User Service', () => {
|
|
264
|
+
beforeEach(() => {
|
|
265
|
+
// 모든 로그 Mock
|
|
266
|
+
consola.mockTypes(() => vi.fn())
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
afterEach(() => {
|
|
270
|
+
// Mock 복원
|
|
271
|
+
consola.restoreAll()
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
it('should log user creation', async () => {
|
|
275
|
+
const spy = vi.fn()
|
|
276
|
+
consola.mockTypes((type) => spy)
|
|
277
|
+
|
|
278
|
+
await createUser({ name: 'Alice' })
|
|
279
|
+
|
|
280
|
+
expect(spy).toHaveBeenCalledWith('User created:', 'Alice')
|
|
281
|
+
})
|
|
282
|
+
})
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Pause/Resume
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
import { consola } from 'consola'
|
|
289
|
+
|
|
290
|
+
describe('API Tests', () => {
|
|
291
|
+
beforeAll(() => {
|
|
292
|
+
// 테스트 중 로그 일시 중지
|
|
293
|
+
consola.pauseLogs()
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
afterAll(() => {
|
|
297
|
+
// 로그 재개
|
|
298
|
+
consola.resumeLogs()
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
it('should not log during test', () => {
|
|
302
|
+
consola.log('This will not be printed')
|
|
303
|
+
})
|
|
304
|
+
})
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Vitest 통합
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
import { consola } from 'consola'
|
|
311
|
+
import { beforeEach, afterEach, vi } from 'vitest'
|
|
312
|
+
|
|
313
|
+
beforeEach(() => {
|
|
314
|
+
consola.mockTypes(() => vi.fn())
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
afterEach(() => {
|
|
318
|
+
consola.restoreAll()
|
|
319
|
+
})
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
</testing>
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
<advanced>
|
|
327
|
+
|
|
328
|
+
## 고급 기능
|
|
329
|
+
|
|
330
|
+
### Console Redirect
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
import { consola } from 'consola'
|
|
334
|
+
|
|
335
|
+
// console.log를 consola로 리다이렉트
|
|
336
|
+
consola.wrapConsole()
|
|
337
|
+
|
|
338
|
+
console.log('This uses consola now!') // consola.log()로 출력됨
|
|
339
|
+
|
|
340
|
+
// 복원
|
|
341
|
+
consola.restoreConsole()
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
**Pause/Resume, Mock 기능:** 자세한 내용은 `<testing>` 및 `<utilities>` 섹션을 참고하세요.
|
|
345
|
+
|
|
346
|
+
</advanced>
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
<utilities>
|
|
351
|
+
|
|
352
|
+
## 유틸리티
|
|
353
|
+
|
|
354
|
+
### withTag
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
import { consola } from 'consola'
|
|
358
|
+
|
|
359
|
+
// 태그 추가
|
|
360
|
+
const apiLogger = consola.withTag('API')
|
|
361
|
+
apiLogger.info('Request received') // [API] Request received
|
|
362
|
+
|
|
363
|
+
const dbLogger = consola.withTag('DB')
|
|
364
|
+
dbLogger.warn('Connection slow') // [DB] Connection slow
|
|
365
|
+
|
|
366
|
+
// 다중 태그
|
|
367
|
+
const authLogger = consola.withTag('API').withTag('Auth')
|
|
368
|
+
authLogger.info('User logged in') // [API] [Auth] User logged in
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### wrapConsole
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
import { consola } from 'consola'
|
|
375
|
+
|
|
376
|
+
// console.* 메서드를 consola로 래핑
|
|
377
|
+
consola.wrapConsole()
|
|
378
|
+
|
|
379
|
+
console.log('Uses consola') // consola.log()
|
|
380
|
+
console.error('Uses consola') // consola.error()
|
|
381
|
+
|
|
382
|
+
// 복원
|
|
383
|
+
consola.restoreConsole()
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### wrapStd
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
import { consola } from 'consola'
|
|
390
|
+
|
|
391
|
+
// process.stdout/stderr를 consola로 래핑
|
|
392
|
+
consola.wrapStd()
|
|
393
|
+
|
|
394
|
+
process.stdout.write('Uses consola\n') // consola.log()
|
|
395
|
+
process.stderr.write('Uses consola\n') // consola.error()
|
|
396
|
+
|
|
397
|
+
// 복원
|
|
398
|
+
consola.restoreStd()
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### wrapAll
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
import { consola } from 'consola'
|
|
405
|
+
|
|
406
|
+
// console + process.std* 모두 래핑
|
|
407
|
+
consola.wrapAll()
|
|
408
|
+
|
|
409
|
+
console.log('Uses consola')
|
|
410
|
+
process.stdout.write('Uses consola\n')
|
|
411
|
+
|
|
412
|
+
// 복원
|
|
413
|
+
consola.restoreAll()
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
</utilities>
|
|
417
|
+
|
|
418
|
+
---
|
|
419
|
+
|
|
420
|
+
<patterns>
|
|
421
|
+
|
|
422
|
+
## Hono 패턴
|
|
423
|
+
|
|
424
|
+
### Middleware에서 consola 사용
|
|
425
|
+
|
|
426
|
+
```typescript
|
|
427
|
+
import { createMiddleware } from 'hono/factory'
|
|
428
|
+
import { logger } from '@/lib/logger'
|
|
429
|
+
|
|
430
|
+
// ✅ 요청 로깅 미들웨어
|
|
431
|
+
export const requestLogger = createMiddleware(async (c, next) => {
|
|
432
|
+
const start = Date.now()
|
|
433
|
+
logger.info(`→ ${c.req.method} ${c.req.path}`)
|
|
434
|
+
|
|
435
|
+
await next()
|
|
436
|
+
|
|
437
|
+
const ms = Date.now() - start
|
|
438
|
+
logger.success(`← ${c.req.method} ${c.req.path} - ${ms}ms`)
|
|
439
|
+
})
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Route Handler에서 consola 사용
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
import { Hono } from 'hono'
|
|
446
|
+
import { zValidator } from '@hono/zod-validator'
|
|
447
|
+
import { logger } from '@/lib/logger'
|
|
448
|
+
import { createUserSchema } from '@/schemas/user'
|
|
449
|
+
|
|
450
|
+
const app = new Hono()
|
|
451
|
+
|
|
452
|
+
// ✅ GET 요청 로깅
|
|
453
|
+
app.get('/users', async (c) => {
|
|
454
|
+
logger.debug('Fetching users...')
|
|
455
|
+
const users = await prisma.user.findMany()
|
|
456
|
+
logger.info(`Found ${users.length} users`)
|
|
457
|
+
return c.json(users)
|
|
458
|
+
})
|
|
459
|
+
|
|
460
|
+
// ✅ POST 요청 로깅 (zValidator + consola)
|
|
461
|
+
app.post('/users', zValidator('json', createUserSchema), async (c) => {
|
|
462
|
+
const data = c.req.valid('json')
|
|
463
|
+
|
|
464
|
+
logger.info('Creating user:', data.email)
|
|
465
|
+
|
|
466
|
+
try {
|
|
467
|
+
const user = await prisma.user.create({ data })
|
|
468
|
+
logger.success('User created:', user.id)
|
|
469
|
+
return c.json(user, 201)
|
|
470
|
+
} catch (error) {
|
|
471
|
+
logger.error('Failed to create user:', error)
|
|
472
|
+
throw error
|
|
473
|
+
}
|
|
474
|
+
})
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### Error Handler에서 consola 사용
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
import { Hono } from 'hono'
|
|
481
|
+
import { HTTPException } from 'hono/http-exception'
|
|
482
|
+
import { logger } from '@/lib/logger'
|
|
483
|
+
|
|
484
|
+
const app = new Hono()
|
|
485
|
+
|
|
486
|
+
// ✅ 에러 핸들러
|
|
487
|
+
app.onError((err, c) => {
|
|
488
|
+
// HTTPException 처리
|
|
489
|
+
if (err instanceof HTTPException) {
|
|
490
|
+
logger.warn('HTTP Exception:', err.status, err.message)
|
|
491
|
+
return c.json({ error: err.message }, err.status)
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// 일반 에러
|
|
495
|
+
logger.error('Unhandled error:', err)
|
|
496
|
+
return c.json({ error: 'Internal Server Error' }, 500)
|
|
497
|
+
})
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### 환경별 로거 설정
|
|
501
|
+
|
|
502
|
+
```typescript
|
|
503
|
+
// lib/logger.ts
|
|
504
|
+
import { createConsola } from 'consola'
|
|
505
|
+
|
|
506
|
+
export const logger = createConsola({
|
|
507
|
+
level: process.env.NODE_ENV === 'production' ? 2 : 5, // prod: warn, dev: trace
|
|
508
|
+
fancy: process.env.NODE_ENV !== 'production', // 개발 환경에서만 색상/아이콘
|
|
509
|
+
formatOptions: {
|
|
510
|
+
date: true, // 타임스탬프
|
|
511
|
+
},
|
|
512
|
+
})
|
|
513
|
+
|
|
514
|
+
// 사용
|
|
515
|
+
import { logger } from '@/lib/logger'
|
|
516
|
+
|
|
517
|
+
logger.info('Application started')
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### Sentry 통합 (서버용)
|
|
521
|
+
|
|
522
|
+
```typescript
|
|
523
|
+
import { createConsola, type ConsolaReporter } from 'consola'
|
|
524
|
+
import * as Sentry from '@sentry/node'
|
|
525
|
+
|
|
526
|
+
// Sentry 리포터
|
|
527
|
+
const sentryReporter: ConsolaReporter = {
|
|
528
|
+
log(logObj) {
|
|
529
|
+
if (logObj.level <= 1) { // error, fatal
|
|
530
|
+
Sentry.captureException(new Error(logObj.args.join(' ')), {
|
|
531
|
+
level: logObj.level === 0 ? 'fatal' : 'error',
|
|
532
|
+
tags: {
|
|
533
|
+
type: logObj.type,
|
|
534
|
+
},
|
|
535
|
+
})
|
|
536
|
+
}
|
|
537
|
+
},
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
export const logger = createConsola({
|
|
541
|
+
level: 2, // warn, error, fatal
|
|
542
|
+
reporters: [
|
|
543
|
+
consola.options.reporters[0], // 콘솔
|
|
544
|
+
sentryReporter, // Sentry
|
|
545
|
+
],
|
|
546
|
+
})
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
### 라우트별 로거 생성
|
|
550
|
+
|
|
551
|
+
```typescript
|
|
552
|
+
import { Hono } from 'hono'
|
|
553
|
+
import { logger } from '@/lib/logger'
|
|
554
|
+
|
|
555
|
+
const app = new Hono()
|
|
556
|
+
|
|
557
|
+
// ✅ 라우트별 태그 로거
|
|
558
|
+
app.get('/api/posts', async (c) => {
|
|
559
|
+
const postLogger = logger.withTag('Post')
|
|
560
|
+
|
|
561
|
+
postLogger.debug('Fetching posts...')
|
|
562
|
+
const posts = await prisma.post.findMany()
|
|
563
|
+
postLogger.info(`Found ${posts.length} posts`)
|
|
564
|
+
|
|
565
|
+
return c.json(posts)
|
|
566
|
+
})
|
|
567
|
+
|
|
568
|
+
app.get('/api/users', async (c) => {
|
|
569
|
+
const userLogger = logger.withTag('User')
|
|
570
|
+
|
|
571
|
+
userLogger.debug('Fetching users...')
|
|
572
|
+
const users = await prisma.user.findMany()
|
|
573
|
+
userLogger.info(`Found ${users.length} users`)
|
|
574
|
+
|
|
575
|
+
return c.json(users)
|
|
576
|
+
})
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
</patterns>
|
|
580
|
+
|
|
581
|
+
---
|
|
582
|
+
|
|
583
|
+
<dos_and_donts>
|
|
584
|
+
|
|
585
|
+
## Do's & Don'ts
|
|
586
|
+
|
|
587
|
+
### ✅ Do
|
|
588
|
+
|
|
589
|
+
| 상황 | 방법 |
|
|
590
|
+
|------|------|
|
|
591
|
+
| **로깅** | `consola.log()` 사용 |
|
|
592
|
+
| **에러** | `consola.error()` 사용 |
|
|
593
|
+
| **성공** | `consola.success()` 사용 |
|
|
594
|
+
| **태그** | `.withTag()` 사용 |
|
|
595
|
+
| **환경별** | `CONSOLA_LEVEL` 환경 변수 설정 |
|
|
596
|
+
| **테스트** | `mockTypes()` 또는 `pauseLogs()` 사용 |
|
|
597
|
+
| **프로덕션** | 로그 레벨 2 이하 (warn, error, fatal) |
|
|
598
|
+
| **개발** | 로그 레벨 5 (debug/trace 포함) |
|
|
599
|
+
|
|
600
|
+
### ❌ Don't
|
|
601
|
+
|
|
602
|
+
| 상황 | 이유 |
|
|
603
|
+
|------|------|
|
|
604
|
+
| **console.log** | ❌ 직접 사용 금지 → `consola.log()` 사용 |
|
|
605
|
+
| **프로덕션 로그** | ❌ 로그 레벨 미설정 → 불필요한 로그 출력 |
|
|
606
|
+
| **테스트 출력** | ❌ 실제 콘솔 출력 → Mock 사용 |
|
|
607
|
+
| **에러 무시** | ❌ `console.error()` 직접 사용 → `consola.error()` + 리포터 |
|
|
608
|
+
|
|
609
|
+
### 환경별 패턴
|
|
610
|
+
|
|
611
|
+
```typescript
|
|
612
|
+
// ❌ 로그 레벨 미설정
|
|
613
|
+
import { consola } from 'consola'
|
|
614
|
+
consola.debug('This will be printed in production!') // 불필요한 로그
|
|
615
|
+
|
|
616
|
+
// ✅ 환경별 로그 레벨 설정
|
|
617
|
+
import { createConsola } from 'consola'
|
|
618
|
+
|
|
619
|
+
export const logger = createConsola({
|
|
620
|
+
level: process.env.NODE_ENV === 'production' ? 2 : 5,
|
|
621
|
+
})
|
|
622
|
+
|
|
623
|
+
logger.debug('This is only printed in development')
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
### 테스트 패턴
|
|
627
|
+
|
|
628
|
+
```typescript
|
|
629
|
+
// ❌ 테스트 중 콘솔 출력
|
|
630
|
+
describe('Test', () => {
|
|
631
|
+
it('should work', () => {
|
|
632
|
+
consola.log('This will clutter test output') // 출력됨
|
|
633
|
+
})
|
|
634
|
+
})
|
|
635
|
+
|
|
636
|
+
// ✅ Mock 사용
|
|
637
|
+
describe('Test', () => {
|
|
638
|
+
beforeEach(() => consola.mockTypes(() => vi.fn()))
|
|
639
|
+
afterEach(() => consola.restoreAll())
|
|
640
|
+
|
|
641
|
+
it('should work', () => {
|
|
642
|
+
consola.log('This will not clutter test output') // Mock됨
|
|
643
|
+
})
|
|
644
|
+
})
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
</dos_and_donts>
|
|
648
|
+
|
|
649
|
+
---
|
|
650
|
+
|
|
651
|
+
<quick_reference>
|
|
652
|
+
|
|
653
|
+
## Quick Reference
|
|
654
|
+
|
|
655
|
+
```typescript
|
|
656
|
+
// 설치
|
|
657
|
+
import { consola } from 'consola'
|
|
658
|
+
import { consola } from 'consola/basic' // 80% 크기 감소
|
|
659
|
+
|
|
660
|
+
// 기본 로깅
|
|
661
|
+
consola.log('Log')
|
|
662
|
+
consola.info('Info')
|
|
663
|
+
consola.warn('Warning')
|
|
664
|
+
consola.error('Error')
|
|
665
|
+
consola.success('Success')
|
|
666
|
+
|
|
667
|
+
// 태그
|
|
668
|
+
const logger = consola.withTag('API')
|
|
669
|
+
logger.info('Request') // [API] Request
|
|
670
|
+
|
|
671
|
+
// 로그 레벨
|
|
672
|
+
consola.level = 3 // log까지만 출력
|
|
673
|
+
CONSOLA_LEVEL=3 # 환경 변수
|
|
674
|
+
|
|
675
|
+
// 테스트
|
|
676
|
+
consola.mockTypes(() => vi.fn())
|
|
677
|
+
consola.pauseLogs()
|
|
678
|
+
consola.resumeLogs()
|
|
679
|
+
consola.restoreAll()
|
|
680
|
+
|
|
681
|
+
// 커스텀 리포터
|
|
682
|
+
const logger = createConsola({
|
|
683
|
+
reporters: [consola.options.reporters[0], customReporter],
|
|
684
|
+
})
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
</quick_reference>
|