evlog 0.0.0-reserved → 0.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/README.md +421 -0
- package/dist/error.d.mts +47 -0
- package/dist/error.d.ts +47 -0
- package/dist/error.mjs +64 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.mjs +4 -0
- package/dist/logger.d.mts +40 -0
- package/dist/logger.d.ts +40 -0
- package/dist/logger.mjs +134 -0
- package/dist/nitro/plugin.d.mts +5 -0
- package/dist/nitro/plugin.d.ts +5 -0
- package/dist/nitro/plugin.mjs +33 -0
- package/dist/nuxt/module.d.mts +18 -0
- package/dist/nuxt/module.d.ts +18 -0
- package/dist/nuxt/module.mjs +48 -0
- package/dist/runtime/composables/index.d.mts +4 -0
- package/dist/runtime/composables/index.d.ts +4 -0
- package/dist/runtime/composables/index.mjs +2 -0
- package/dist/runtime/composables/log.d.mts +18 -0
- package/dist/runtime/composables/log.d.ts +18 -0
- package/dist/runtime/composables/log.mjs +69 -0
- package/dist/runtime/composables/useLogger.d.mts +21 -0
- package/dist/runtime/composables/useLogger.d.ts +21 -0
- package/dist/runtime/composables/useLogger.mjs +11 -0
- package/dist/runtime/plugin.client.d.mts +3 -0
- package/dist/runtime/plugin.client.d.ts +3 -0
- package/dist/runtime/plugin.client.mjs +13 -0
- package/dist/types.d.mts +135 -0
- package/dist/types.d.ts +135 -0
- package/dist/types.mjs +1 -0
- package/dist/utils.d.mts +43 -0
- package/dist/utils.d.ts +43 -0
- package/dist/utils.mjs +57 -0
- package/package.json +93 -5
- package/index.js +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
# evlog
|
|
2
|
+
|
|
3
|
+
[](https://npmjs.com/package/evlog)
|
|
4
|
+
[](https://npm.chart.dev/evlog)
|
|
5
|
+
[](https://github.com/HugoRCD/evlog/actions/workflows/ci.yml)
|
|
6
|
+
[](https://bundlephobia.com/package/evlog)
|
|
7
|
+
[](https://nuxt.com/)
|
|
8
|
+
[](https://github.com/HugoRCD/evlog/blob/main/LICENSE)
|
|
9
|
+
|
|
10
|
+
**Your logs are lying to you.**
|
|
11
|
+
|
|
12
|
+
A single request generates 10+ log lines. When production breaks at 3am, you're grep-ing through noise, praying you'll find signal. Your errors say "Something went wrong" – thanks, very helpful.
|
|
13
|
+
|
|
14
|
+
**evlog fixes this.** One log per request. All context included. Errors that explain themselves.
|
|
15
|
+
|
|
16
|
+
## Why evlog?
|
|
17
|
+
|
|
18
|
+
### The Problem
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
// server/api/checkout.post.ts
|
|
22
|
+
|
|
23
|
+
// ❌ Scattered logs - impossible to debug
|
|
24
|
+
console.log('Request received')
|
|
25
|
+
console.log('User:', user.id)
|
|
26
|
+
console.log('Cart loaded')
|
|
27
|
+
console.log('Payment failed') // Good luck finding this at 3am
|
|
28
|
+
|
|
29
|
+
throw new Error('Something went wrong') // 🤷♂️
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### The Solution
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// server/api/checkout.post.ts
|
|
36
|
+
import { useLogger } from 'evlog'
|
|
37
|
+
|
|
38
|
+
// ✅ One comprehensive event per request
|
|
39
|
+
export default defineEventHandler(async (event) => {
|
|
40
|
+
const log = useLogger(event) // Auto-injected by evlog
|
|
41
|
+
|
|
42
|
+
log.set({ user: { id: user.id, plan: 'premium' } })
|
|
43
|
+
log.set({ cart: { items: 3, total: 9999 } })
|
|
44
|
+
log.error(error, { step: 'payment' })
|
|
45
|
+
|
|
46
|
+
// Emits ONE event with ALL context + duration (automatic)
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Output:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"timestamp": "2025-01-24T10:23:45.612Z",
|
|
55
|
+
"level": "error",
|
|
56
|
+
"service": "my-app",
|
|
57
|
+
"method": "POST",
|
|
58
|
+
"path": "/api/checkout",
|
|
59
|
+
"duration": "1.2s",
|
|
60
|
+
"user": { "id": "123", "plan": "premium" },
|
|
61
|
+
"cart": { "items": 3, "total": 9999 },
|
|
62
|
+
"error": { "message": "Card declined", "step": "payment" }
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Built for AI-Assisted Development
|
|
67
|
+
|
|
68
|
+
We're in the age of AI agents writing and debugging code. When an agent encounters an error, it needs **clear, structured context** to understand what happened and how to fix it.
|
|
69
|
+
|
|
70
|
+
Traditional logs force agents to grep through noise. evlog gives them:
|
|
71
|
+
- **One event per request** with all context in one place
|
|
72
|
+
- **Self-documenting errors** with `why` and `fix` fields
|
|
73
|
+
- **Structured JSON** that's easy to parse and reason about
|
|
74
|
+
|
|
75
|
+
Your AI copilot will thank you.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Installation
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm install evlog
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Nuxt Integration
|
|
86
|
+
|
|
87
|
+
The recommended way to use evlog. Zero config, everything just works.
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
// nuxt.config.ts
|
|
91
|
+
export default defineNuxtConfig({
|
|
92
|
+
modules: ['evlog/nuxt'],
|
|
93
|
+
|
|
94
|
+
evlog: {
|
|
95
|
+
env: {
|
|
96
|
+
service: 'my-app',
|
|
97
|
+
environment: process.env.NODE_ENV,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
})
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
That's it. Now use `useLogger(event)` in any API route:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// server/api/checkout.post.ts
|
|
107
|
+
import { useLogger, defineError } from 'evlog'
|
|
108
|
+
|
|
109
|
+
export default defineEventHandler(async (event) => {
|
|
110
|
+
const log = useLogger(event)
|
|
111
|
+
|
|
112
|
+
// Authenticate user and add to wide event
|
|
113
|
+
const user = await requireAuth(event)
|
|
114
|
+
log.set({ user: { id: user.id, plan: user.plan } })
|
|
115
|
+
|
|
116
|
+
// Load cart and add to wide event
|
|
117
|
+
const cart = await getCart(user.id)
|
|
118
|
+
log.set({ cart: { items: cart.items.length, total: cart.total } })
|
|
119
|
+
|
|
120
|
+
// Process payment
|
|
121
|
+
try {
|
|
122
|
+
const payment = await processPayment(cart, user)
|
|
123
|
+
log.set({ payment: { id: payment.id, method: payment.method } })
|
|
124
|
+
} catch (error) {
|
|
125
|
+
log.error(error, { step: 'payment' })
|
|
126
|
+
|
|
127
|
+
throw defineError({
|
|
128
|
+
message: 'Payment failed',
|
|
129
|
+
why: error.message,
|
|
130
|
+
fix: 'Try a different payment method or contact your bank',
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Create order
|
|
135
|
+
const order = await createOrder(cart, user)
|
|
136
|
+
log.set({ order: { id: order.id, status: order.status } })
|
|
137
|
+
|
|
138
|
+
return order
|
|
139
|
+
// log.emit() called automatically at request end
|
|
140
|
+
})
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The wide event emitted at the end contains **everything**:
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"timestamp": "2026-01-24T10:23:45.612Z",
|
|
148
|
+
"level": "info",
|
|
149
|
+
"service": "my-app",
|
|
150
|
+
"method": "POST",
|
|
151
|
+
"path": "/api/checkout",
|
|
152
|
+
"duration": "1.2s",
|
|
153
|
+
"user": { "id": "user_123", "plan": "premium" },
|
|
154
|
+
"cart": { "items": 3, "total": 9999 },
|
|
155
|
+
"payment": { "id": "pay_xyz", "method": "card" },
|
|
156
|
+
"order": { "id": "order_abc", "status": "created" },
|
|
157
|
+
"status": 200
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Nitro Integration
|
|
162
|
+
|
|
163
|
+
Works with **any framework powered by Nitro**: Nuxt, Analog, Vinxi, SolidStart, TanStack Start, and more.
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
// nitro.config.ts
|
|
167
|
+
export default defineNitroConfig({
|
|
168
|
+
plugins: ['evlog/nitro'],
|
|
169
|
+
})
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Same API, same wide events:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// routes/api/documents/[id]/export.post.ts
|
|
176
|
+
import { useLogger, defineError } from 'evlog'
|
|
177
|
+
|
|
178
|
+
export default defineEventHandler(async (event) => {
|
|
179
|
+
const log = useLogger(event)
|
|
180
|
+
|
|
181
|
+
// Get document ID from route params
|
|
182
|
+
const documentId = getRouterParam(event, 'id')
|
|
183
|
+
log.set({ document: { id: documentId } })
|
|
184
|
+
|
|
185
|
+
// Parse request body for export options
|
|
186
|
+
const body = await readBody(event)
|
|
187
|
+
log.set({ export: { format: body.format, includeComments: body.includeComments } })
|
|
188
|
+
|
|
189
|
+
// Load document from database
|
|
190
|
+
const document = await db.documents.findUnique({ where: { id: documentId } })
|
|
191
|
+
if (!document) {
|
|
192
|
+
throw defineError({
|
|
193
|
+
message: 'Document not found',
|
|
194
|
+
why: `No document with ID "${documentId}" exists`,
|
|
195
|
+
fix: 'Check the document ID and try again',
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
log.set({ document: { id: documentId, title: document.title, pages: document.pages.length } })
|
|
199
|
+
|
|
200
|
+
// Generate export
|
|
201
|
+
try {
|
|
202
|
+
const exportResult = await generateExport(document, body.format)
|
|
203
|
+
log.set({ export: { format: body.format, size: exportResult.size, pages: exportResult.pages } })
|
|
204
|
+
|
|
205
|
+
return { url: exportResult.url, expiresAt: exportResult.expiresAt }
|
|
206
|
+
} catch (error) {
|
|
207
|
+
log.error(error, { step: 'export-generation' })
|
|
208
|
+
|
|
209
|
+
throw defineError({
|
|
210
|
+
message: 'Export failed',
|
|
211
|
+
why: `Failed to generate ${body.format} export: ${error.message}`,
|
|
212
|
+
fix: 'Try a different format or contact support',
|
|
213
|
+
})
|
|
214
|
+
}
|
|
215
|
+
// log.emit() called automatically - outputs one comprehensive wide event
|
|
216
|
+
})
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Output when the export completes:
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
{
|
|
223
|
+
"timestamp": "2025-01-24T14:32:10.123Z",
|
|
224
|
+
"level": "info",
|
|
225
|
+
"service": "document-api",
|
|
226
|
+
"method": "POST",
|
|
227
|
+
"path": "/api/documents/doc_123/export",
|
|
228
|
+
"duration": "2.4s",
|
|
229
|
+
"document": { "id": "doc_123", "title": "Q4 Report", "pages": 24 },
|
|
230
|
+
"export": { "format": "pdf", "size": 1240000, "pages": 24 },
|
|
231
|
+
"status": 200
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Structured Errors
|
|
236
|
+
|
|
237
|
+
Errors should tell you **what** happened, **why**, and **how to fix it**.
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
// server/api/repos/sync.post.ts
|
|
241
|
+
import { useLogger, defineError } from 'evlog'
|
|
242
|
+
|
|
243
|
+
export default defineEventHandler(async (event) => {
|
|
244
|
+
const log = useLogger(event)
|
|
245
|
+
|
|
246
|
+
log.set({ repo: { owner: 'acme', name: 'my-project' } })
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
const result = await syncWithGitHub()
|
|
250
|
+
log.set({ sync: { commits: result.commits, files: result.files } })
|
|
251
|
+
return result
|
|
252
|
+
} catch (error) {
|
|
253
|
+
log.error(error, { step: 'github-sync' })
|
|
254
|
+
|
|
255
|
+
throw defineError({
|
|
256
|
+
message: 'Failed to sync repository',
|
|
257
|
+
why: 'GitHub API rate limit exceeded',
|
|
258
|
+
fix: 'Wait 1 hour or use a different token',
|
|
259
|
+
link: 'https://docs.github.com/en/rest/rate-limit',
|
|
260
|
+
cause: error,
|
|
261
|
+
})
|
|
262
|
+
}
|
|
263
|
+
})
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Console output (development):
|
|
267
|
+
|
|
268
|
+
```
|
|
269
|
+
Error: Failed to sync repository
|
|
270
|
+
Why: GitHub API rate limit exceeded
|
|
271
|
+
Fix: Wait 1 hour or use a different token
|
|
272
|
+
More info: https://docs.github.com/en/rest/rate-limit
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Standalone TypeScript
|
|
276
|
+
|
|
277
|
+
For scripts, workers, or any TypeScript project:
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
// scripts/migrate.ts
|
|
281
|
+
import { initLogger, log, createRequestLogger } from 'evlog'
|
|
282
|
+
|
|
283
|
+
// Initialize once at script start
|
|
284
|
+
initLogger({
|
|
285
|
+
env: {
|
|
286
|
+
service: 'migration-script',
|
|
287
|
+
environment: 'production',
|
|
288
|
+
},
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
// Simple logging
|
|
292
|
+
log.info('migration', 'Starting database migration')
|
|
293
|
+
log.info({ action: 'migration', tables: ['users', 'orders'] })
|
|
294
|
+
|
|
295
|
+
// Or use request logger for a logical operation
|
|
296
|
+
const migrationLog = createRequestLogger({ action: 'full-migration' })
|
|
297
|
+
|
|
298
|
+
migrationLog.set({ tables: ['users', 'orders', 'products'] })
|
|
299
|
+
migrationLog.set({ rowsProcessed: 15000 })
|
|
300
|
+
migrationLog.emit()
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
// workers/sync-job.ts
|
|
305
|
+
import { initLogger, createRequestLogger, defineError } from 'evlog'
|
|
306
|
+
|
|
307
|
+
initLogger({
|
|
308
|
+
env: {
|
|
309
|
+
service: 'sync-worker',
|
|
310
|
+
environment: process.env.NODE_ENV,
|
|
311
|
+
},
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
async function processSyncJob(job: Job) {
|
|
315
|
+
const log = createRequestLogger({ jobId: job.id, type: 'sync' })
|
|
316
|
+
|
|
317
|
+
try {
|
|
318
|
+
log.set({ source: job.source, target: job.target })
|
|
319
|
+
|
|
320
|
+
const result = await performSync(job)
|
|
321
|
+
log.set({ recordsSynced: result.count })
|
|
322
|
+
|
|
323
|
+
return result
|
|
324
|
+
} catch (error) {
|
|
325
|
+
log.error(error, { step: 'sync' })
|
|
326
|
+
throw error
|
|
327
|
+
} finally {
|
|
328
|
+
log.emit()
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## API Reference
|
|
334
|
+
|
|
335
|
+
### `initLogger(config)`
|
|
336
|
+
|
|
337
|
+
Initialize the logger. Required for standalone usage, automatic with Nuxt/Nitro plugins.
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
initLogger({
|
|
341
|
+
env: {
|
|
342
|
+
service: string // Service name
|
|
343
|
+
environment: string // 'production' | 'development' | 'test'
|
|
344
|
+
version?: string // App version
|
|
345
|
+
commitHash?: string // Git commit
|
|
346
|
+
region?: string // Deployment region
|
|
347
|
+
},
|
|
348
|
+
pretty?: boolean // Pretty print (default: true in dev)
|
|
349
|
+
})
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### `log`
|
|
353
|
+
|
|
354
|
+
Simple logging API.
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
log.info('tag', 'message') // Tagged log
|
|
358
|
+
log.info({ key: 'value' }) // Wide event
|
|
359
|
+
log.error('tag', 'message')
|
|
360
|
+
log.warn('tag', 'message')
|
|
361
|
+
log.debug('tag', 'message')
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### `createRequestLogger(options)`
|
|
365
|
+
|
|
366
|
+
Create a request-scoped logger for wide events.
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
const log = createRequestLogger({
|
|
370
|
+
method: 'POST',
|
|
371
|
+
path: '/checkout',
|
|
372
|
+
requestId: 'req_123',
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
log.set({ user: { id: '123' } }) // Add context
|
|
376
|
+
log.error(error, { step: 'x' }) // Log error with context
|
|
377
|
+
log.emit() // Emit final event
|
|
378
|
+
log.getContext() // Get current context
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### `defineError(options)`
|
|
382
|
+
|
|
383
|
+
Create a structured error.
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
defineError({
|
|
387
|
+
message: string // What happened
|
|
388
|
+
why?: string // Why it happened
|
|
389
|
+
fix?: string // How to fix it
|
|
390
|
+
link?: string // Documentation URL
|
|
391
|
+
cause?: Error // Original error
|
|
392
|
+
})
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
## Framework Support
|
|
396
|
+
|
|
397
|
+
evlog works with any framework powered by [Nitro](https://nitro.unjs.io/):
|
|
398
|
+
|
|
399
|
+
| Framework | Integration |
|
|
400
|
+
|-----------|-------------|
|
|
401
|
+
| **Nuxt** | `modules: ['evlog/nuxt']` |
|
|
402
|
+
| **Analog** | `plugins: ['evlog/nitro']` |
|
|
403
|
+
| **Vinxi** | `plugins: ['evlog/nitro']` |
|
|
404
|
+
| **SolidStart** | `plugins: ['evlog/nitro']` |
|
|
405
|
+
| **TanStack Start** | `plugins: ['evlog/nitro']` |
|
|
406
|
+
| **Standalone Nitro** | `plugins: ['evlog/nitro']` |
|
|
407
|
+
|
|
408
|
+
## Philosophy
|
|
409
|
+
|
|
410
|
+
Inspired by [Logging Sucks](https://loggingsucks.com/) by [Boris Tane](https://github.com/boristane).
|
|
411
|
+
|
|
412
|
+
1. **Wide Events**: One log per request with all context
|
|
413
|
+
2. **Structured Errors**: Errors that explain themselves
|
|
414
|
+
3. **Request Scoping**: Accumulate context, emit once
|
|
415
|
+
4. **Pretty for Dev, JSON for Prod**: Human-readable locally, machine-parseable in production
|
|
416
|
+
|
|
417
|
+
## License
|
|
418
|
+
|
|
419
|
+
[MIT](./LICENSE)
|
|
420
|
+
|
|
421
|
+
Made by [@HugoRCD](https://github.com/HugoRCD)
|
package/dist/error.d.mts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ErrorOptions } from './types.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Structured error with context for better debugging
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* throw new EvlogError({
|
|
9
|
+
* message: 'Failed to sync repository',
|
|
10
|
+
* why: 'GitHub API rate limit exceeded',
|
|
11
|
+
* fix: 'Wait 1 hour or use a different token',
|
|
12
|
+
* link: 'https://docs.github.com/en/rest/rate-limit',
|
|
13
|
+
* cause: originalError,
|
|
14
|
+
* })
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
declare class EvlogError extends Error {
|
|
18
|
+
readonly why?: string;
|
|
19
|
+
readonly fix?: string;
|
|
20
|
+
readonly link?: string;
|
|
21
|
+
constructor(options: ErrorOptions | string);
|
|
22
|
+
/**
|
|
23
|
+
* Format error for console output with colors
|
|
24
|
+
*/
|
|
25
|
+
toString(): string;
|
|
26
|
+
/**
|
|
27
|
+
* Convert to plain object for JSON serialization
|
|
28
|
+
*/
|
|
29
|
+
toJSON(): Record<string, unknown>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Create an EvlogError (functional alternative to `new EvlogError()`)
|
|
33
|
+
*
|
|
34
|
+
* Named `defineError` to avoid conflict with Nuxt's built-in `createError`
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* throw defineError({
|
|
39
|
+
* message: 'Payment failed',
|
|
40
|
+
* why: 'Card declined by issuer',
|
|
41
|
+
* fix: 'Try a different payment method',
|
|
42
|
+
* })
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
declare function defineError(options: ErrorOptions | string): EvlogError;
|
|
46
|
+
|
|
47
|
+
export { EvlogError, defineError };
|
package/dist/error.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ErrorOptions } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Structured error with context for better debugging
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* throw new EvlogError({
|
|
9
|
+
* message: 'Failed to sync repository',
|
|
10
|
+
* why: 'GitHub API rate limit exceeded',
|
|
11
|
+
* fix: 'Wait 1 hour or use a different token',
|
|
12
|
+
* link: 'https://docs.github.com/en/rest/rate-limit',
|
|
13
|
+
* cause: originalError,
|
|
14
|
+
* })
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
declare class EvlogError extends Error {
|
|
18
|
+
readonly why?: string;
|
|
19
|
+
readonly fix?: string;
|
|
20
|
+
readonly link?: string;
|
|
21
|
+
constructor(options: ErrorOptions | string);
|
|
22
|
+
/**
|
|
23
|
+
* Format error for console output with colors
|
|
24
|
+
*/
|
|
25
|
+
toString(): string;
|
|
26
|
+
/**
|
|
27
|
+
* Convert to plain object for JSON serialization
|
|
28
|
+
*/
|
|
29
|
+
toJSON(): Record<string, unknown>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Create an EvlogError (functional alternative to `new EvlogError()`)
|
|
33
|
+
*
|
|
34
|
+
* Named `defineError` to avoid conflict with Nuxt's built-in `createError`
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* throw defineError({
|
|
39
|
+
* message: 'Payment failed',
|
|
40
|
+
* why: 'Card declined by issuer',
|
|
41
|
+
* fix: 'Try a different payment method',
|
|
42
|
+
* })
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
declare function defineError(options: ErrorOptions | string): EvlogError;
|
|
46
|
+
|
|
47
|
+
export { EvlogError, defineError };
|
package/dist/error.mjs
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { colors, isServer } from './utils.mjs';
|
|
2
|
+
|
|
3
|
+
class EvlogError extends Error {
|
|
4
|
+
why;
|
|
5
|
+
fix;
|
|
6
|
+
link;
|
|
7
|
+
constructor(options) {
|
|
8
|
+
const opts = typeof options === "string" ? { message: options } : options;
|
|
9
|
+
super(opts.message, { cause: opts.cause });
|
|
10
|
+
this.name = "EvlogError";
|
|
11
|
+
this.why = opts.why;
|
|
12
|
+
this.fix = opts.fix;
|
|
13
|
+
this.link = opts.link;
|
|
14
|
+
if (Error.captureStackTrace) {
|
|
15
|
+
Error.captureStackTrace(this, EvlogError);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Format error for console output with colors
|
|
20
|
+
*/
|
|
21
|
+
toString() {
|
|
22
|
+
const useColors = isServer();
|
|
23
|
+
const red = useColors ? colors.red : "";
|
|
24
|
+
const yellow = useColors ? colors.yellow : "";
|
|
25
|
+
const cyan = useColors ? colors.cyan : "";
|
|
26
|
+
const dim = useColors ? colors.dim : "";
|
|
27
|
+
const reset = useColors ? colors.reset : "";
|
|
28
|
+
const bold = useColors ? colors.bold : "";
|
|
29
|
+
const lines = [];
|
|
30
|
+
lines.push(`${red}${bold}Error:${reset} ${this.message}`);
|
|
31
|
+
if (this.why) {
|
|
32
|
+
lines.push(`${yellow}Why:${reset} ${this.why}`);
|
|
33
|
+
}
|
|
34
|
+
if (this.fix) {
|
|
35
|
+
lines.push(`${cyan}Fix:${reset} ${this.fix}`);
|
|
36
|
+
}
|
|
37
|
+
if (this.link) {
|
|
38
|
+
lines.push(`${dim}More info:${reset} ${this.link}`);
|
|
39
|
+
}
|
|
40
|
+
if (this.cause) {
|
|
41
|
+
lines.push(`${dim}Caused by:${reset} ${this.cause.message}`);
|
|
42
|
+
}
|
|
43
|
+
return lines.join("\n");
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Convert to plain object for JSON serialization
|
|
47
|
+
*/
|
|
48
|
+
toJSON() {
|
|
49
|
+
return {
|
|
50
|
+
name: this.name,
|
|
51
|
+
message: this.message,
|
|
52
|
+
why: this.why,
|
|
53
|
+
fix: this.fix,
|
|
54
|
+
link: this.link,
|
|
55
|
+
cause: this.cause instanceof Error ? { name: this.cause.name, message: this.cause.message } : void 0,
|
|
56
|
+
stack: this.stack
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function defineError(options) {
|
|
61
|
+
return new EvlogError(options);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { EvlogError, defineError };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { EvlogError, defineError } from './error.mjs';
|
|
2
|
+
export { createRequestLogger, getEnvironment, initLogger, log } from './logger.mjs';
|
|
3
|
+
export { useLogger } from './runtime/composables/useLogger.mjs';
|
|
4
|
+
export { BaseWideEvent, EnvironmentContext, ErrorOptions, EvlogEventContext, Log, LogLevel, LoggerConfig, RequestLogger, WideEvent } from './types.mjs';
|
|
5
|
+
import 'h3';
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { EvlogError, defineError } from './error.js';
|
|
2
|
+
export { createRequestLogger, getEnvironment, initLogger, log } from './logger.js';
|
|
3
|
+
export { useLogger } from './runtime/composables/useLogger.js';
|
|
4
|
+
export { BaseWideEvent, EnvironmentContext, ErrorOptions, EvlogEventContext, Log, LogLevel, LoggerConfig, RequestLogger, WideEvent } from './types.js';
|
|
5
|
+
import 'h3';
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { RequestLogger, EnvironmentContext, LoggerConfig, Log } from './types.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Initialize the logger with configuration.
|
|
5
|
+
* Call this once at application startup.
|
|
6
|
+
*/
|
|
7
|
+
declare function initLogger(config?: LoggerConfig): void;
|
|
8
|
+
/**
|
|
9
|
+
* Simple logging API - as easy as console.log
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* log.info('auth', 'User logged in')
|
|
14
|
+
* log.error({ action: 'payment', error: 'failed' })
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
declare const log: Log;
|
|
18
|
+
interface RequestLoggerOptions {
|
|
19
|
+
method?: string;
|
|
20
|
+
path?: string;
|
|
21
|
+
requestId?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Create a request-scoped logger for building wide events.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* const log = createRequestLogger({ method: 'POST', path: '/checkout' })
|
|
29
|
+
* log.set({ user: { id: '123' } })
|
|
30
|
+
* log.set({ cart: { items: 3 } })
|
|
31
|
+
* log.emit()
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
declare function createRequestLogger(options?: RequestLoggerOptions): RequestLogger;
|
|
35
|
+
/**
|
|
36
|
+
* Get the current environment context.
|
|
37
|
+
*/
|
|
38
|
+
declare function getEnvironment(): EnvironmentContext;
|
|
39
|
+
|
|
40
|
+
export { createRequestLogger, getEnvironment, initLogger, log };
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { RequestLogger, EnvironmentContext, LoggerConfig, Log } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Initialize the logger with configuration.
|
|
5
|
+
* Call this once at application startup.
|
|
6
|
+
*/
|
|
7
|
+
declare function initLogger(config?: LoggerConfig): void;
|
|
8
|
+
/**
|
|
9
|
+
* Simple logging API - as easy as console.log
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* log.info('auth', 'User logged in')
|
|
14
|
+
* log.error({ action: 'payment', error: 'failed' })
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
declare const log: Log;
|
|
18
|
+
interface RequestLoggerOptions {
|
|
19
|
+
method?: string;
|
|
20
|
+
path?: string;
|
|
21
|
+
requestId?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Create a request-scoped logger for building wide events.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* const log = createRequestLogger({ method: 'POST', path: '/checkout' })
|
|
29
|
+
* log.set({ user: { id: '123' } })
|
|
30
|
+
* log.set({ cart: { items: 3 } })
|
|
31
|
+
* log.emit()
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
declare function createRequestLogger(options?: RequestLoggerOptions): RequestLogger;
|
|
35
|
+
/**
|
|
36
|
+
* Get the current environment context.
|
|
37
|
+
*/
|
|
38
|
+
declare function getEnvironment(): EnvironmentContext;
|
|
39
|
+
|
|
40
|
+
export { createRequestLogger, getEnvironment, initLogger, log };
|