@champpaba/claude-agent-kit 3.0.2 → 3.2.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/.claude/CHANGELOG.md +707 -0
- package/.claude/CLAUDE.md +128 -613
- package/.claude/agents/_shared/pre-work-checklist.md +108 -7
- package/.claude/commands/cdev.md +36 -0
- package/.claude/commands/csetup.md +292 -1791
- package/.claude/commands/cview.md +364 -364
- package/.claude/contexts/design/accessibility.md +611 -611
- package/.claude/contexts/design/layout.md +400 -400
- package/.claude/contexts/design/responsive.md +551 -551
- package/.claude/contexts/design/shadows.md +522 -522
- package/.claude/contexts/design/typography.md +465 -465
- package/.claude/contexts/domain/README.md +164 -164
- package/.claude/contexts/patterns/agent-coordination.md +388 -388
- package/.claude/contexts/patterns/development-principles.md +513 -513
- package/.claude/contexts/patterns/error-handling.md +478 -478
- package/.claude/contexts/patterns/logging.md +424 -424
- package/.claude/contexts/patterns/tdd-classification.md +516 -516
- package/.claude/contexts/patterns/testing.md +413 -413
- package/.claude/lib/README.md +3 -3
- package/.claude/lib/detailed-guides/taskmaster-analysis.md +1 -1
- package/.claude/lib/task-analyzer.md +144 -0
- package/.claude/lib/tdd-workflow.md +2 -1
- package/.claude/lib/validation-gates.md +484 -484
- package/.claude/settings.local.json +42 -42
- package/.claude/templates/PROJECT_STATUS.template.yml +16 -41
- package/.claude/templates/context-template.md +45 -45
- package/.claude/templates/flags-template.json +42 -42
- package/.claude/templates/phases-sections/accessibility-test.md +17 -17
- package/.claude/templates/phases-sections/api-design.md +37 -37
- package/.claude/templates/phases-sections/backend-tests.md +16 -16
- package/.claude/templates/phases-sections/backend.md +37 -37
- package/.claude/templates/phases-sections/business-logic-validation.md +16 -16
- package/.claude/templates/phases-sections/component-tests.md +17 -17
- package/.claude/templates/phases-sections/contract-backend.md +16 -16
- package/.claude/templates/phases-sections/contract-frontend.md +16 -16
- package/.claude/templates/phases-sections/database.md +35 -35
- package/.claude/templates/phases-sections/e2e-tests.md +16 -16
- package/.claude/templates/phases-sections/fix-implementation.md +17 -17
- package/.claude/templates/phases-sections/frontend-integration.md +18 -18
- package/.claude/templates/phases-sections/manual-flow-test.md +15 -15
- package/.claude/templates/phases-sections/manual-ux-test.md +16 -16
- package/.claude/templates/phases-sections/refactor-implementation.md +17 -17
- package/.claude/templates/phases-sections/refactor.md +16 -16
- package/.claude/templates/phases-sections/regression-tests.md +15 -15
- package/.claude/templates/phases-sections/responsive-test.md +16 -16
- package/.claude/templates/phases-sections/script-implementation.md +43 -43
- package/.claude/templates/phases-sections/test-coverage.md +16 -16
- package/.claude/templates/phases-sections/user-approval.md +14 -14
- package/LICENSE +21 -21
- package/package.json +1 -1
- package/.claude/lib/tdd-classifier.md +0 -345
|
@@ -1,424 +1,424 @@
|
|
|
1
|
-
# Logging & Observability Patterns
|
|
2
|
-
|
|
3
|
-
**Critical Rule:** Every API call, database operation, external service interaction, user action, and error MUST be logged with proper context.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## When to Log
|
|
8
|
-
|
|
9
|
-
| Event Type | Log Level | Required Fields |
|
|
10
|
-
|------------|-----------|-----------------|
|
|
11
|
-
| **API Route Entry/Exit** | INFO | `route`, `method`, `requestId`, `duration` |
|
|
12
|
-
| **Database Operations** | INFO | `operation`, `table`, `duration`, `rowCount` |
|
|
13
|
-
| **External API Calls** | INFO | `service`, `endpoint`, `duration`, `statusCode` |
|
|
14
|
-
| **User Actions** | INFO | `action`, `userId/sessionId`, `timestamp` |
|
|
15
|
-
| **Errors & Exceptions** | ERROR | `error`, `stack`, `context`, `userId` |
|
|
16
|
-
| **Performance Metrics** | INFO | `operation`, `duration`, `memoryUsage` |
|
|
17
|
-
| **State Changes** | DEBUG | `store`, `action`, `prevState`, `nextState` |
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Implementation Patterns
|
|
22
|
-
|
|
23
|
-
### API Route Pattern (Next.js)
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
// app/api/items/route.ts
|
|
27
|
-
import { NextRequest, NextResponse } from 'next/server'
|
|
28
|
-
import { logger } from '@/lib/logger'
|
|
29
|
-
|
|
30
|
-
export async function POST(request: NextRequest) {
|
|
31
|
-
const startTime = Date.now()
|
|
32
|
-
const requestId = crypto.randomUUID()
|
|
33
|
-
|
|
34
|
-
logger.info('api_route_entry', {
|
|
35
|
-
requestId,
|
|
36
|
-
route: '/api/items',
|
|
37
|
-
method: 'POST',
|
|
38
|
-
timestamp: new Date().toISOString()
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
const body = await request.json()
|
|
43
|
-
const result = await createItem(body)
|
|
44
|
-
|
|
45
|
-
logger.info('api_route_success', {
|
|
46
|
-
requestId,
|
|
47
|
-
route: '/api/items',
|
|
48
|
-
duration: Date.now() - startTime,
|
|
49
|
-
resultSize: JSON.stringify(result).length
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
return NextResponse.json(result)
|
|
53
|
-
} catch (error) {
|
|
54
|
-
logger.error('api_route_error', {
|
|
55
|
-
requestId,
|
|
56
|
-
route: '/api/items',
|
|
57
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
58
|
-
stack: error instanceof Error ? error.stack : undefined,
|
|
59
|
-
duration: Date.now() - startTime
|
|
60
|
-
})
|
|
61
|
-
throw error
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
### Database Operation Pattern (Prisma)
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
// lib/db-operations.ts
|
|
72
|
-
import { prisma } from '@/lib/db'
|
|
73
|
-
import { logger } from '@/lib/logger'
|
|
74
|
-
|
|
75
|
-
export async function createUser(data: UserData) {
|
|
76
|
-
const startTime = Date.now()
|
|
77
|
-
|
|
78
|
-
logger.info('db_operation_start', {
|
|
79
|
-
operation: 'createUser',
|
|
80
|
-
table: 'User'
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
const user = await prisma.user.create({ data })
|
|
85
|
-
|
|
86
|
-
logger.info('db_operation_success', {
|
|
87
|
-
operation: 'createUser',
|
|
88
|
-
table: 'User',
|
|
89
|
-
userId: user.id,
|
|
90
|
-
duration: Date.now() - startTime
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
return user
|
|
94
|
-
} catch (error) {
|
|
95
|
-
logger.error('db_operation_error', {
|
|
96
|
-
operation: 'createUser',
|
|
97
|
-
table: 'User',
|
|
98
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
99
|
-
duration: Date.now() - startTime
|
|
100
|
-
})
|
|
101
|
-
throw error
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
### Database Pattern (SQLAlchemy - Python)
|
|
109
|
-
|
|
110
|
-
```python
|
|
111
|
-
# app/db/user.py
|
|
112
|
-
from sqlalchemy.ext.asyncio import AsyncSession
|
|
113
|
-
from app.models.user import User
|
|
114
|
-
import logging
|
|
115
|
-
import time
|
|
116
|
-
|
|
117
|
-
logger = logging.getLogger(__name__)
|
|
118
|
-
|
|
119
|
-
async def create_user(db: AsyncSession, email: str, name: str) -> User:
|
|
120
|
-
start_time = time.time()
|
|
121
|
-
|
|
122
|
-
logger.info({"event": "db_operation_start", "operation": "create_user", "table": "users"})
|
|
123
|
-
|
|
124
|
-
try:
|
|
125
|
-
user = User(email=email, name=name)
|
|
126
|
-
db.add(user)
|
|
127
|
-
await db.commit()
|
|
128
|
-
await db.refresh(user)
|
|
129
|
-
|
|
130
|
-
logger.info({
|
|
131
|
-
"event": "db_operation_success",
|
|
132
|
-
"operation": "create_user",
|
|
133
|
-
"table": "users",
|
|
134
|
-
"user_id": user.id,
|
|
135
|
-
"duration": time.time() - start_time
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
return user
|
|
139
|
-
except Exception as e:
|
|
140
|
-
logger.error({
|
|
141
|
-
"event": "db_operation_error",
|
|
142
|
-
"operation": "create_user",
|
|
143
|
-
"table": "users",
|
|
144
|
-
"error": str(e),
|
|
145
|
-
"duration": time.time() - start_time
|
|
146
|
-
})
|
|
147
|
-
raise
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
### External API Pattern
|
|
153
|
-
|
|
154
|
-
```typescript
|
|
155
|
-
// lib/external-api.ts
|
|
156
|
-
import { logger } from '@/lib/logger'
|
|
157
|
-
|
|
158
|
-
export async function fetchExternalData(userId: string) {
|
|
159
|
-
const startTime = Date.now()
|
|
160
|
-
|
|
161
|
-
logger.info('external_api_start', {
|
|
162
|
-
service: 'external-api',
|
|
163
|
-
userId,
|
|
164
|
-
endpoint: '/api/data'
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
try {
|
|
168
|
-
const response = await fetch('https://api.example.com/data', {
|
|
169
|
-
headers: { Authorization: `Bearer ${process.env.API_KEY}` }
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
const result = await response.json()
|
|
173
|
-
|
|
174
|
-
logger.info('external_api_success', {
|
|
175
|
-
service: 'external-api',
|
|
176
|
-
userId,
|
|
177
|
-
statusCode: response.status,
|
|
178
|
-
duration: Date.now() - startTime
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
return result
|
|
182
|
-
} catch (error) {
|
|
183
|
-
logger.error('external_api_error', {
|
|
184
|
-
service: 'external-api',
|
|
185
|
-
userId,
|
|
186
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
187
|
-
duration: Date.now() - startTime
|
|
188
|
-
})
|
|
189
|
-
throw error
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
---
|
|
195
|
-
|
|
196
|
-
### State Management Logging (Zustand)
|
|
197
|
-
|
|
198
|
-
```typescript
|
|
199
|
-
// lib/stores/app-store.ts
|
|
200
|
-
import { create } from 'zustand'
|
|
201
|
-
import { devtools, persist } from 'zustand/middleware'
|
|
202
|
-
import { logger } from '@/lib/logger'
|
|
203
|
-
|
|
204
|
-
interface AppState {
|
|
205
|
-
userId: string | null
|
|
206
|
-
currentView: string
|
|
207
|
-
setUserId: (id: string) => void
|
|
208
|
-
setCurrentView: (view: string) => void
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export const useAppStore = create<AppState>()(
|
|
212
|
-
devtools(
|
|
213
|
-
persist(
|
|
214
|
-
(set, get) => ({
|
|
215
|
-
userId: null,
|
|
216
|
-
currentView: 'home',
|
|
217
|
-
|
|
218
|
-
setUserId: (id) => {
|
|
219
|
-
logger.info('store_action', {
|
|
220
|
-
store: 'app',
|
|
221
|
-
action: 'setUserId',
|
|
222
|
-
userId: id,
|
|
223
|
-
prevState: get().userId
|
|
224
|
-
})
|
|
225
|
-
set({ userId: id })
|
|
226
|
-
},
|
|
227
|
-
|
|
228
|
-
setCurrentView: (view) => {
|
|
229
|
-
logger.info('store_action', {
|
|
230
|
-
store: 'app',
|
|
231
|
-
action: 'setCurrentView',
|
|
232
|
-
userId: get().userId,
|
|
233
|
-
prevView: get().currentView,
|
|
234
|
-
nextView: view
|
|
235
|
-
})
|
|
236
|
-
set({ currentView: view })
|
|
237
|
-
}
|
|
238
|
-
}),
|
|
239
|
-
{ name: 'app-storage' }
|
|
240
|
-
)
|
|
241
|
-
)
|
|
242
|
-
)
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
---
|
|
246
|
-
|
|
247
|
-
### TanStack Query Logging Pattern
|
|
248
|
-
|
|
249
|
-
```typescript
|
|
250
|
-
// hooks/use-create-item.ts
|
|
251
|
-
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
|
252
|
-
import { logger } from '@/lib/logger'
|
|
253
|
-
|
|
254
|
-
export function useCreateItem(userId: string) {
|
|
255
|
-
const queryClient = useQueryClient()
|
|
256
|
-
|
|
257
|
-
return useMutation({
|
|
258
|
-
mutationFn: async (data: ItemData) => {
|
|
259
|
-
logger.info('mutation_start', {
|
|
260
|
-
mutation: 'createItem',
|
|
261
|
-
userId,
|
|
262
|
-
dataSize: JSON.stringify(data).length
|
|
263
|
-
})
|
|
264
|
-
|
|
265
|
-
const response = await fetch('/api/items', {
|
|
266
|
-
method: 'POST',
|
|
267
|
-
headers: { 'Content-Type': 'application/json' },
|
|
268
|
-
body: JSON.stringify(data)
|
|
269
|
-
})
|
|
270
|
-
|
|
271
|
-
if (!response.ok) throw new Error('Creation failed')
|
|
272
|
-
return response.json()
|
|
273
|
-
},
|
|
274
|
-
|
|
275
|
-
onSuccess: (result) => {
|
|
276
|
-
logger.info('mutation_success', {
|
|
277
|
-
mutation: 'createItem',
|
|
278
|
-
userId,
|
|
279
|
-
resultId: result.id
|
|
280
|
-
})
|
|
281
|
-
queryClient.invalidateQueries({ queryKey: ['items', userId] })
|
|
282
|
-
},
|
|
283
|
-
|
|
284
|
-
onError: (error) => {
|
|
285
|
-
logger.error('mutation_error', {
|
|
286
|
-
mutation: 'createItem',
|
|
287
|
-
userId,
|
|
288
|
-
error: error instanceof Error ? error.message : 'Unknown error'
|
|
289
|
-
})
|
|
290
|
-
}
|
|
291
|
-
})
|
|
292
|
-
}
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
---
|
|
296
|
-
|
|
297
|
-
## Logger Implementation
|
|
298
|
-
|
|
299
|
-
### TypeScript/JavaScript
|
|
300
|
-
|
|
301
|
-
```typescript
|
|
302
|
-
// lib/logger.ts
|
|
303
|
-
type LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR'
|
|
304
|
-
|
|
305
|
-
interface LogContext {
|
|
306
|
-
[key: string]: unknown
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
class Logger {
|
|
310
|
-
private log(level: LogLevel, event: string, context?: LogContext) {
|
|
311
|
-
const timestamp = new Date().toISOString()
|
|
312
|
-
const logEntry = {
|
|
313
|
-
timestamp,
|
|
314
|
-
level,
|
|
315
|
-
event,
|
|
316
|
-
...context
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// Development: Pretty console output
|
|
320
|
-
if (process.env.NODE_ENV === 'development') {
|
|
321
|
-
console[level.toLowerCase() as 'log'](`[${level}] ${event}`, context)
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Production: Structured JSON logs
|
|
325
|
-
if (process.env.NODE_ENV === 'production') {
|
|
326
|
-
console[level.toLowerCase() as 'log'](JSON.stringify(logEntry))
|
|
327
|
-
// Optional: Send to monitoring service (Sentry, LogRocket, etc.)
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
debug(event: string, context?: LogContext) {
|
|
332
|
-
this.log('DEBUG', event, context)
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
info(event: string, context?: LogContext) {
|
|
336
|
-
this.log('INFO', event, context)
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
warn(event: string, context?: LogContext) {
|
|
340
|
-
this.log('WARN', event, context)
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
error(event: string, context?: LogContext) {
|
|
344
|
-
this.log('ERROR', event, context)
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
export const logger = new Logger()
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
### Python
|
|
352
|
-
|
|
353
|
-
```python
|
|
354
|
-
# lib/logger.py
|
|
355
|
-
import logging
|
|
356
|
-
import json
|
|
357
|
-
from datetime import datetime
|
|
358
|
-
from typing import Any, Dict
|
|
359
|
-
|
|
360
|
-
class StructuredLogger:
|
|
361
|
-
def __init__(self, name: str):
|
|
362
|
-
self.logger = logging.getLogger(name)
|
|
363
|
-
self.logger.setLevel(logging.DEBUG)
|
|
364
|
-
|
|
365
|
-
def _log(self, level: str, event: str, context: Dict[str, Any] = None):
|
|
366
|
-
log_entry = {
|
|
367
|
-
"timestamp": datetime.utcnow().isoformat(),
|
|
368
|
-
"level": level,
|
|
369
|
-
"event": event,
|
|
370
|
-
**(context or {})
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
# Development: Human-readable
|
|
374
|
-
if os.getenv("ENV") == "development":
|
|
375
|
-
self.logger.log(getattr(logging, level), f"{event}: {context}")
|
|
376
|
-
# Production: JSON
|
|
377
|
-
else:
|
|
378
|
-
self.logger.log(getattr(logging, level), json.dumps(log_entry))
|
|
379
|
-
|
|
380
|
-
def debug(self, event: str, context: Dict[str, Any] = None):
|
|
381
|
-
self._log("DEBUG", event, context)
|
|
382
|
-
|
|
383
|
-
def info(self, event: str, context: Dict[str, Any] = None):
|
|
384
|
-
self._log("INFO", event, context)
|
|
385
|
-
|
|
386
|
-
def warning(self, event: str, context: Dict[str, Any] = None):
|
|
387
|
-
self._log("WARNING", event, context)
|
|
388
|
-
|
|
389
|
-
def error(self, event: str, context: Dict[str, Any] = None):
|
|
390
|
-
self._log("ERROR", event, context)
|
|
391
|
-
|
|
392
|
-
logger = StructuredLogger(__name__)
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
---
|
|
396
|
-
|
|
397
|
-
## Quick Reference
|
|
398
|
-
|
|
399
|
-
```typescript
|
|
400
|
-
// API Route
|
|
401
|
-
logger.info('api_route_entry', { route, method, requestId })
|
|
402
|
-
logger.info('api_route_success', { route, duration, requestId })
|
|
403
|
-
logger.error('api_route_error', { route, error, stack, requestId })
|
|
404
|
-
|
|
405
|
-
// Database
|
|
406
|
-
logger.info('db_operation_start', { operation, table })
|
|
407
|
-
logger.info('db_operation_success', { operation, table, duration })
|
|
408
|
-
logger.error('db_operation_error', { operation, table, error, duration })
|
|
409
|
-
|
|
410
|
-
// External API
|
|
411
|
-
logger.info('external_api_start', { service, endpoint })
|
|
412
|
-
logger.info('external_api_success', { service, statusCode, duration })
|
|
413
|
-
logger.error('external_api_error', { service, error, duration })
|
|
414
|
-
|
|
415
|
-
// State Management
|
|
416
|
-
logger.info('store_action', { store, action, context })
|
|
417
|
-
|
|
418
|
-
// Performance
|
|
419
|
-
logger.info('performance_metric', { operation, duration, memoryUsage })
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
---
|
|
423
|
-
|
|
424
|
-
**💡 Remember:** If an action happens without logs, it's invisible in production!
|
|
1
|
+
# Logging & Observability Patterns
|
|
2
|
+
|
|
3
|
+
**Critical Rule:** Every API call, database operation, external service interaction, user action, and error MUST be logged with proper context.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## When to Log
|
|
8
|
+
|
|
9
|
+
| Event Type | Log Level | Required Fields |
|
|
10
|
+
|------------|-----------|-----------------|
|
|
11
|
+
| **API Route Entry/Exit** | INFO | `route`, `method`, `requestId`, `duration` |
|
|
12
|
+
| **Database Operations** | INFO | `operation`, `table`, `duration`, `rowCount` |
|
|
13
|
+
| **External API Calls** | INFO | `service`, `endpoint`, `duration`, `statusCode` |
|
|
14
|
+
| **User Actions** | INFO | `action`, `userId/sessionId`, `timestamp` |
|
|
15
|
+
| **Errors & Exceptions** | ERROR | `error`, `stack`, `context`, `userId` |
|
|
16
|
+
| **Performance Metrics** | INFO | `operation`, `duration`, `memoryUsage` |
|
|
17
|
+
| **State Changes** | DEBUG | `store`, `action`, `prevState`, `nextState` |
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Implementation Patterns
|
|
22
|
+
|
|
23
|
+
### API Route Pattern (Next.js)
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// app/api/items/route.ts
|
|
27
|
+
import { NextRequest, NextResponse } from 'next/server'
|
|
28
|
+
import { logger } from '@/lib/logger'
|
|
29
|
+
|
|
30
|
+
export async function POST(request: NextRequest) {
|
|
31
|
+
const startTime = Date.now()
|
|
32
|
+
const requestId = crypto.randomUUID()
|
|
33
|
+
|
|
34
|
+
logger.info('api_route_entry', {
|
|
35
|
+
requestId,
|
|
36
|
+
route: '/api/items',
|
|
37
|
+
method: 'POST',
|
|
38
|
+
timestamp: new Date().toISOString()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const body = await request.json()
|
|
43
|
+
const result = await createItem(body)
|
|
44
|
+
|
|
45
|
+
logger.info('api_route_success', {
|
|
46
|
+
requestId,
|
|
47
|
+
route: '/api/items',
|
|
48
|
+
duration: Date.now() - startTime,
|
|
49
|
+
resultSize: JSON.stringify(result).length
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
return NextResponse.json(result)
|
|
53
|
+
} catch (error) {
|
|
54
|
+
logger.error('api_route_error', {
|
|
55
|
+
requestId,
|
|
56
|
+
route: '/api/items',
|
|
57
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
58
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
59
|
+
duration: Date.now() - startTime
|
|
60
|
+
})
|
|
61
|
+
throw error
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
### Database Operation Pattern (Prisma)
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
// lib/db-operations.ts
|
|
72
|
+
import { prisma } from '@/lib/db'
|
|
73
|
+
import { logger } from '@/lib/logger'
|
|
74
|
+
|
|
75
|
+
export async function createUser(data: UserData) {
|
|
76
|
+
const startTime = Date.now()
|
|
77
|
+
|
|
78
|
+
logger.info('db_operation_start', {
|
|
79
|
+
operation: 'createUser',
|
|
80
|
+
table: 'User'
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const user = await prisma.user.create({ data })
|
|
85
|
+
|
|
86
|
+
logger.info('db_operation_success', {
|
|
87
|
+
operation: 'createUser',
|
|
88
|
+
table: 'User',
|
|
89
|
+
userId: user.id,
|
|
90
|
+
duration: Date.now() - startTime
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
return user
|
|
94
|
+
} catch (error) {
|
|
95
|
+
logger.error('db_operation_error', {
|
|
96
|
+
operation: 'createUser',
|
|
97
|
+
table: 'User',
|
|
98
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
99
|
+
duration: Date.now() - startTime
|
|
100
|
+
})
|
|
101
|
+
throw error
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
### Database Pattern (SQLAlchemy - Python)
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
# app/db/user.py
|
|
112
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
113
|
+
from app.models.user import User
|
|
114
|
+
import logging
|
|
115
|
+
import time
|
|
116
|
+
|
|
117
|
+
logger = logging.getLogger(__name__)
|
|
118
|
+
|
|
119
|
+
async def create_user(db: AsyncSession, email: str, name: str) -> User:
|
|
120
|
+
start_time = time.time()
|
|
121
|
+
|
|
122
|
+
logger.info({"event": "db_operation_start", "operation": "create_user", "table": "users"})
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
user = User(email=email, name=name)
|
|
126
|
+
db.add(user)
|
|
127
|
+
await db.commit()
|
|
128
|
+
await db.refresh(user)
|
|
129
|
+
|
|
130
|
+
logger.info({
|
|
131
|
+
"event": "db_operation_success",
|
|
132
|
+
"operation": "create_user",
|
|
133
|
+
"table": "users",
|
|
134
|
+
"user_id": user.id,
|
|
135
|
+
"duration": time.time() - start_time
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
return user
|
|
139
|
+
except Exception as e:
|
|
140
|
+
logger.error({
|
|
141
|
+
"event": "db_operation_error",
|
|
142
|
+
"operation": "create_user",
|
|
143
|
+
"table": "users",
|
|
144
|
+
"error": str(e),
|
|
145
|
+
"duration": time.time() - start_time
|
|
146
|
+
})
|
|
147
|
+
raise
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
### External API Pattern
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
// lib/external-api.ts
|
|
156
|
+
import { logger } from '@/lib/logger'
|
|
157
|
+
|
|
158
|
+
export async function fetchExternalData(userId: string) {
|
|
159
|
+
const startTime = Date.now()
|
|
160
|
+
|
|
161
|
+
logger.info('external_api_start', {
|
|
162
|
+
service: 'external-api',
|
|
163
|
+
userId,
|
|
164
|
+
endpoint: '/api/data'
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
const response = await fetch('https://api.example.com/data', {
|
|
169
|
+
headers: { Authorization: `Bearer ${process.env.API_KEY}` }
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
const result = await response.json()
|
|
173
|
+
|
|
174
|
+
logger.info('external_api_success', {
|
|
175
|
+
service: 'external-api',
|
|
176
|
+
userId,
|
|
177
|
+
statusCode: response.status,
|
|
178
|
+
duration: Date.now() - startTime
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
return result
|
|
182
|
+
} catch (error) {
|
|
183
|
+
logger.error('external_api_error', {
|
|
184
|
+
service: 'external-api',
|
|
185
|
+
userId,
|
|
186
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
187
|
+
duration: Date.now() - startTime
|
|
188
|
+
})
|
|
189
|
+
throw error
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
### State Management Logging (Zustand)
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
// lib/stores/app-store.ts
|
|
200
|
+
import { create } from 'zustand'
|
|
201
|
+
import { devtools, persist } from 'zustand/middleware'
|
|
202
|
+
import { logger } from '@/lib/logger'
|
|
203
|
+
|
|
204
|
+
interface AppState {
|
|
205
|
+
userId: string | null
|
|
206
|
+
currentView: string
|
|
207
|
+
setUserId: (id: string) => void
|
|
208
|
+
setCurrentView: (view: string) => void
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export const useAppStore = create<AppState>()(
|
|
212
|
+
devtools(
|
|
213
|
+
persist(
|
|
214
|
+
(set, get) => ({
|
|
215
|
+
userId: null,
|
|
216
|
+
currentView: 'home',
|
|
217
|
+
|
|
218
|
+
setUserId: (id) => {
|
|
219
|
+
logger.info('store_action', {
|
|
220
|
+
store: 'app',
|
|
221
|
+
action: 'setUserId',
|
|
222
|
+
userId: id,
|
|
223
|
+
prevState: get().userId
|
|
224
|
+
})
|
|
225
|
+
set({ userId: id })
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
setCurrentView: (view) => {
|
|
229
|
+
logger.info('store_action', {
|
|
230
|
+
store: 'app',
|
|
231
|
+
action: 'setCurrentView',
|
|
232
|
+
userId: get().userId,
|
|
233
|
+
prevView: get().currentView,
|
|
234
|
+
nextView: view
|
|
235
|
+
})
|
|
236
|
+
set({ currentView: view })
|
|
237
|
+
}
|
|
238
|
+
}),
|
|
239
|
+
{ name: 'app-storage' }
|
|
240
|
+
)
|
|
241
|
+
)
|
|
242
|
+
)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
### TanStack Query Logging Pattern
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
// hooks/use-create-item.ts
|
|
251
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
|
252
|
+
import { logger } from '@/lib/logger'
|
|
253
|
+
|
|
254
|
+
export function useCreateItem(userId: string) {
|
|
255
|
+
const queryClient = useQueryClient()
|
|
256
|
+
|
|
257
|
+
return useMutation({
|
|
258
|
+
mutationFn: async (data: ItemData) => {
|
|
259
|
+
logger.info('mutation_start', {
|
|
260
|
+
mutation: 'createItem',
|
|
261
|
+
userId,
|
|
262
|
+
dataSize: JSON.stringify(data).length
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
const response = await fetch('/api/items', {
|
|
266
|
+
method: 'POST',
|
|
267
|
+
headers: { 'Content-Type': 'application/json' },
|
|
268
|
+
body: JSON.stringify(data)
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
if (!response.ok) throw new Error('Creation failed')
|
|
272
|
+
return response.json()
|
|
273
|
+
},
|
|
274
|
+
|
|
275
|
+
onSuccess: (result) => {
|
|
276
|
+
logger.info('mutation_success', {
|
|
277
|
+
mutation: 'createItem',
|
|
278
|
+
userId,
|
|
279
|
+
resultId: result.id
|
|
280
|
+
})
|
|
281
|
+
queryClient.invalidateQueries({ queryKey: ['items', userId] })
|
|
282
|
+
},
|
|
283
|
+
|
|
284
|
+
onError: (error) => {
|
|
285
|
+
logger.error('mutation_error', {
|
|
286
|
+
mutation: 'createItem',
|
|
287
|
+
userId,
|
|
288
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
289
|
+
})
|
|
290
|
+
}
|
|
291
|
+
})
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Logger Implementation
|
|
298
|
+
|
|
299
|
+
### TypeScript/JavaScript
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
// lib/logger.ts
|
|
303
|
+
type LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR'
|
|
304
|
+
|
|
305
|
+
interface LogContext {
|
|
306
|
+
[key: string]: unknown
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
class Logger {
|
|
310
|
+
private log(level: LogLevel, event: string, context?: LogContext) {
|
|
311
|
+
const timestamp = new Date().toISOString()
|
|
312
|
+
const logEntry = {
|
|
313
|
+
timestamp,
|
|
314
|
+
level,
|
|
315
|
+
event,
|
|
316
|
+
...context
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Development: Pretty console output
|
|
320
|
+
if (process.env.NODE_ENV === 'development') {
|
|
321
|
+
console[level.toLowerCase() as 'log'](`[${level}] ${event}`, context)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Production: Structured JSON logs
|
|
325
|
+
if (process.env.NODE_ENV === 'production') {
|
|
326
|
+
console[level.toLowerCase() as 'log'](JSON.stringify(logEntry))
|
|
327
|
+
// Optional: Send to monitoring service (Sentry, LogRocket, etc.)
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
debug(event: string, context?: LogContext) {
|
|
332
|
+
this.log('DEBUG', event, context)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
info(event: string, context?: LogContext) {
|
|
336
|
+
this.log('INFO', event, context)
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
warn(event: string, context?: LogContext) {
|
|
340
|
+
this.log('WARN', event, context)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
error(event: string, context?: LogContext) {
|
|
344
|
+
this.log('ERROR', event, context)
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export const logger = new Logger()
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Python
|
|
352
|
+
|
|
353
|
+
```python
|
|
354
|
+
# lib/logger.py
|
|
355
|
+
import logging
|
|
356
|
+
import json
|
|
357
|
+
from datetime import datetime
|
|
358
|
+
from typing import Any, Dict
|
|
359
|
+
|
|
360
|
+
class StructuredLogger:
|
|
361
|
+
def __init__(self, name: str):
|
|
362
|
+
self.logger = logging.getLogger(name)
|
|
363
|
+
self.logger.setLevel(logging.DEBUG)
|
|
364
|
+
|
|
365
|
+
def _log(self, level: str, event: str, context: Dict[str, Any] = None):
|
|
366
|
+
log_entry = {
|
|
367
|
+
"timestamp": datetime.utcnow().isoformat(),
|
|
368
|
+
"level": level,
|
|
369
|
+
"event": event,
|
|
370
|
+
**(context or {})
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
# Development: Human-readable
|
|
374
|
+
if os.getenv("ENV") == "development":
|
|
375
|
+
self.logger.log(getattr(logging, level), f"{event}: {context}")
|
|
376
|
+
# Production: JSON
|
|
377
|
+
else:
|
|
378
|
+
self.logger.log(getattr(logging, level), json.dumps(log_entry))
|
|
379
|
+
|
|
380
|
+
def debug(self, event: str, context: Dict[str, Any] = None):
|
|
381
|
+
self._log("DEBUG", event, context)
|
|
382
|
+
|
|
383
|
+
def info(self, event: str, context: Dict[str, Any] = None):
|
|
384
|
+
self._log("INFO", event, context)
|
|
385
|
+
|
|
386
|
+
def warning(self, event: str, context: Dict[str, Any] = None):
|
|
387
|
+
self._log("WARNING", event, context)
|
|
388
|
+
|
|
389
|
+
def error(self, event: str, context: Dict[str, Any] = None):
|
|
390
|
+
self._log("ERROR", event, context)
|
|
391
|
+
|
|
392
|
+
logger = StructuredLogger(__name__)
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## Quick Reference
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
// API Route
|
|
401
|
+
logger.info('api_route_entry', { route, method, requestId })
|
|
402
|
+
logger.info('api_route_success', { route, duration, requestId })
|
|
403
|
+
logger.error('api_route_error', { route, error, stack, requestId })
|
|
404
|
+
|
|
405
|
+
// Database
|
|
406
|
+
logger.info('db_operation_start', { operation, table })
|
|
407
|
+
logger.info('db_operation_success', { operation, table, duration })
|
|
408
|
+
logger.error('db_operation_error', { operation, table, error, duration })
|
|
409
|
+
|
|
410
|
+
// External API
|
|
411
|
+
logger.info('external_api_start', { service, endpoint })
|
|
412
|
+
logger.info('external_api_success', { service, statusCode, duration })
|
|
413
|
+
logger.error('external_api_error', { service, error, duration })
|
|
414
|
+
|
|
415
|
+
// State Management
|
|
416
|
+
logger.info('store_action', { store, action, context })
|
|
417
|
+
|
|
418
|
+
// Performance
|
|
419
|
+
logger.info('performance_metric', { operation, duration, memoryUsage })
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
**💡 Remember:** If an action happens without logs, it's invisible in production!
|