evlog 1.2.0 → 1.4.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 +153 -1
- package/dist/adapters/axiom.d.mts +62 -0
- package/dist/adapters/axiom.d.ts +62 -0
- package/dist/adapters/axiom.mjs +65 -0
- package/dist/adapters/otlp.d.mts +83 -0
- package/dist/adapters/otlp.d.ts +83 -0
- package/dist/adapters/otlp.mjs +202 -0
- package/dist/logger.mjs +7 -3
- package/dist/nitro/plugin.mjs +24 -8
- package/dist/nuxt/module.d.mts +65 -1
- package/dist/nuxt/module.d.ts +65 -1
- package/dist/runtime/server/useLogger.d.mts +10 -1
- package/dist/runtime/server/useLogger.d.ts +10 -1
- package/dist/runtime/server/useLogger.mjs +4 -1
- package/dist/types.d.mts +24 -2
- package/dist/types.d.ts +24 -2
- package/dist/utils.d.mts +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.mjs +8 -4
- package/dist/workers.d.mts +45 -0
- package/dist/workers.d.ts +45 -0
- package/dist/workers.mjs +53 -0
- package/package.json +22 -1
package/README.md
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
[](https://npmjs.com/package/evlog)
|
|
4
4
|
[](https://npm.chart.dev/evlog)
|
|
5
5
|
[](https://github.com/HugoRCD/evlog/actions/workflows/ci.yml)
|
|
6
|
-
[](https://www.typescriptlang.org/)
|
|
7
7
|
[](https://nuxt.com/)
|
|
8
|
+
[](https://evlog.dev)
|
|
8
9
|
[](https://github.com/HugoRCD/evlog/blob/main/LICENSE)
|
|
9
10
|
|
|
10
11
|
**Your logs are lying to you.**
|
|
@@ -346,6 +347,128 @@ async function processSyncJob(job: Job) {
|
|
|
346
347
|
}
|
|
347
348
|
```
|
|
348
349
|
|
|
350
|
+
## Cloudflare Workers
|
|
351
|
+
|
|
352
|
+
Use the Workers adapter for structured logs and correct platform severity.
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
// src/index.ts
|
|
356
|
+
import { initWorkersLogger, createWorkersLogger } from 'evlog/workers'
|
|
357
|
+
|
|
358
|
+
initWorkersLogger({
|
|
359
|
+
env: { service: 'edge-api' },
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
export default {
|
|
363
|
+
async fetch(request: Request) {
|
|
364
|
+
const log = createWorkersLogger(request)
|
|
365
|
+
|
|
366
|
+
try {
|
|
367
|
+
log.set({ route: 'health' })
|
|
368
|
+
const response = new Response('ok', { status: 200 })
|
|
369
|
+
log.emit({ status: response.status })
|
|
370
|
+
return response
|
|
371
|
+
} catch (error) {
|
|
372
|
+
log.error(error as Error)
|
|
373
|
+
log.emit({ status: 500 })
|
|
374
|
+
throw error
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Disable invocation logs to avoid duplicate request logs:
|
|
381
|
+
|
|
382
|
+
```toml
|
|
383
|
+
# wrangler.toml
|
|
384
|
+
[observability.logs]
|
|
385
|
+
invocation_logs = false
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
Notes:
|
|
389
|
+
- `requestId` defaults to `cf-ray` when available
|
|
390
|
+
- `request.cf` is included (colo, country, asn) unless disabled
|
|
391
|
+
- Use `headerAllowlist` to avoid logging sensitive headers
|
|
392
|
+
|
|
393
|
+
## Adapters
|
|
394
|
+
|
|
395
|
+
Send your logs to external observability platforms with built-in adapters.
|
|
396
|
+
|
|
397
|
+
### Axiom
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
// server/plugins/evlog-drain.ts
|
|
401
|
+
import { createAxiomDrain } from 'evlog/axiom'
|
|
402
|
+
|
|
403
|
+
export default defineNitroPlugin((nitroApp) => {
|
|
404
|
+
nitroApp.hooks.hook('evlog:drain', createAxiomDrain())
|
|
405
|
+
})
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Set environment variables:
|
|
409
|
+
|
|
410
|
+
```bash
|
|
411
|
+
NUXT_AXIOM_TOKEN=xaat-your-token
|
|
412
|
+
NUXT_AXIOM_DATASET=your-dataset
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### OTLP (OpenTelemetry)
|
|
416
|
+
|
|
417
|
+
Works with Grafana, Datadog, Honeycomb, and any OTLP-compatible backend.
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
// server/plugins/evlog-drain.ts
|
|
421
|
+
import { createOTLPDrain } from 'evlog/otlp'
|
|
422
|
+
|
|
423
|
+
export default defineNitroPlugin((nitroApp) => {
|
|
424
|
+
nitroApp.hooks.hook('evlog:drain', createOTLPDrain())
|
|
425
|
+
})
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
Set environment variables:
|
|
429
|
+
|
|
430
|
+
```bash
|
|
431
|
+
NUXT_OTLP_ENDPOINT=http://localhost:4318
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Multiple Destinations
|
|
435
|
+
|
|
436
|
+
Send logs to multiple services:
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
// server/plugins/evlog-drain.ts
|
|
440
|
+
import { createAxiomDrain } from 'evlog/axiom'
|
|
441
|
+
import { createOTLPDrain } from 'evlog/otlp'
|
|
442
|
+
|
|
443
|
+
export default defineNitroPlugin((nitroApp) => {
|
|
444
|
+
const axiom = createAxiomDrain()
|
|
445
|
+
const otlp = createOTLPDrain()
|
|
446
|
+
|
|
447
|
+
nitroApp.hooks.hook('evlog:drain', async (ctx) => {
|
|
448
|
+
await Promise.allSettled([axiom(ctx), otlp(ctx)])
|
|
449
|
+
})
|
|
450
|
+
})
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Custom Adapters
|
|
454
|
+
|
|
455
|
+
Build your own adapter for any destination:
|
|
456
|
+
|
|
457
|
+
```typescript
|
|
458
|
+
// server/plugins/evlog-drain.ts
|
|
459
|
+
export default defineNitroPlugin((nitroApp) => {
|
|
460
|
+
nitroApp.hooks.hook('evlog:drain', async (ctx) => {
|
|
461
|
+
await fetch('https://your-service.com/logs', {
|
|
462
|
+
method: 'POST',
|
|
463
|
+
headers: { 'Content-Type': 'application/json' },
|
|
464
|
+
body: JSON.stringify(ctx.event),
|
|
465
|
+
})
|
|
466
|
+
})
|
|
467
|
+
})
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
> See the [full documentation](https://evlog.hrcd.fr/adapters/overview) for adapter configuration options, troubleshooting, and advanced patterns.
|
|
471
|
+
|
|
349
472
|
## API Reference
|
|
350
473
|
|
|
351
474
|
### `initLogger(config)`
|
|
@@ -362,6 +485,7 @@ initLogger({
|
|
|
362
485
|
region?: string // Deployment region
|
|
363
486
|
},
|
|
364
487
|
pretty?: boolean // Pretty print (default: true in dev)
|
|
488
|
+
stringify?: boolean // JSON.stringify output (default: true, false for Workers)
|
|
365
489
|
include?: string[] // Route patterns to log (glob), e.g. ['/api/**']
|
|
366
490
|
sampling?: {
|
|
367
491
|
rates?: { // Head sampling (random per level)
|
|
@@ -479,6 +603,34 @@ log.emit() // Emit final event
|
|
|
479
603
|
log.getContext() // Get current context
|
|
480
604
|
```
|
|
481
605
|
|
|
606
|
+
### `initWorkersLogger(options?)`
|
|
607
|
+
|
|
608
|
+
Initialize evlog for Cloudflare Workers (object logs + correct severity).
|
|
609
|
+
|
|
610
|
+
```typescript
|
|
611
|
+
import { initWorkersLogger } from 'evlog/workers'
|
|
612
|
+
|
|
613
|
+
initWorkersLogger({
|
|
614
|
+
env: { service: 'edge-api' },
|
|
615
|
+
})
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
### `createWorkersLogger(request, options?)`
|
|
619
|
+
|
|
620
|
+
Create a request-scoped logger for Workers. Auto-extracts `cf-ray`, `request.cf`, method, and path.
|
|
621
|
+
|
|
622
|
+
```typescript
|
|
623
|
+
import { createWorkersLogger } from 'evlog/workers'
|
|
624
|
+
|
|
625
|
+
const log = createWorkersLogger(request, {
|
|
626
|
+
requestId: 'custom-id', // Override cf-ray (default: cf-ray header)
|
|
627
|
+
headers: ['x-request-id'], // Headers to include (default: none)
|
|
628
|
+
})
|
|
629
|
+
|
|
630
|
+
log.set({ user: { id: '123' } })
|
|
631
|
+
log.emit({ status: 200 })
|
|
632
|
+
```
|
|
633
|
+
|
|
482
634
|
### `createError(options)`
|
|
483
635
|
|
|
484
636
|
Create a structured error with HTTP status support. Import from `evlog` directly to avoid conflicts with Nuxt/Nitro's `createError`.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { DrainContext, WideEvent } from '../types.mjs';
|
|
2
|
+
|
|
3
|
+
interface AxiomConfig {
|
|
4
|
+
/** Axiom dataset name */
|
|
5
|
+
dataset: string;
|
|
6
|
+
/** Axiom API token */
|
|
7
|
+
token: string;
|
|
8
|
+
/** Organization ID (required for Personal Access Tokens) */
|
|
9
|
+
orgId?: string;
|
|
10
|
+
/** Base URL for Axiom API. Default: https://api.axiom.co */
|
|
11
|
+
baseUrl?: string;
|
|
12
|
+
/** Request timeout in milliseconds. Default: 5000 */
|
|
13
|
+
timeout?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create a drain function for sending logs to Axiom.
|
|
17
|
+
*
|
|
18
|
+
* Configuration priority (highest to lowest):
|
|
19
|
+
* 1. Overrides passed to createAxiomDrain()
|
|
20
|
+
* 2. runtimeConfig.evlog.axiom
|
|
21
|
+
* 3. runtimeConfig.axiom
|
|
22
|
+
* 4. Environment variables: NUXT_AXIOM_*, AXIOM_*
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* // Zero config - just set NUXT_AXIOM_TOKEN and NUXT_AXIOM_DATASET env vars
|
|
27
|
+
* nitroApp.hooks.hook('evlog:drain', createAxiomDrain())
|
|
28
|
+
*
|
|
29
|
+
* // With overrides
|
|
30
|
+
* nitroApp.hooks.hook('evlog:drain', createAxiomDrain({
|
|
31
|
+
* dataset: 'my-dataset',
|
|
32
|
+
* }))
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
declare function createAxiomDrain(overrides?: Partial<AxiomConfig>): (ctx: DrainContext) => Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Send a single event to Axiom.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* await sendToAxiom(event, {
|
|
42
|
+
* dataset: 'my-logs',
|
|
43
|
+
* token: process.env.AXIOM_TOKEN!,
|
|
44
|
+
* })
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
declare function sendToAxiom(event: WideEvent, config: AxiomConfig): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Send a batch of events to Axiom.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* await sendBatchToAxiom(events, {
|
|
54
|
+
* dataset: 'my-logs',
|
|
55
|
+
* token: process.env.AXIOM_TOKEN!,
|
|
56
|
+
* })
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
declare function sendBatchToAxiom(events: WideEvent[], config: AxiomConfig): Promise<void>;
|
|
60
|
+
|
|
61
|
+
export { createAxiomDrain, sendBatchToAxiom, sendToAxiom };
|
|
62
|
+
export type { AxiomConfig };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { DrainContext, WideEvent } from '../types.js';
|
|
2
|
+
|
|
3
|
+
interface AxiomConfig {
|
|
4
|
+
/** Axiom dataset name */
|
|
5
|
+
dataset: string;
|
|
6
|
+
/** Axiom API token */
|
|
7
|
+
token: string;
|
|
8
|
+
/** Organization ID (required for Personal Access Tokens) */
|
|
9
|
+
orgId?: string;
|
|
10
|
+
/** Base URL for Axiom API. Default: https://api.axiom.co */
|
|
11
|
+
baseUrl?: string;
|
|
12
|
+
/** Request timeout in milliseconds. Default: 5000 */
|
|
13
|
+
timeout?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create a drain function for sending logs to Axiom.
|
|
17
|
+
*
|
|
18
|
+
* Configuration priority (highest to lowest):
|
|
19
|
+
* 1. Overrides passed to createAxiomDrain()
|
|
20
|
+
* 2. runtimeConfig.evlog.axiom
|
|
21
|
+
* 3. runtimeConfig.axiom
|
|
22
|
+
* 4. Environment variables: NUXT_AXIOM_*, AXIOM_*
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* // Zero config - just set NUXT_AXIOM_TOKEN and NUXT_AXIOM_DATASET env vars
|
|
27
|
+
* nitroApp.hooks.hook('evlog:drain', createAxiomDrain())
|
|
28
|
+
*
|
|
29
|
+
* // With overrides
|
|
30
|
+
* nitroApp.hooks.hook('evlog:drain', createAxiomDrain({
|
|
31
|
+
* dataset: 'my-dataset',
|
|
32
|
+
* }))
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
declare function createAxiomDrain(overrides?: Partial<AxiomConfig>): (ctx: DrainContext) => Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Send a single event to Axiom.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* await sendToAxiom(event, {
|
|
42
|
+
* dataset: 'my-logs',
|
|
43
|
+
* token: process.env.AXIOM_TOKEN!,
|
|
44
|
+
* })
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
declare function sendToAxiom(event: WideEvent, config: AxiomConfig): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Send a batch of events to Axiom.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* await sendBatchToAxiom(events, {
|
|
54
|
+
* dataset: 'my-logs',
|
|
55
|
+
* token: process.env.AXIOM_TOKEN!,
|
|
56
|
+
* })
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
declare function sendBatchToAxiom(events: WideEvent[], config: AxiomConfig): Promise<void>;
|
|
60
|
+
|
|
61
|
+
export { createAxiomDrain, sendBatchToAxiom, sendToAxiom };
|
|
62
|
+
export type { AxiomConfig };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
function getRuntimeConfig() {
|
|
2
|
+
try {
|
|
3
|
+
const { useRuntimeConfig } = require("nitropack/runtime");
|
|
4
|
+
return useRuntimeConfig();
|
|
5
|
+
} catch {
|
|
6
|
+
return void 0;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
function createAxiomDrain(overrides) {
|
|
10
|
+
return async (ctx) => {
|
|
11
|
+
const runtimeConfig = getRuntimeConfig();
|
|
12
|
+
const evlogAxiom = runtimeConfig?.evlog?.axiom;
|
|
13
|
+
const rootAxiom = runtimeConfig?.axiom;
|
|
14
|
+
const config = {
|
|
15
|
+
dataset: overrides?.dataset ?? evlogAxiom?.dataset ?? rootAxiom?.dataset ?? process.env.NUXT_AXIOM_DATASET ?? process.env.AXIOM_DATASET,
|
|
16
|
+
token: overrides?.token ?? evlogAxiom?.token ?? rootAxiom?.token ?? process.env.NUXT_AXIOM_TOKEN ?? process.env.AXIOM_TOKEN,
|
|
17
|
+
orgId: overrides?.orgId ?? evlogAxiom?.orgId ?? rootAxiom?.orgId ?? process.env.NUXT_AXIOM_ORG_ID ?? process.env.AXIOM_ORG_ID,
|
|
18
|
+
baseUrl: overrides?.baseUrl ?? evlogAxiom?.baseUrl ?? rootAxiom?.baseUrl ?? process.env.NUXT_AXIOM_URL ?? process.env.AXIOM_URL,
|
|
19
|
+
timeout: overrides?.timeout ?? evlogAxiom?.timeout ?? rootAxiom?.timeout
|
|
20
|
+
};
|
|
21
|
+
if (!config.dataset || !config.token) {
|
|
22
|
+
console.error("[evlog/axiom] Missing dataset or token. Set NUXT_AXIOM_TOKEN/NUXT_AXIOM_DATASET env vars or pass to createAxiomDrain()");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
await sendToAxiom(ctx.event, config);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error("[evlog/axiom] Failed to send event:", error);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
async function sendToAxiom(event, config) {
|
|
33
|
+
await sendBatchToAxiom([event], config);
|
|
34
|
+
}
|
|
35
|
+
async function sendBatchToAxiom(events, config) {
|
|
36
|
+
const baseUrl = config.baseUrl ?? "https://api.axiom.co";
|
|
37
|
+
const timeout = config.timeout ?? 5e3;
|
|
38
|
+
const url = `${baseUrl}/v1/datasets/${encodeURIComponent(config.dataset)}/ingest`;
|
|
39
|
+
const headers = {
|
|
40
|
+
"Content-Type": "application/json",
|
|
41
|
+
"Authorization": `Bearer ${config.token}`
|
|
42
|
+
};
|
|
43
|
+
if (config.orgId) {
|
|
44
|
+
headers["X-Axiom-Org-Id"] = config.orgId;
|
|
45
|
+
}
|
|
46
|
+
const controller = new AbortController();
|
|
47
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
48
|
+
try {
|
|
49
|
+
const response = await fetch(url, {
|
|
50
|
+
method: "POST",
|
|
51
|
+
headers,
|
|
52
|
+
body: JSON.stringify(events),
|
|
53
|
+
signal: controller.signal
|
|
54
|
+
});
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
const text = await response.text().catch(() => "Unknown error");
|
|
57
|
+
const safeText = text.length > 200 ? `${text.slice(0, 200)}...[truncated]` : text;
|
|
58
|
+
throw new Error(`Axiom API error: ${response.status} ${response.statusText} - ${safeText}`);
|
|
59
|
+
}
|
|
60
|
+
} finally {
|
|
61
|
+
clearTimeout(timeoutId);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { createAxiomDrain, sendBatchToAxiom, sendToAxiom };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { DrainContext, WideEvent } from '../types.mjs';
|
|
2
|
+
|
|
3
|
+
interface OTLPConfig {
|
|
4
|
+
/** OTLP HTTP endpoint (e.g., http://localhost:4318) */
|
|
5
|
+
endpoint: string;
|
|
6
|
+
/** Override service name (defaults to event.service) */
|
|
7
|
+
serviceName?: string;
|
|
8
|
+
/** Additional resource attributes */
|
|
9
|
+
resourceAttributes?: Record<string, string | number | boolean>;
|
|
10
|
+
/** Custom headers (e.g., for authentication) */
|
|
11
|
+
headers?: Record<string, string>;
|
|
12
|
+
/** Request timeout in milliseconds. Default: 5000 */
|
|
13
|
+
timeout?: number;
|
|
14
|
+
}
|
|
15
|
+
/** OTLP Log Record structure */
|
|
16
|
+
interface OTLPLogRecord {
|
|
17
|
+
timeUnixNano: string;
|
|
18
|
+
severityNumber: number;
|
|
19
|
+
severityText: string;
|
|
20
|
+
body: {
|
|
21
|
+
stringValue: string;
|
|
22
|
+
};
|
|
23
|
+
attributes: Array<{
|
|
24
|
+
key: string;
|
|
25
|
+
value: {
|
|
26
|
+
stringValue?: string;
|
|
27
|
+
intValue?: string;
|
|
28
|
+
boolValue?: boolean;
|
|
29
|
+
};
|
|
30
|
+
}>;
|
|
31
|
+
traceId?: string;
|
|
32
|
+
spanId?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Convert an evlog WideEvent to an OTLP LogRecord.
|
|
36
|
+
*/
|
|
37
|
+
declare function toOTLPLogRecord(event: WideEvent): OTLPLogRecord;
|
|
38
|
+
/**
|
|
39
|
+
* Create a drain function for sending logs to an OTLP endpoint.
|
|
40
|
+
*
|
|
41
|
+
* Configuration priority (highest to lowest):
|
|
42
|
+
* 1. Overrides passed to createOTLPDrain()
|
|
43
|
+
* 2. runtimeConfig.evlog.otlp (NUXT_EVLOG_OTLP_*)
|
|
44
|
+
* 3. runtimeConfig.otlp (NUXT_OTLP_*)
|
|
45
|
+
* 4. Environment variables: OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* // Zero config - reads from runtimeConfig or env vars
|
|
50
|
+
* nitroApp.hooks.hook('evlog:drain', createOTLPDrain())
|
|
51
|
+
*
|
|
52
|
+
* // With overrides
|
|
53
|
+
* nitroApp.hooks.hook('evlog:drain', createOTLPDrain({
|
|
54
|
+
* endpoint: 'http://localhost:4318',
|
|
55
|
+
* }))
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
declare function createOTLPDrain(overrides?: Partial<OTLPConfig>): (ctx: DrainContext) => Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Send a single event to an OTLP endpoint.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* await sendToOTLP(event, {
|
|
65
|
+
* endpoint: 'http://localhost:4318',
|
|
66
|
+
* })
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
declare function sendToOTLP(event: WideEvent, config: OTLPConfig): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Send a batch of events to an OTLP endpoint.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* await sendBatchToOTLP(events, {
|
|
76
|
+
* endpoint: 'http://localhost:4318',
|
|
77
|
+
* })
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
declare function sendBatchToOTLP(events: WideEvent[], config: OTLPConfig): Promise<void>;
|
|
81
|
+
|
|
82
|
+
export { createOTLPDrain, sendBatchToOTLP, sendToOTLP, toOTLPLogRecord };
|
|
83
|
+
export type { OTLPConfig, OTLPLogRecord };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { DrainContext, WideEvent } from '../types.js';
|
|
2
|
+
|
|
3
|
+
interface OTLPConfig {
|
|
4
|
+
/** OTLP HTTP endpoint (e.g., http://localhost:4318) */
|
|
5
|
+
endpoint: string;
|
|
6
|
+
/** Override service name (defaults to event.service) */
|
|
7
|
+
serviceName?: string;
|
|
8
|
+
/** Additional resource attributes */
|
|
9
|
+
resourceAttributes?: Record<string, string | number | boolean>;
|
|
10
|
+
/** Custom headers (e.g., for authentication) */
|
|
11
|
+
headers?: Record<string, string>;
|
|
12
|
+
/** Request timeout in milliseconds. Default: 5000 */
|
|
13
|
+
timeout?: number;
|
|
14
|
+
}
|
|
15
|
+
/** OTLP Log Record structure */
|
|
16
|
+
interface OTLPLogRecord {
|
|
17
|
+
timeUnixNano: string;
|
|
18
|
+
severityNumber: number;
|
|
19
|
+
severityText: string;
|
|
20
|
+
body: {
|
|
21
|
+
stringValue: string;
|
|
22
|
+
};
|
|
23
|
+
attributes: Array<{
|
|
24
|
+
key: string;
|
|
25
|
+
value: {
|
|
26
|
+
stringValue?: string;
|
|
27
|
+
intValue?: string;
|
|
28
|
+
boolValue?: boolean;
|
|
29
|
+
};
|
|
30
|
+
}>;
|
|
31
|
+
traceId?: string;
|
|
32
|
+
spanId?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Convert an evlog WideEvent to an OTLP LogRecord.
|
|
36
|
+
*/
|
|
37
|
+
declare function toOTLPLogRecord(event: WideEvent): OTLPLogRecord;
|
|
38
|
+
/**
|
|
39
|
+
* Create a drain function for sending logs to an OTLP endpoint.
|
|
40
|
+
*
|
|
41
|
+
* Configuration priority (highest to lowest):
|
|
42
|
+
* 1. Overrides passed to createOTLPDrain()
|
|
43
|
+
* 2. runtimeConfig.evlog.otlp (NUXT_EVLOG_OTLP_*)
|
|
44
|
+
* 3. runtimeConfig.otlp (NUXT_OTLP_*)
|
|
45
|
+
* 4. Environment variables: OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* // Zero config - reads from runtimeConfig or env vars
|
|
50
|
+
* nitroApp.hooks.hook('evlog:drain', createOTLPDrain())
|
|
51
|
+
*
|
|
52
|
+
* // With overrides
|
|
53
|
+
* nitroApp.hooks.hook('evlog:drain', createOTLPDrain({
|
|
54
|
+
* endpoint: 'http://localhost:4318',
|
|
55
|
+
* }))
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
declare function createOTLPDrain(overrides?: Partial<OTLPConfig>): (ctx: DrainContext) => Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Send a single event to an OTLP endpoint.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* await sendToOTLP(event, {
|
|
65
|
+
* endpoint: 'http://localhost:4318',
|
|
66
|
+
* })
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
declare function sendToOTLP(event: WideEvent, config: OTLPConfig): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Send a batch of events to an OTLP endpoint.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* await sendBatchToOTLP(events, {
|
|
76
|
+
* endpoint: 'http://localhost:4318',
|
|
77
|
+
* })
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
declare function sendBatchToOTLP(events: WideEvent[], config: OTLPConfig): Promise<void>;
|
|
81
|
+
|
|
82
|
+
export { createOTLPDrain, sendBatchToOTLP, sendToOTLP, toOTLPLogRecord };
|
|
83
|
+
export type { OTLPConfig, OTLPLogRecord };
|