evlog 2.8.0 → 2.10.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 +28 -0
- package/dist/{_drain-C9Nr-6Wc.mjs → _drain-CJDuM0ua.mjs} +7 -2
- package/dist/_drain-CJDuM0ua.mjs.map +1 -0
- package/dist/{_http-C2UoHWgm.mjs → _http-rRIz2Q0L.mjs} +22 -19
- package/dist/_http-rRIz2Q0L.mjs.map +1 -0
- package/dist/adapters/axiom.d.mts +1 -1
- package/dist/adapters/axiom.mjs +4 -4
- package/dist/adapters/axiom.mjs.map +1 -1
- package/dist/adapters/better-stack.d.mts +1 -1
- package/dist/adapters/better-stack.mjs +4 -4
- package/dist/adapters/better-stack.mjs.map +1 -1
- package/dist/adapters/fs.d.mts +1 -1
- package/dist/adapters/fs.mjs +1 -1
- package/dist/adapters/hyperdx.d.mts +65 -0
- package/dist/adapters/hyperdx.d.mts.map +1 -0
- package/dist/adapters/hyperdx.mjs +93 -0
- package/dist/adapters/hyperdx.mjs.map +1 -0
- package/dist/adapters/otlp.d.mts +1 -1
- package/dist/adapters/otlp.mjs +4 -4
- package/dist/adapters/otlp.mjs.map +1 -1
- package/dist/adapters/posthog.d.mts +1 -1
- package/dist/adapters/posthog.mjs +6 -6
- package/dist/adapters/posthog.mjs.map +1 -1
- package/dist/adapters/sentry.d.mts +1 -1
- package/dist/adapters/sentry.mjs +4 -4
- package/dist/adapters/sentry.mjs.map +1 -1
- package/dist/ai/index.d.mts +82 -5
- package/dist/ai/index.d.mts.map +1 -1
- package/dist/ai/index.mjs +237 -96
- package/dist/ai/index.mjs.map +1 -1
- package/dist/browser.d.mts +3 -1
- package/dist/browser.d.mts.map +1 -1
- package/dist/browser.mjs +2 -2
- package/dist/browser.mjs.map +1 -1
- package/dist/{dist-BFn8qsRC.mjs → dist-BsWcv7B8.mjs} +1 -1
- package/dist/{dist-BFn8qsRC.mjs.map → dist-BsWcv7B8.mjs.map} +1 -1
- package/dist/elysia/index.d.mts +2 -2
- package/dist/elysia/index.d.mts.map +1 -1
- package/dist/elysia/index.mjs +7 -7
- package/dist/elysia/index.mjs.map +1 -1
- package/dist/enrichers.d.mts +1 -1
- package/dist/{error-BheHTFFB.d.mts → error-BJ-I4sim.d.mts} +2 -2
- package/dist/{error-BheHTFFB.d.mts.map → error-BJ-I4sim.d.mts.map} +1 -1
- package/dist/error.d.mts +1 -1
- package/dist/{errors-D8WVZclz.d.mts → errors-DBIBK0Bt.d.mts} +2 -2
- package/dist/{errors-D8WVZclz.d.mts.map → errors-DBIBK0Bt.d.mts.map} +1 -1
- package/dist/{errors-BQgyQ9xe.mjs → errors-gH4C9KSC.mjs} +1 -1
- package/dist/{errors-BQgyQ9xe.mjs.map → errors-gH4C9KSC.mjs.map} +1 -1
- package/dist/express/index.d.mts +2 -2
- package/dist/express/index.mjs +1 -1
- package/dist/fastify/index.d.mts +2 -2
- package/dist/fastify/index.mjs +1 -1
- package/dist/{headers-DJ_YZbxT.mjs → headers-DrdQ6uG5.mjs} +3 -3
- package/dist/{headers-DJ_YZbxT.mjs.map → headers-DrdQ6uG5.mjs.map} +1 -1
- package/dist/hono/index.d.mts +2 -2
- package/dist/hono/index.mjs +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/{logger-BkXYNnHP.d.mts → logger-DU3aQIUk.d.mts} +13 -3
- package/dist/logger-DU3aQIUk.d.mts.map +1 -0
- package/dist/logger.d.mts +2 -2
- package/dist/logger.mjs +16 -1
- package/dist/logger.mjs.map +1 -1
- package/dist/{middleware-B-4hPOVG.d.mts → middleware-BjERCCEd.d.mts} +2 -2
- package/dist/{middleware-B-4hPOVG.d.mts.map → middleware-BjERCCEd.d.mts.map} +1 -1
- package/dist/nestjs/index.d.mts +2 -2
- package/dist/nestjs/index.mjs +1 -1
- package/dist/next/client.d.mts +1 -1
- package/dist/next/index.d.mts +4 -4
- package/dist/next/index.d.mts.map +1 -1
- package/dist/next/index.mjs +3 -2
- package/dist/next/index.mjs.map +1 -1
- package/dist/next/instrumentation.d.mts +75 -0
- package/dist/next/instrumentation.d.mts.map +1 -0
- package/dist/next/instrumentation.mjs +108 -0
- package/dist/next/instrumentation.mjs.map +1 -0
- package/dist/nitro/errorHandler.mjs +2 -2
- package/dist/nitro/module.d.mts +2 -2
- package/dist/nitro/plugin.mjs +5 -4
- package/dist/nitro/plugin.mjs.map +1 -1
- package/dist/nitro/v3/errorHandler.mjs +3 -3
- package/dist/nitro/v3/middleware.d.mts +3 -1
- package/dist/nitro/v3/middleware.d.mts.map +1 -1
- package/dist/nitro/v3/middleware.mjs +2 -1
- package/dist/nitro/v3/middleware.mjs.map +1 -1
- package/dist/nitro/v3/module.d.mts +1 -1
- package/dist/nitro/v3/plugin.mjs +8 -7
- package/dist/nitro/v3/plugin.mjs.map +1 -1
- package/dist/nitro/v3/useLogger.d.mts +1 -1
- package/dist/{nitro-DCNNxY_7.d.mts → nitro-BXmkH7BD.d.mts} +2 -2
- package/dist/{nitro-DCNNxY_7.d.mts.map → nitro-BXmkH7BD.d.mts.map} +1 -1
- package/dist/{nitro-CzyGROOC.mjs → nitro-Dpq5ZmcM.mjs} +2 -2
- package/dist/{nitro-CzyGROOC.mjs.map → nitro-Dpq5ZmcM.mjs.map} +1 -1
- package/dist/nuxt/module.d.mts +4 -3
- package/dist/nuxt/module.d.mts.map +1 -1
- package/dist/nuxt/module.mjs +4 -2
- package/dist/nuxt/module.mjs.map +1 -1
- package/dist/{parseError-B08FS7EQ.d.mts → parseError-CcvBYsbl.d.mts} +2 -2
- package/dist/parseError-CcvBYsbl.d.mts.map +1 -0
- package/dist/react-router/index.d.mts +47 -0
- package/dist/react-router/index.d.mts.map +1 -0
- package/dist/react-router/index.mjs +59 -0
- package/dist/react-router/index.mjs.map +1 -0
- package/dist/{routes-CGPmbzCZ.mjs → routes-CE3_c-iZ.mjs} +1 -1
- package/dist/{routes-CGPmbzCZ.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 +3 -1
- package/dist/runtime/client/log.mjs.map +1 -1
- package/dist/runtime/server/useLogger.d.mts +1 -1
- package/dist/runtime/utils/parseError.d.mts +2 -2
- package/dist/runtime/utils/parseError.mjs +2 -1
- package/dist/runtime/utils/parseError.mjs.map +1 -1
- package/dist/sveltekit/index.d.mts +2 -2
- package/dist/sveltekit/index.mjs +3 -3
- package/dist/toolkit.d.mts +3 -3
- package/dist/toolkit.mjs +3 -3
- package/dist/{types-CBpJBj_7.d.mts → types-DbVDS9eu.d.mts} +8 -1
- package/dist/types-DbVDS9eu.d.mts.map +1 -0
- package/dist/types.d.mts +1 -1
- package/dist/{useLogger-DBPGEDf_.d.mts → useLogger-DY9IByDJ.d.mts} +2 -2
- package/dist/{useLogger-DBPGEDf_.d.mts.map → useLogger-DY9IByDJ.d.mts.map} +1 -1
- package/dist/utils.d.mts +1 -1
- package/dist/vite/index.d.mts +1 -1
- package/dist/workers.d.mts +1 -1
- package/package.json +50 -20
- package/dist/_drain-C9Nr-6Wc.mjs.map +0 -1
- package/dist/_http-C2UoHWgm.mjs.map +0 -1
- package/dist/logger-BkXYNnHP.d.mts.map +0 -1
- package/dist/parseError-B08FS7EQ.d.mts.map +0 -1
- package/dist/types-CBpJBj_7.d.mts.map +0 -1
package/README.md
CHANGED
|
@@ -464,6 +464,33 @@ Use `useLogger()` to access the logger from anywhere in the call stack.
|
|
|
464
464
|
|
|
465
465
|
See the full [elysia example](https://github.com/HugoRCD/evlog/tree/main/examples/elysia) for a complete working project.
|
|
466
466
|
|
|
467
|
+
## React Router
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
// app/root.tsx
|
|
471
|
+
import { initLogger } from 'evlog'
|
|
472
|
+
import { evlog, loggerContext } from 'evlog/react-router'
|
|
473
|
+
|
|
474
|
+
initLogger({ env: { service: 'react-router-api' } })
|
|
475
|
+
|
|
476
|
+
export const middleware: Route.MiddlewareFunction[] = [
|
|
477
|
+
evlog(),
|
|
478
|
+
]
|
|
479
|
+
|
|
480
|
+
// app/routes/api.users.$id.tsx
|
|
481
|
+
import { loggerContext } from 'evlog/react-router'
|
|
482
|
+
|
|
483
|
+
export async function loader({ params, context }: Route.LoaderArgs) {
|
|
484
|
+
const log = context.get(loggerContext)
|
|
485
|
+
log.set({ users: { count: 42 } })
|
|
486
|
+
return { users: [] }
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
Use `context.get(loggerContext)` in loaders/actions, or `useLogger()` from anywhere in the call stack. Requires `v8_middleware: true` in `react-router.config.ts`.
|
|
491
|
+
|
|
492
|
+
See the full [react-router example](https://github.com/HugoRCD/evlog/tree/main/examples/react-router) for a complete working project.
|
|
493
|
+
|
|
467
494
|
## NestJS
|
|
468
495
|
|
|
469
496
|
```typescript
|
|
@@ -1095,6 +1122,7 @@ try {
|
|
|
1095
1122
|
| **Nitro v3** | `modules: [evlog()]` with `import evlog from 'evlog/nitro/v3'` |
|
|
1096
1123
|
| **Nitro v2** | `modules: [evlog()]` with `import evlog from 'evlog/nitro'` |
|
|
1097
1124
|
| **TanStack Start** | Nitro v3 module setup ([example](./examples/tanstack-start)) |
|
|
1125
|
+
| **React Router** | `evlog()` middleware with `import { evlog } from 'evlog/react-router'` ([example](./examples/react-router)) |
|
|
1098
1126
|
| **NestJS** | `EvlogModule.forRoot()` with `import { EvlogModule } from 'evlog/nestjs'` ([example](./examples/nestjs)) |
|
|
1099
1127
|
| **Express** | `app.use(evlog())` with `import { evlog } from 'evlog/express'` ([example](./examples/express)) |
|
|
1100
1128
|
| **Hono** | `app.use(evlog())` with `import { evlog } from 'evlog/hono'` ([example](./examples/hono)) |
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
//#region src/adapters/_drain.ts
|
|
2
|
+
/**
|
|
3
|
+
* Build a drain callback for `evlog:drain` (or `initLogger({ drain })`).
|
|
4
|
+
* The returned function is async so `resolve` can load Nitro runtime config; hosts typically attach
|
|
5
|
+
* the resulting promise to `waitUntil` so the HTTP response is not blocked (see Nitro plugin).
|
|
6
|
+
*/
|
|
2
7
|
function defineDrain(options) {
|
|
3
8
|
return async (ctx) => {
|
|
4
9
|
const contexts = Array.isArray(ctx) ? ctx : [ctx];
|
|
5
10
|
if (contexts.length === 0) return;
|
|
6
|
-
const config = options.resolve();
|
|
11
|
+
const config = await options.resolve();
|
|
7
12
|
if (!config) return;
|
|
8
13
|
try {
|
|
9
14
|
await options.send(contexts.map((c) => c.event), config);
|
|
@@ -15,4 +20,4 @@ function defineDrain(options) {
|
|
|
15
20
|
//#endregion
|
|
16
21
|
export { defineDrain as t };
|
|
17
22
|
|
|
18
|
-
//# sourceMappingURL=_drain-
|
|
23
|
+
//# sourceMappingURL=_drain-CJDuM0ua.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_drain-CJDuM0ua.mjs","names":[],"sources":["../src/adapters/_drain.ts"],"sourcesContent":["import type { DrainContext, WideEvent } from '../types'\n\nexport interface DrainOptions<TConfig> {\n name: string\n resolve: () => TConfig | null | Promise<TConfig | null>\n send: (events: WideEvent[], config: TConfig) => Promise<void>\n}\n\n/**\n * Build a drain callback for `evlog:drain` (or `initLogger({ drain })`).\n * The returned function is async so `resolve` can load Nitro runtime config; hosts typically attach\n * the resulting promise to `waitUntil` so the HTTP response is not blocked (see Nitro plugin).\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 = await 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(`[evlog/${options.name}] Failed to send events:`, error)\n }\n }\n}\n"],"mappings":";;;;;;AAaA,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,MAAM,QAAQ,SAAS;AACtC,MAAI,CAAC,OAAQ;AAEb,MAAI;AACF,SAAM,QAAQ,KAAK,SAAS,KAAI,MAAK,EAAE,MAAM,EAAE,OAAO;WAC/C,OAAO;AACd,WAAQ,MAAM,UAAU,QAAQ,KAAK,2BAA2B,MAAM"}
|
|
@@ -1,27 +1,30 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
//#region \0rolldown/runtime.js
|
|
3
|
-
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
4
|
-
//#endregion
|
|
5
1
|
//#region src/adapters/_config.ts
|
|
6
2
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
3
|
+
* Nitro runtime modules resolved via dynamic `import()` (Workers-safe: avoids a bundler-injected
|
|
4
|
+
* `createRequire` polyfill from sync `require()`). Module namespaces are cached after first
|
|
5
|
+
* successful load; `useRuntimeConfig()` is still invoked on each call so config stays current.
|
|
6
|
+
*
|
|
7
|
+
* Drain handlers remain non-blocking for the HTTP response when the host provides `waitUntil`
|
|
8
|
+
* (see Nitro plugin); the extra `await` here only sequences work inside that background drain.
|
|
10
9
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
let nitropackRuntime;
|
|
11
|
+
let nitroV3Runtime;
|
|
12
|
+
async function getRuntimeConfig() {
|
|
13
|
+
if (nitropackRuntime === void 0) try {
|
|
14
|
+
nitropackRuntime = await import("nitropack/runtime");
|
|
15
|
+
} catch {
|
|
16
|
+
nitropackRuntime = null;
|
|
17
|
+
}
|
|
18
|
+
if (nitropackRuntime) return nitropackRuntime.useRuntimeConfig();
|
|
19
|
+
if (nitroV3Runtime === void 0) try {
|
|
20
|
+
nitroV3Runtime = await import("nitro/runtime-config");
|
|
19
21
|
} catch {
|
|
20
|
-
|
|
22
|
+
nitroV3Runtime = null;
|
|
21
23
|
}
|
|
24
|
+
if (nitroV3Runtime) return nitroV3Runtime.useRuntimeConfig();
|
|
22
25
|
}
|
|
23
|
-
function resolveAdapterConfig(namespace, fields, overrides) {
|
|
24
|
-
const runtimeConfig = getRuntimeConfig();
|
|
26
|
+
async function resolveAdapterConfig(namespace, fields, overrides) {
|
|
27
|
+
const runtimeConfig = await getRuntimeConfig();
|
|
25
28
|
const evlogNs = runtimeConfig?.evlog?.[namespace];
|
|
26
29
|
const rootNs = runtimeConfig?.[namespace];
|
|
27
30
|
const config = {};
|
|
@@ -79,4 +82,4 @@ async function httpPost({ url, headers, body, timeout, label, retries = 2 }) {
|
|
|
79
82
|
//#endregion
|
|
80
83
|
export { resolveAdapterConfig as n, httpPost as t };
|
|
81
84
|
|
|
82
|
-
//# sourceMappingURL=_http-
|
|
85
|
+
//# sourceMappingURL=_http-rRIz2Q0L.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_http-rRIz2Q0L.mjs","names":[],"sources":["../src/adapters/_config.ts","../src/adapters/_http.ts"],"sourcesContent":["/**\n * Nitro runtime modules resolved via dynamic `import()` (Workers-safe: avoids a bundler-injected\n * `createRequire` polyfill from sync `require()`). Module namespaces are cached after first\n * successful load; `useRuntimeConfig()` is still invoked on each call so config stays current.\n *\n * Drain handlers remain non-blocking for the HTTP response when the host provides `waitUntil`\n * (see Nitro plugin); the extra `await` here only sequences work inside that background drain.\n */\nlet nitropackRuntime: typeof import('nitropack/runtime') | null | undefined\nlet nitroV3Runtime: typeof import('nitro/runtime-config') | null | undefined\n\nexport async function getRuntimeConfig(): Promise<Record<string, any> | undefined> {\n if (nitropackRuntime === undefined) {\n try {\n nitropackRuntime = await import('nitropack/runtime')\n } catch {\n nitropackRuntime = null\n }\n }\n if (nitropackRuntime) {\n return nitropackRuntime.useRuntimeConfig()\n }\n\n if (nitroV3Runtime === undefined) {\n try {\n nitroV3Runtime = await import('nitro/runtime-config')\n } catch {\n nitroV3Runtime = null\n }\n }\n if (nitroV3Runtime) {\n return nitroV3Runtime.useRuntimeConfig()\n }\n return undefined\n}\n\nexport interface ConfigField<T> {\n key: keyof T & string\n env?: string[]\n}\n\nexport async function resolveAdapterConfig<T>(\n namespace: string,\n fields: ConfigField<T>[],\n overrides?: Partial<T>,\n): Promise<Partial<T>> {\n const runtimeConfig = await getRuntimeConfig()\n const evlogNs = runtimeConfig?.evlog?.[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 ?? evlogNs?.[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","export interface HttpPostOptions {\n url: string\n headers: Record<string, string>\n body: string\n timeout: number\n label: string\n retries?: number\n}\n\nfunction isRetryable(error: unknown): boolean {\n if (error instanceof DOMException && error.name === 'AbortError') return true\n if (error instanceof TypeError) return true\n if (error instanceof Error) {\n const match = error.message.match(/API error: (\\d+)/)\n if (match) return Number.parseInt(match[1]) >= 500\n }\n return false\n}\n\nexport async function httpPost({ url, headers, body, timeout, label, retries = 2 }: HttpPostOptions): Promise<void> {\n const normalizedRetries = Number.isFinite(retries) && retries >= 0 ? Math.floor(retries) : 2\n\n let lastError: Error | undefined\n\n for (let attempt = 0; attempt <= normalizedRetries; attempt++) {\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\n clearTimeout(timeoutId)\n return\n } catch (error) {\n clearTimeout(timeoutId)\n\n if (error instanceof DOMException && error.name === 'AbortError') {\n lastError = new Error(`${label} request timed out after ${timeout}ms`)\n } else {\n lastError = error as Error\n }\n\n if (!isRetryable(error) || attempt === normalizedRetries) {\n throw lastError\n }\n\n await new Promise<void>(r => setTimeout(r, 200 * 2 ** attempt))\n }\n }\n\n throw lastError!\n}\n"],"mappings":";;;;;;;;;AAQA,IAAI;AACJ,IAAI;AAEJ,eAAsB,mBAA6D;AACjF,KAAI,qBAAqB,KAAA,EACvB,KAAI;AACF,qBAAmB,MAAM,OAAO;SAC1B;AACN,qBAAmB;;AAGvB,KAAI,iBACF,QAAO,iBAAiB,kBAAkB;AAG5C,KAAI,mBAAmB,KAAA,EACrB,KAAI;AACF,mBAAiB,MAAM,OAAO;SACxB;AACN,mBAAiB;;AAGrB,KAAI,eACF,QAAO,eAAe,kBAAkB;;AAU5C,eAAsB,qBACpB,WACA,QACA,WACqB;CACrB,MAAM,gBAAgB,MAAM,kBAAkB;CAC9C,MAAM,UAAU,eAAe,QAAQ;CACvC,MAAM,SAAS,gBAAgB;CAE/B,MAAM,SAAkC,EAAE;AAE1C,MAAK,MAAM,EAAE,KAAK,SAAS,OACzB,QAAO,OACL,YAAY,QACT,UAAU,QACV,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;;;;;AC1DpB,SAAS,YAAY,OAAyB;AAC5C,KAAI,iBAAiB,gBAAgB,MAAM,SAAS,aAAc,QAAO;AACzE,KAAI,iBAAiB,UAAW,QAAO;AACvC,KAAI,iBAAiB,OAAO;EAC1B,MAAM,QAAQ,MAAM,QAAQ,MAAM,mBAAmB;AACrD,MAAI,MAAO,QAAO,OAAO,SAAS,MAAM,GAAG,IAAI;;AAEjD,QAAO;;AAGT,eAAsB,SAAS,EAAE,KAAK,SAAS,MAAM,SAAS,OAAO,UAAU,KAAqC;CAClH,MAAM,oBAAoB,OAAO,SAAS,QAAQ,IAAI,WAAW,IAAI,KAAK,MAAM,QAAQ,GAAG;CAE3F,IAAI;AAEJ,MAAK,IAAI,UAAU,GAAG,WAAW,mBAAmB,WAAW;EAC7D,MAAM,aAAa,IAAI,iBAAiB;EACxC,MAAM,YAAY,iBAAiB,WAAW,OAAO,EAAE,QAAQ;AAE/D,MAAI;GACF,MAAM,WAAW,MAAM,MAAM,KAAK;IAChC,QAAQ;IACR;IACA;IACA,QAAQ,WAAW;IACpB,CAAC;AAEF,OAAI,CAAC,SAAS,IAAI;IAChB,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,YAAY,gBAAgB;IAC/D,MAAM,WAAW,KAAK,SAAS,MAAM,GAAG,KAAK,MAAM,GAAG,IAAI,CAAC,kBAAkB;AAC7E,UAAM,IAAI,MAAM,GAAG,MAAM,cAAc,SAAS,OAAO,GAAG,SAAS,WAAW,KAAK,WAAW;;AAGhG,gBAAa,UAAU;AACvB;WACO,OAAO;AACd,gBAAa,UAAU;AAEvB,OAAI,iBAAiB,gBAAgB,MAAM,SAAS,aAClD,6BAAY,IAAI,MAAM,GAAG,MAAM,2BAA2B,QAAQ,IAAI;OAEtE,aAAY;AAGd,OAAI,CAAC,YAAY,MAAM,IAAI,YAAY,kBACrC,OAAM;AAGR,SAAM,IAAI,SAAc,MAAK,WAAW,GAAG,MAAM,KAAK,QAAQ,CAAC;;;AAInE,OAAM"}
|
package/dist/adapters/axiom.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as resolveAdapterConfig, t as httpPost } from "../_http-
|
|
2
|
-
import { t as defineDrain } from "../_drain-
|
|
1
|
+
import { n as resolveAdapterConfig, t as httpPost } from "../_http-rRIz2Q0L.mjs";
|
|
2
|
+
import { t as defineDrain } from "../_drain-CJDuM0ua.mjs";
|
|
3
3
|
//#region src/adapters/axiom.ts
|
|
4
4
|
const AXIOM_FIELDS = [
|
|
5
5
|
{
|
|
@@ -48,8 +48,8 @@ const AXIOM_FIELDS = [
|
|
|
48
48
|
function createAxiomDrain(overrides) {
|
|
49
49
|
return defineDrain({
|
|
50
50
|
name: "axiom",
|
|
51
|
-
resolve: () => {
|
|
52
|
-
const config = resolveAdapterConfig("axiom", AXIOM_FIELDS, overrides);
|
|
51
|
+
resolve: async () => {
|
|
52
|
+
const config = await resolveAdapterConfig("axiom", AXIOM_FIELDS, overrides);
|
|
53
53
|
if (!config.dataset || !config.token) {
|
|
54
54
|
console.error("[evlog/axiom] Missing dataset or token. Set NUXT_AXIOM_TOKEN/NUXT_AXIOM_DATASET env vars or pass to createAxiomDrain()");
|
|
55
55
|
return null;
|
|
@@ -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 /** Number of retry attempts on transient failures. Default: 2 */\n retries?: 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 { key: 'retries' },\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.evlog.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('evlog:drain', createAxiomDrain())\n *\n * // With overrides\n * nitroApp.hooks.hook('evlog: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('[evlog/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('[evlog/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 retries: config.retries,\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(`[evlog/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":";;;AA4CA,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;CAClB,EAAE,KAAK,WAAW;CACnB;;;;;;;;;;;;;;;;;;;;;AAsBD,SAAgB,iBAAiB,WAAkC;AACjE,QAAO,YAAyB;EAC9B,MAAM;EACN,
|
|
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 /** Number of retry attempts on transient failures. Default: 2 */\n retries?: 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 { key: 'retries' },\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.evlog.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('evlog:drain', createAxiomDrain())\n *\n * // With overrides\n * nitroApp.hooks.hook('evlog:drain', createAxiomDrain({\n * dataset: 'my-dataset',\n * }))\n * ```\n */\nexport function createAxiomDrain(overrides?: Partial<AxiomConfig>) {\n return defineDrain<AxiomConfig>({\n name: 'axiom',\n resolve: async () => {\n const config = await resolveAdapterConfig<ResolvedAxiomConfig>(\n 'axiom',\n AXIOM_FIELDS,\n overrides as Partial<ResolvedAxiomConfig>,\n )\n if (!config.dataset || !config.token) {\n console.error('[evlog/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('[evlog/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 retries: config.retries,\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(`[evlog/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":";;;AA4CA,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;CAClB,EAAE,KAAK,WAAW;CACnB;;;;;;;;;;;;;;;;;;;;;AAsBD,SAAgB,iBAAiB,WAAkC;AACjE,QAAO,YAAyB;EAC9B,MAAM;EACN,SAAS,YAAY;GACnB,MAAM,SAAS,MAAM,qBACnB,SACA,cACA,UACD;AACD,OAAI,CAAC,OAAO,WAAW,CAAC,OAAO,OAAO;AACpC,YAAQ,MAAM,yHAAyH;AACvI,WAAO;;AAGT,OAAI,OAAO,WAAW,OAAO,SAAS;AACpC,YAAQ,KAAK,uFAAuF;AACpG,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,SAAS,OAAO;EAChB,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,0BAA0B,OAAO,QAAQ,6DAA6D;AAEnH,SAAO,GADS,OAAO,QAAQ,QAAQ,QAAQ,GAAG,CAChC,aAAa"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as resolveAdapterConfig, t as httpPost } from "../_http-
|
|
2
|
-
import { t as defineDrain } from "../_drain-
|
|
1
|
+
import { n as resolveAdapterConfig, t as httpPost } from "../_http-rRIz2Q0L.mjs";
|
|
2
|
+
import { t as defineDrain } from "../_drain-CJDuM0ua.mjs";
|
|
3
3
|
//#region src/adapters/better-stack.ts
|
|
4
4
|
const BETTER_STACK_FIELDS = [
|
|
5
5
|
{
|
|
@@ -47,8 +47,8 @@ function toBetterStackEvent(event) {
|
|
|
47
47
|
function createBetterStackDrain(overrides) {
|
|
48
48
|
return defineDrain({
|
|
49
49
|
name: "better-stack",
|
|
50
|
-
resolve: () => {
|
|
51
|
-
const config = resolveAdapterConfig("betterStack", BETTER_STACK_FIELDS, overrides);
|
|
50
|
+
resolve: async () => {
|
|
51
|
+
const config = await resolveAdapterConfig("betterStack", BETTER_STACK_FIELDS, overrides);
|
|
52
52
|
if (!config.sourceToken) {
|
|
53
53
|
console.error("[evlog/better-stack] Missing source token. Set NUXT_BETTER_STACK_SOURCE_TOKEN env var or pass to createBetterStackDrain()");
|
|
54
54
|
return null;
|
|
@@ -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 /** Number of retry attempts on transient failures. Default: 2 */\n retries?: 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 { key: 'retries' },\n]\n\n/**\n * Transform an evlog 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.evlog.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('evlog:drain', createBetterStackDrain())\n *\n * // With overrides\n * nitroApp.hooks.hook('evlog: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('[evlog/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 retries: config.retries,\n label: 'Better Stack',\n })\n}\n"],"mappings":";;;AAiBA,MAAM,sBAAwD;CAC5D;EAAE,KAAK;EAAe,KAAK,CAAC,kCAAkC,4BAA4B;EAAE;CAC5F;EAAE,KAAK;EAAY,KAAK,CAAC,8BAA8B,wBAAwB;EAAE;CACjF,EAAE,KAAK,WAAW;CAClB,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,
|
|
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 /** Number of retry attempts on transient failures. Default: 2 */\n retries?: 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 { key: 'retries' },\n]\n\n/**\n * Transform an evlog 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.evlog.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('evlog:drain', createBetterStackDrain())\n *\n * // With overrides\n * nitroApp.hooks.hook('evlog: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: async () => {\n const config = await resolveAdapterConfig<BetterStackConfig>('betterStack', BETTER_STACK_FIELDS, overrides)\n if (!config.sourceToken) {\n console.error('[evlog/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 retries: config.retries,\n label: 'Better Stack',\n })\n}\n"],"mappings":";;;AAiBA,MAAM,sBAAwD;CAC5D;EAAE,KAAK;EAAe,KAAK,CAAC,kCAAkC,4BAA4B;EAAE;CAC5F;EAAE,KAAK;EAAY,KAAK,CAAC,8BAA8B,wBAAwB;EAAE;CACjF,EAAE,KAAK,WAAW;CAClB,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,SAAS,YAAY;GACnB,MAAM,SAAS,MAAM,qBAAwC,eAAe,qBAAqB,UAAU;AAC3G,OAAI,CAAC,OAAO,aAAa;AACvB,YAAQ,MAAM,4HAA4H;AAC1I,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,SAAS,OAAO;EAChB,OAAO;EACR,CAAC"}
|
package/dist/adapters/fs.d.mts
CHANGED
package/dist/adapters/fs.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as defineDrain } from "../_drain-
|
|
1
|
+
import { t as defineDrain } from "../_drain-CJDuM0ua.mjs";
|
|
2
2
|
import { join, sep } from "node:path";
|
|
3
3
|
import { appendFile, mkdir, readdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
4
4
|
//#region src/adapters/fs.ts
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { T as WideEvent, r as DrainContext } from "../types-DbVDS9eu.mjs";
|
|
2
|
+
import { OTLPConfig } from "./otlp.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/adapters/hyperdx.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* HyperDX cloud OTLP HTTP base URL.
|
|
7
|
+
* @see https://hyperdx.io/docs/install/opentelemetry — “Our OpenTelemetry HTTP endpoint is hosted at `https://in-otel.hyperdx.io` …”
|
|
8
|
+
*/
|
|
9
|
+
declare const HYPERDX_DEFAULT_OTLP_HTTP_ENDPOINT = "https://in-otel.hyperdx.io";
|
|
10
|
+
interface HyperDXConfig {
|
|
11
|
+
/**
|
|
12
|
+
* Ingestion API key. Sent as the `authorization` header value, matching HyperDX’s OpenTelemetry docs:
|
|
13
|
+
* `authorization: <YOUR_HYPERDX_API_KEY_HERE>`
|
|
14
|
+
* @see https://hyperdx.io/docs/install/opentelemetry
|
|
15
|
+
*/
|
|
16
|
+
apiKey: string;
|
|
17
|
+
/**
|
|
18
|
+
* OTLP HTTP base URL (evlog appends `/v1/logs`). Defaults to {@link HYPERDX_DEFAULT_OTLP_HTTP_ENDPOINT}.
|
|
19
|
+
* Self-hosted: set to your OTLP HTTP endpoint (same shape as `otlphttp` `endpoint` in HyperDX’s collector example).
|
|
20
|
+
*/
|
|
21
|
+
endpoint?: string;
|
|
22
|
+
/** Passed through to the OTLP encoder; maps to `service.name`. */
|
|
23
|
+
serviceName?: string;
|
|
24
|
+
/** Additional OTLP resource attributes. */
|
|
25
|
+
resourceAttributes?: Record<string, string | number | boolean>;
|
|
26
|
+
/** Request timeout in milliseconds. Default: 5000 */
|
|
27
|
+
timeout?: number;
|
|
28
|
+
/** Number of retry attempts on transient failures. Default: 2 */
|
|
29
|
+
retries?: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Map HyperDX config to {@link OTLPConfig}: same wire format as HyperDX’s documented `otlphttp` exporter
|
|
33
|
+
* (`endpoint` + `authorization` header).
|
|
34
|
+
*/
|
|
35
|
+
declare function toHyperDXOTLPConfig(config: HyperDXConfig): OTLPConfig;
|
|
36
|
+
/**
|
|
37
|
+
* Create a drain that sends wide events to HyperDX via OTLP/HTTP.
|
|
38
|
+
*
|
|
39
|
+
* Matches [HyperDX OpenTelemetry ingest](https://hyperdx.io/docs/install/opentelemetry):
|
|
40
|
+
* HTTP base URL defaults to `https://in-otel.hyperdx.io`; requests use the `authorization` header set to your API key.
|
|
41
|
+
*
|
|
42
|
+
* Configuration priority (highest to lowest):
|
|
43
|
+
* 1. Overrides passed to `createHyperDXDrain()`
|
|
44
|
+
* 2. `runtimeConfig.evlog.hyperdx`
|
|
45
|
+
* 3. `runtimeConfig.hyperdx`
|
|
46
|
+
* 4. Environment variables: `NUXT_HYPERDX_*`, `HYPERDX_*` (and `OTEL_SERVICE_NAME` for service name)
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* nitroApp.hooks.hook('evlog:drain', createHyperDXDrain())
|
|
51
|
+
* // HYPERDX_API_KEY in env
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
declare function createHyperDXDrain(overrides?: Partial<HyperDXConfig>): (ctx: DrainContext | DrainContext[]) => Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Send a single wide event to HyperDX (OTLP/HTTP).
|
|
57
|
+
*/
|
|
58
|
+
declare function sendToHyperDX(event: WideEvent, config: HyperDXConfig): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Send a batch of wide events to HyperDX (OTLP/HTTP).
|
|
61
|
+
*/
|
|
62
|
+
declare function sendBatchToHyperDX(events: WideEvent[], config: HyperDXConfig): Promise<void>;
|
|
63
|
+
//#endregion
|
|
64
|
+
export { HYPERDX_DEFAULT_OTLP_HTTP_ENDPOINT, HyperDXConfig, createHyperDXDrain, sendBatchToHyperDX, sendToHyperDX, toHyperDXOTLPConfig };
|
|
65
|
+
//# sourceMappingURL=hyperdx.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hyperdx.d.mts","names":[],"sources":["../../src/adapters/hyperdx.ts"],"mappings":";;;;;;;;cAWa,kCAAA;AAAA,UAEI,aAAA;;AAFjB;;;;EAQE,MAAA;EANe;;;;EAWf,QAAA;EAAA;EAEA,WAAA;EAEA;EAAA,kBAAA,GAAqB,MAAA;EAErB;EAAA,OAAA;EAEO;EAAP,OAAA;AAAA;;;;;iBAgBc,mBAAA,CAAoB,MAAA,EAAQ,aAAA,GAAgB,UAAA;;;;AAgC5D;;;;;;;;;;;;;;;iBAAgB,kBAAA,CAAmB,SAAA,GAAY,OAAA,CAAQ,aAAA,KAAc,GAAA,EAAf,YAAA,GAAe,YAAA,OAAA,OAAA;;;;iBAkB/C,aAAA,CAAc,KAAA,EAAO,SAAA,EAAW,MAAA,EAAQ,aAAA,GAAgB,OAAA;;;;iBAOxD,kBAAA,CAAmB,MAAA,EAAQ,SAAA,IAAa,MAAA,EAAQ,aAAA,GAAgB,OAAA"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { n as resolveAdapterConfig } from "../_http-rRIz2Q0L.mjs";
|
|
2
|
+
import { t as defineDrain } from "../_drain-CJDuM0ua.mjs";
|
|
3
|
+
import { sendBatchToOTLP } from "./otlp.mjs";
|
|
4
|
+
//#region src/adapters/hyperdx.ts
|
|
5
|
+
/**
|
|
6
|
+
* HyperDX cloud OTLP HTTP base URL.
|
|
7
|
+
* @see https://hyperdx.io/docs/install/opentelemetry — “Our OpenTelemetry HTTP endpoint is hosted at `https://in-otel.hyperdx.io` …”
|
|
8
|
+
*/
|
|
9
|
+
const HYPERDX_DEFAULT_OTLP_HTTP_ENDPOINT = "https://in-otel.hyperdx.io";
|
|
10
|
+
const HYPERDX_FIELDS = [
|
|
11
|
+
{
|
|
12
|
+
key: "apiKey",
|
|
13
|
+
env: ["NUXT_HYPERDX_API_KEY", "HYPERDX_API_KEY"]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
key: "endpoint",
|
|
17
|
+
env: ["NUXT_HYPERDX_OTLP_ENDPOINT", "HYPERDX_OTLP_ENDPOINT"]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
key: "serviceName",
|
|
21
|
+
env: [
|
|
22
|
+
"NUXT_HYPERDX_SERVICE_NAME",
|
|
23
|
+
"HYPERDX_SERVICE_NAME",
|
|
24
|
+
"NUXT_OTLP_SERVICE_NAME",
|
|
25
|
+
"OTEL_SERVICE_NAME"
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
{ key: "resourceAttributes" },
|
|
29
|
+
{ key: "timeout" },
|
|
30
|
+
{ key: "retries" }
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Map HyperDX config to {@link OTLPConfig}: same wire format as HyperDX’s documented `otlphttp` exporter
|
|
34
|
+
* (`endpoint` + `authorization` header).
|
|
35
|
+
*/
|
|
36
|
+
function toHyperDXOTLPConfig(config) {
|
|
37
|
+
return {
|
|
38
|
+
endpoint: config.endpoint ?? "https://in-otel.hyperdx.io",
|
|
39
|
+
headers: { authorization: config.apiKey },
|
|
40
|
+
serviceName: config.serviceName,
|
|
41
|
+
resourceAttributes: config.resourceAttributes,
|
|
42
|
+
timeout: config.timeout,
|
|
43
|
+
retries: config.retries
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Create a drain that sends wide events to HyperDX via OTLP/HTTP.
|
|
48
|
+
*
|
|
49
|
+
* Matches [HyperDX OpenTelemetry ingest](https://hyperdx.io/docs/install/opentelemetry):
|
|
50
|
+
* HTTP base URL defaults to `https://in-otel.hyperdx.io`; requests use the `authorization` header set to your API key.
|
|
51
|
+
*
|
|
52
|
+
* Configuration priority (highest to lowest):
|
|
53
|
+
* 1. Overrides passed to `createHyperDXDrain()`
|
|
54
|
+
* 2. `runtimeConfig.evlog.hyperdx`
|
|
55
|
+
* 3. `runtimeConfig.hyperdx`
|
|
56
|
+
* 4. Environment variables: `NUXT_HYPERDX_*`, `HYPERDX_*` (and `OTEL_SERVICE_NAME` for service name)
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* nitroApp.hooks.hook('evlog:drain', createHyperDXDrain())
|
|
61
|
+
* // HYPERDX_API_KEY in env
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
function createHyperDXDrain(overrides) {
|
|
65
|
+
return defineDrain({
|
|
66
|
+
name: "hyperdx",
|
|
67
|
+
resolve: async () => {
|
|
68
|
+
const config = await resolveAdapterConfig("hyperdx", HYPERDX_FIELDS, overrides);
|
|
69
|
+
if (!config.apiKey) {
|
|
70
|
+
console.error("[evlog/hyperdx] Missing apiKey. Set HYPERDX_API_KEY or NUXT_HYPERDX_API_KEY, or pass to createHyperDXDrain()");
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
return config;
|
|
74
|
+
},
|
|
75
|
+
send: (events, config) => sendBatchToOTLP(events, toHyperDXOTLPConfig(config))
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Send a single wide event to HyperDX (OTLP/HTTP).
|
|
80
|
+
*/
|
|
81
|
+
async function sendToHyperDX(event, config) {
|
|
82
|
+
await sendBatchToHyperDX([event], config);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Send a batch of wide events to HyperDX (OTLP/HTTP).
|
|
86
|
+
*/
|
|
87
|
+
async function sendBatchToHyperDX(events, config) {
|
|
88
|
+
await sendBatchToOTLP(events, toHyperDXOTLPConfig(config));
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
export { HYPERDX_DEFAULT_OTLP_HTTP_ENDPOINT, createHyperDXDrain, sendBatchToHyperDX, sendToHyperDX, toHyperDXOTLPConfig };
|
|
92
|
+
|
|
93
|
+
//# sourceMappingURL=hyperdx.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hyperdx.mjs","names":[],"sources":["../../src/adapters/hyperdx.ts"],"sourcesContent":["import type { WideEvent } from '../types'\nimport type { ConfigField } from './_config'\nimport { resolveAdapterConfig } from './_config'\nimport { defineDrain } from './_drain'\nimport type { OTLPConfig } from './otlp'\nimport { sendBatchToOTLP } from './otlp'\n\n/**\n * HyperDX cloud OTLP HTTP base URL.\n * @see https://hyperdx.io/docs/install/opentelemetry — “Our OpenTelemetry HTTP endpoint is hosted at `https://in-otel.hyperdx.io` …”\n */\nexport const HYPERDX_DEFAULT_OTLP_HTTP_ENDPOINT = 'https://in-otel.hyperdx.io'\n\nexport interface HyperDXConfig {\n /**\n * Ingestion API key. Sent as the `authorization` header value, matching HyperDX’s OpenTelemetry docs:\n * `authorization: <YOUR_HYPERDX_API_KEY_HERE>`\n * @see https://hyperdx.io/docs/install/opentelemetry\n */\n apiKey: string\n /**\n * OTLP HTTP base URL (evlog appends `/v1/logs`). Defaults to {@link HYPERDX_DEFAULT_OTLP_HTTP_ENDPOINT}.\n * Self-hosted: set to your OTLP HTTP endpoint (same shape as `otlphttp` `endpoint` in HyperDX’s collector example).\n */\n endpoint?: string\n /** Passed through to the OTLP encoder; maps to `service.name`. */\n serviceName?: string\n /** Additional OTLP resource attributes. */\n resourceAttributes?: Record<string, string | number | boolean>\n /** Request timeout in milliseconds. Default: 5000 */\n timeout?: number\n /** Number of retry attempts on transient failures. Default: 2 */\n retries?: number\n}\n\nconst HYPERDX_FIELDS: ConfigField<HyperDXConfig>[] = [\n { key: 'apiKey', env: ['NUXT_HYPERDX_API_KEY', 'HYPERDX_API_KEY'] },\n { key: 'endpoint', env: ['NUXT_HYPERDX_OTLP_ENDPOINT', 'HYPERDX_OTLP_ENDPOINT'] },\n { key: 'serviceName', env: ['NUXT_HYPERDX_SERVICE_NAME', 'HYPERDX_SERVICE_NAME', 'NUXT_OTLP_SERVICE_NAME', 'OTEL_SERVICE_NAME'] },\n { key: 'resourceAttributes' },\n { key: 'timeout' },\n { key: 'retries' },\n]\n\n/**\n * Map HyperDX config to {@link OTLPConfig}: same wire format as HyperDX’s documented `otlphttp` exporter\n * (`endpoint` + `authorization` header).\n */\nexport function toHyperDXOTLPConfig(config: HyperDXConfig): OTLPConfig {\n return {\n endpoint: config.endpoint ?? HYPERDX_DEFAULT_OTLP_HTTP_ENDPOINT,\n headers: {\n // HyperDX docs (OpenTelemetry): headers.authorization = API key\n authorization: config.apiKey,\n },\n serviceName: config.serviceName,\n resourceAttributes: config.resourceAttributes,\n timeout: config.timeout,\n retries: config.retries,\n }\n}\n\n/**\n * Create a drain that sends wide events to HyperDX via OTLP/HTTP.\n *\n * Matches [HyperDX OpenTelemetry ingest](https://hyperdx.io/docs/install/opentelemetry):\n * HTTP base URL defaults to `https://in-otel.hyperdx.io`; requests use the `authorization` header set to your API key.\n *\n * Configuration priority (highest to lowest):\n * 1. Overrides passed to `createHyperDXDrain()`\n * 2. `runtimeConfig.evlog.hyperdx`\n * 3. `runtimeConfig.hyperdx`\n * 4. Environment variables: `NUXT_HYPERDX_*`, `HYPERDX_*` (and `OTEL_SERVICE_NAME` for service name)\n *\n * @example\n * ```ts\n * nitroApp.hooks.hook('evlog:drain', createHyperDXDrain())\n * // HYPERDX_API_KEY in env\n * ```\n */\nexport function createHyperDXDrain(overrides?: Partial<HyperDXConfig>) {\n return defineDrain<HyperDXConfig>({\n name: 'hyperdx',\n resolve: async () => {\n const config = await resolveAdapterConfig<HyperDXConfig>('hyperdx', HYPERDX_FIELDS, overrides)\n if (!config.apiKey) {\n console.error('[evlog/hyperdx] Missing apiKey. Set HYPERDX_API_KEY or NUXT_HYPERDX_API_KEY, or pass to createHyperDXDrain()')\n return null\n }\n return config as HyperDXConfig\n },\n send: (events, config) => sendBatchToOTLP(events, toHyperDXOTLPConfig(config)),\n })\n}\n\n/**\n * Send a single wide event to HyperDX (OTLP/HTTP).\n */\nexport async function sendToHyperDX(event: WideEvent, config: HyperDXConfig): Promise<void> {\n await sendBatchToHyperDX([event], config)\n}\n\n/**\n * Send a batch of wide events to HyperDX (OTLP/HTTP).\n */\nexport async function sendBatchToHyperDX(events: WideEvent[], config: HyperDXConfig): Promise<void> {\n await sendBatchToOTLP(events, toHyperDXOTLPConfig(config))\n}\n"],"mappings":";;;;;;;;AAWA,MAAa,qCAAqC;AAwBlD,MAAM,iBAA+C;CACnD;EAAE,KAAK;EAAU,KAAK,CAAC,wBAAwB,kBAAkB;EAAE;CACnE;EAAE,KAAK;EAAY,KAAK,CAAC,8BAA8B,wBAAwB;EAAE;CACjF;EAAE,KAAK;EAAe,KAAK;GAAC;GAA6B;GAAwB;GAA0B;GAAoB;EAAE;CACjI,EAAE,KAAK,sBAAsB;CAC7B,EAAE,KAAK,WAAW;CAClB,EAAE,KAAK,WAAW;CACnB;;;;;AAMD,SAAgB,oBAAoB,QAAmC;AACrE,QAAO;EACL,UAAU,OAAO,YAAA;EACjB,SAAS,EAEP,eAAe,OAAO,QACvB;EACD,aAAa,OAAO;EACpB,oBAAoB,OAAO;EAC3B,SAAS,OAAO;EAChB,SAAS,OAAO;EACjB;;;;;;;;;;;;;;;;;;;;AAqBH,SAAgB,mBAAmB,WAAoC;AACrE,QAAO,YAA2B;EAChC,MAAM;EACN,SAAS,YAAY;GACnB,MAAM,SAAS,MAAM,qBAAoC,WAAW,gBAAgB,UAAU;AAC9F,OAAI,CAAC,OAAO,QAAQ;AAClB,YAAQ,MAAM,+GAA+G;AAC7H,WAAO;;AAET,UAAO;;EAET,OAAO,QAAQ,WAAW,gBAAgB,QAAQ,oBAAoB,OAAO,CAAC;EAC/E,CAAC;;;;;AAMJ,eAAsB,cAAc,OAAkB,QAAsC;AAC1F,OAAM,mBAAmB,CAAC,MAAM,EAAE,OAAO;;;;;AAM3C,eAAsB,mBAAmB,QAAqB,QAAsC;AAClG,OAAM,gBAAgB,QAAQ,oBAAoB,OAAO,CAAC"}
|
package/dist/adapters/otlp.d.mts
CHANGED
package/dist/adapters/otlp.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as resolveAdapterConfig, t as httpPost } from "../_http-
|
|
2
|
-
import { t as defineDrain } from "../_drain-
|
|
1
|
+
import { n as resolveAdapterConfig, t as httpPost } from "../_http-rRIz2Q0L.mjs";
|
|
2
|
+
import { t as defineDrain } from "../_drain-CJDuM0ua.mjs";
|
|
3
3
|
import { n as OTEL_SEVERITY_TEXT, t as OTEL_SEVERITY_NUMBER } from "../_severity-BLiOKoxh.mjs";
|
|
4
4
|
//#region src/adapters/otlp.ts
|
|
5
5
|
const OTLP_FIELDS = [
|
|
@@ -129,8 +129,8 @@ function getHeadersFromEnv() {
|
|
|
129
129
|
function createOTLPDrain(overrides) {
|
|
130
130
|
return defineDrain({
|
|
131
131
|
name: "otlp",
|
|
132
|
-
resolve: () => {
|
|
133
|
-
const config = resolveAdapterConfig("otlp", OTLP_FIELDS, overrides);
|
|
132
|
+
resolve: async () => {
|
|
133
|
+
const config = await resolveAdapterConfig("otlp", OTLP_FIELDS, overrides);
|
|
134
134
|
if (!config.headers) config.headers = getHeadersFromEnv();
|
|
135
135
|
if (!config.endpoint) {
|
|
136
136
|
console.error("[evlog/otlp] Missing endpoint. Set NUXT_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_ENDPOINT env var, or pass to createOTLPDrain()");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"otlp.mjs","names":[],"sources":["../../src/adapters/otlp.ts"],"sourcesContent":["import type { WideEvent } from '../types'\nimport type { ConfigField } from './_config'\nimport { resolveAdapterConfig } from './_config'\nimport { defineDrain } from './_drain'\nimport { httpPost } from './_http'\nimport { OTEL_SEVERITY_NUMBER, OTEL_SEVERITY_TEXT } from './_severity'\n\nexport interface OTLPConfig {\n /** OTLP HTTP endpoint (e.g., http://localhost:4318) */\n endpoint: string\n /** Override service name (defaults to event.service) */\n serviceName?: string\n /** Additional resource attributes */\n resourceAttributes?: Record<string, string | number | boolean>\n /** Custom headers (e.g., for authentication) */\n headers?: Record<string, string>\n /** Request timeout in milliseconds. Default: 5000 */\n timeout?: number\n /** Number of retry attempts on transient failures. Default: 2 */\n retries?: number\n}\n\n/** OTLP Log Record structure */\nexport interface OTLPLogRecord {\n timeUnixNano: string\n severityNumber: number\n severityText: string\n body: { stringValue: string }\n attributes: Array<{\n key: string\n value: { stringValue?: string, intValue?: string, boolValue?: boolean }\n }>\n traceId?: string\n spanId?: string\n}\n\n/** OTLP Resource structure */\ninterface OTLPResource {\n attributes: Array<{\n key: string\n value: { stringValue?: string, intValue?: string, boolValue?: boolean }\n }>\n}\n\n/** OTLP Scope structure */\ninterface OTLPScope {\n name: string\n version?: string\n}\n\n/** OTLP ExportLogsServiceRequest structure */\ninterface ExportLogsServiceRequest {\n resourceLogs: Array<{\n resource: OTLPResource\n scopeLogs: Array<{\n scope: OTLPScope\n logRecords: OTLPLogRecord[]\n }>\n }>\n}\n\nconst OTLP_FIELDS: ConfigField<OTLPConfig>[] = [\n { key: 'endpoint', env: ['NUXT_OTLP_ENDPOINT', 'OTEL_EXPORTER_OTLP_ENDPOINT'] },\n { key: 'serviceName', env: ['NUXT_OTLP_SERVICE_NAME', 'OTEL_SERVICE_NAME'] },\n { key: 'headers' },\n { key: 'resourceAttributes' },\n { key: 'timeout' },\n { key: 'retries' },\n]\n\n/**\n * Convert a value to OTLP attribute value format.\n */\nfunction toAttributeValue(value: unknown): { stringValue?: string, intValue?: string, boolValue?: boolean } {\n if (typeof value === 'boolean') {\n return { boolValue: value }\n }\n if (typeof value === 'number' && Number.isInteger(value)) {\n return { intValue: String(value) }\n }\n if (typeof value === 'string') {\n return { stringValue: value }\n }\n // For complex types, serialize to JSON string\n return { stringValue: JSON.stringify(value) }\n}\n\n/**\n * Convert an evlog WideEvent to an OTLP LogRecord.\n */\nexport function toOTLPLogRecord(event: WideEvent): OTLPLogRecord {\n const timestamp = new Date(event.timestamp).getTime() * 1_000_000 // Convert to nanoseconds\n\n // Extract known fields, rest goes to attributes\n const { level, traceId, spanId, ...rest } = event\n // Remove base fields from rest (they're handled as resource attributes)\n delete (rest as Record<string, unknown>).timestamp\n delete (rest as Record<string, unknown>).service\n delete (rest as Record<string, unknown>).environment\n delete (rest as Record<string, unknown>).version\n delete (rest as Record<string, unknown>).commitHash\n delete (rest as Record<string, unknown>).region\n\n const attributes: OTLPLogRecord['attributes'] = []\n\n // Add all remaining event fields as attributes\n for (const [key, value] of Object.entries(rest)) {\n if (value !== undefined && value !== null) {\n attributes.push({\n key,\n value: toAttributeValue(value),\n })\n }\n }\n\n const record: OTLPLogRecord = {\n timeUnixNano: String(timestamp),\n severityNumber: OTEL_SEVERITY_NUMBER[level] ?? 9,\n severityText: OTEL_SEVERITY_TEXT[level] ?? 'INFO',\n body: { stringValue: JSON.stringify(event) },\n attributes,\n }\n\n // Add trace context if present\n if (typeof traceId === 'string') {\n record.traceId = traceId\n }\n if (typeof spanId === 'string') {\n record.spanId = spanId\n }\n\n return record\n}\n\n/**\n * Build OTLP resource attributes from event and config.\n */\nfunction buildResourceAttributes(\n event: WideEvent,\n config: OTLPConfig,\n): OTLPResource['attributes'] {\n const attributes: OTLPResource['attributes'] = []\n\n // Service name\n attributes.push({\n key: 'service.name',\n value: { stringValue: config.serviceName ?? event.service },\n })\n\n // Environment\n if (event.environment) {\n attributes.push({\n key: 'deployment.environment',\n value: { stringValue: event.environment },\n })\n }\n\n // Version\n if (event.version) {\n attributes.push({\n key: 'service.version',\n value: { stringValue: event.version },\n })\n }\n\n // Region\n if (event.region) {\n attributes.push({\n key: 'cloud.region',\n value: { stringValue: event.region },\n })\n }\n\n // Commit hash\n if (event.commitHash) {\n attributes.push({\n key: 'vcs.commit.id',\n value: { stringValue: event.commitHash },\n })\n }\n\n // Custom resource attributes from config\n if (config.resourceAttributes) {\n for (const [key, value] of Object.entries(config.resourceAttributes)) {\n attributes.push({\n key,\n value: toAttributeValue(value),\n })\n }\n }\n\n return attributes\n}\n\n/**\n * Build headers from OTEL env vars.\n * Kept inline as OTLP-specific (parses OTEL_EXPORTER_OTLP_HEADERS=key=val,key=val).\n */\nfunction getHeadersFromEnv(): Record<string, string> | undefined {\n const headersEnv = process.env.OTEL_EXPORTER_OTLP_HEADERS || process.env.NUXT_OTLP_HEADERS\n if (headersEnv) {\n const headers: Record<string, string> = {}\n const decoded = decodeURIComponent(headersEnv)\n for (const pair of decoded.split(',')) {\n const eqIndex = pair.indexOf('=')\n if (eqIndex > 0) {\n const key = pair.slice(0, eqIndex).trim()\n const value = pair.slice(eqIndex + 1).trim()\n if (key && value) {\n headers[key] = value\n }\n }\n }\n if (Object.keys(headers).length > 0) return headers\n }\n\n const auth = process.env.NUXT_OTLP_AUTH\n if (auth) {\n return { Authorization: auth }\n }\n\n return undefined\n}\n\n/**\n * Create a drain function for sending logs to an OTLP endpoint.\n *\n * Configuration priority (highest to lowest):\n * 1. Overrides passed to createOTLPDrain()\n * 2. runtimeConfig.evlog.otlp (NUXT_EVLOG_OTLP_*)\n * 3. runtimeConfig.otlp (NUXT_OTLP_*)\n * 4. Environment variables: OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME\n *\n * @example\n * ```ts\n * // Zero config - reads from runtimeConfig or env vars\n * nitroApp.hooks.hook('evlog:drain', createOTLPDrain())\n *\n * // With overrides\n * nitroApp.hooks.hook('evlog:drain', createOTLPDrain({\n * endpoint: 'http://localhost:4318',\n * }))\n * ```\n */\nexport function createOTLPDrain(overrides?: Partial<OTLPConfig>) {\n return defineDrain<OTLPConfig>({\n name: 'otlp',\n resolve: () => {\n const config = resolveAdapterConfig<OTLPConfig>('otlp', OTLP_FIELDS, overrides)\n\n // OTLP-specific: resolve headers from env if not provided via config\n if (!config.headers) {\n config.headers = getHeadersFromEnv()\n }\n\n if (!config.endpoint) {\n console.error('[evlog/otlp] Missing endpoint. Set NUXT_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_ENDPOINT env var, or pass to createOTLPDrain()')\n return null\n }\n return config as OTLPConfig\n },\n send: sendBatchToOTLP,\n })\n}\n\n/**\n * Send a single event to an OTLP endpoint.\n *\n * @example\n * ```ts\n * await sendToOTLP(event, {\n * endpoint: 'http://localhost:4318',\n * })\n * ```\n */\nexport async function sendToOTLP(event: WideEvent, config: OTLPConfig): Promise<void> {\n await sendBatchToOTLP([event], config)\n}\n\n/**\n * Send a batch of events to an OTLP endpoint.\n *\n * @example\n * ```ts\n * await sendBatchToOTLP(events, {\n * endpoint: 'http://localhost:4318',\n * })\n * ```\n */\nexport async function sendBatchToOTLP(events: WideEvent[], config: OTLPConfig): Promise<void> {\n if (events.length === 0) return\n\n const url = `${config.endpoint.replace(/\\/$/, '')}/v1/logs`\n\n // Group events by (service, environment) so each gets correct OTLP resource attributes\n const grouped = new Map<string, WideEvent[]>()\n for (const event of events) {\n const key = `${event.service}::${event.environment}`\n const group = grouped.get(key)\n if (group) {\n group.push(event)\n } else {\n grouped.set(key, [event])\n }\n }\n\n const payload: ExportLogsServiceRequest = {\n resourceLogs: Array.from(grouped.values()).map((groupEvents) => ({\n resource: { attributes: buildResourceAttributes(groupEvents[0]!, config) },\n scopeLogs: [\n {\n scope: { name: 'evlog', version: '1.0.0' },\n logRecords: groupEvents.map(toOTLPLogRecord),\n },\n ],\n })),\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...config.headers,\n }\n\n await httpPost({\n url,\n headers,\n body: JSON.stringify(payload),\n timeout: config.timeout ?? 5000,\n retries: config.retries,\n label: 'OTLP',\n })\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"otlp.mjs","names":[],"sources":["../../src/adapters/otlp.ts"],"sourcesContent":["import type { WideEvent } from '../types'\nimport type { ConfigField } from './_config'\nimport { resolveAdapterConfig } from './_config'\nimport { defineDrain } from './_drain'\nimport { httpPost } from './_http'\nimport { OTEL_SEVERITY_NUMBER, OTEL_SEVERITY_TEXT } from './_severity'\n\nexport interface OTLPConfig {\n /** OTLP HTTP endpoint (e.g., http://localhost:4318) */\n endpoint: string\n /** Override service name (defaults to event.service) */\n serviceName?: string\n /** Additional resource attributes */\n resourceAttributes?: Record<string, string | number | boolean>\n /** Custom headers (e.g., for authentication) */\n headers?: Record<string, string>\n /** Request timeout in milliseconds. Default: 5000 */\n timeout?: number\n /** Number of retry attempts on transient failures. Default: 2 */\n retries?: number\n}\n\n/** OTLP Log Record structure */\nexport interface OTLPLogRecord {\n timeUnixNano: string\n severityNumber: number\n severityText: string\n body: { stringValue: string }\n attributes: Array<{\n key: string\n value: { stringValue?: string, intValue?: string, boolValue?: boolean }\n }>\n traceId?: string\n spanId?: string\n}\n\n/** OTLP Resource structure */\ninterface OTLPResource {\n attributes: Array<{\n key: string\n value: { stringValue?: string, intValue?: string, boolValue?: boolean }\n }>\n}\n\n/** OTLP Scope structure */\ninterface OTLPScope {\n name: string\n version?: string\n}\n\n/** OTLP ExportLogsServiceRequest structure */\ninterface ExportLogsServiceRequest {\n resourceLogs: Array<{\n resource: OTLPResource\n scopeLogs: Array<{\n scope: OTLPScope\n logRecords: OTLPLogRecord[]\n }>\n }>\n}\n\nconst OTLP_FIELDS: ConfigField<OTLPConfig>[] = [\n { key: 'endpoint', env: ['NUXT_OTLP_ENDPOINT', 'OTEL_EXPORTER_OTLP_ENDPOINT'] },\n { key: 'serviceName', env: ['NUXT_OTLP_SERVICE_NAME', 'OTEL_SERVICE_NAME'] },\n { key: 'headers' },\n { key: 'resourceAttributes' },\n { key: 'timeout' },\n { key: 'retries' },\n]\n\n/**\n * Convert a value to OTLP attribute value format.\n */\nfunction toAttributeValue(value: unknown): { stringValue?: string, intValue?: string, boolValue?: boolean } {\n if (typeof value === 'boolean') {\n return { boolValue: value }\n }\n if (typeof value === 'number' && Number.isInteger(value)) {\n return { intValue: String(value) }\n }\n if (typeof value === 'string') {\n return { stringValue: value }\n }\n // For complex types, serialize to JSON string\n return { stringValue: JSON.stringify(value) }\n}\n\n/**\n * Convert an evlog WideEvent to an OTLP LogRecord.\n */\nexport function toOTLPLogRecord(event: WideEvent): OTLPLogRecord {\n const timestamp = new Date(event.timestamp).getTime() * 1_000_000 // Convert to nanoseconds\n\n // Extract known fields, rest goes to attributes\n const { level, traceId, spanId, ...rest } = event\n // Remove base fields from rest (they're handled as resource attributes)\n delete (rest as Record<string, unknown>).timestamp\n delete (rest as Record<string, unknown>).service\n delete (rest as Record<string, unknown>).environment\n delete (rest as Record<string, unknown>).version\n delete (rest as Record<string, unknown>).commitHash\n delete (rest as Record<string, unknown>).region\n\n const attributes: OTLPLogRecord['attributes'] = []\n\n // Add all remaining event fields as attributes\n for (const [key, value] of Object.entries(rest)) {\n if (value !== undefined && value !== null) {\n attributes.push({\n key,\n value: toAttributeValue(value),\n })\n }\n }\n\n const record: OTLPLogRecord = {\n timeUnixNano: String(timestamp),\n severityNumber: OTEL_SEVERITY_NUMBER[level] ?? 9,\n severityText: OTEL_SEVERITY_TEXT[level] ?? 'INFO',\n body: { stringValue: JSON.stringify(event) },\n attributes,\n }\n\n // Add trace context if present\n if (typeof traceId === 'string') {\n record.traceId = traceId\n }\n if (typeof spanId === 'string') {\n record.spanId = spanId\n }\n\n return record\n}\n\n/**\n * Build OTLP resource attributes from event and config.\n */\nfunction buildResourceAttributes(\n event: WideEvent,\n config: OTLPConfig,\n): OTLPResource['attributes'] {\n const attributes: OTLPResource['attributes'] = []\n\n // Service name\n attributes.push({\n key: 'service.name',\n value: { stringValue: config.serviceName ?? event.service },\n })\n\n // Environment\n if (event.environment) {\n attributes.push({\n key: 'deployment.environment',\n value: { stringValue: event.environment },\n })\n }\n\n // Version\n if (event.version) {\n attributes.push({\n key: 'service.version',\n value: { stringValue: event.version },\n })\n }\n\n // Region\n if (event.region) {\n attributes.push({\n key: 'cloud.region',\n value: { stringValue: event.region },\n })\n }\n\n // Commit hash\n if (event.commitHash) {\n attributes.push({\n key: 'vcs.commit.id',\n value: { stringValue: event.commitHash },\n })\n }\n\n // Custom resource attributes from config\n if (config.resourceAttributes) {\n for (const [key, value] of Object.entries(config.resourceAttributes)) {\n attributes.push({\n key,\n value: toAttributeValue(value),\n })\n }\n }\n\n return attributes\n}\n\n/**\n * Build headers from OTEL env vars.\n * Kept inline as OTLP-specific (parses OTEL_EXPORTER_OTLP_HEADERS=key=val,key=val).\n */\nfunction getHeadersFromEnv(): Record<string, string> | undefined {\n const headersEnv = process.env.OTEL_EXPORTER_OTLP_HEADERS || process.env.NUXT_OTLP_HEADERS\n if (headersEnv) {\n const headers: Record<string, string> = {}\n const decoded = decodeURIComponent(headersEnv)\n for (const pair of decoded.split(',')) {\n const eqIndex = pair.indexOf('=')\n if (eqIndex > 0) {\n const key = pair.slice(0, eqIndex).trim()\n const value = pair.slice(eqIndex + 1).trim()\n if (key && value) {\n headers[key] = value\n }\n }\n }\n if (Object.keys(headers).length > 0) return headers\n }\n\n const auth = process.env.NUXT_OTLP_AUTH\n if (auth) {\n return { Authorization: auth }\n }\n\n return undefined\n}\n\n/**\n * Create a drain function for sending logs to an OTLP endpoint.\n *\n * Configuration priority (highest to lowest):\n * 1. Overrides passed to createOTLPDrain()\n * 2. runtimeConfig.evlog.otlp (NUXT_EVLOG_OTLP_*)\n * 3. runtimeConfig.otlp (NUXT_OTLP_*)\n * 4. Environment variables: OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME\n *\n * @example\n * ```ts\n * // Zero config - reads from runtimeConfig or env vars\n * nitroApp.hooks.hook('evlog:drain', createOTLPDrain())\n *\n * // With overrides\n * nitroApp.hooks.hook('evlog:drain', createOTLPDrain({\n * endpoint: 'http://localhost:4318',\n * }))\n * ```\n */\nexport function createOTLPDrain(overrides?: Partial<OTLPConfig>) {\n return defineDrain<OTLPConfig>({\n name: 'otlp',\n resolve: async () => {\n const config = await resolveAdapterConfig<OTLPConfig>('otlp', OTLP_FIELDS, overrides)\n\n // OTLP-specific: resolve headers from env if not provided via config\n if (!config.headers) {\n config.headers = getHeadersFromEnv()\n }\n\n if (!config.endpoint) {\n console.error('[evlog/otlp] Missing endpoint. Set NUXT_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_ENDPOINT env var, or pass to createOTLPDrain()')\n return null\n }\n return config as OTLPConfig\n },\n send: sendBatchToOTLP,\n })\n}\n\n/**\n * Send a single event to an OTLP endpoint.\n *\n * @example\n * ```ts\n * await sendToOTLP(event, {\n * endpoint: 'http://localhost:4318',\n * })\n * ```\n */\nexport async function sendToOTLP(event: WideEvent, config: OTLPConfig): Promise<void> {\n await sendBatchToOTLP([event], config)\n}\n\n/**\n * Send a batch of events to an OTLP endpoint.\n *\n * @example\n * ```ts\n * await sendBatchToOTLP(events, {\n * endpoint: 'http://localhost:4318',\n * })\n * ```\n */\nexport async function sendBatchToOTLP(events: WideEvent[], config: OTLPConfig): Promise<void> {\n if (events.length === 0) return\n\n const url = `${config.endpoint.replace(/\\/$/, '')}/v1/logs`\n\n // Group events by (service, environment) so each gets correct OTLP resource attributes\n const grouped = new Map<string, WideEvent[]>()\n for (const event of events) {\n const key = `${event.service}::${event.environment}`\n const group = grouped.get(key)\n if (group) {\n group.push(event)\n } else {\n grouped.set(key, [event])\n }\n }\n\n const payload: ExportLogsServiceRequest = {\n resourceLogs: Array.from(grouped.values()).map((groupEvents) => ({\n resource: { attributes: buildResourceAttributes(groupEvents[0]!, config) },\n scopeLogs: [\n {\n scope: { name: 'evlog', version: '1.0.0' },\n logRecords: groupEvents.map(toOTLPLogRecord),\n },\n ],\n })),\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...config.headers,\n }\n\n await httpPost({\n url,\n headers,\n body: JSON.stringify(payload),\n timeout: config.timeout ?? 5000,\n retries: config.retries,\n label: 'OTLP',\n })\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as resolveAdapterConfig, t as httpPost } from "../_http-
|
|
2
|
-
import { t as defineDrain } from "../_drain-
|
|
1
|
+
import { n as resolveAdapterConfig, t as httpPost } from "../_http-rRIz2Q0L.mjs";
|
|
2
|
+
import { t as defineDrain } from "../_drain-CJDuM0ua.mjs";
|
|
3
3
|
import { sendBatchToOTLP } from "./otlp.mjs";
|
|
4
4
|
//#region src/adapters/posthog.ts
|
|
5
5
|
const POSTHOG_FIELDS = [
|
|
@@ -70,8 +70,8 @@ function toPostHogEvent(event, config) {
|
|
|
70
70
|
function createPostHogDrain(overrides) {
|
|
71
71
|
return defineDrain({
|
|
72
72
|
name: "posthog",
|
|
73
|
-
resolve: () => {
|
|
74
|
-
const config = resolveAdapterConfig("posthog", POSTHOG_FIELDS, overrides);
|
|
73
|
+
resolve: async () => {
|
|
74
|
+
const config = await resolveAdapterConfig("posthog", POSTHOG_FIELDS, overrides);
|
|
75
75
|
if (!config.apiKey) {
|
|
76
76
|
console.error("[evlog/posthog] Missing apiKey. Set NUXT_POSTHOG_API_KEY/POSTHOG_API_KEY env var or pass to createPostHogDrain()");
|
|
77
77
|
return null;
|
|
@@ -130,8 +130,8 @@ async function sendBatchToPostHog(events, config) {
|
|
|
130
130
|
function createPostHogEventsDrain(overrides) {
|
|
131
131
|
return defineDrain({
|
|
132
132
|
name: "posthog-events",
|
|
133
|
-
resolve: () => {
|
|
134
|
-
const config = resolveAdapterConfig("posthog", POSTHOG_EVENTS_FIELDS, overrides);
|
|
133
|
+
resolve: async () => {
|
|
134
|
+
const config = await resolveAdapterConfig("posthog", POSTHOG_EVENTS_FIELDS, overrides);
|
|
135
135
|
if (!config.apiKey) {
|
|
136
136
|
console.error("[evlog/posthog-events] Missing apiKey. Set NUXT_POSTHOG_API_KEY/POSTHOG_API_KEY env var or pass to createPostHogEventsDrain()");
|
|
137
137
|
return null;
|