@safaricom-mxl/log 0.0.4 → 0.0.5
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 +121 -40
- package/dist/{_http-CTtzGDc6.mjs → _http-BpkAshj6.mjs} +2 -2
- package/dist/_http-BpkAshj6.mjs.map +1 -0
- package/dist/{_severity-CLNgC2HU.mjs → _severity-Q1BuITU_.mjs} +1 -1
- package/dist/{_severity-CLNgC2HU.mjs.map → _severity-Q1BuITU_.mjs.map} +1 -1
- package/dist/adapters/axiom.d.mts +3 -3
- package/dist/adapters/axiom.mjs +6 -6
- package/dist/adapters/axiom.mjs.map +1 -1
- package/dist/adapters/better-stack.d.mts +4 -4
- package/dist/adapters/better-stack.mjs +5 -5
- package/dist/adapters/better-stack.mjs.map +1 -1
- package/dist/adapters/otlp.d.mts +4 -4
- package/dist/adapters/otlp.mjs +17 -13
- package/dist/adapters/otlp.mjs.map +1 -1
- package/dist/adapters/posthog.d.mts +5 -5
- package/dist/adapters/posthog.mjs +7 -7
- package/dist/adapters/posthog.mjs.map +1 -1
- package/dist/adapters/sentry.d.mts +3 -3
- package/dist/adapters/sentry.mjs +7 -7
- package/dist/adapters/sentry.mjs.map +1 -1
- package/dist/browser.d.mts +1 -1
- package/dist/browser.mjs +2 -2
- package/dist/browser.mjs.map +1 -1
- package/dist/{dist-Dalk68oO.mjs → dist-BsWcv7B8.mjs} +1 -1
- package/dist/{dist-Dalk68oO.mjs.map → dist-BsWcv7B8.mjs.map} +1 -1
- package/dist/elysia/index.d.mts +2 -2
- package/dist/elysia/index.mjs +2 -2
- package/dist/elysia/index.mjs.map +1 -1
- package/dist/enrichers.d.mts +1 -1
- package/dist/{error-IMQ3w1i0.d.mts → error-D4JQHcSq.d.mts} +2 -2
- package/dist/{error-IMQ3w1i0.d.mts.map → error-D4JQHcSq.d.mts.map} +1 -1
- package/dist/error.d.mts +1 -1
- package/dist/errors-BP9QdDrY.mjs +18 -0
- package/dist/errors-BP9QdDrY.mjs.map +1 -0
- package/dist/errors-DL0gLqac.d.mts +39 -0
- package/dist/errors-DL0gLqac.d.mts.map +1 -0
- package/dist/express/index.d.mts +4 -4
- package/dist/express/index.mjs +4 -4
- package/dist/express/index.mjs.map +1 -1
- package/dist/fastify/index.d.mts +5 -5
- package/dist/fastify/index.mjs +6 -6
- package/dist/fastify/index.mjs.map +1 -1
- package/dist/{headers-BnDMFr6p.mjs → headers-Crw-o-No.mjs} +7 -5
- package/dist/headers-Crw-o-No.mjs.map +1 -0
- package/dist/hono/index.d.mts +4 -4
- package/dist/hono/index.mjs +3 -3
- package/dist/hono/index.mjs.map +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/{logger-B6moO_0c.d.mts → logger-Dwb7G3sg.d.mts} +2 -2
- package/dist/{logger-B6moO_0c.d.mts.map → logger-Dwb7G3sg.d.mts.map} +1 -1
- package/dist/logger.d.mts +1 -1
- package/dist/logger.mjs +45 -2
- package/dist/logger.mjs.map +1 -1
- package/dist/middleware-Cbh6eBkJ.d.mts +75 -0
- package/dist/middleware-Cbh6eBkJ.d.mts.map +1 -0
- package/dist/nestjs/index.d.mts +6 -6
- package/dist/nestjs/index.mjs +5 -5
- package/dist/nestjs/index.mjs.map +1 -1
- package/dist/next/client.d.mts +2 -2
- package/dist/next/client.mjs +1 -1
- package/dist/next/client.mjs.map +1 -1
- package/dist/next/index.d.mts +9 -9
- package/dist/next/index.d.mts.map +1 -1
- package/dist/next/index.mjs +12 -12
- package/dist/next/index.mjs.map +1 -1
- package/dist/nitro/errorHandler.mjs +2 -1
- package/dist/nitro/errorHandler.mjs.map +1 -1
- package/dist/nitro/module.d.mts +2 -2
- package/dist/nitro/module.mjs.map +1 -1
- package/dist/nitro/plugin.mjs +8 -8
- package/dist/nitro/plugin.mjs.map +1 -1
- package/dist/nitro/v3/errorHandler.mjs +3 -2
- package/dist/nitro/v3/errorHandler.mjs.map +1 -1
- package/dist/nitro/v3/middleware.d.mts +1 -1
- package/dist/nitro/v3/middleware.mjs +1 -1
- package/dist/nitro/v3/middleware.mjs.map +1 -1
- package/dist/nitro/v3/module.d.mts +1 -1
- package/dist/nitro/v3/module.mjs +3 -1
- package/dist/nitro/v3/module.mjs.map +1 -1
- package/dist/nitro/v3/plugin.d.mts +4 -2
- package/dist/nitro/v3/plugin.mjs +11 -11
- package/dist/nitro/v3/plugin.mjs.map +1 -1
- package/dist/nitro/v3/useLogger.d.mts +1 -1
- package/dist/nitro/v3/useLogger.mjs +1 -1
- package/dist/nitro/v3/useLogger.mjs.map +1 -1
- package/dist/{nitro-BsDTWASN.mjs → nitro-BYA2IdQR.mjs} +4 -9
- package/dist/nitro-BYA2IdQR.mjs.map +1 -0
- package/dist/{nitro-iuEZtGVq.d.mts → nitro-Bqj5GhQh.d.mts} +2 -2
- package/dist/nitro-Bqj5GhQh.d.mts.map +1 -0
- package/dist/nuxt/module.d.mts +3 -3
- package/dist/nuxt/module.mjs +4 -4
- package/dist/nuxt/module.mjs.map +1 -1
- package/dist/{parseError-DGjBRrb3.d.mts → parseError-Ljsd4G0m.d.mts} +2 -2
- package/dist/parseError-Ljsd4G0m.d.mts.map +1 -0
- package/dist/pipeline.d.mts +1 -1
- package/dist/pipeline.mjs +7 -7
- package/dist/pipeline.mjs.map +1 -1
- package/dist/{routes-DdmLpEnG.mjs → routes-CE3_c-iZ.mjs} +1 -1
- package/dist/{routes-DdmLpEnG.mjs.map → routes-CE3_c-iZ.mjs.map} +1 -1
- package/dist/runtime/client/log.d.mts +1 -1
- package/dist/runtime/client/log.d.mts.map +1 -1
- package/dist/runtime/client/log.mjs +5 -11
- package/dist/runtime/client/log.mjs.map +1 -1
- package/dist/runtime/server/useLogger.d.mts +1 -1
- package/dist/runtime/server/useLogger.mjs +1 -1
- package/dist/runtime/server/useLogger.mjs.map +1 -1
- package/dist/runtime/utils/parseError.d.mts +2 -2
- package/dist/{storage-CZLHKrbu.mjs → storage-CxDBSIoI.mjs} +6 -4
- package/dist/storage-CxDBSIoI.mjs.map +1 -0
- package/dist/sveltekit/index.d.mts +11 -11
- package/dist/sveltekit/index.mjs +13 -12
- package/dist/sveltekit/index.mjs.map +1 -1
- package/dist/toolkit.d.mts +38 -0
- package/dist/toolkit.d.mts.map +1 -0
- package/dist/toolkit.mjs +5 -0
- package/dist/{types-abpnM9XB.d.mts → types-B82IuY7M.d.mts} +18 -18
- package/dist/types-B82IuY7M.d.mts.map +1 -0
- package/dist/types.d.mts +1 -1
- package/dist/{useLogger-DW6-kpBP.d.mts → useLogger-B4LUeTYl.d.mts} +2 -2
- package/dist/{useLogger-DW6-kpBP.d.mts.map → useLogger-B4LUeTYl.d.mts.map} +1 -1
- package/dist/utils.d.mts +18 -2
- package/dist/utils.d.mts.map +1 -1
- package/dist/utils.mjs +27 -1
- package/dist/utils.mjs.map +1 -1
- package/dist/workers.d.mts +2 -2
- package/dist/workers.mjs +1 -1
- package/dist/workers.mjs.map +1 -1
- package/package.json +76 -34
- package/dist/_http-CTtzGDc6.mjs.map +0 -1
- package/dist/headers-BnDMFr6p.mjs.map +0 -1
- package/dist/middleware-CVOU14aj.d.mts +0 -36
- package/dist/middleware-CVOU14aj.d.mts.map +0 -1
- package/dist/nitro-BsDTWASN.mjs.map +0 -1
- package/dist/nitro-iuEZtGVq.d.mts.map +0 -1
- package/dist/parseError-DGjBRrb3.d.mts.map +0 -1
- package/dist/storage-CZLHKrbu.mjs.map +0 -1
- package/dist/types-abpnM9XB.d.mts.map +0 -1
package/README.md
CHANGED
|
@@ -203,7 +203,7 @@ export default defineNitroConfig({
|
|
|
203
203
|
})
|
|
204
204
|
```
|
|
205
205
|
|
|
206
|
-
Then use `useLogger` in any route. Import from
|
|
206
|
+
Then use `useLogger` in any route. Import from `@safaricom-mxl/log/nitro/v3` (v3) or `@safaricom-mxl/log/nitro` (v2):
|
|
207
207
|
|
|
208
208
|
```typescript
|
|
209
209
|
// routes/api/documents/[id]/export.post.ts
|
|
@@ -375,43 +375,116 @@ Notes:
|
|
|
375
375
|
|
|
376
376
|
## Hono
|
|
377
377
|
|
|
378
|
-
Use the standalone API to create one wide event per request from a Hono middleware.
|
|
379
|
-
|
|
380
378
|
```typescript
|
|
381
379
|
// src/index.ts
|
|
382
|
-
import { serve } from '@hono/node-server'
|
|
383
380
|
import { Hono } from 'hono'
|
|
384
|
-
import {
|
|
381
|
+
import { initLogger } from '@safaricom-mxl/log'
|
|
382
|
+
import { mxllog, type MxllogVariables } from '@safaricom-mxl/log/hono'
|
|
385
383
|
|
|
386
|
-
initLogger({
|
|
387
|
-
|
|
384
|
+
initLogger({ env: { service: 'hono-api' } })
|
|
385
|
+
|
|
386
|
+
const app = new Hono<MxllogVariables>()
|
|
387
|
+
app.use(mxllog())
|
|
388
|
+
|
|
389
|
+
app.get('/api/users', (c) => {
|
|
390
|
+
const log = c.get('log')
|
|
391
|
+
log.set({ users: { count: 42 } })
|
|
392
|
+
return c.json({ users: [] })
|
|
388
393
|
})
|
|
394
|
+
```
|
|
389
395
|
|
|
390
|
-
|
|
396
|
+
See the full [hono example](https://github.com/HugoRCD/mxllog/tree/main/examples/hono) for a complete working project.
|
|
391
397
|
|
|
392
|
-
|
|
393
|
-
const startedAt = Date.now()
|
|
394
|
-
const log = createRequestLogger({ method: c.req.method, path: c.req.path })
|
|
398
|
+
## Express
|
|
395
399
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
400
|
+
```typescript
|
|
401
|
+
// src/index.ts
|
|
402
|
+
import express from 'express'
|
|
403
|
+
import { initLogger } from '@safaricom-mxl/log'
|
|
404
|
+
import { mxllog, useLogger } from '@safaricom-mxl/log/express'
|
|
405
|
+
|
|
406
|
+
initLogger({ env: { service: 'express-api' } })
|
|
407
|
+
|
|
408
|
+
const app = express()
|
|
409
|
+
app.use(mxllog())
|
|
410
|
+
|
|
411
|
+
app.get('/api/users', (req, res) => {
|
|
412
|
+
req.log.set({ users: { count: 42 } })
|
|
413
|
+
res.json({ users: [] })
|
|
407
414
|
})
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Use `useLogger()` to access the logger from anywhere in the call stack without passing `req`.
|
|
418
|
+
|
|
419
|
+
See the full [express example](https://github.com/HugoRCD/mxllog/tree/main/examples/express) for a complete working project.
|
|
420
|
+
|
|
421
|
+
## Fastify
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
// src/index.ts
|
|
425
|
+
import Fastify from 'fastify'
|
|
426
|
+
import { initLogger } from '@safaricom-mxl/log'
|
|
427
|
+
import { mxllog, useLogger } from '@safaricom-mxl/log/fastify'
|
|
428
|
+
|
|
429
|
+
initLogger({ env: { service: 'fastify-api' } })
|
|
408
430
|
|
|
409
|
-
app
|
|
431
|
+
const app = Fastify({ logger: false })
|
|
432
|
+
await app.register(mxllog)
|
|
410
433
|
|
|
411
|
-
|
|
434
|
+
app.get('/api/users', async (request) => {
|
|
435
|
+
request.log.set({ users: { count: 42 } })
|
|
436
|
+
return { users: [] }
|
|
437
|
+
})
|
|
412
438
|
```
|
|
413
439
|
|
|
414
|
-
|
|
440
|
+
`request.log` is the mxllog wide-event logger (shadows Fastify's built-in pino logger on the request). Use `useLogger()` to access the logger from anywhere in the call stack.
|
|
441
|
+
|
|
442
|
+
See the full [fastify example](https://github.com/HugoRCD/mxllog/tree/main/examples/fastify) for a complete working project.
|
|
443
|
+
|
|
444
|
+
## Elysia
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
// src/index.ts
|
|
448
|
+
import { Elysia } from 'elysia'
|
|
449
|
+
import { initLogger } from '@safaricom-mxl/log'
|
|
450
|
+
import { mxllog, useLogger } from '@safaricom-mxl/log/elysia'
|
|
451
|
+
|
|
452
|
+
initLogger({ env: { service: 'elysia-api' } })
|
|
453
|
+
|
|
454
|
+
const app = new Elysia()
|
|
455
|
+
.use(mxllog())
|
|
456
|
+
.get('/api/users', ({ log }) => {
|
|
457
|
+
log.set({ users: { count: 42 } })
|
|
458
|
+
return { users: [] }
|
|
459
|
+
})
|
|
460
|
+
.listen(3000)
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
Use `useLogger()` to access the logger from anywhere in the call stack.
|
|
464
|
+
|
|
465
|
+
See the full [elysia example](https://github.com/HugoRCD/mxllog/tree/main/examples/elysia) for a complete working project.
|
|
466
|
+
|
|
467
|
+
## NestJS
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
// src/app.module.ts
|
|
471
|
+
import { Module } from '@nestjs/common'
|
|
472
|
+
import { MxllogModule } from '@safaricom-mxl/log/nestjs'
|
|
473
|
+
|
|
474
|
+
@Module({
|
|
475
|
+
imports: [MxllogModule.forRoot()],
|
|
476
|
+
})
|
|
477
|
+
export class AppModule {}
|
|
478
|
+
|
|
479
|
+
// In any controller or service:
|
|
480
|
+
import { useLogger } from '@safaricom-mxl/log/nestjs'
|
|
481
|
+
const log = useLogger()
|
|
482
|
+
log.set({ users: { count: 42 } })
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
`MxllogModule.forRoot()` registers a global middleware that creates a request-scoped logger for every request. Use `useLogger()` to access it anywhere in the call stack, or `req.log` directly. Supports `forRootAsync()` for async configuration.
|
|
486
|
+
|
|
487
|
+
See the full [nestjs example](https://github.com/HugoRCD/mxllog/tree/main/examples/nestjs) for a complete working project.
|
|
415
488
|
|
|
416
489
|
## Browser
|
|
417
490
|
|
|
@@ -504,7 +577,7 @@ Use the `mxllog:enrich` hook to add derived context after emit, before drain.
|
|
|
504
577
|
```typescript
|
|
505
578
|
// server/plugins/mxllog-enrich.ts
|
|
506
579
|
export default defineNitroPlugin((nitroApp) => {
|
|
507
|
-
nitroApp.hooks.hook('
|
|
580
|
+
nitroApp.hooks.hook('mxllog:enrich', (ctx) => {
|
|
508
581
|
ctx.event.deploymentId = process.env.DEPLOYMENT_ID
|
|
509
582
|
})
|
|
510
583
|
})
|
|
@@ -529,7 +602,7 @@ export default defineNitroPlugin((nitroApp) => {
|
|
|
529
602
|
createTraceContextEnricher(),
|
|
530
603
|
]
|
|
531
604
|
|
|
532
|
-
nitroApp.hooks.hook('
|
|
605
|
+
nitroApp.hooks.hook('mxllog:enrich', (ctx) => {
|
|
533
606
|
for (const enricher of enrich) enricher(ctx)
|
|
534
607
|
})
|
|
535
608
|
})
|
|
@@ -573,7 +646,7 @@ Example custom enricher:
|
|
|
573
646
|
```typescript
|
|
574
647
|
// server/plugins/mxllog-enrich.ts
|
|
575
648
|
export default defineNitroPlugin((nitroApp) => {
|
|
576
|
-
nitroApp.hooks.hook('
|
|
649
|
+
nitroApp.hooks.hook('mxllog:enrich', (ctx) => {
|
|
577
650
|
// Add deployment metadata
|
|
578
651
|
ctx.event.deploymentId = process.env.DEPLOYMENT_ID
|
|
579
652
|
ctx.event.region = process.env.FLY_REGION
|
|
@@ -598,7 +671,7 @@ Send your logs to external observability platforms with built-in adapters.
|
|
|
598
671
|
import { createAxiomDrain } from '@safaricom-mxl/log/axiom'
|
|
599
672
|
|
|
600
673
|
export default defineNitroPlugin((nitroApp) => {
|
|
601
|
-
nitroApp.hooks.hook('
|
|
674
|
+
nitroApp.hooks.hook('mxllog:drain', createAxiomDrain())
|
|
602
675
|
})
|
|
603
676
|
```
|
|
604
677
|
|
|
@@ -618,7 +691,7 @@ Works with Grafana, Datadog, Honeycomb, and any OTLP-compatible backend.
|
|
|
618
691
|
import { createOTLPDrain } from '@safaricom-mxl/log/otlp'
|
|
619
692
|
|
|
620
693
|
export default defineNitroPlugin((nitroApp) => {
|
|
621
|
-
nitroApp.hooks.hook('
|
|
694
|
+
nitroApp.hooks.hook('mxllog:drain', createOTLPDrain())
|
|
622
695
|
})
|
|
623
696
|
```
|
|
624
697
|
|
|
@@ -635,7 +708,7 @@ NUXT_OTLP_ENDPOINT=http://localhost:4318
|
|
|
635
708
|
import { createPostHogDrain } from '@safaricom-mxl/log/posthog'
|
|
636
709
|
|
|
637
710
|
export default defineNitroPlugin((nitroApp) => {
|
|
638
|
-
nitroApp.hooks.hook('
|
|
711
|
+
nitroApp.hooks.hook('mxllog:drain', createPostHogDrain())
|
|
639
712
|
})
|
|
640
713
|
```
|
|
641
714
|
|
|
@@ -653,7 +726,7 @@ NUXT_POSTHOG_HOST=https://us.i.posthog.com # Optional: for EU or self-hosted
|
|
|
653
726
|
import { createSentryDrain } from '@safaricom-mxl/log/sentry'
|
|
654
727
|
|
|
655
728
|
export default defineNitroPlugin((nitroApp) => {
|
|
656
|
-
nitroApp.hooks.hook('
|
|
729
|
+
nitroApp.hooks.hook('mxllog:drain', createSentryDrain())
|
|
657
730
|
})
|
|
658
731
|
```
|
|
659
732
|
|
|
@@ -670,7 +743,7 @@ NUXT_SENTRY_DSN=https://public@o0.ingest.sentry.io/123
|
|
|
670
743
|
import { createBetterStackDrain } from '@safaricom-mxl/log/better-stack'
|
|
671
744
|
|
|
672
745
|
export default defineNitroPlugin((nitroApp) => {
|
|
673
|
-
nitroApp.hooks.hook('
|
|
746
|
+
nitroApp.hooks.hook('mxllog:drain', createBetterStackDrain())
|
|
674
747
|
})
|
|
675
748
|
```
|
|
676
749
|
|
|
@@ -693,7 +766,7 @@ export default defineNitroPlugin((nitroApp) => {
|
|
|
693
766
|
const axiom = createAxiomDrain()
|
|
694
767
|
const otlp = createOTLPDrain()
|
|
695
768
|
|
|
696
|
-
nitroApp.hooks.hook('
|
|
769
|
+
nitroApp.hooks.hook('mxllog:drain', async (ctx) => {
|
|
697
770
|
await Promise.allSettled([axiom(ctx), otlp(ctx)])
|
|
698
771
|
})
|
|
699
772
|
})
|
|
@@ -706,7 +779,7 @@ Build your own adapter for any destination:
|
|
|
706
779
|
```typescript
|
|
707
780
|
// server/plugins/mxllog-drain.ts
|
|
708
781
|
export default defineNitroPlugin((nitroApp) => {
|
|
709
|
-
nitroApp.hooks.hook('
|
|
782
|
+
nitroApp.hooks.hook('mxllog:drain', async (ctx) => {
|
|
710
783
|
await fetch('https://your-service.com/logs', {
|
|
711
784
|
method: 'POST',
|
|
712
785
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -741,7 +814,7 @@ export default defineNitroPlugin((nitroApp) => {
|
|
|
741
814
|
|
|
742
815
|
const drain = pipeline(createAxiomDrain())
|
|
743
816
|
|
|
744
|
-
nitroApp.hooks.hook('
|
|
817
|
+
nitroApp.hooks.hook('mxllog:drain', drain)
|
|
745
818
|
nitroApp.hooks.hook('close', () => drain.flush())
|
|
746
819
|
})
|
|
747
820
|
```
|
|
@@ -859,7 +932,7 @@ For business-specific conditions (premium users, feature flags), use the `mxllog
|
|
|
859
932
|
```typescript
|
|
860
933
|
// server/plugins/mxllog-custom.ts
|
|
861
934
|
export default defineNitroPlugin((nitroApp) => {
|
|
862
|
-
nitroApp.hooks.hook('
|
|
935
|
+
nitroApp.hooks.hook('mxllog:emit:keep', (ctx) => {
|
|
863
936
|
// Always keep logs for premium users
|
|
864
937
|
if (ctx.context.user?.premium) {
|
|
865
938
|
ctx.shouldKeep = true
|
|
@@ -940,7 +1013,7 @@ log.emit({ status: 200 })
|
|
|
940
1013
|
|
|
941
1014
|
### `createError(options)`
|
|
942
1015
|
|
|
943
|
-
Create a structured error with HTTP status support. Import from `
|
|
1016
|
+
Create a structured error with HTTP status support. Import from `@safaricom-mxl/log` directly to avoid conflicts with Nuxt/Nitro's `createError`.
|
|
944
1017
|
|
|
945
1018
|
> **Note**: `createMxllogError` is also available as an auto-imported alias in Nuxt/Nitro to avoid conflicts.
|
|
946
1019
|
|
|
@@ -991,16 +1064,24 @@ try {
|
|
|
991
1064
|
|-----------|-------------|
|
|
992
1065
|
| **Nuxt** | `modules: ['@safaricom-mxl/log/nuxt']` |
|
|
993
1066
|
| **Next.js** | `createMxllog()` factory with `import { createMxllog } from '@safaricom-mxl/log/next'` ([example](./examples/nextjs)) |
|
|
1067
|
+
| **SvelteKit** | `export const { handle, handleError } = createMxllogHooks()` with `import { createMxllogHooks } from '@safaricom-mxl/log/sveltekit'` ([example](./examples/sveltekit)) |
|
|
994
1068
|
| **Nitro v3** | `modules: [mxllog()]` with `import mxllog from '@safaricom-mxl/log/nitro/v3'` |
|
|
995
1069
|
| **Nitro v2** | `modules: [mxllog()]` with `import mxllog from '@safaricom-mxl/log/nitro'` |
|
|
1070
|
+
| **TanStack Start** | Nitro v3 module setup ([example](./examples/tanstack-start)) |
|
|
1071
|
+
| **NestJS** | `MxllogModule.forRoot()` with `import { MxllogModule } from '@safaricom-mxl/log/nestjs'` ([example](./examples/nestjs)) |
|
|
1072
|
+
| **Express** | `app.use(mxllog())` with `import { mxllog } from '@safaricom-mxl/log/express'` ([example](./examples/express)) |
|
|
1073
|
+
| **Hono** | `app.use(mxllog())` with `import { mxllog } from '@safaricom-mxl/log/hono'` ([example](./examples/hono)) |
|
|
1074
|
+
| **Fastify** | `app.register(mxllog)` with `import { mxllog } from '@safaricom-mxl/log/fastify'` ([example](./examples/fastify)) |
|
|
1075
|
+
| **Elysia** | `.use(mxllog())` with `import { mxllog } from '@safaricom-mxl/log/elysia'` ([example](./examples/elysia)) |
|
|
1076
|
+
| **Cloudflare Workers** | Manual setup with `import { initLogger, createRequestLogger } from '@safaricom-mxl/log'` ([example](./examples/workers)) |
|
|
1077
|
+
| **Custom** | Build your own with `import { createMiddlewareLogger } from '@safaricom-mxl/log/toolkit'` ([guide](https://mxllog.dev/frameworks/custom-integration)) |
|
|
996
1078
|
| **Analog** | Nitro v2 module setup |
|
|
997
1079
|
| **Vinxi** | Nitro v2 module setup |
|
|
998
1080
|
| **SolidStart** | Nitro v2 module setup ([example](./examples/solidstart)) |
|
|
999
|
-
| **TanStack Start** | Nitro v3 module setup ([example](./examples/tanstack-start)) |
|
|
1000
1081
|
|
|
1001
1082
|
## Agent Skills
|
|
1002
1083
|
|
|
1003
|
-
mxllog provides [Agent Skills](https://
|
|
1084
|
+
mxllog provides [Agent Skills](https://www.mxllog.dev/getting-started/agent-skills) to help AI coding assistants understand and implement proper logging patterns in your codebase.
|
|
1004
1085
|
|
|
1005
1086
|
### Installation
|
|
1006
1087
|
|
|
@@ -41,7 +41,7 @@ function defineDrain(options) {
|
|
|
41
41
|
try {
|
|
42
42
|
await options.send(contexts.map((c) => c.event), config);
|
|
43
43
|
} catch (error) {
|
|
44
|
-
console.error(`[
|
|
44
|
+
console.error(`[mxllog/${options.name}] Failed to send events:`, error);
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
47
|
}
|
|
@@ -69,4 +69,4 @@ async function httpPost({ url, headers, body, timeout, label }) {
|
|
|
69
69
|
//#endregion
|
|
70
70
|
export { defineDrain as n, resolveAdapterConfig as r, httpPost as t };
|
|
71
71
|
|
|
72
|
-
//# sourceMappingURL=_http-
|
|
72
|
+
//# sourceMappingURL=_http-BpkAshj6.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_http-BpkAshj6.mjs","names":[],"sources":["../src/adapters/_config.ts","../src/adapters/_drain.ts","../src/adapters/_http.ts"],"sourcesContent":["/**\n * Try to get runtime config from Nitro/Nuxt environment.\n * Returns undefined if not in a Nitro context.\n */\nexport function getRuntimeConfig(): Record<string, any> | undefined {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { useRuntimeConfig } = require('nitropack/runtime')\n return useRuntimeConfig()\n } catch {\n return undefined\n }\n}\n\nexport interface ConfigField<T> {\n key: keyof T & string\n env?: string[]\n}\n\nexport function resolveAdapterConfig<T>(\n namespace: string,\n fields: ConfigField<T>[],\n overrides?: Partial<T>,\n): Partial<T> {\n const runtimeConfig = getRuntimeConfig()\n const mxllogNs = runtimeConfig?.mxllog?.[namespace]\n const rootNs = runtimeConfig?.[namespace]\n\n const config: Record<string, unknown> = {}\n\n for (const { key, env } of fields) {\n config[key] =\n overrides?.[key]\n ?? mxllogNs?.[key]\n ?? rootNs?.[key]\n ?? resolveEnv(env)\n }\n\n return config as Partial<T>\n}\n\nfunction resolveEnv(envKeys?: string[]): string | undefined {\n if (!envKeys) return undefined\n for (const key of envKeys) {\n const val = process.env[key]\n if (val) return val\n }\n return undefined\n}\n","import type { DrainContext, WideEvent } from '../types'\n\nexport interface DrainOptions<TConfig> {\n name: string\n resolve: () => TConfig | null\n send: (events: WideEvent[], config: TConfig) => Promise<void>\n}\n\nexport function defineDrain<TConfig>(options: DrainOptions<TConfig>): (ctx: DrainContext | DrainContext[]) => Promise<void> {\n return async (ctx: DrainContext | DrainContext[]) => {\n const contexts = Array.isArray(ctx) ? ctx : [ctx]\n if (contexts.length === 0) return\n\n const config = options.resolve()\n if (!config) return\n\n try {\n await options.send(contexts.map(c => c.event), config)\n } catch (error) {\n console.error(`[mxllog/${options.name}] Failed to send events:`, error)\n }\n }\n}\n","export interface HttpPostOptions {\n url: string\n headers: Record<string, string>\n body: string\n timeout: number\n label: string\n}\n\nexport async function httpPost({ url, headers, body, timeout, label }: HttpPostOptions): Promise<void> {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body,\n signal: controller.signal,\n })\n\n if (!response.ok) {\n const text = await response.text().catch(() => 'Unknown error')\n const safeText = text.length > 200 ? `${text.slice(0, 200)}...[truncated]` : text\n throw new Error(`${label} API error: ${response.status} ${response.statusText} - ${safeText}`)\n }\n } finally {\n clearTimeout(timeoutId)\n }\n}\n"],"mappings":";;;;;;;;;AAIA,SAAgB,mBAAoD;AAClE,KAAI;EAEF,MAAM,EAAE,qBAAA,UAA6B,oBAAoB;AACzD,SAAO,kBAAkB;SACnB;AACN;;;AASJ,SAAgB,qBACd,WACA,QACA,WACY;CACZ,MAAM,gBAAgB,kBAAkB;CACxC,MAAM,WAAW,eAAe,SAAS;CACzC,MAAM,SAAS,gBAAgB;CAE/B,MAAM,SAAkC,EAAE;AAE1C,MAAK,MAAM,EAAE,KAAK,SAAS,OACzB,QAAO,OACL,YAAY,QACT,WAAW,QACX,SAAS,QACT,WAAW,IAAI;AAGtB,QAAO;;AAGT,SAAS,WAAW,SAAwC;AAC1D,KAAI,CAAC,QAAS,QAAO,KAAA;AACrB,MAAK,MAAM,OAAO,SAAS;EACzB,MAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,IAAK,QAAO;;;;;ACrCpB,SAAgB,YAAqB,SAAuF;AAC1H,QAAO,OAAO,QAAuC;EACnD,MAAM,WAAW,MAAM,QAAQ,IAAI,GAAG,MAAM,CAAC,IAAI;AACjD,MAAI,SAAS,WAAW,EAAG;EAE3B,MAAM,SAAS,QAAQ,SAAS;AAChC,MAAI,CAAC,OAAQ;AAEb,MAAI;AACF,SAAM,QAAQ,KAAK,SAAS,KAAI,MAAK,EAAE,MAAM,EAAE,OAAO;WAC/C,OAAO;AACd,WAAQ,MAAM,WAAW,QAAQ,KAAK,2BAA2B,MAAM;;;;;;ACX7E,eAAsB,SAAS,EAAE,KAAK,SAAS,MAAM,SAAS,SAAyC;CACrG,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,YAAY,iBAAiB,WAAW,OAAO,EAAE,QAAQ;AAE/D,KAAI;EACF,MAAM,WAAW,MAAM,MAAM,KAAK;GAChC,QAAQ;GACR;GACA;GACA,QAAQ,WAAW;GACpB,CAAC;AAEF,MAAI,CAAC,SAAS,IAAI;GAChB,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,YAAY,gBAAgB;GAC/D,MAAM,WAAW,KAAK,SAAS,MAAM,GAAG,KAAK,MAAM,GAAG,IAAI,CAAC,kBAAkB;AAC7E,SAAM,IAAI,MAAM,GAAG,MAAM,cAAc,SAAS,OAAO,GAAG,SAAS,WAAW,KAAK,WAAW;;WAExF;AACR,eAAa,UAAU"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_severity-
|
|
1
|
+
{"version":3,"file":"_severity-Q1BuITU_.mjs","names":[],"sources":["../src/adapters/_severity.ts"],"sourcesContent":["import type { LogLevel } from '../types'\n\nexport const OTEL_SEVERITY_NUMBER: Record<LogLevel, number> = {\n debug: 5,\n info: 9,\n warn: 13,\n error: 17,\n}\n\nexport const OTEL_SEVERITY_TEXT: Record<LogLevel, string> = {\n debug: 'DEBUG',\n info: 'INFO',\n warn: 'WARN',\n error: 'ERROR',\n}\n"],"mappings":";AAEA,MAAa,uBAAiD;CAC5D,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACR;AAED,MAAa,qBAA+C;CAC1D,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACR"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { T as WideEvent, r as DrainContext } from "../types-
|
|
1
|
+
import { T as WideEvent, r as DrainContext } from "../types-B82IuY7M.mjs";
|
|
2
2
|
//#region src/adapters/axiom.d.ts
|
|
3
3
|
interface BaseAxiomConfig {
|
|
4
4
|
/** Axiom dataset name */
|
|
@@ -39,10 +39,10 @@ type AxiomConfig = BaseAxiomConfig & (EdgeAxiomConfig | EndpointAxiomConfig);
|
|
|
39
39
|
* @example
|
|
40
40
|
* ```ts
|
|
41
41
|
* // Zero config - just set NUXT_AXIOM_TOKEN and NUXT_AXIOM_DATASET env vars
|
|
42
|
-
* nitroApp.hooks.hook('
|
|
42
|
+
* nitroApp.hooks.hook('mxllog:drain', createAxiomDrain())
|
|
43
43
|
*
|
|
44
44
|
* // With overrides
|
|
45
|
-
* nitroApp.hooks.hook('
|
|
45
|
+
* nitroApp.hooks.hook('mxllog:drain', createAxiomDrain({
|
|
46
46
|
* dataset: 'my-dataset',
|
|
47
47
|
* }))
|
|
48
48
|
* ```
|
package/dist/adapters/axiom.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as defineDrain, r as resolveAdapterConfig, t as httpPost } from "../_http-
|
|
1
|
+
import { n as defineDrain, r as resolveAdapterConfig, t as httpPost } from "../_http-BpkAshj6.mjs";
|
|
2
2
|
//#region src/adapters/axiom.ts
|
|
3
3
|
const AXIOM_FIELDS = [
|
|
4
4
|
{
|
|
@@ -35,10 +35,10 @@ const AXIOM_FIELDS = [
|
|
|
35
35
|
* @example
|
|
36
36
|
* ```ts
|
|
37
37
|
* // Zero config - just set NUXT_AXIOM_TOKEN and NUXT_AXIOM_DATASET env vars
|
|
38
|
-
* nitroApp.hooks.hook('
|
|
38
|
+
* nitroApp.hooks.hook('mxllog:drain', createAxiomDrain())
|
|
39
39
|
*
|
|
40
40
|
* // With overrides
|
|
41
|
-
* nitroApp.hooks.hook('
|
|
41
|
+
* nitroApp.hooks.hook('mxllog:drain', createAxiomDrain({
|
|
42
42
|
* dataset: 'my-dataset',
|
|
43
43
|
* }))
|
|
44
44
|
* ```
|
|
@@ -49,11 +49,11 @@ function createAxiomDrain(overrides) {
|
|
|
49
49
|
resolve: () => {
|
|
50
50
|
const config = resolveAdapterConfig("axiom", AXIOM_FIELDS, overrides);
|
|
51
51
|
if (!config.dataset || !config.token) {
|
|
52
|
-
console.error("[
|
|
52
|
+
console.error("[mxllog/axiom] Missing dataset or token. Set NUXT_AXIOM_TOKEN/NUXT_AXIOM_DATASET env vars or pass to createAxiomDrain()");
|
|
53
53
|
return null;
|
|
54
54
|
}
|
|
55
55
|
if (config.edgeUrl && config.baseUrl) {
|
|
56
|
-
console.warn("[
|
|
56
|
+
console.warn("[mxllog/axiom] Both edgeUrl and baseUrl are set. edgeUrl takes precedence for ingest.");
|
|
57
57
|
delete config.baseUrl;
|
|
58
58
|
}
|
|
59
59
|
return config;
|
|
@@ -113,7 +113,7 @@ function resolveIngestUrl(config) {
|
|
|
113
113
|
parsed.pathname = parsed.pathname.replace(/\/+$/, "");
|
|
114
114
|
return parsed.toString();
|
|
115
115
|
} catch {
|
|
116
|
-
console.warn(`[
|
|
116
|
+
console.warn(`[mxllog/axiom] edgeUrl "${config.edgeUrl}" is not a valid URL, falling back to string concatenation.`);
|
|
117
117
|
return `${config.edgeUrl.replace(/\/+$/, "")}/v1/ingest/${encodedDataset}`;
|
|
118
118
|
}
|
|
119
119
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"axiom.mjs","names":[],"sources":["../../src/adapters/axiom.ts"],"sourcesContent":["import type { WideEvent } from '../types'\nimport type { ConfigField } from './_config'\nimport { resolveAdapterConfig } from './_config'\nimport { defineDrain } from './_drain'\nimport { httpPost } from './_http'\n\ninterface BaseAxiomConfig {\n /** Axiom dataset name */\n dataset: string\n /** Axiom API token */\n token: string\n /** Organization ID (required for Personal Access Tokens) */\n orgId?: string\n /** Request timeout in milliseconds. Default: 5000 */\n timeout?: number\n}\n\ninterface EdgeAxiomConfig {\n /**\n * Edge URL for Axiom ingest/query endpoints.\n * If no path is provided, uses /v1/ingest/{dataset}.\n * If a custom path is provided, it is used as-is (trailing slash trimmed).\n */\n edgeUrl: string\n /** Mutually exclusive with edgeUrl. */\n baseUrl?: never\n}\n\ninterface EndpointAxiomConfig {\n /** Base URL for Axiom API. Uses /v1/datasets/{dataset}/ingest. */\n baseUrl?: string\n /** Mutually exclusive with baseUrl. */\n edgeUrl?: never\n}\n\nexport type AxiomConfig = BaseAxiomConfig & (EdgeAxiomConfig | EndpointAxiomConfig)\n\ntype ResolvedAxiomConfig = BaseAxiomConfig & {\n edgeUrl?: string\n baseUrl?: string\n}\n\nconst AXIOM_FIELDS: ConfigField<ResolvedAxiomConfig>[] = [\n { key: 'dataset', env: ['NUXT_AXIOM_DATASET', 'AXIOM_DATASET'] },\n { key: 'token', env: ['NUXT_AXIOM_TOKEN', 'AXIOM_TOKEN'] },\n { key: 'orgId', env: ['NUXT_AXIOM_ORG_ID', 'AXIOM_ORG_ID'] },\n { key: 'edgeUrl', env: ['NUXT_AXIOM_EDGE_URL', 'AXIOM_EDGE_URL'] },\n { key: 'baseUrl', env: ['NUXT_AXIOM_URL', 'AXIOM_URL'] },\n { key: 'timeout' },\n]\n\n/**\n * Create a drain function for sending logs to Axiom.\n *\n * Configuration priority (highest to lowest):\n * 1. Overrides passed to createAxiomDrain()\n * 2. runtimeConfig.mxllog.axiom\n * 3. runtimeConfig.axiom\n * 4. Environment variables: NUXT_AXIOM_*, AXIOM_*\n *\n * @example\n * ```ts\n * // Zero config - just set NUXT_AXIOM_TOKEN and NUXT_AXIOM_DATASET env vars\n * nitroApp.hooks.hook('
|
|
1
|
+
{"version":3,"file":"axiom.mjs","names":[],"sources":["../../src/adapters/axiom.ts"],"sourcesContent":["import type { WideEvent } from '../types'\nimport type { ConfigField } from './_config'\nimport { resolveAdapterConfig } from './_config'\nimport { defineDrain } from './_drain'\nimport { httpPost } from './_http'\n\ninterface BaseAxiomConfig {\n /** Axiom dataset name */\n dataset: string\n /** Axiom API token */\n token: string\n /** Organization ID (required for Personal Access Tokens) */\n orgId?: string\n /** Request timeout in milliseconds. Default: 5000 */\n timeout?: number\n}\n\ninterface EdgeAxiomConfig {\n /**\n * Edge URL for Axiom ingest/query endpoints.\n * If no path is provided, uses /v1/ingest/{dataset}.\n * If a custom path is provided, it is used as-is (trailing slash trimmed).\n */\n edgeUrl: string\n /** Mutually exclusive with edgeUrl. */\n baseUrl?: never\n}\n\ninterface EndpointAxiomConfig {\n /** Base URL for Axiom API. Uses /v1/datasets/{dataset}/ingest. */\n baseUrl?: string\n /** Mutually exclusive with baseUrl. */\n edgeUrl?: never\n}\n\nexport type AxiomConfig = BaseAxiomConfig & (EdgeAxiomConfig | EndpointAxiomConfig)\n\ntype ResolvedAxiomConfig = BaseAxiomConfig & {\n edgeUrl?: string\n baseUrl?: string\n}\n\nconst AXIOM_FIELDS: ConfigField<ResolvedAxiomConfig>[] = [\n { key: 'dataset', env: ['NUXT_AXIOM_DATASET', 'AXIOM_DATASET'] },\n { key: 'token', env: ['NUXT_AXIOM_TOKEN', 'AXIOM_TOKEN'] },\n { key: 'orgId', env: ['NUXT_AXIOM_ORG_ID', 'AXIOM_ORG_ID'] },\n { key: 'edgeUrl', env: ['NUXT_AXIOM_EDGE_URL', 'AXIOM_EDGE_URL'] },\n { key: 'baseUrl', env: ['NUXT_AXIOM_URL', 'AXIOM_URL'] },\n { key: 'timeout' },\n]\n\n/**\n * Create a drain function for sending logs to Axiom.\n *\n * Configuration priority (highest to lowest):\n * 1. Overrides passed to createAxiomDrain()\n * 2. runtimeConfig.mxllog.axiom\n * 3. runtimeConfig.axiom\n * 4. Environment variables: NUXT_AXIOM_*, AXIOM_*\n *\n * @example\n * ```ts\n * // Zero config - just set NUXT_AXIOM_TOKEN and NUXT_AXIOM_DATASET env vars\n * nitroApp.hooks.hook('mxllog:drain', createAxiomDrain())\n *\n * // With overrides\n * nitroApp.hooks.hook('mxllog:drain', createAxiomDrain({\n * dataset: 'my-dataset',\n * }))\n * ```\n */\nexport function createAxiomDrain(overrides?: Partial<AxiomConfig>) {\n return defineDrain<AxiomConfig>({\n name: 'axiom',\n resolve: () => {\n const config = resolveAdapterConfig<ResolvedAxiomConfig>(\n 'axiom',\n AXIOM_FIELDS,\n overrides as Partial<ResolvedAxiomConfig>,\n )\n if (!config.dataset || !config.token) {\n console.error('[mxllog/axiom] Missing dataset or token. Set NUXT_AXIOM_TOKEN/NUXT_AXIOM_DATASET env vars or pass to createAxiomDrain()')\n return null\n }\n\n if (config.edgeUrl && config.baseUrl) {\n console.warn('[mxllog/axiom] Both edgeUrl and baseUrl are set. edgeUrl takes precedence for ingest.')\n delete config.baseUrl\n }\n\n return config as AxiomConfig\n },\n send: sendBatchToAxiom,\n })\n}\n\n/**\n * Send a single event to Axiom.\n *\n * @example\n * ```ts\n * await sendToAxiom(event, {\n * dataset: 'my-logs',\n * token: process.env.AXIOM_TOKEN!,\n * })\n * ```\n */\nexport async function sendToAxiom(event: WideEvent, config: AxiomConfig): Promise<void> {\n await sendBatchToAxiom([event], config)\n}\n\n/**\n * Send a batch of events to Axiom.\n *\n * @example\n * ```ts\n * await sendBatchToAxiom(events, {\n * dataset: 'my-logs',\n * token: process.env.AXIOM_TOKEN!,\n * })\n * ```\n */\nexport async function sendBatchToAxiom(events: WideEvent[], config: AxiomConfig): Promise<void> {\n const url = resolveIngestUrl(config)\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${config.token}`,\n }\n\n if (config.orgId) {\n headers['X-Axiom-Org-Id'] = config.orgId\n }\n\n await httpPost({\n url,\n headers,\n body: JSON.stringify(events),\n timeout: config.timeout ?? 5000,\n label: 'Axiom',\n })\n}\n\nfunction resolveIngestUrl(config: AxiomConfig): string {\n const encodedDataset = encodeURIComponent(config.dataset)\n\n if (!config.edgeUrl) {\n const baseUrl = config.baseUrl ?? 'https://api.axiom.co'\n return `${baseUrl}/v1/datasets/${encodedDataset}/ingest`\n }\n\n try {\n const parsed = new URL(config.edgeUrl)\n\n if (parsed.pathname === '' || parsed.pathname === '/') {\n parsed.pathname = `/v1/ingest/${encodedDataset}`\n return parsed.toString()\n }\n\n parsed.pathname = parsed.pathname.replace(/\\/+$/, '')\n return parsed.toString()\n } catch {\n console.warn(`[mxllog/axiom] edgeUrl \"${config.edgeUrl}\" is not a valid URL, falling back to string concatenation.`)\n const trimmed = config.edgeUrl.replace(/\\/+$/, '')\n return `${trimmed}/v1/ingest/${encodedDataset}`\n }\n}\n"],"mappings":";;AA0CA,MAAM,eAAmD;CACvD;EAAE,KAAK;EAAW,KAAK,CAAC,sBAAsB,gBAAgB;EAAE;CAChE;EAAE,KAAK;EAAS,KAAK,CAAC,oBAAoB,cAAc;EAAE;CAC1D;EAAE,KAAK;EAAS,KAAK,CAAC,qBAAqB,eAAe;EAAE;CAC5D;EAAE,KAAK;EAAW,KAAK,CAAC,uBAAuB,iBAAiB;EAAE;CAClE;EAAE,KAAK;EAAW,KAAK,CAAC,kBAAkB,YAAY;EAAE;CACxD,EAAE,KAAK,WAAW;CACnB;;;;;;;;;;;;;;;;;;;;;AAsBD,SAAgB,iBAAiB,WAAkC;AACjE,QAAO,YAAyB;EAC9B,MAAM;EACN,eAAe;GACb,MAAM,SAAS,qBACb,SACA,cACA,UACD;AACD,OAAI,CAAC,OAAO,WAAW,CAAC,OAAO,OAAO;AACpC,YAAQ,MAAM,0HAA0H;AACxI,WAAO;;AAGT,OAAI,OAAO,WAAW,OAAO,SAAS;AACpC,YAAQ,KAAK,wFAAwF;AACrG,WAAO,OAAO;;AAGhB,UAAO;;EAET,MAAM;EACP,CAAC;;;;;;;;;;;;;AAcJ,eAAsB,YAAY,OAAkB,QAAoC;AACtF,OAAM,iBAAiB,CAAC,MAAM,EAAE,OAAO;;;;;;;;;;;;;AAczC,eAAsB,iBAAiB,QAAqB,QAAoC;CAC9F,MAAM,MAAM,iBAAiB,OAAO;CAEpC,MAAM,UAAkC;EACtC,gBAAgB;EAChB,iBAAiB,UAAU,OAAO;EACnC;AAED,KAAI,OAAO,MACT,SAAQ,oBAAoB,OAAO;AAGrC,OAAM,SAAS;EACb;EACA;EACA,MAAM,KAAK,UAAU,OAAO;EAC5B,SAAS,OAAO,WAAW;EAC3B,OAAO;EACR,CAAC;;AAGJ,SAAS,iBAAiB,QAA6B;CACrD,MAAM,iBAAiB,mBAAmB,OAAO,QAAQ;AAEzD,KAAI,CAAC,OAAO,QAEV,QAAO,GADS,OAAO,WAAW,uBAChB,eAAe,eAAe;AAGlD,KAAI;EACF,MAAM,SAAS,IAAI,IAAI,OAAO,QAAQ;AAEtC,MAAI,OAAO,aAAa,MAAM,OAAO,aAAa,KAAK;AACrD,UAAO,WAAW,cAAc;AAChC,UAAO,OAAO,UAAU;;AAG1B,SAAO,WAAW,OAAO,SAAS,QAAQ,QAAQ,GAAG;AACrD,SAAO,OAAO,UAAU;SAClB;AACN,UAAQ,KAAK,2BAA2B,OAAO,QAAQ,6DAA6D;AAEpH,SAAO,GADS,OAAO,QAAQ,QAAQ,QAAQ,GAAG,CAChC,aAAa"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { T as WideEvent, r as DrainContext } from "../types-
|
|
1
|
+
import { T as WideEvent, r as DrainContext } from "../types-B82IuY7M.mjs";
|
|
2
2
|
//#region src/adapters/better-stack.d.ts
|
|
3
3
|
interface BetterStackConfig {
|
|
4
4
|
/** Better Stack source token */
|
|
@@ -9,7 +9,7 @@ interface BetterStackConfig {
|
|
|
9
9
|
timeout?: number;
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
-
* Transform an
|
|
12
|
+
* Transform an mxllog wide event into a Better Stack event.
|
|
13
13
|
* Maps `timestamp` to `dt` (Better Stack's expected field).
|
|
14
14
|
*/
|
|
15
15
|
declare function toBetterStackEvent(event: WideEvent): Record<string, unknown>;
|
|
@@ -25,10 +25,10 @@ declare function toBetterStackEvent(event: WideEvent): Record<string, unknown>;
|
|
|
25
25
|
* @example
|
|
26
26
|
* ```ts
|
|
27
27
|
* // Zero config - just set NUXT_BETTER_STACK_SOURCE_TOKEN env var
|
|
28
|
-
* nitroApp.hooks.hook('
|
|
28
|
+
* nitroApp.hooks.hook('mxllog:drain', createBetterStackDrain())
|
|
29
29
|
*
|
|
30
30
|
* // With overrides
|
|
31
|
-
* nitroApp.hooks.hook('
|
|
31
|
+
* nitroApp.hooks.hook('mxllog:drain', createBetterStackDrain({
|
|
32
32
|
* sourceToken: 'my-token',
|
|
33
33
|
* }))
|
|
34
34
|
* ```
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as defineDrain, r as resolveAdapterConfig, t as httpPost } from "../_http-
|
|
1
|
+
import { n as defineDrain, r as resolveAdapterConfig, t as httpPost } from "../_http-BpkAshj6.mjs";
|
|
2
2
|
//#region src/adapters/better-stack.ts
|
|
3
3
|
const BETTER_STACK_FIELDS = [
|
|
4
4
|
{
|
|
@@ -12,7 +12,7 @@ const BETTER_STACK_FIELDS = [
|
|
|
12
12
|
{ key: "timeout" }
|
|
13
13
|
];
|
|
14
14
|
/**
|
|
15
|
-
* Transform an
|
|
15
|
+
* Transform an mxllog wide event into a Better Stack event.
|
|
16
16
|
* Maps `timestamp` to `dt` (Better Stack's expected field).
|
|
17
17
|
*/
|
|
18
18
|
function toBetterStackEvent(event) {
|
|
@@ -34,10 +34,10 @@ function toBetterStackEvent(event) {
|
|
|
34
34
|
* @example
|
|
35
35
|
* ```ts
|
|
36
36
|
* // Zero config - just set NUXT_BETTER_STACK_SOURCE_TOKEN env var
|
|
37
|
-
* nitroApp.hooks.hook('
|
|
37
|
+
* nitroApp.hooks.hook('mxllog:drain', createBetterStackDrain())
|
|
38
38
|
*
|
|
39
39
|
* // With overrides
|
|
40
|
-
* nitroApp.hooks.hook('
|
|
40
|
+
* nitroApp.hooks.hook('mxllog:drain', createBetterStackDrain({
|
|
41
41
|
* sourceToken: 'my-token',
|
|
42
42
|
* }))
|
|
43
43
|
* ```
|
|
@@ -48,7 +48,7 @@ function createBetterStackDrain(overrides) {
|
|
|
48
48
|
resolve: () => {
|
|
49
49
|
const config = resolveAdapterConfig("betterStack", BETTER_STACK_FIELDS, overrides);
|
|
50
50
|
if (!config.sourceToken) {
|
|
51
|
-
console.error("[
|
|
51
|
+
console.error("[mxllog/better-stack] Missing source token. Set NUXT_BETTER_STACK_SOURCE_TOKEN env var or pass to createBetterStackDrain()");
|
|
52
52
|
return null;
|
|
53
53
|
}
|
|
54
54
|
return config;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"better-stack.mjs","names":[],"sources":["../../src/adapters/better-stack.ts"],"sourcesContent":["import type { WideEvent } from '../types'\nimport type { ConfigField } from './_config'\nimport { resolveAdapterConfig } from './_config'\nimport { defineDrain } from './_drain'\nimport { httpPost } from './_http'\n\nexport interface BetterStackConfig {\n /** Better Stack source token */\n sourceToken: string\n /** Logtail ingestion endpoint. Default: https://in.logs.betterstack.com */\n endpoint?: string\n /** Request timeout in milliseconds. Default: 5000 */\n timeout?: number\n}\n\nconst BETTER_STACK_FIELDS: ConfigField<BetterStackConfig>[] = [\n { key: 'sourceToken', env: ['NUXT_BETTER_STACK_SOURCE_TOKEN', 'BETTER_STACK_SOURCE_TOKEN'] },\n { key: 'endpoint', env: ['NUXT_BETTER_STACK_ENDPOINT', 'BETTER_STACK_ENDPOINT'] },\n { key: 'timeout' },\n]\n\n/**\n * Transform an
|
|
1
|
+
{"version":3,"file":"better-stack.mjs","names":[],"sources":["../../src/adapters/better-stack.ts"],"sourcesContent":["import type { WideEvent } from '../types'\nimport type { ConfigField } from './_config'\nimport { resolveAdapterConfig } from './_config'\nimport { defineDrain } from './_drain'\nimport { httpPost } from './_http'\n\nexport interface BetterStackConfig {\n /** Better Stack source token */\n sourceToken: string\n /** Logtail ingestion endpoint. Default: https://in.logs.betterstack.com */\n endpoint?: string\n /** Request timeout in milliseconds. Default: 5000 */\n timeout?: number\n}\n\nconst BETTER_STACK_FIELDS: ConfigField<BetterStackConfig>[] = [\n { key: 'sourceToken', env: ['NUXT_BETTER_STACK_SOURCE_TOKEN', 'BETTER_STACK_SOURCE_TOKEN'] },\n { key: 'endpoint', env: ['NUXT_BETTER_STACK_ENDPOINT', 'BETTER_STACK_ENDPOINT'] },\n { key: 'timeout' },\n]\n\n/**\n * Transform an mxllog wide event into a Better Stack event.\n * Maps `timestamp` to `dt` (Better Stack's expected field).\n */\nexport function toBetterStackEvent(event: WideEvent): Record<string, unknown> {\n const { timestamp, ...rest } = event\n return { ...rest, dt: timestamp }\n}\n\n/**\n * Create a drain function for sending logs to Better Stack.\n *\n * Configuration priority (highest to lowest):\n * 1. Overrides passed to createBetterStackDrain()\n * 2. runtimeConfig.mxllog.betterStack\n * 3. runtimeConfig.betterStack\n * 4. Environment variables: NUXT_BETTER_STACK_*, BETTER_STACK_*\n *\n * @example\n * ```ts\n * // Zero config - just set NUXT_BETTER_STACK_SOURCE_TOKEN env var\n * nitroApp.hooks.hook('mxllog:drain', createBetterStackDrain())\n *\n * // With overrides\n * nitroApp.hooks.hook('mxllog:drain', createBetterStackDrain({\n * sourceToken: 'my-token',\n * }))\n * ```\n */\nexport function createBetterStackDrain(overrides?: Partial<BetterStackConfig>) {\n return defineDrain<BetterStackConfig>({\n name: 'better-stack',\n resolve: () => {\n const config = resolveAdapterConfig<BetterStackConfig>('betterStack', BETTER_STACK_FIELDS, overrides)\n if (!config.sourceToken) {\n console.error('[mxllog/better-stack] Missing source token. Set NUXT_BETTER_STACK_SOURCE_TOKEN env var or pass to createBetterStackDrain()')\n return null\n }\n return config as BetterStackConfig\n },\n send: sendBatchToBetterStack,\n })\n}\n\n/**\n * Send a single event to Better Stack.\n *\n * @example\n * ```ts\n * await sendToBetterStack(event, {\n * sourceToken: process.env.BETTER_STACK_SOURCE_TOKEN!,\n * })\n * ```\n */\nexport async function sendToBetterStack(event: WideEvent, config: BetterStackConfig): Promise<void> {\n await sendBatchToBetterStack([event], config)\n}\n\n/**\n * Send a batch of events to Better Stack.\n *\n * @example\n * ```ts\n * await sendBatchToBetterStack(events, {\n * sourceToken: process.env.BETTER_STACK_SOURCE_TOKEN!,\n * })\n * ```\n */\nexport async function sendBatchToBetterStack(events: WideEvent[], config: BetterStackConfig): Promise<void> {\n const endpoint = (config.endpoint ?? 'https://in.logs.betterstack.com').replace(/\\/+$/, '')\n\n await httpPost({\n url: endpoint,\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${config.sourceToken}`,\n },\n body: JSON.stringify(events.map(toBetterStackEvent)),\n timeout: config.timeout ?? 5000,\n label: 'Better Stack',\n })\n}\n"],"mappings":";;AAeA,MAAM,sBAAwD;CAC5D;EAAE,KAAK;EAAe,KAAK,CAAC,kCAAkC,4BAA4B;EAAE;CAC5F;EAAE,KAAK;EAAY,KAAK,CAAC,8BAA8B,wBAAwB;EAAE;CACjF,EAAE,KAAK,WAAW;CACnB;;;;;AAMD,SAAgB,mBAAmB,OAA2C;CAC5E,MAAM,EAAE,WAAW,GAAG,SAAS;AAC/B,QAAO;EAAE,GAAG;EAAM,IAAI;EAAW;;;;;;;;;;;;;;;;;;;;;;AAuBnC,SAAgB,uBAAuB,WAAwC;AAC7E,QAAO,YAA+B;EACpC,MAAM;EACN,eAAe;GACb,MAAM,SAAS,qBAAwC,eAAe,qBAAqB,UAAU;AACrG,OAAI,CAAC,OAAO,aAAa;AACvB,YAAQ,MAAM,6HAA6H;AAC3I,WAAO;;AAET,UAAO;;EAET,MAAM;EACP,CAAC;;;;;;;;;;;;AAaJ,eAAsB,kBAAkB,OAAkB,QAA0C;AAClG,OAAM,uBAAuB,CAAC,MAAM,EAAE,OAAO;;;;;;;;;;;;AAa/C,eAAsB,uBAAuB,QAAqB,QAA0C;AAG1G,OAAM,SAAS;EACb,MAHgB,OAAO,YAAY,mCAAmC,QAAQ,QAAQ,GAAG;EAIzF,SAAS;GACP,gBAAgB;GAChB,iBAAiB,UAAU,OAAO;GACnC;EACD,MAAM,KAAK,UAAU,OAAO,IAAI,mBAAmB,CAAC;EACpD,SAAS,OAAO,WAAW;EAC3B,OAAO;EACR,CAAC"}
|
package/dist/adapters/otlp.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { T as WideEvent, r as DrainContext } from "../types-
|
|
1
|
+
import { T as WideEvent, r as DrainContext } from "../types-B82IuY7M.mjs";
|
|
2
2
|
//#region src/adapters/otlp.d.ts
|
|
3
3
|
interface OTLPConfig {
|
|
4
4
|
/** OTLP HTTP endpoint (e.g., http://localhost:4318) */
|
|
@@ -32,7 +32,7 @@ interface OTLPLogRecord {
|
|
|
32
32
|
spanId?: string;
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
|
-
* Convert an
|
|
35
|
+
* Convert an mxllog WideEvent to an OTLP LogRecord.
|
|
36
36
|
*/
|
|
37
37
|
declare function toOTLPLogRecord(event: WideEvent): OTLPLogRecord;
|
|
38
38
|
/**
|
|
@@ -47,10 +47,10 @@ declare function toOTLPLogRecord(event: WideEvent): OTLPLogRecord;
|
|
|
47
47
|
* @example
|
|
48
48
|
* ```ts
|
|
49
49
|
* // Zero config - reads from runtimeConfig or env vars
|
|
50
|
-
* nitroApp.hooks.hook('
|
|
50
|
+
* nitroApp.hooks.hook('mxllog:drain', createOTLPDrain())
|
|
51
51
|
*
|
|
52
52
|
* // With overrides
|
|
53
|
-
* nitroApp.hooks.hook('
|
|
53
|
+
* nitroApp.hooks.hook('mxllog:drain', createOTLPDrain({
|
|
54
54
|
* endpoint: 'http://localhost:4318',
|
|
55
55
|
* }))
|
|
56
56
|
* ```
|
package/dist/adapters/otlp.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as defineDrain, r as resolveAdapterConfig, t as httpPost } from "../_http-
|
|
2
|
-
import { n as OTEL_SEVERITY_TEXT, t as OTEL_SEVERITY_NUMBER } from "../_severity-
|
|
1
|
+
import { n as defineDrain, r as resolveAdapterConfig, t as httpPost } from "../_http-BpkAshj6.mjs";
|
|
2
|
+
import { n as OTEL_SEVERITY_TEXT, t as OTEL_SEVERITY_NUMBER } from "../_severity-Q1BuITU_.mjs";
|
|
3
3
|
//#region src/adapters/otlp.ts
|
|
4
4
|
const OTLP_FIELDS = [
|
|
5
5
|
{
|
|
@@ -24,7 +24,7 @@ function toAttributeValue(value) {
|
|
|
24
24
|
return { stringValue: JSON.stringify(value) };
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
|
-
* Convert an
|
|
27
|
+
* Convert an mxllog WideEvent to an OTLP LogRecord.
|
|
28
28
|
*/
|
|
29
29
|
function toOTLPLogRecord(event) {
|
|
30
30
|
const timestamp = new Date(event.timestamp).getTime() * 1e6;
|
|
@@ -116,10 +116,10 @@ function getHeadersFromEnv() {
|
|
|
116
116
|
* @example
|
|
117
117
|
* ```ts
|
|
118
118
|
* // Zero config - reads from runtimeConfig or env vars
|
|
119
|
-
* nitroApp.hooks.hook('
|
|
119
|
+
* nitroApp.hooks.hook('mxllog:drain', createOTLPDrain())
|
|
120
120
|
*
|
|
121
121
|
* // With overrides
|
|
122
|
-
* nitroApp.hooks.hook('
|
|
122
|
+
* nitroApp.hooks.hook('mxllog:drain', createOTLPDrain({
|
|
123
123
|
* endpoint: 'http://localhost:4318',
|
|
124
124
|
* }))
|
|
125
125
|
* ```
|
|
@@ -131,7 +131,7 @@ function createOTLPDrain(overrides) {
|
|
|
131
131
|
const config = resolveAdapterConfig("otlp", OTLP_FIELDS, overrides);
|
|
132
132
|
if (!config.headers) config.headers = getHeadersFromEnv();
|
|
133
133
|
if (!config.endpoint) {
|
|
134
|
-
console.error("[
|
|
134
|
+
console.error("[mxllog/otlp] Missing endpoint. Set NUXT_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_ENDPOINT env var, or pass to createOTLPDrain()");
|
|
135
135
|
return null;
|
|
136
136
|
}
|
|
137
137
|
return config;
|
|
@@ -165,19 +165,23 @@ async function sendToOTLP(event, config) {
|
|
|
165
165
|
async function sendBatchToOTLP(events, config) {
|
|
166
166
|
if (events.length === 0) return;
|
|
167
167
|
const url = `${config.endpoint.replace(/\/$/, "")}/v1/logs`;
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
168
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
169
|
+
for (const event of events) {
|
|
170
|
+
const key = `${event.service}::${event.environment}`;
|
|
171
|
+
const group = grouped.get(key);
|
|
172
|
+
if (group) group.push(event);
|
|
173
|
+
else grouped.set(key, [event]);
|
|
174
|
+
}
|
|
175
|
+
const payload = { resourceLogs: Array.from(grouped.values()).map((groupEvents) => ({
|
|
176
|
+
resource: { attributes: buildResourceAttributes(groupEvents[0], config) },
|
|
173
177
|
scopeLogs: [{
|
|
174
178
|
scope: {
|
|
175
179
|
name: "@safaricom-mxl/log",
|
|
176
180
|
version: "1.0.0"
|
|
177
181
|
},
|
|
178
|
-
logRecords
|
|
182
|
+
logRecords: groupEvents.map(toOTLPLogRecord)
|
|
179
183
|
}]
|
|
180
|
-
}
|
|
184
|
+
})) };
|
|
181
185
|
await httpPost({
|
|
182
186
|
url,
|
|
183
187
|
headers: {
|