evlog 2.9.0 → 2.11.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 +27 -1
- package/dist/{_drain-C9Nr-6Wc.mjs → _drain-YH8ERc5l.mjs} +7 -2
- package/dist/_drain-YH8ERc5l.mjs.map +1 -0
- package/dist/{_http-C2UoHWgm.mjs → _http-C_2wbJw3.mjs} +9 -20
- package/dist/_http-C_2wbJw3.mjs.map +1 -0
- package/dist/{_severity-BLiOKoxh.mjs → _severity-BZhz3f9e.mjs} +1 -1
- package/dist/{_severity-BLiOKoxh.mjs.map → _severity-BZhz3f9e.mjs.map} +1 -1
- package/dist/adapters/axiom.d.mts +1 -1
- package/dist/adapters/axiom.d.mts.map +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.d.mts.map +1 -1
- package/dist/adapters/better-stack.mjs +4 -4
- package/dist/adapters/better-stack.mjs.map +1 -1
- package/dist/adapters/datadog.d.mts +86 -0
- package/dist/adapters/datadog.d.mts.map +1 -0
- package/dist/adapters/datadog.mjs +172 -0
- package/dist/adapters/datadog.mjs.map +1 -0
- package/dist/adapters/fs.d.mts +1 -1
- package/dist/adapters/fs.d.mts.map +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.d.mts.map +1 -1
- package/dist/adapters/otlp.mjs +5 -5
- package/dist/adapters/otlp.mjs.map +1 -1
- package/dist/adapters/posthog.d.mts +1 -1
- package/dist/adapters/posthog.d.mts.map +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.d.mts.map +1 -1
- package/dist/adapters/sentry.mjs +5 -5
- package/dist/adapters/sentry.mjs.map +1 -1
- package/dist/ai/index.d.mts +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/elysia/index.d.mts +2 -2
- package/dist/elysia/index.mjs +1 -1
- package/dist/enrichers.d.mts +1 -1
- package/dist/{error-BjaGNgoo.d.mts → error-plrBYLQk.d.mts} +7 -2
- package/dist/error-plrBYLQk.d.mts.map +1 -0
- package/dist/error.d.mts +1 -1
- package/dist/error.mjs +15 -0
- package/dist/error.mjs.map +1 -1
- package/dist/{errors-BBJmxg3d.d.mts → errors-bPoj9UZk.d.mts} +2 -2
- package/dist/{errors-BBJmxg3d.d.mts.map → errors-bPoj9UZk.d.mts.map} +1 -1
- package/dist/{errors-BJRXUfMg.mjs → errors-gH4C9KSC.mjs} +1 -1
- package/dist/{errors-BJRXUfMg.mjs.map → errors-gH4C9KSC.mjs.map} +1 -1
- package/dist/express/index.d.mts +2 -2
- package/dist/express/index.mjs +2 -2
- package/dist/fastify/index.d.mts +2 -2
- package/dist/fastify/index.d.mts.map +1 -1
- package/dist/fastify/index.mjs +2 -2
- package/dist/{headers-DmzJ3sQ-.mjs → headers-BSi3UHKL.mjs} +3 -3
- package/dist/{headers-DmzJ3sQ-.mjs.map → headers-BSi3UHKL.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-3ZE3g6GW.d.mts → logger-CG1eop2_.d.mts} +13 -3
- package/dist/logger-CG1eop2_.d.mts.map +1 -0
- package/dist/logger.d.mts +2 -2
- package/dist/logger.mjs +18 -2
- package/dist/logger.mjs.map +1 -1
- package/dist/{middleware-B9uwQ5B4.d.mts → middleware-DojmTj9Y.d.mts} +2 -2
- package/dist/{middleware-B9uwQ5B4.d.mts.map → middleware-DojmTj9Y.d.mts.map} +1 -1
- package/dist/nestjs/index.d.mts +2 -2
- package/dist/nestjs/index.mjs +2 -2
- package/dist/next/client.d.mts +3 -3
- package/dist/next/client.d.mts.map +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.d.mts +2 -2
- package/dist/nitro/errorHandler.mjs +2 -2
- package/dist/nitro/module.d.mts +2 -2
- package/dist/nitro/plugin.d.mts +2 -2
- package/dist/nitro/plugin.mjs +4 -8
- package/dist/nitro/plugin.mjs.map +1 -1
- package/dist/nitro/v3/errorHandler.d.mts +2 -2
- package/dist/nitro/v3/errorHandler.mjs +2 -2
- package/dist/nitro/v3/index.d.mts +3 -1
- package/dist/nitro/v3/index.mjs +3 -1
- package/dist/nitro/v3/middleware.d.mts +11 -3
- package/dist/nitro/v3/middleware.d.mts.map +1 -1
- package/dist/nitro/v3/middleware.mjs +1 -21
- package/dist/nitro/v3/middleware.mjs.map +1 -1
- package/dist/nitro/v3/module.d.mts +1 -1
- package/dist/nitro/v3/plugin.d.mts +2 -2
- package/dist/nitro/v3/plugin.mjs +5 -5
- package/dist/nitro/v3/plugin.mjs.map +1 -1
- package/dist/nitro/v3/useLogger.d.mts +1 -1
- package/dist/{nitro-BbTINVdZ.d.mts → nitro-CfGx0wDJ.d.mts} +2 -3
- package/dist/nitro-CfGx0wDJ.d.mts.map +1 -0
- package/dist/{nitro-OmT_M4Pb.mjs → nitro-Dpq5ZmcM.mjs} +2 -2
- package/dist/{nitro-OmT_M4Pb.mjs.map → nitro-Dpq5ZmcM.mjs.map} +1 -1
- package/dist/nitroConfigBridge-fidbf-Y_.mjs +92 -0
- package/dist/nitroConfigBridge-fidbf-Y_.mjs.map +1 -0
- package/dist/nuxt/module.d.mts +6 -5
- package/dist/nuxt/module.d.mts.map +1 -1
- package/dist/nuxt/module.mjs +5 -3
- package/dist/nuxt/module.mjs.map +1 -1
- package/dist/{parseError-DO1qtmGL.d.mts → parseError-B_qXj8x4.d.mts} +2 -2
- package/dist/parseError-B_qXj8x4.d.mts.map +1 -0
- package/dist/react-router/index.d.mts +4 -4
- package/dist/react-router/index.d.mts.map +1 -1
- package/dist/react-router/index.mjs +2 -2
- 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/routes/_evlog/ingest.post.d.mts +2 -2
- package/dist/runtime/server/useLogger.d.mts +1 -1
- package/dist/runtime/utils/parseError.d.mts +2 -2
- package/dist/runtime/utils/parseError.mjs +1 -1
- package/dist/{source-location-DRvDDqfq.mjs → source-location-B1VVgXkh.mjs} +1 -1
- package/dist/{source-location-DRvDDqfq.mjs.map → source-location-B1VVgXkh.mjs.map} +1 -1
- package/dist/{storage-DsueXspk.mjs → storage-B6NPh8rV.mjs} +1 -1
- package/dist/{storage-DsueXspk.mjs.map → storage-B6NPh8rV.mjs.map} +1 -1
- package/dist/sveltekit/index.d.mts +2 -2
- package/dist/sveltekit/index.mjs +4 -4
- package/dist/toolkit.d.mts +3 -3
- package/dist/toolkit.mjs +4 -4
- package/dist/{types-BpsDbwHU.d.mts → types-v_JkG_D7.d.mts} +11 -1
- package/dist/types-v_JkG_D7.d.mts.map +1 -0
- package/dist/types.d.mts +1 -1
- package/dist/{useLogger-C_8vgz0g.d.mts → useLogger-TjKH37BO.d.mts} +2 -2
- package/dist/{useLogger-C_8vgz0g.d.mts.map → useLogger-TjKH37BO.d.mts.map} +1 -1
- package/dist/utils.d.mts +1 -1
- package/dist/vite/index.d.mts +1 -1
- package/dist/vite/index.mjs +1 -1
- package/dist/workers.d.mts +1 -1
- package/package.json +49 -19
- package/dist/_drain-C9Nr-6Wc.mjs.map +0 -1
- package/dist/_http-C2UoHWgm.mjs.map +0 -1
- package/dist/error-BjaGNgoo.d.mts.map +0 -1
- package/dist/logger-3ZE3g6GW.d.mts.map +0 -1
- package/dist/nitro-BbTINVdZ.d.mts.map +0 -1
- package/dist/parseError-DO1qtmGL.d.mts.map +0 -1
- package/dist/types-BpsDbwHU.d.mts.map +0 -1
package/README.md
CHANGED
|
@@ -754,6 +754,29 @@ Set environment variables:
|
|
|
754
754
|
NUXT_OTLP_ENDPOINT=http://localhost:4318
|
|
755
755
|
```
|
|
756
756
|
|
|
757
|
+
### Datadog
|
|
758
|
+
|
|
759
|
+
```typescript
|
|
760
|
+
// server/plugins/evlog-drain.ts
|
|
761
|
+
import { createDatadogDrain } from 'evlog/datadog'
|
|
762
|
+
|
|
763
|
+
export default defineNitroPlugin((nitroApp) => {
|
|
764
|
+
nitroApp.hooks.hook('evlog:drain', createDatadogDrain())
|
|
765
|
+
})
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
Set environment variables:
|
|
769
|
+
|
|
770
|
+
```bash
|
|
771
|
+
NUXT_DATADOG_API_KEY=your-api-key
|
|
772
|
+
# Optional — defaults to datadoghq.com
|
|
773
|
+
NUXT_DATADOG_SITE=datadoghq.eu
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
You can also use standard Datadog names: `DD_API_KEY` and `DD_SITE`.
|
|
777
|
+
|
|
778
|
+
Wide events are sent with a short **`message` line** (method, path, level) and full context under the **`evlog`** attribute (facets like `@evlog.path`). See the [Datadog adapter docs](https://www.evlog.dev/adapters/datadog).
|
|
779
|
+
|
|
757
780
|
### PostHog
|
|
758
781
|
|
|
759
782
|
```typescript
|
|
@@ -1081,9 +1104,12 @@ createError({
|
|
|
1081
1104
|
fix?: string // How to fix it
|
|
1082
1105
|
link?: string // Documentation URL
|
|
1083
1106
|
cause?: Error // Original error
|
|
1107
|
+
internal?: Record<string, unknown> // Backend-only; never in HTTP body or toJSON()
|
|
1084
1108
|
})
|
|
1085
1109
|
```
|
|
1086
1110
|
|
|
1111
|
+
**`internal`** — Optional context for support, auditing, or debugging (IDs, gateway codes, raw diagnostics). It is stored on `EvlogError` and exposed as `error.internal` in server code. It is **not** included in JSON error responses, `toJSON()`, or `parseError()` results. When the error is passed to `log.error()` (or thrown in integrations that record errors on the wide event), `internal` is copied into the emitted event under `error.internal`.
|
|
1112
|
+
|
|
1087
1113
|
### `parseError(error)`
|
|
1088
1114
|
|
|
1089
1115
|
Parse a caught error into a flat structure with all evlog fields. Auto-imported in Nuxt.
|
|
@@ -1141,7 +1167,7 @@ evlog provides [Agent Skills](https://www.evlog.dev/getting-started/agent-skills
|
|
|
1141
1167
|
### Installation
|
|
1142
1168
|
|
|
1143
1169
|
```bash
|
|
1144
|
-
npx add
|
|
1170
|
+
npx skills add https://www.evlog.dev
|
|
1145
1171
|
```
|
|
1146
1172
|
|
|
1147
1173
|
### What it does
|
|
@@ -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-YH8ERc5l.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_drain-YH8ERc5l.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,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
//#region \0rolldown/runtime.js
|
|
3
|
-
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
4
|
-
//#endregion
|
|
1
|
+
import { t as getNitroRuntimeConfigRecord } from "./nitroConfigBridge-fidbf-Y_.mjs";
|
|
5
2
|
//#region src/adapters/_config.ts
|
|
6
3
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* Adapter runtime-config reads go through `getNitroRuntimeConfigRecord` in
|
|
5
|
+
* `shared/nitroConfigBridge.ts` (documented there — Workers-safe dynamic imports).
|
|
6
|
+
*
|
|
7
|
+
* Drain handlers remain non-blocking when the host provides `waitUntil`.
|
|
10
8
|
*/
|
|
11
9
|
function getRuntimeConfig() {
|
|
12
|
-
|
|
13
|
-
const { useRuntimeConfig } = __require("nitropack/runtime");
|
|
14
|
-
return useRuntimeConfig();
|
|
15
|
-
} catch {}
|
|
16
|
-
try {
|
|
17
|
-
const { useRuntimeConfig } = __require("nitro/runtime-config");
|
|
18
|
-
return useRuntimeConfig();
|
|
19
|
-
} catch {
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
10
|
+
return getNitroRuntimeConfigRecord();
|
|
22
11
|
}
|
|
23
|
-
function resolveAdapterConfig(namespace, fields, overrides) {
|
|
24
|
-
const runtimeConfig = getRuntimeConfig();
|
|
12
|
+
async function resolveAdapterConfig(namespace, fields, overrides) {
|
|
13
|
+
const runtimeConfig = await getRuntimeConfig();
|
|
25
14
|
const evlogNs = runtimeConfig?.evlog?.[namespace];
|
|
26
15
|
const rootNs = runtimeConfig?.[namespace];
|
|
27
16
|
const config = {};
|
|
@@ -79,4 +68,4 @@ async function httpPost({ url, headers, body, timeout, label, retries = 2 }) {
|
|
|
79
68
|
//#endregion
|
|
80
69
|
export { resolveAdapterConfig as n, httpPost as t };
|
|
81
70
|
|
|
82
|
-
//# sourceMappingURL=_http-
|
|
71
|
+
//# sourceMappingURL=_http-C_2wbJw3.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_http-C_2wbJw3.mjs","names":[],"sources":["../src/adapters/_config.ts","../src/adapters/_http.ts"],"sourcesContent":["import { getNitroRuntimeConfigRecord } from '../shared/nitroConfigBridge'\n\n/**\n * Adapter runtime-config reads go through `getNitroRuntimeConfigRecord` in\n * `shared/nitroConfigBridge.ts` (documented there — Workers-safe dynamic imports).\n *\n * Drain handlers remain non-blocking when the host provides `waitUntil`.\n */\n\nexport function getRuntimeConfig(): Promise<Record<string, any> | undefined> {\n return getNitroRuntimeConfigRecord()\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":";;;;;;;;AASA,SAAgB,mBAA6D;AAC3E,QAAO,6BAA6B;;AAQtC,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;;;;;ACnCpB,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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_severity-
|
|
1
|
+
{"version":3,"file":"_severity-BZhz3f9e.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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"axiom.d.mts","names":[],"sources":["../../src/adapters/axiom.ts"],"mappings":";;UAMU,eAAA;;EAER,OAAA
|
|
1
|
+
{"version":3,"file":"axiom.d.mts","names":[],"sources":["../../src/adapters/axiom.ts"],"mappings":";;UAMU,eAAA;;EAER,OAAA;EAFQ;EAIR,KAAA;;EAEA,KAAA;EAJA;EAMA,OAAA;EAFA;EAIA,OAAA;AAAA;AAAA,UAGQ,eAAA;EAHD;AAAA;;;;EASP,OAAA;EAKQ;EAHR,OAAA;AAAA;AAAA,UAGQ,mBAAA;EAID;EAFP,OAAA;EAKqB;EAHrB,OAAA;AAAA;AAAA,KAGU,WAAA,GAAc,eAAA,IAAmB,eAAA,GAAkB,mBAAA;;;;;;;;;AAqC/D;;;;;;;;;;;;iBAAgB,gBAAA,CAAiB,SAAA,GAAY,OAAA,CAAQ,WAAA,KAAY,GAAA,EAAb,YAAA,GAAa,YAAA,OAAA,OAAA;;;;;;;AAoCjE;;;;;iBAAsB,WAAA,CAAY,KAAA,EAAO,SAAA,EAAW,MAAA,EAAQ,WAAA,GAAc,OAAA;;;;;;;;;;AAe1E;;iBAAsB,gBAAA,CAAiB,MAAA,EAAQ,SAAA,IAAa,MAAA,EAAQ,WAAA,GAAc,OAAA"}
|
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-C_2wbJw3.mjs";
|
|
2
|
+
import { t as defineDrain } from "../_drain-YH8ERc5l.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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"better-stack.d.mts","names":[],"sources":["../../src/adapters/better-stack.ts"],"mappings":";;UAMiB,iBAAA;;EAEf,WAAA
|
|
1
|
+
{"version":3,"file":"better-stack.d.mts","names":[],"sources":["../../src/adapters/better-stack.ts"],"mappings":";;UAMiB,iBAAA;;EAEf,WAAA;EAFe;EAIf,QAAA;;EAEA,OAAA;EAJA;EAMA,OAAA;AAAA;;;;AAcF;iBAAgB,kBAAA,CAAmB,KAAA,EAAO,SAAA,GAAY,MAAA;;;;;;;;AAyBtD;;;;;;;;;;;;;iBAAgB,sBAAA,CAAuB,SAAA,GAAY,OAAA,CAAQ,iBAAA,KAAkB,GAAA,EAAnB,YAAA,GAAmB,YAAA,OAAA,OAAA;;;;;;AAyB7E;;;;;iBAAsB,iBAAA,CAAkB,KAAA,EAAO,SAAA,EAAW,MAAA,EAAQ,iBAAA,GAAoB,OAAA;;;;;;;;;;AActF;iBAAsB,sBAAA,CAAuB,MAAA,EAAQ,SAAA,IAAa,MAAA,EAAQ,iBAAA,GAAoB,OAAA"}
|
|
@@ -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-C_2wbJw3.mjs";
|
|
2
|
+
import { t as defineDrain } from "../_drain-YH8ERc5l.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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { T as WideEvent, r as DrainContext } from "../types-v_JkG_D7.mjs";
|
|
2
|
+
//#region src/adapters/datadog.d.ts
|
|
3
|
+
interface DatadogConfig {
|
|
4
|
+
/** Datadog API key with Logs intake permission */
|
|
5
|
+
apiKey: string;
|
|
6
|
+
/**
|
|
7
|
+
* Datadog site hostname (e.g. `datadoghq.com`, `datadoghq.eu`, `us3.datadoghq.com`, `ddog-gov.com`).
|
|
8
|
+
* Ignored when `intakeUrl` is set. Default: `datadoghq.com`
|
|
9
|
+
*/
|
|
10
|
+
site?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Full Logs HTTP intake URL. When set, overrides the URL derived from `site`.
|
|
13
|
+
* Default: `https://http-intake.logs.${site}/api/v2/logs`
|
|
14
|
+
*/
|
|
15
|
+
intakeUrl?: string;
|
|
16
|
+
/** Request timeout in milliseconds. Default: 5000 */
|
|
17
|
+
timeout?: number;
|
|
18
|
+
/** Number of retry attempts on transient failures. Default: 2 */
|
|
19
|
+
retries?: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Datadog treats **`status`** as log severity. evlog uses **`status`** for HTTP response codes on the wide event and
|
|
23
|
+
* inside **`error`** (structured errors). Rename every **numeric** `status` at any depth to **`httpStatusCode`** so
|
|
24
|
+
* nothing in the payload collides with reserved severity when Datadog processes attributes.
|
|
25
|
+
*
|
|
26
|
+
* Does not mutate the original {@link WideEvent} (builds new objects).
|
|
27
|
+
*/
|
|
28
|
+
declare function sanitizeWideEventForDatadog(event: WideEvent): Record<string, unknown>;
|
|
29
|
+
/**
|
|
30
|
+
* Single-line summary for Datadog’s `message` column (Live Tail / Explorer list view).
|
|
31
|
+
* Full context stays under {@link toDatadogLog}'s `evlog` object.
|
|
32
|
+
*/
|
|
33
|
+
declare function formatDatadogMessageLine(event: WideEvent): string;
|
|
34
|
+
/**
|
|
35
|
+
* Severity for Datadog’s reserved `status` field (drives Live Tail coloring and facets).
|
|
36
|
+
*
|
|
37
|
+
* Uses the wide event’s **`level`** first (`log.error()` / `log.warn()`). If the level is
|
|
38
|
+
* still `info`, falls back to the HTTP **`status`** on the wide event (`status: 4xx` → `warn`,
|
|
39
|
+
* `5xx` → `error`) so client/server error responses are visible even when no `log.error()`
|
|
40
|
+
* ran. Purely business errors on **HTTP 200** only change Datadog if you call `log.error()`.
|
|
41
|
+
*/
|
|
42
|
+
declare function resolveDatadogLogStatus(event: WideEvent): 'error' | 'warn' | 'info' | 'debug';
|
|
43
|
+
/**
|
|
44
|
+
* Map an evlog wide event to a [Datadog Logs API v2](https://docs.datadoghq.com/api/latest/logs/) log object.
|
|
45
|
+
*
|
|
46
|
+
* Shape:
|
|
47
|
+
* - **`message`** — short line for the list view (`formatDatadogMessageLine`)
|
|
48
|
+
* - **`evlog`** — full sanitized wide event (HTTP codes as `httpStatusCode`); use facets like `@evlog.path`
|
|
49
|
+
* - **`status`**, **`service`**, **`ddsource`**, **`ddtags`**, **`timestamp`** — Datadog standard fields
|
|
50
|
+
*/
|
|
51
|
+
declare function toDatadogLog(event: WideEvent): Record<string, unknown>;
|
|
52
|
+
/**
|
|
53
|
+
* Resolve the Logs intake URL from configuration.
|
|
54
|
+
*/
|
|
55
|
+
declare function resolveDatadogIntakeUrl(config: Pick<DatadogConfig, 'site' | 'intakeUrl'>): string;
|
|
56
|
+
/**
|
|
57
|
+
* Create a drain function for sending logs to Datadog via the HTTP Logs intake API.
|
|
58
|
+
*
|
|
59
|
+
* Configuration priority (highest to lowest):
|
|
60
|
+
* 1. Overrides passed to `createDatadogDrain()`
|
|
61
|
+
* 2. `runtimeConfig.evlog.datadog`
|
|
62
|
+
* 3. `runtimeConfig.datadog`
|
|
63
|
+
* 4. Environment variables: `NUXT_DATADOG_*`, `DATADOG_*`, and common `DD_*` aliases
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* // Zero config — set DD_API_KEY (or NUXT_DATADOG_API_KEY) and optionally DD_SITE
|
|
68
|
+
* nitroApp.hooks.hook('evlog:drain', createDatadogDrain())
|
|
69
|
+
*
|
|
70
|
+
* nitroApp.hooks.hook('evlog:drain', createDatadogDrain({
|
|
71
|
+
* site: 'datadoghq.eu',
|
|
72
|
+
* }))
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
declare function createDatadogDrain(overrides?: Partial<DatadogConfig>): (ctx: DrainContext | DrainContext[]) => Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Send a single wide event to Datadog.
|
|
78
|
+
*/
|
|
79
|
+
declare function sendToDatadog(event: WideEvent, config: DatadogConfig): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Send a batch of wide events to Datadog in one request.
|
|
82
|
+
*/
|
|
83
|
+
declare function sendBatchToDatadog(events: WideEvent[], config: DatadogConfig): Promise<void>;
|
|
84
|
+
//#endregion
|
|
85
|
+
export { DatadogConfig, createDatadogDrain, formatDatadogMessageLine, resolveDatadogIntakeUrl, resolveDatadogLogStatus, sanitizeWideEventForDatadog, sendBatchToDatadog, sendToDatadog, toDatadogLog };
|
|
86
|
+
//# sourceMappingURL=datadog.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"datadog.d.mts","names":[],"sources":["../../src/adapters/datadog.ts"],"mappings":";;UAMiB,aAAA;;EAEf,MAAA;EAFe;;;;EAOf,IAAA;EAAA;;;;EAKA,SAAA;EAIO;EAFP,OAAA;EAsByC;EApBzC,OAAA;AAAA;;;;;;AA2CF;;iBAvBgB,2BAAA,CAA4B,KAAA,EAAO,SAAA,GAAY,MAAA;;;AAgD/D;;iBAzBgB,wBAAA,CAAyB,KAAA,EAAO,SAAA;;;AA2ChD;;;;;;iBAlBgB,uBAAA,CAAwB,KAAA,EAAO,SAAA;;;AAwC/C;;;;;;iBAtBgB,YAAA,CAAa,KAAA,EAAO,SAAA,GAAY,MAAA;;;AAiDhD;iBA3BgB,uBAAA,CAAwB,MAAA,EAAQ,IAAA,CAAK,aAAA;;;;;;;;;;;;;;;;;;AA6CrD;;iBAlBgB,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,172 @@
|
|
|
1
|
+
import { n as resolveAdapterConfig, t as httpPost } from "../_http-C_2wbJw3.mjs";
|
|
2
|
+
import { t as defineDrain } from "../_drain-YH8ERc5l.mjs";
|
|
3
|
+
//#region src/adapters/datadog.ts
|
|
4
|
+
const DATADOG_FIELDS = [
|
|
5
|
+
{
|
|
6
|
+
key: "apiKey",
|
|
7
|
+
env: [
|
|
8
|
+
"NUXT_DATADOG_API_KEY",
|
|
9
|
+
"DATADOG_API_KEY",
|
|
10
|
+
"DD_API_KEY"
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
key: "site",
|
|
15
|
+
env: [
|
|
16
|
+
"NUXT_DATADOG_SITE",
|
|
17
|
+
"DATADOG_SITE",
|
|
18
|
+
"DD_SITE"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
key: "intakeUrl",
|
|
23
|
+
env: ["NUXT_DATADOG_LOGS_URL", "DATADOG_LOGS_URL"]
|
|
24
|
+
},
|
|
25
|
+
{ key: "timeout" },
|
|
26
|
+
{ key: "retries" }
|
|
27
|
+
];
|
|
28
|
+
const DEFAULT_SITE = "datadoghq.com";
|
|
29
|
+
/**
|
|
30
|
+
* Datadog treats **`status`** as log severity. evlog uses **`status`** for HTTP response codes on the wide event and
|
|
31
|
+
* inside **`error`** (structured errors). Rename every **numeric** `status` at any depth to **`httpStatusCode`** so
|
|
32
|
+
* nothing in the payload collides with reserved severity when Datadog processes attributes.
|
|
33
|
+
*
|
|
34
|
+
* Does not mutate the original {@link WideEvent} (builds new objects).
|
|
35
|
+
*/
|
|
36
|
+
function sanitizeWideEventForDatadog(event) {
|
|
37
|
+
return deepRenameNumericHttpStatus(event);
|
|
38
|
+
}
|
|
39
|
+
function deepRenameNumericHttpStatus(value) {
|
|
40
|
+
if (value === null || typeof value !== "object") return value;
|
|
41
|
+
if (Array.isArray(value)) return value.map(deepRenameNumericHttpStatus);
|
|
42
|
+
const obj = value;
|
|
43
|
+
const out = {};
|
|
44
|
+
for (const [k, v] of Object.entries(obj)) if (k === "status" && typeof v === "number") out.httpStatusCode = v;
|
|
45
|
+
else out[k] = deepRenameNumericHttpStatus(v);
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Single-line summary for Datadog’s `message` column (Live Tail / Explorer list view).
|
|
50
|
+
* Full context stays under {@link toDatadogLog}'s `evlog` object.
|
|
51
|
+
*/
|
|
52
|
+
function formatDatadogMessageLine(event) {
|
|
53
|
+
const levelU = event.level.toUpperCase();
|
|
54
|
+
const method = typeof event.method === "string" ? event.method : "";
|
|
55
|
+
const path = typeof event.path === "string" ? event.path : "";
|
|
56
|
+
const code = typeof event.status === "number" ? event.status : void 0;
|
|
57
|
+
const head = [
|
|
58
|
+
levelU,
|
|
59
|
+
method,
|
|
60
|
+
path
|
|
61
|
+
].filter((p) => p.length > 0).join(" ");
|
|
62
|
+
let line = code !== void 0 ? head ? `${head} (${code})` : `${levelU} (${code})` : head || levelU;
|
|
63
|
+
if (!method && !path && line === levelU && event.service) line = `${levelU} ${event.service}`;
|
|
64
|
+
return line;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Severity for Datadog’s reserved `status` field (drives Live Tail coloring and facets).
|
|
68
|
+
*
|
|
69
|
+
* Uses the wide event’s **`level`** first (`log.error()` / `log.warn()`). If the level is
|
|
70
|
+
* still `info`, falls back to the HTTP **`status`** on the wide event (`status: 4xx` → `warn`,
|
|
71
|
+
* `5xx` → `error`) so client/server error responses are visible even when no `log.error()`
|
|
72
|
+
* ran. Purely business errors on **HTTP 200** only change Datadog if you call `log.error()`.
|
|
73
|
+
*/
|
|
74
|
+
function resolveDatadogLogStatus(event) {
|
|
75
|
+
if (event.level === "error") return "error";
|
|
76
|
+
if (event.level === "warn") return "warn";
|
|
77
|
+
if (event.level === "debug") return "debug";
|
|
78
|
+
const code = typeof event.status === "number" ? event.status : void 0;
|
|
79
|
+
if (code !== void 0 && code >= 500) return "error";
|
|
80
|
+
if (code !== void 0 && code >= 400) return "warn";
|
|
81
|
+
return "info";
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Map an evlog wide event to a [Datadog Logs API v2](https://docs.datadoghq.com/api/latest/logs/) log object.
|
|
85
|
+
*
|
|
86
|
+
* Shape:
|
|
87
|
+
* - **`message`** — short line for the list view (`formatDatadogMessageLine`)
|
|
88
|
+
* - **`evlog`** — full sanitized wide event (HTTP codes as `httpStatusCode`); use facets like `@evlog.path`
|
|
89
|
+
* - **`status`**, **`service`**, **`ddsource`**, **`ddtags`**, **`timestamp`** — Datadog standard fields
|
|
90
|
+
*/
|
|
91
|
+
function toDatadogLog(event) {
|
|
92
|
+
const ms = Date.parse(event.timestamp);
|
|
93
|
+
const tags = [`env:${event.environment}`];
|
|
94
|
+
const versionTag = event.version;
|
|
95
|
+
if (versionTag !== void 0 && versionTag !== null && versionTag !== "") tags.push(`version:${String(versionTag)}`);
|
|
96
|
+
return {
|
|
97
|
+
message: formatDatadogMessageLine(event),
|
|
98
|
+
evlog: sanitizeWideEventForDatadog(event),
|
|
99
|
+
service: event.service,
|
|
100
|
+
status: resolveDatadogLogStatus(event),
|
|
101
|
+
ddsource: "evlog",
|
|
102
|
+
ddtags: tags.join(","),
|
|
103
|
+
...Number.isFinite(ms) ? { timestamp: ms } : {}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Resolve the Logs intake URL from configuration.
|
|
108
|
+
*/
|
|
109
|
+
function resolveDatadogIntakeUrl(config) {
|
|
110
|
+
if (config.intakeUrl) return config.intakeUrl.replace(/\/+$/, "");
|
|
111
|
+
return `https://http-intake.logs.${(config.site ?? DEFAULT_SITE).replace(/^\./, "").replace(/\/+$/, "")}/api/v2/logs`;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Create a drain function for sending logs to Datadog via the HTTP Logs intake API.
|
|
115
|
+
*
|
|
116
|
+
* Configuration priority (highest to lowest):
|
|
117
|
+
* 1. Overrides passed to `createDatadogDrain()`
|
|
118
|
+
* 2. `runtimeConfig.evlog.datadog`
|
|
119
|
+
* 3. `runtimeConfig.datadog`
|
|
120
|
+
* 4. Environment variables: `NUXT_DATADOG_*`, `DATADOG_*`, and common `DD_*` aliases
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```ts
|
|
124
|
+
* // Zero config — set DD_API_KEY (or NUXT_DATADOG_API_KEY) and optionally DD_SITE
|
|
125
|
+
* nitroApp.hooks.hook('evlog:drain', createDatadogDrain())
|
|
126
|
+
*
|
|
127
|
+
* nitroApp.hooks.hook('evlog:drain', createDatadogDrain({
|
|
128
|
+
* site: 'datadoghq.eu',
|
|
129
|
+
* }))
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
function createDatadogDrain(overrides) {
|
|
133
|
+
return defineDrain({
|
|
134
|
+
name: "datadog",
|
|
135
|
+
resolve: async () => {
|
|
136
|
+
const config = await resolveAdapterConfig("datadog", DATADOG_FIELDS, overrides);
|
|
137
|
+
if (!config.apiKey) {
|
|
138
|
+
console.error("[evlog/datadog] Missing API key. Set NUXT_DATADOG_API_KEY, DATADOG_API_KEY, or DD_API_KEY, or pass apiKey to createDatadogDrain()");
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
return config;
|
|
142
|
+
},
|
|
143
|
+
send: sendBatchToDatadog
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Send a single wide event to Datadog.
|
|
148
|
+
*/
|
|
149
|
+
async function sendToDatadog(event, config) {
|
|
150
|
+
await sendBatchToDatadog([event], config);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Send a batch of wide events to Datadog in one request.
|
|
154
|
+
*/
|
|
155
|
+
async function sendBatchToDatadog(events, config) {
|
|
156
|
+
if (events.length === 0) return;
|
|
157
|
+
await httpPost({
|
|
158
|
+
url: resolveDatadogIntakeUrl(config),
|
|
159
|
+
headers: {
|
|
160
|
+
"Content-Type": "application/json",
|
|
161
|
+
"DD-API-KEY": config.apiKey
|
|
162
|
+
},
|
|
163
|
+
body: JSON.stringify(events.map(toDatadogLog)),
|
|
164
|
+
timeout: config.timeout ?? 5e3,
|
|
165
|
+
retries: config.retries,
|
|
166
|
+
label: "Datadog"
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
//#endregion
|
|
170
|
+
export { createDatadogDrain, formatDatadogMessageLine, resolveDatadogIntakeUrl, resolveDatadogLogStatus, sanitizeWideEventForDatadog, sendBatchToDatadog, sendToDatadog, toDatadogLog };
|
|
171
|
+
|
|
172
|
+
//# sourceMappingURL=datadog.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"datadog.mjs","names":[],"sources":["../../src/adapters/datadog.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 DatadogConfig {\n /** Datadog API key with Logs intake permission */\n apiKey: string\n /**\n * Datadog site hostname (e.g. `datadoghq.com`, `datadoghq.eu`, `us3.datadoghq.com`, `ddog-gov.com`).\n * Ignored when `intakeUrl` is set. Default: `datadoghq.com`\n */\n site?: string\n /**\n * Full Logs HTTP intake URL. When set, overrides the URL derived from `site`.\n * Default: `https://http-intake.logs.${site}/api/v2/logs`\n */\n intakeUrl?: 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 DATADOG_FIELDS: ConfigField<DatadogConfig>[] = [\n { key: 'apiKey', env: ['NUXT_DATADOG_API_KEY', 'DATADOG_API_KEY', 'DD_API_KEY'] },\n { key: 'site', env: ['NUXT_DATADOG_SITE', 'DATADOG_SITE', 'DD_SITE'] },\n { key: 'intakeUrl', env: ['NUXT_DATADOG_LOGS_URL', 'DATADOG_LOGS_URL'] },\n { key: 'timeout' },\n { key: 'retries' },\n]\n\nconst DEFAULT_SITE = 'datadoghq.com'\n\n/**\n * Datadog treats **`status`** as log severity. evlog uses **`status`** for HTTP response codes on the wide event and\n * inside **`error`** (structured errors). Rename every **numeric** `status` at any depth to **`httpStatusCode`** so\n * nothing in the payload collides with reserved severity when Datadog processes attributes.\n *\n * Does not mutate the original {@link WideEvent} (builds new objects).\n */\nexport function sanitizeWideEventForDatadog(event: WideEvent): Record<string, unknown> {\n return deepRenameNumericHttpStatus(event as Record<string, unknown>) as Record<string, unknown>\n}\n\nfunction deepRenameNumericHttpStatus(value: unknown): unknown {\n if (value === null || typeof value !== 'object') return value\n if (Array.isArray(value)) return value.map(deepRenameNumericHttpStatus)\n const obj = value as Record<string, unknown>\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(obj)) {\n if (k === 'status' && typeof v === 'number') {\n out.httpStatusCode = v\n } else {\n out[k] = deepRenameNumericHttpStatus(v)\n }\n }\n return out\n}\n\n/**\n * Single-line summary for Datadog’s `message` column (Live Tail / Explorer list view).\n * Full context stays under {@link toDatadogLog}'s `evlog` object.\n */\nexport function formatDatadogMessageLine(event: WideEvent): string {\n const levelU = event.level.toUpperCase()\n const method = typeof event.method === 'string' ? event.method : ''\n const path = typeof event.path === 'string' ? event.path : ''\n const code = typeof event.status === 'number' ? event.status : undefined\n\n const head = [levelU, method, path].filter(p => p.length > 0).join(' ')\n let line = code !== undefined\n ? (head ? `${head} (${code})` : `${levelU} (${code})`)\n : (head || levelU)\n\n if (!method && !path && line === levelU && event.service) {\n line = `${levelU} ${event.service}`\n }\n return line\n}\n\n/**\n * Severity for Datadog’s reserved `status` field (drives Live Tail coloring and facets).\n *\n * Uses the wide event’s **`level`** first (`log.error()` / `log.warn()`). If the level is\n * still `info`, falls back to the HTTP **`status`** on the wide event (`status: 4xx` → `warn`,\n * `5xx` → `error`) so client/server error responses are visible even when no `log.error()`\n * ran. Purely business errors on **HTTP 200** only change Datadog if you call `log.error()`.\n */\nexport function resolveDatadogLogStatus(event: WideEvent): 'error' | 'warn' | 'info' | 'debug' {\n if (event.level === 'error') return 'error'\n if (event.level === 'warn') return 'warn'\n if (event.level === 'debug') return 'debug'\n const code = typeof event.status === 'number' ? event.status : undefined\n if (code !== undefined && code >= 500) return 'error'\n if (code !== undefined && code >= 400) return 'warn'\n return 'info'\n}\n\n/**\n * Map an evlog wide event to a [Datadog Logs API v2](https://docs.datadoghq.com/api/latest/logs/) log object.\n *\n * Shape:\n * - **`message`** — short line for the list view (`formatDatadogMessageLine`)\n * - **`evlog`** — full sanitized wide event (HTTP codes as `httpStatusCode`); use facets like `@evlog.path`\n * - **`status`**, **`service`**, **`ddsource`**, **`ddtags`**, **`timestamp`** — Datadog standard fields\n */\nexport function toDatadogLog(event: WideEvent): Record<string, unknown> {\n const ms = Date.parse(event.timestamp)\n const tags = [`env:${event.environment}`]\n const versionTag = event.version\n if (versionTag !== undefined && versionTag !== null && versionTag !== '') {\n tags.push(`version:${String(versionTag)}`)\n }\n\n return {\n message: formatDatadogMessageLine(event),\n evlog: sanitizeWideEventForDatadog(event),\n service: event.service,\n status: resolveDatadogLogStatus(event),\n ddsource: 'evlog',\n ddtags: tags.join(','),\n ...(Number.isFinite(ms) ? { timestamp: ms } : {}),\n }\n}\n\n/**\n * Resolve the Logs intake URL from configuration.\n */\nexport function resolveDatadogIntakeUrl(config: Pick<DatadogConfig, 'site' | 'intakeUrl'>): string {\n if (config.intakeUrl) {\n return config.intakeUrl.replace(/\\/+$/, '')\n }\n const site = (config.site ?? DEFAULT_SITE).replace(/^\\./, '').replace(/\\/+$/, '')\n return `https://http-intake.logs.${site}/api/v2/logs`\n}\n\n/**\n * Create a drain function for sending logs to Datadog via the HTTP Logs intake API.\n *\n * Configuration priority (highest to lowest):\n * 1. Overrides passed to `createDatadogDrain()`\n * 2. `runtimeConfig.evlog.datadog`\n * 3. `runtimeConfig.datadog`\n * 4. Environment variables: `NUXT_DATADOG_*`, `DATADOG_*`, and common `DD_*` aliases\n *\n * @example\n * ```ts\n * // Zero config — set DD_API_KEY (or NUXT_DATADOG_API_KEY) and optionally DD_SITE\n * nitroApp.hooks.hook('evlog:drain', createDatadogDrain())\n *\n * nitroApp.hooks.hook('evlog:drain', createDatadogDrain({\n * site: 'datadoghq.eu',\n * }))\n * ```\n */\nexport function createDatadogDrain(overrides?: Partial<DatadogConfig>) {\n return defineDrain<DatadogConfig>({\n name: 'datadog',\n resolve: async () => {\n const config = await resolveAdapterConfig<DatadogConfig>('datadog', DATADOG_FIELDS, overrides)\n if (!config.apiKey) {\n console.error('[evlog/datadog] Missing API key. Set NUXT_DATADOG_API_KEY, DATADOG_API_KEY, or DD_API_KEY, or pass apiKey to createDatadogDrain()')\n return null\n }\n return config as DatadogConfig\n },\n send: sendBatchToDatadog,\n })\n}\n\n/**\n * Send a single wide event to Datadog.\n */\nexport async function sendToDatadog(event: WideEvent, config: DatadogConfig): Promise<void> {\n await sendBatchToDatadog([event], config)\n}\n\n/**\n * Send a batch of wide events to Datadog in one request.\n */\nexport async function sendBatchToDatadog(events: WideEvent[], config: DatadogConfig): Promise<void> {\n if (events.length === 0) return\n\n const url = resolveDatadogIntakeUrl(config)\n\n await httpPost({\n url,\n headers: {\n 'Content-Type': 'application/json',\n 'DD-API-KEY': config.apiKey,\n },\n body: JSON.stringify(events.map(toDatadogLog)),\n timeout: config.timeout ?? 5000,\n retries: config.retries,\n label: 'Datadog',\n })\n}\n"],"mappings":";;;AAyBA,MAAM,iBAA+C;CACnD;EAAE,KAAK;EAAU,KAAK;GAAC;GAAwB;GAAmB;GAAa;EAAE;CACjF;EAAE,KAAK;EAAQ,KAAK;GAAC;GAAqB;GAAgB;GAAU;EAAE;CACtE;EAAE,KAAK;EAAa,KAAK,CAAC,yBAAyB,mBAAmB;EAAE;CACxE,EAAE,KAAK,WAAW;CAClB,EAAE,KAAK,WAAW;CACnB;AAED,MAAM,eAAe;;;;;;;;AASrB,SAAgB,4BAA4B,OAA2C;AACrF,QAAO,4BAA4B,MAAiC;;AAGtE,SAAS,4BAA4B,OAAyB;AAC5D,KAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;AACxD,KAAI,MAAM,QAAQ,MAAM,CAAE,QAAO,MAAM,IAAI,4BAA4B;CACvE,MAAM,MAAM;CACZ,MAAM,MAA+B,EAAE;AACvC,MAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,IAAI,CACtC,KAAI,MAAM,YAAY,OAAO,MAAM,SACjC,KAAI,iBAAiB;KAErB,KAAI,KAAK,4BAA4B,EAAE;AAG3C,QAAO;;;;;;AAOT,SAAgB,yBAAyB,OAA0B;CACjE,MAAM,SAAS,MAAM,MAAM,aAAa;CACxC,MAAM,SAAS,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS;CACjE,MAAM,OAAO,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO;CAC3D,MAAM,OAAO,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS,KAAA;CAE/D,MAAM,OAAO;EAAC;EAAQ;EAAQ;EAAK,CAAC,QAAO,MAAK,EAAE,SAAS,EAAE,CAAC,KAAK,IAAI;CACvE,IAAI,OAAO,SAAS,KAAA,IACf,OAAO,GAAG,KAAK,IAAI,KAAK,KAAK,GAAG,OAAO,IAAI,KAAK,KAChD,QAAQ;AAEb,KAAI,CAAC,UAAU,CAAC,QAAQ,SAAS,UAAU,MAAM,QAC/C,QAAO,GAAG,OAAO,GAAG,MAAM;AAE5B,QAAO;;;;;;;;;;AAWT,SAAgB,wBAAwB,OAAuD;AAC7F,KAAI,MAAM,UAAU,QAAS,QAAO;AACpC,KAAI,MAAM,UAAU,OAAQ,QAAO;AACnC,KAAI,MAAM,UAAU,QAAS,QAAO;CACpC,MAAM,OAAO,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS,KAAA;AAC/D,KAAI,SAAS,KAAA,KAAa,QAAQ,IAAK,QAAO;AAC9C,KAAI,SAAS,KAAA,KAAa,QAAQ,IAAK,QAAO;AAC9C,QAAO;;;;;;;;;;AAWT,SAAgB,aAAa,OAA2C;CACtE,MAAM,KAAK,KAAK,MAAM,MAAM,UAAU;CACtC,MAAM,OAAO,CAAC,OAAO,MAAM,cAAc;CACzC,MAAM,aAAa,MAAM;AACzB,KAAI,eAAe,KAAA,KAAa,eAAe,QAAQ,eAAe,GACpE,MAAK,KAAK,WAAW,OAAO,WAAW,GAAG;AAG5C,QAAO;EACL,SAAS,yBAAyB,MAAM;EACxC,OAAO,4BAA4B,MAAM;EACzC,SAAS,MAAM;EACf,QAAQ,wBAAwB,MAAM;EACtC,UAAU;EACV,QAAQ,KAAK,KAAK,IAAI;EACtB,GAAI,OAAO,SAAS,GAAG,GAAG,EAAE,WAAW,IAAI,GAAG,EAAE;EACjD;;;;;AAMH,SAAgB,wBAAwB,QAA2D;AACjG,KAAI,OAAO,UACT,QAAO,OAAO,UAAU,QAAQ,QAAQ,GAAG;AAG7C,QAAO,6BADO,OAAO,QAAQ,cAAc,QAAQ,OAAO,GAAG,CAAC,QAAQ,QAAQ,GAAG,CACzC;;;;;;;;;;;;;;;;;;;;;AAsB1C,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,oIAAoI;AAClJ,WAAO;;AAET,UAAO;;EAET,MAAM;EACP,CAAC;;;;;AAMJ,eAAsB,cAAc,OAAkB,QAAsC;AAC1F,OAAM,mBAAmB,CAAC,MAAM,EAAE,OAAO;;;;;AAM3C,eAAsB,mBAAmB,QAAqB,QAAsC;AAClG,KAAI,OAAO,WAAW,EAAG;AAIzB,OAAM,SAAS;EACb,KAHU,wBAAwB,OAAO;EAIzC,SAAS;GACP,gBAAgB;GAChB,cAAc,OAAO;GACtB;EACD,MAAM,KAAK,UAAU,OAAO,IAAI,aAAa,CAAC;EAC9C,SAAS,OAAO,WAAW;EAC3B,SAAS,OAAO;EAChB,OAAO;EACR,CAAC"}
|
package/dist/adapters/fs.d.mts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.d.mts","names":[],"sources":["../../src/adapters/fs.ts"],"mappings":";;UAKiB,QAAA;;EAEf,GAAA
|
|
1
|
+
{"version":3,"file":"fs.d.mts","names":[],"sources":["../../src/adapters/fs.ts"],"mappings":";;UAKiB,QAAA;;EAEf,GAAA;EAFe;EAIf,QAAA;;EAEA,cAAA;EAJA;EAMA,MAAA;AAAA;AAAA,iBAwEoB,SAAA,CAAU,KAAA,EAAO,SAAA,EAAW,MAAA,EAAQ,QAAA,GAAW,OAAA;AAAA,iBAI/C,cAAA,CAAe,MAAA,EAAQ,SAAA,IAAa,MAAA,EAAQ,QAAA,GAAW,OAAA;;AAJ7E;;;;;;;;;;;;;;;AAIA;;;iBAqCgB,aAAA,CAAc,SAAA,GAAY,OAAA,CAAQ,QAAA,KAAS,GAAA,EAAV,YAAA,GAAU,YAAA,OAAA,OAAA"}
|
package/dist/adapters/fs.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as defineDrain } from "../_drain-
|
|
1
|
+
import { t as defineDrain } from "../_drain-YH8ERc5l.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
|