evlog 2.14.1 → 2.16.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/LICENSE +21 -0
- package/README.md +4 -4
- package/dist/adapters/axiom.d.mts +18 -27
- package/dist/adapters/axiom.d.mts.map +1 -1
- package/dist/adapters/axiom.mjs +40 -30
- package/dist/adapters/axiom.mjs.map +1 -1
- package/dist/adapters/better-stack.d.mts +11 -24
- package/dist/adapters/better-stack.d.mts.map +1 -1
- package/dist/adapters/better-stack.mjs +34 -29
- package/dist/adapters/better-stack.mjs.map +1 -1
- package/dist/adapters/datadog.d.mts +1 -1
- package/dist/adapters/datadog.d.mts.map +1 -1
- package/dist/adapters/datadog.mjs +10 -4
- package/dist/adapters/datadog.mjs.map +1 -1
- package/dist/adapters/fs.d.mts +2 -2
- package/dist/adapters/fs.d.mts.map +1 -1
- package/dist/adapters/fs.mjs +19 -7
- package/dist/adapters/fs.mjs.map +1 -1
- package/dist/adapters/hyperdx.d.mts +1 -1
- package/dist/adapters/hyperdx.mjs +1 -2
- package/dist/adapters/hyperdx.mjs.map +1 -1
- package/dist/adapters/otlp.d.mts +1 -1
- package/dist/adapters/otlp.d.mts.map +1 -1
- package/dist/adapters/otlp.mjs +36 -31
- package/dist/adapters/otlp.mjs.map +1 -1
- package/dist/adapters/posthog.d.mts +50 -70
- package/dist/adapters/posthog.d.mts.map +1 -1
- package/dist/adapters/posthog.mjs +50 -85
- 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 +15 -5
- package/dist/adapters/sentry.mjs.map +1 -1
- package/dist/ai/index.d.mts +15 -1
- package/dist/ai/index.d.mts.map +1 -1
- package/dist/ai/index.mjs +48 -16
- package/dist/ai/index.mjs.map +1 -1
- package/dist/{audit-CTIviX3P.d.mts → audit-X1uUukm3.d.mts} +145 -2
- package/dist/audit-X1uUukm3.d.mts.map +1 -0
- package/dist/{audit-DQoBo7Dl.mjs → audit-pV5aLGP0.mjs} +153 -13
- package/dist/audit-pV5aLGP0.mjs.map +1 -0
- package/dist/better-auth/index.d.mts +1 -1
- package/dist/browser.d.mts +1 -1
- package/dist/define-CuXOqecD.d.mts +57 -0
- package/dist/define-CuXOqecD.d.mts.map +1 -0
- package/dist/define-D6OJdSUH.mjs +63 -0
- package/dist/define-D6OJdSUH.mjs.map +1 -0
- package/dist/{dist-Do8P4zWd.mjs → dist-BIlS38vi.mjs} +1 -1
- package/dist/dist-BIlS38vi.mjs.map +1 -0
- package/dist/drain-ByWUeOQC.mjs +160 -0
- package/dist/drain-ByWUeOQC.mjs.map +1 -0
- package/dist/elysia/index.d.mts +25 -2
- package/dist/elysia/index.d.mts.map +1 -1
- package/dist/elysia/index.mjs +53 -20
- package/dist/elysia/index.mjs.map +1 -1
- package/dist/enricher-DYTr9I16.d.mts +42 -0
- package/dist/enricher-DYTr9I16.d.mts.map +1 -0
- package/dist/enricher-Dy06T17G.mjs +95 -0
- package/dist/enricher-Dy06T17G.mjs.map +1 -0
- package/dist/enrichers.d.mts +16 -9
- package/dist/enrichers.d.mts.map +1 -1
- package/dist/enrichers.mjs +81 -64
- package/dist/enrichers.mjs.map +1 -1
- package/dist/{error-C7gSQVqk.d.mts → error-Cpc7RVz6.d.mts} +7 -2
- package/dist/error-Cpc7RVz6.d.mts.map +1 -0
- package/dist/error.d.mts +1 -1
- package/dist/error.mjs +8 -1
- package/dist/error.mjs.map +1 -1
- package/dist/{errors-BJRXUfMg.mjs → errors-BQgyQ9xe.mjs} +1 -1
- package/dist/{errors-BJRXUfMg.mjs.map → errors-BQgyQ9xe.mjs.map} +1 -1
- package/dist/{errors-4MPmTzjY.d.mts → errors-prnQ3kES.d.mts} +2 -2
- package/dist/{errors-4MPmTzjY.d.mts.map → errors-prnQ3kES.d.mts.map} +1 -1
- package/dist/event-DcHmEm3O.mjs +55 -0
- package/dist/event-DcHmEm3O.mjs.map +1 -0
- package/dist/express/index.d.mts +2 -2
- package/dist/express/index.d.mts.map +1 -1
- package/dist/express/index.mjs +17 -15
- package/dist/express/index.mjs.map +1 -1
- package/dist/fastify/index.d.mts +2 -2
- package/dist/fastify/index.d.mts.map +1 -1
- package/dist/fastify/index.mjs +19 -20
- package/dist/fastify/index.mjs.map +1 -1
- package/dist/fork-DPN8aL8O.mjs +227 -0
- package/dist/fork-DPN8aL8O.mjs.map +1 -0
- package/dist/{headers-D74M0wsg.mjs → headers-CU-QqnYg.mjs} +19 -2
- package/dist/headers-CU-QqnYg.mjs.map +1 -0
- package/dist/hono/index.d.mts +2 -2
- package/dist/hono/index.d.mts.map +1 -1
- package/dist/hono/index.mjs +14 -10
- package/dist/hono/index.mjs.map +1 -1
- package/dist/http.d.mts +1 -1
- package/dist/index.d.mts +8 -7
- package/dist/index.mjs +3 -2
- package/dist/integration-DSZPbI9N.mjs +75 -0
- package/dist/integration-DSZPbI9N.mjs.map +1 -0
- package/dist/{logger-DttRJRGa.d.mts → logger-U8lgdc9x.d.mts} +9 -3
- package/dist/logger-U8lgdc9x.d.mts.map +1 -0
- package/dist/logger.d.mts +2 -2
- package/dist/logger.mjs +2 -2
- package/dist/middleware-CAQHJRN1.d.mts +72 -0
- package/dist/middleware-CAQHJRN1.d.mts.map +1 -0
- package/dist/nestjs/index.d.mts +2 -2
- package/dist/nestjs/index.mjs +3 -4
- package/dist/nestjs/index.mjs.map +1 -1
- package/dist/next/client.d.mts +1 -1
- package/dist/next/index.d.mts +4 -4
- package/dist/next/index.mjs +3 -3
- package/dist/next/instrumentation.d.mts +1 -1
- package/dist/next/instrumentation.mjs +1 -1
- package/dist/nitro/errorHandler.mjs +2 -2
- package/dist/nitro/module.d.mts +2 -2
- package/dist/nitro/plugin.mjs +21 -11
- package/dist/nitro/plugin.mjs.map +1 -1
- package/dist/nitro/v3/errorHandler.mjs +3 -3
- package/dist/nitro/v3/index.d.mts +2 -2
- package/dist/nitro/v3/module.d.mts +1 -1
- package/dist/nitro/v3/plugin.mjs +29 -17
- package/dist/nitro/v3/plugin.mjs.map +1 -1
- package/dist/nitro/v3/useLogger.d.mts +1 -1
- package/dist/{nitro-CPPRCPbG.d.mts → nitro-C6Bd682U.d.mts} +2 -2
- package/dist/{nitro-CPPRCPbG.d.mts.map → nitro-C6Bd682U.d.mts.map} +1 -1
- package/dist/{nitro-OmT_M4Pb.mjs → nitro-DavLelNz.mjs} +2 -2
- package/dist/nitro-DavLelNz.mjs.map +1 -0
- package/dist/{nitroConfigBridge-C37lXaNm.mjs → nitroConfigBridge-aZ1e5upQ.mjs} +1 -1
- package/dist/nitroConfigBridge-aZ1e5upQ.mjs.map +1 -0
- package/dist/nuxt/module.d.mts +1 -1
- package/dist/nuxt/module.mjs +2 -2
- package/dist/{parseError-o1GpZEOR.d.mts → parseError-B-dKF6Fd.d.mts} +2 -2
- package/dist/parseError-B-dKF6Fd.d.mts.map +1 -0
- package/dist/react-router/index.d.mts +2 -2
- package/dist/react-router/index.mjs +3 -4
- package/dist/react-router/index.mjs.map +1 -1
- package/dist/{routes-CGPmbzCZ.mjs → routes-B48wm7Pb.mjs} +1 -1
- package/dist/{routes-CGPmbzCZ.mjs.map → routes-B48wm7Pb.mjs.map} +1 -1
- package/dist/runtime/client/log.d.mts +1 -1
- package/dist/runtime/server/routes/_evlog/ingest.post.mjs +21 -10
- package/dist/runtime/server/routes/_evlog/ingest.post.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 +9 -1
- package/dist/runtime/utils/parseError.mjs.map +1 -1
- package/dist/{_severity-CQijvfhU.mjs → severity-BYWZ96Sb.mjs} +6 -2
- package/dist/severity-BYWZ96Sb.mjs.map +1 -0
- package/dist/{source-location-DRvDDqfq.mjs → source-location-Dco0cRTz.mjs} +3 -3
- package/dist/source-location-Dco0cRTz.mjs.map +1 -0
- package/dist/storage-BT-3fT1-.mjs +27 -0
- package/dist/storage-BT-3fT1-.mjs.map +1 -0
- package/dist/sveltekit/index.d.mts +2 -2
- package/dist/sveltekit/index.mjs +5 -6
- package/dist/sveltekit/index.mjs.map +1 -1
- package/dist/toolkit.d.mts +288 -12
- package/dist/toolkit.d.mts.map +1 -1
- package/dist/toolkit.mjs +13 -7
- package/dist/types.d.mts +1 -1
- package/dist/{useLogger-CyPP1sVB.d.mts → useLogger-CoNgTjp5.d.mts} +2 -2
- package/dist/{useLogger-CyPP1sVB.d.mts.map → useLogger-CoNgTjp5.d.mts.map} +1 -1
- package/dist/{utils-Dmin7wVL.d.mts → utils-Db4qhBWn.d.mts} +2 -2
- package/dist/{utils-Dmin7wVL.d.mts.map → utils-Db4qhBWn.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/dist/workers.mjs +1 -1
- package/package.json +22 -19
- package/dist/_drain-CmCtsuF6.mjs +0 -23
- package/dist/_drain-CmCtsuF6.mjs.map +0 -1
- package/dist/_http-BY1e9pwC.mjs +0 -78
- package/dist/_http-BY1e9pwC.mjs.map +0 -1
- package/dist/_severity-CQijvfhU.mjs.map +0 -1
- package/dist/audit-CTIviX3P.d.mts.map +0 -1
- package/dist/audit-DQoBo7Dl.mjs.map +0 -1
- package/dist/dist-Do8P4zWd.mjs.map +0 -1
- package/dist/error-C7gSQVqk.d.mts.map +0 -1
- package/dist/fork-D1j1Fuzy.mjs +0 -72
- package/dist/fork-D1j1Fuzy.mjs.map +0 -1
- package/dist/headers-D74M0wsg.mjs.map +0 -1
- package/dist/logger-DttRJRGa.d.mts.map +0 -1
- package/dist/middleware-CTnDsST-.d.mts +0 -93
- package/dist/middleware-CTnDsST-.d.mts.map +0 -1
- package/dist/middleware-oAccqyPp.mjs +0 -123
- package/dist/middleware-oAccqyPp.mjs.map +0 -1
- package/dist/nitro-OmT_M4Pb.mjs.map +0 -1
- package/dist/nitroConfigBridge-C37lXaNm.mjs.map +0 -1
- package/dist/parseError-o1GpZEOR.d.mts.map +0 -1
- package/dist/source-location-DRvDDqfq.mjs.map +0 -1
- package/dist/storage-CFGTn37X.mjs +0 -46
- package/dist/storage-CFGTn37X.mjs.map +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 HugoRCD
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
[](https://evlog.dev)
|
|
12
12
|
[](https://github.com/HugoRCD/evlog/blob/main/LICENSE)
|
|
13
13
|
|
|
14
|
-
**
|
|
14
|
+
**Digging through logs is not observability. It's hope.**
|
|
15
15
|
|
|
16
|
-
A single request generates 10+ log lines. When production breaks at 3am, you're
|
|
16
|
+
A single request generates 10+ log lines. When production breaks at 3am, you're sifting scattered lines for a needle of signal. Your errors say "Something went wrong" — thanks, very helpful.
|
|
17
17
|
|
|
18
|
-
**evlog
|
|
18
|
+
**evlog is different.** One wide event per operation. All the context. Errors that explain *why* and what to do next.
|
|
19
19
|
|
|
20
20
|
## Why evlog?
|
|
21
21
|
|
|
@@ -758,7 +758,7 @@ export default defineEventHandler(async (event) => {
|
|
|
758
758
|
|
|
759
759
|
`AuditFields` is exported and merges with `BaseWideEvent` — augment it with `declare module` if you need extra typed fields. Audit events are always force-kept by tail sampling and get a deterministic `idempotencyKey` so retries are safe across drains.
|
|
760
760
|
|
|
761
|
-
See [the Audit Logs guide](https://evlog.dev/logging/audit) for compliance, GDPR, and recipe details.
|
|
761
|
+
See [the Audit Logs guide](https://evlog.dev/logging/audit/overview) for compliance, GDPR, and recipe details.
|
|
762
762
|
|
|
763
763
|
## AI SDK Integration
|
|
764
764
|
|
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
import { F as DrainContext, it as WideEvent } from "../audit-
|
|
1
|
+
import { F as DrainContext, it as WideEvent } from "../audit-X1uUukm3.mjs";
|
|
2
2
|
//#region src/adapters/axiom.d.ts
|
|
3
3
|
interface BaseAxiomConfig {
|
|
4
|
-
/** Axiom dataset name */
|
|
4
|
+
/** Axiom dataset name. */
|
|
5
5
|
dataset: string;
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Axiom API key.
|
|
8
|
+
*
|
|
9
|
+
* @example `xaat-...`
|
|
10
|
+
*/
|
|
11
|
+
apiKey: string;
|
|
12
|
+
/**
|
|
13
|
+
* @deprecated Renamed to {@link BaseAxiomConfig.apiKey}. Will be removed in
|
|
14
|
+
* the next major version. Pass `apiKey` instead.
|
|
15
|
+
*/
|
|
16
|
+
token?: string;
|
|
17
|
+
/** Organization ID (required for Personal Access Tokens). */
|
|
9
18
|
orgId?: string;
|
|
10
19
|
/** Request timeout in milliseconds. Default: 5000 */
|
|
11
20
|
timeout?: number;
|
|
@@ -36,42 +45,24 @@ type AxiomConfig = BaseAxiomConfig & (EdgeAxiomConfig | EndpointAxiomConfig);
|
|
|
36
45
|
* 1. Overrides passed to createAxiomDrain()
|
|
37
46
|
* 2. runtimeConfig.evlog.axiom
|
|
38
47
|
* 3. runtimeConfig.axiom
|
|
39
|
-
* 4. Environment variables:
|
|
48
|
+
* 4. Environment variables: NUXT_AXIOM_API_KEY, AXIOM_API_KEY (or legacy `*_TOKEN`)
|
|
40
49
|
*
|
|
41
50
|
* @example
|
|
42
51
|
* ```ts
|
|
43
|
-
* // Zero config
|
|
44
|
-
*
|
|
52
|
+
* // Zero config — set NUXT_AXIOM_API_KEY and NUXT_AXIOM_DATASET
|
|
53
|
+
* initLogger({ drain: createAxiomDrain() })
|
|
45
54
|
*
|
|
46
55
|
* // With overrides
|
|
47
|
-
*
|
|
48
|
-
* dataset: 'my-dataset',
|
|
49
|
-
* }))
|
|
56
|
+
* initLogger({ drain: createAxiomDrain({ dataset: 'my-dataset' }) })
|
|
50
57
|
* ```
|
|
51
58
|
*/
|
|
52
59
|
declare function createAxiomDrain(overrides?: Partial<AxiomConfig>): (ctx: DrainContext | DrainContext[]) => Promise<void>;
|
|
53
60
|
/**
|
|
54
61
|
* Send a single event to Axiom.
|
|
55
|
-
*
|
|
56
|
-
* @example
|
|
57
|
-
* ```ts
|
|
58
|
-
* await sendToAxiom(event, {
|
|
59
|
-
* dataset: 'my-logs',
|
|
60
|
-
* token: process.env.AXIOM_TOKEN!,
|
|
61
|
-
* })
|
|
62
|
-
* ```
|
|
63
62
|
*/
|
|
64
63
|
declare function sendToAxiom(event: WideEvent, config: AxiomConfig): Promise<void>;
|
|
65
64
|
/**
|
|
66
65
|
* Send a batch of events to Axiom.
|
|
67
|
-
*
|
|
68
|
-
* @example
|
|
69
|
-
* ```ts
|
|
70
|
-
* await sendBatchToAxiom(events, {
|
|
71
|
-
* dataset: 'my-logs',
|
|
72
|
-
* token: process.env.AXIOM_TOKEN!,
|
|
73
|
-
* })
|
|
74
|
-
* ```
|
|
75
66
|
*/
|
|
76
67
|
declare function sendBatchToAxiom(events: WideEvent[], config: AxiomConfig): Promise<void>;
|
|
77
68
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"axiom.d.mts","names":[],"sources":["../../src/adapters/axiom.ts"],"mappings":";;UAMU,eAAA;;EAER,OAAA;EAFQ;
|
|
1
|
+
{"version":3,"file":"axiom.d.mts","names":[],"sources":["../../src/adapters/axiom.ts"],"mappings":";;UAMU,eAAA;;EAER,OAAA;EAFQ;;;;;EAQR,MAAA;EAKA;;;;EAAA,KAAA;EAMO;EAJP,KAAA;EAOuB;EALvB,OAAA;EAWA;EATA,OAAA;AAAA;AAAA,UAGQ,eAAA;;;;AAkBV;;EAZE,OAAA;EAYwB;EAVxB,OAAA;AAAA;AAAA,UAGQ,mBAAA;EAOwE;EALhF,OAAA;EAK2C;EAH3C,OAAA;AAAA;AAAA,KAGU,WAAA,GAAc,eAAA,IAAmB,eAAA,GAAkB,mBAAA;AAkD/D;;;;;;;;;;;;;;;;;;AAAA,iBAAgB,gBAAA,CAAiB,SAAA,GAAY,OAAA,CAAQ,WAAA,KAAY,GAAA,EAAb,YAAA,GAAa,YAAA,OAAA,OAAA;AAmCjE;;;AAAA,iBAAsB,WAAA,CAAY,KAAA,EAAO,SAAA,EAAW,MAAA,EAAQ,WAAA,GAAc,OAAA;;;;iBAOpD,gBAAA,CAAiB,MAAA,EAAQ,SAAA,IAAa,MAAA,EAAQ,WAAA,GAAc,OAAA"}
|
package/dist/adapters/axiom.mjs
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as defineDrain } from "../_drain-CmCtsuF6.mjs";
|
|
1
|
+
import { a as resolveAdapterConfig, n as defineHttpDrain, r as httpPost } from "../drain-ByWUeOQC.mjs";
|
|
3
2
|
//#region src/adapters/axiom.ts
|
|
4
3
|
const AXIOM_FIELDS = [
|
|
5
4
|
{
|
|
6
5
|
key: "dataset",
|
|
7
6
|
env: ["NUXT_AXIOM_DATASET", "AXIOM_DATASET"]
|
|
8
7
|
},
|
|
8
|
+
{
|
|
9
|
+
key: "apiKey",
|
|
10
|
+
env: ["NUXT_AXIOM_API_KEY", "AXIOM_API_KEY"]
|
|
11
|
+
},
|
|
9
12
|
{
|
|
10
13
|
key: "token",
|
|
11
14
|
env: ["NUXT_AXIOM_TOKEN", "AXIOM_TOKEN"]
|
|
@@ -25,6 +28,17 @@ const AXIOM_FIELDS = [
|
|
|
25
28
|
{ key: "timeout" },
|
|
26
29
|
{ key: "retries" }
|
|
27
30
|
];
|
|
31
|
+
let warnedAboutToken = false;
|
|
32
|
+
function applyApiKeyAlias(config) {
|
|
33
|
+
if (!config.apiKey && config.token) {
|
|
34
|
+
if (!warnedAboutToken) {
|
|
35
|
+
warnedAboutToken = true;
|
|
36
|
+
console.warn("[evlog/axiom] `token` is deprecated, use `apiKey` instead. (Env: NUXT_AXIOM_TOKEN/AXIOM_TOKEN → NUXT_AXIOM_API_KEY/AXIOM_API_KEY.)");
|
|
37
|
+
}
|
|
38
|
+
config.apiKey = config.token;
|
|
39
|
+
}
|
|
40
|
+
return config;
|
|
41
|
+
}
|
|
28
42
|
/**
|
|
29
43
|
* Create a drain function for sending logs to Axiom.
|
|
30
44
|
*
|
|
@@ -32,26 +46,24 @@ const AXIOM_FIELDS = [
|
|
|
32
46
|
* 1. Overrides passed to createAxiomDrain()
|
|
33
47
|
* 2. runtimeConfig.evlog.axiom
|
|
34
48
|
* 3. runtimeConfig.axiom
|
|
35
|
-
* 4. Environment variables:
|
|
49
|
+
* 4. Environment variables: NUXT_AXIOM_API_KEY, AXIOM_API_KEY (or legacy `*_TOKEN`)
|
|
36
50
|
*
|
|
37
51
|
* @example
|
|
38
52
|
* ```ts
|
|
39
|
-
* // Zero config
|
|
40
|
-
*
|
|
53
|
+
* // Zero config — set NUXT_AXIOM_API_KEY and NUXT_AXIOM_DATASET
|
|
54
|
+
* initLogger({ drain: createAxiomDrain() })
|
|
41
55
|
*
|
|
42
56
|
* // With overrides
|
|
43
|
-
*
|
|
44
|
-
* dataset: 'my-dataset',
|
|
45
|
-
* }))
|
|
57
|
+
* initLogger({ drain: createAxiomDrain({ dataset: 'my-dataset' }) })
|
|
46
58
|
* ```
|
|
47
59
|
*/
|
|
48
60
|
function createAxiomDrain(overrides) {
|
|
49
|
-
return
|
|
61
|
+
return defineHttpDrain({
|
|
50
62
|
name: "axiom",
|
|
51
63
|
resolve: async () => {
|
|
52
|
-
const config = await resolveAdapterConfig("axiom", AXIOM_FIELDS, overrides);
|
|
53
|
-
if (!config.dataset || !config.
|
|
54
|
-
console.error("[evlog/axiom] Missing dataset or
|
|
64
|
+
const config = applyApiKeyAlias(await resolveAdapterConfig("axiom", AXIOM_FIELDS, overrides));
|
|
65
|
+
if (!config.dataset || !config.apiKey) {
|
|
66
|
+
console.error("[evlog/axiom] Missing dataset or apiKey. Set NUXT_AXIOM_API_KEY/NUXT_AXIOM_DATASET env vars or pass to createAxiomDrain()");
|
|
55
67
|
return null;
|
|
56
68
|
}
|
|
57
69
|
if (config.edgeUrl && config.baseUrl) {
|
|
@@ -60,39 +72,37 @@ function createAxiomDrain(overrides) {
|
|
|
60
72
|
}
|
|
61
73
|
return config;
|
|
62
74
|
},
|
|
63
|
-
|
|
75
|
+
encode: (events, config) => {
|
|
76
|
+
const url = resolveIngestUrl(config);
|
|
77
|
+
const headers = {
|
|
78
|
+
"Content-Type": "application/json",
|
|
79
|
+
"Authorization": `Bearer ${config.apiKey}`
|
|
80
|
+
};
|
|
81
|
+
if (config.orgId) headers["X-Axiom-Org-Id"] = config.orgId;
|
|
82
|
+
return {
|
|
83
|
+
url,
|
|
84
|
+
headers,
|
|
85
|
+
body: JSON.stringify(events)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
64
88
|
});
|
|
65
89
|
}
|
|
66
90
|
/**
|
|
67
91
|
* Send a single event to Axiom.
|
|
68
|
-
*
|
|
69
|
-
* @example
|
|
70
|
-
* ```ts
|
|
71
|
-
* await sendToAxiom(event, {
|
|
72
|
-
* dataset: 'my-logs',
|
|
73
|
-
* token: process.env.AXIOM_TOKEN!,
|
|
74
|
-
* })
|
|
75
|
-
* ```
|
|
76
92
|
*/
|
|
77
93
|
async function sendToAxiom(event, config) {
|
|
78
94
|
await sendBatchToAxiom([event], config);
|
|
79
95
|
}
|
|
80
96
|
/**
|
|
81
97
|
* Send a batch of events to Axiom.
|
|
82
|
-
*
|
|
83
|
-
* @example
|
|
84
|
-
* ```ts
|
|
85
|
-
* await sendBatchToAxiom(events, {
|
|
86
|
-
* dataset: 'my-logs',
|
|
87
|
-
* token: process.env.AXIOM_TOKEN!,
|
|
88
|
-
* })
|
|
89
|
-
* ```
|
|
90
98
|
*/
|
|
91
99
|
async function sendBatchToAxiom(events, config) {
|
|
100
|
+
const apiKey = config.apiKey ?? config.token;
|
|
101
|
+
if (!apiKey) throw new Error("[evlog/axiom] Missing apiKey");
|
|
92
102
|
const url = resolveIngestUrl(config);
|
|
93
103
|
const headers = {
|
|
94
104
|
"Content-Type": "application/json",
|
|
95
|
-
"Authorization": `Bearer ${
|
|
105
|
+
"Authorization": `Bearer ${apiKey}`
|
|
96
106
|
};
|
|
97
107
|
if (config.orgId) headers["X-Axiom-Org-Id"] = config.orgId;
|
|
98
108
|
await httpPost({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"axiom.mjs","names":[],"sources":["../../src/adapters/axiom.ts"],"sourcesContent":["import type { WideEvent } from '../types'\nimport type { ConfigField } from '
|
|
1
|
+
{"version":3,"file":"axiom.mjs","names":[],"sources":["../../src/adapters/axiom.ts"],"sourcesContent":["import type { WideEvent } from '../types'\nimport type { ConfigField } from '../shared/config'\nimport { resolveAdapterConfig } from '../shared/config'\nimport { defineHttpDrain } from '../shared/drain'\nimport { httpPost } from '../shared/http'\n\ninterface BaseAxiomConfig {\n /** Axiom dataset name. */\n dataset: string\n /**\n * Axiom API key.\n *\n * @example `xaat-...`\n */\n apiKey: string\n /**\n * @deprecated Renamed to {@link BaseAxiomConfig.apiKey}. Will be removed in\n * the next major version. Pass `apiKey` instead.\n */\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: 'apiKey', env: ['NUXT_AXIOM_API_KEY', 'AXIOM_API_KEY'] },\n // Deprecated env var names — resolved as a fallback for `apiKey` below.\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\nlet warnedAboutToken = false\n\nfunction applyApiKeyAlias(config: ResolvedAxiomConfig): ResolvedAxiomConfig {\n if (!config.apiKey && config.token) {\n if (!warnedAboutToken) {\n warnedAboutToken = true\n console.warn('[evlog/axiom] `token` is deprecated, use `apiKey` instead. (Env: NUXT_AXIOM_TOKEN/AXIOM_TOKEN → NUXT_AXIOM_API_KEY/AXIOM_API_KEY.)')\n }\n config.apiKey = config.token\n }\n return config\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_API_KEY, AXIOM_API_KEY (or legacy `*_TOKEN`)\n *\n * @example\n * ```ts\n * // Zero config — set NUXT_AXIOM_API_KEY and NUXT_AXIOM_DATASET\n * initLogger({ drain: createAxiomDrain() })\n *\n * // With overrides\n * initLogger({ drain: createAxiomDrain({ dataset: 'my-dataset' }) })\n * ```\n */\nexport function createAxiomDrain(overrides?: Partial<AxiomConfig>) {\n return defineHttpDrain<AxiomConfig>({\n name: 'axiom',\n resolve: async () => {\n const resolved = await resolveAdapterConfig<ResolvedAxiomConfig>(\n 'axiom',\n AXIOM_FIELDS,\n overrides as Partial<ResolvedAxiomConfig>,\n )\n const config = applyApiKeyAlias(resolved)\n if (!config.dataset || !config.apiKey) {\n console.error('[evlog/axiom] Missing dataset or apiKey. Set NUXT_AXIOM_API_KEY/NUXT_AXIOM_DATASET env vars or pass to createAxiomDrain()')\n return null\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 return config as AxiomConfig\n },\n encode: (events, config) => {\n const url = resolveIngestUrl(config)\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${config.apiKey}`,\n }\n if (config.orgId) headers['X-Axiom-Org-Id'] = config.orgId\n return { url, headers, body: JSON.stringify(events) }\n },\n })\n}\n\n/**\n * Send a single event to Axiom.\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 */\nexport async function sendBatchToAxiom(events: WideEvent[], config: AxiomConfig): Promise<void> {\n const apiKey = config.apiKey ?? config.token\n if (!apiKey) {\n throw new Error('[evlog/axiom] Missing apiKey')\n }\n const url = resolveIngestUrl(config)\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n }\n if (config.orgId) headers['X-Axiom-Org-Id'] = config.orgId\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 if (parsed.pathname === '' || parsed.pathname === '/') {\n parsed.pathname = `/v1/ingest/${encodedDataset}`\n return parsed.toString()\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":";;AAqDA,MAAM,eAAmD;CACvD;EAAE,KAAK;EAAW,KAAK,CAAC,sBAAsB,gBAAgB;EAAE;CAChE;EAAE,KAAK;EAAU,KAAK,CAAC,sBAAsB,gBAAgB;EAAE;CAE/D;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;AAED,IAAI,mBAAmB;AAEvB,SAAS,iBAAiB,QAAkD;AAC1E,KAAI,CAAC,OAAO,UAAU,OAAO,OAAO;AAClC,MAAI,CAAC,kBAAkB;AACrB,sBAAmB;AACnB,WAAQ,KAAK,qIAAqI;;AAEpJ,SAAO,SAAS,OAAO;;AAEzB,QAAO;;;;;;;;;;;;;;;;;;;;AAqBT,SAAgB,iBAAiB,WAAkC;AACjE,QAAO,gBAA6B;EAClC,MAAM;EACN,SAAS,YAAY;GAMnB,MAAM,SAAS,iBAAiB,MALT,qBACrB,SACA,cACA,UACD,CACwC;AACzC,OAAI,CAAC,OAAO,WAAW,CAAC,OAAO,QAAQ;AACrC,YAAQ,MAAM,4HAA4H;AAC1I,WAAO;;AAET,OAAI,OAAO,WAAW,OAAO,SAAS;AACpC,YAAQ,KAAK,uFAAuF;AACpG,WAAO,OAAO;;AAEhB,UAAO;;EAET,SAAS,QAAQ,WAAW;GAC1B,MAAM,MAAM,iBAAiB,OAAO;GACpC,MAAM,UAAkC;IACtC,gBAAgB;IAChB,iBAAiB,UAAU,OAAO;IACnC;AACD,OAAI,OAAO,MAAO,SAAQ,oBAAoB,OAAO;AACrD,UAAO;IAAE;IAAK;IAAS,MAAM,KAAK,UAAU,OAAO;IAAE;;EAExD,CAAC;;;;;AAMJ,eAAsB,YAAY,OAAkB,QAAoC;AACtF,OAAM,iBAAiB,CAAC,MAAM,EAAE,OAAO;;;;;AAMzC,eAAsB,iBAAiB,QAAqB,QAAoC;CAC9F,MAAM,SAAS,OAAO,UAAU,OAAO;AACvC,KAAI,CAAC,OACH,OAAM,IAAI,MAAM,+BAA+B;CAEjD,MAAM,MAAM,iBAAiB,OAAO;CACpC,MAAM,UAAkC;EACtC,gBAAgB;EAChB,iBAAiB,UAAU;EAC5B;AACD,KAAI,OAAO,MAAO,SAAQ,oBAAoB,OAAO;AACrD,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;AACtC,MAAI,OAAO,aAAa,MAAM,OAAO,aAAa,KAAK;AACrD,UAAO,WAAW,cAAc;AAChC,UAAO,OAAO,UAAU;;AAE1B,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,GAC9B,CAAC,aAAa"}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import { F as DrainContext, it as WideEvent } from "../audit-
|
|
1
|
+
import { F as DrainContext, it as WideEvent } from "../audit-X1uUukm3.mjs";
|
|
2
2
|
//#region src/adapters/better-stack.d.ts
|
|
3
3
|
interface BetterStackConfig {
|
|
4
|
-
/** Better Stack
|
|
5
|
-
|
|
4
|
+
/** Better Stack API key (replaces deprecated `sourceToken`). */
|
|
5
|
+
apiKey: string;
|
|
6
|
+
/**
|
|
7
|
+
* @deprecated Renamed to {@link BetterStackConfig.apiKey}. Will be removed
|
|
8
|
+
* in the next major version. Pass `apiKey` instead.
|
|
9
|
+
*/
|
|
10
|
+
sourceToken?: string;
|
|
6
11
|
/** Logtail ingestion endpoint. Default: https://in.logs.betterstack.com */
|
|
7
12
|
endpoint?: string;
|
|
8
13
|
/** Request timeout in milliseconds. Default: 5000 */
|
|
@@ -22,40 +27,22 @@ declare function toBetterStackEvent(event: WideEvent): Record<string, unknown>;
|
|
|
22
27
|
* 1. Overrides passed to createBetterStackDrain()
|
|
23
28
|
* 2. runtimeConfig.evlog.betterStack
|
|
24
29
|
* 3. runtimeConfig.betterStack
|
|
25
|
-
* 4. Environment variables:
|
|
30
|
+
* 4. Environment variables: NUXT_BETTER_STACK_API_KEY, BETTER_STACK_API_KEY (or legacy `*_SOURCE_TOKEN`)
|
|
26
31
|
*
|
|
27
32
|
* @example
|
|
28
33
|
* ```ts
|
|
29
|
-
*
|
|
30
|
-
* nitroApp.hooks.hook('evlog:drain', createBetterStackDrain())
|
|
34
|
+
* initLogger({ drain: createBetterStackDrain() })
|
|
31
35
|
*
|
|
32
|
-
*
|
|
33
|
-
* nitroApp.hooks.hook('evlog:drain', createBetterStackDrain({
|
|
34
|
-
* sourceToken: 'my-token',
|
|
35
|
-
* }))
|
|
36
|
+
* initLogger({ drain: createBetterStackDrain({ apiKey: 'my-key' }) })
|
|
36
37
|
* ```
|
|
37
38
|
*/
|
|
38
39
|
declare function createBetterStackDrain(overrides?: Partial<BetterStackConfig>): (ctx: DrainContext | DrainContext[]) => Promise<void>;
|
|
39
40
|
/**
|
|
40
41
|
* Send a single event to Better Stack.
|
|
41
|
-
*
|
|
42
|
-
* @example
|
|
43
|
-
* ```ts
|
|
44
|
-
* await sendToBetterStack(event, {
|
|
45
|
-
* sourceToken: process.env.BETTER_STACK_SOURCE_TOKEN!,
|
|
46
|
-
* })
|
|
47
|
-
* ```
|
|
48
42
|
*/
|
|
49
43
|
declare function sendToBetterStack(event: WideEvent, config: BetterStackConfig): Promise<void>;
|
|
50
44
|
/**
|
|
51
45
|
* Send a batch of events to Better Stack.
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* ```ts
|
|
55
|
-
* await sendBatchToBetterStack(events, {
|
|
56
|
-
* sourceToken: process.env.BETTER_STACK_SOURCE_TOKEN!,
|
|
57
|
-
* })
|
|
58
|
-
* ```
|
|
59
46
|
*/
|
|
60
47
|
declare function sendBatchToBetterStack(events: WideEvent[], config: BetterStackConfig): Promise<void>;
|
|
61
48
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"better-stack.d.mts","names":[],"sources":["../../src/adapters/better-stack.ts"],"mappings":";;UAMiB,iBAAA;;EAEf,
|
|
1
|
+
{"version":3,"file":"better-stack.d.mts","names":[],"sources":["../../src/adapters/better-stack.ts"],"mappings":";;UAMiB,iBAAA;;EAEf,MAAA;EAFe;;;;EAOf,WAAA;EAAA;EAEA,QAAA;EAEA;EAAA,OAAA;EAEO;EAAP,OAAA;AAAA;;;;;iBA6Bc,kBAAA,CAAmB,KAAA,EAAO,SAAA,GAAY,MAAA;;;;AAqBtD;;;;;;;;;;;;;iBAAgB,sBAAA,CAAuB,SAAA,GAAY,OAAA,CAAQ,iBAAA,KAAkB,GAAA,EAAnB,YAAA,GAAmB,YAAA,OAAA,OAAA;;;;iBA0BvD,iBAAA,CAAkB,KAAA,EAAO,SAAA,EAAW,MAAA,EAAQ,iBAAA,GAAoB,OAAA;;AAAtF;;iBAOsB,sBAAA,CAAuB,MAAA,EAAQ,SAAA,IAAa,MAAA,EAAQ,iBAAA,GAAoB,OAAA"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as defineDrain } from "../_drain-CmCtsuF6.mjs";
|
|
1
|
+
import { a as resolveAdapterConfig, n as defineHttpDrain, r as httpPost } from "../drain-ByWUeOQC.mjs";
|
|
3
2
|
//#region src/adapters/better-stack.ts
|
|
4
3
|
const BETTER_STACK_FIELDS = [
|
|
4
|
+
{
|
|
5
|
+
key: "apiKey",
|
|
6
|
+
env: ["NUXT_BETTER_STACK_API_KEY", "BETTER_STACK_API_KEY"]
|
|
7
|
+
},
|
|
5
8
|
{
|
|
6
9
|
key: "sourceToken",
|
|
7
10
|
env: ["NUXT_BETTER_STACK_SOURCE_TOKEN", "BETTER_STACK_SOURCE_TOKEN"]
|
|
@@ -13,6 +16,17 @@ const BETTER_STACK_FIELDS = [
|
|
|
13
16
|
{ key: "timeout" },
|
|
14
17
|
{ key: "retries" }
|
|
15
18
|
];
|
|
19
|
+
let warnedAboutSourceToken = false;
|
|
20
|
+
function applyApiKeyAlias(config) {
|
|
21
|
+
if (!config.apiKey && config.sourceToken) {
|
|
22
|
+
if (!warnedAboutSourceToken) {
|
|
23
|
+
warnedAboutSourceToken = true;
|
|
24
|
+
console.warn("[evlog/better-stack] `sourceToken` is deprecated, use `apiKey` instead. (Env: NUXT_BETTER_STACK_SOURCE_TOKEN → NUXT_BETTER_STACK_API_KEY.)");
|
|
25
|
+
}
|
|
26
|
+
config.apiKey = config.sourceToken;
|
|
27
|
+
}
|
|
28
|
+
return config;
|
|
29
|
+
}
|
|
16
30
|
/**
|
|
17
31
|
* Transform an evlog wide event into a Better Stack event.
|
|
18
32
|
* Maps `timestamp` to `dt` (Better Stack's expected field).
|
|
@@ -31,62 +45,53 @@ function toBetterStackEvent(event) {
|
|
|
31
45
|
* 1. Overrides passed to createBetterStackDrain()
|
|
32
46
|
* 2. runtimeConfig.evlog.betterStack
|
|
33
47
|
* 3. runtimeConfig.betterStack
|
|
34
|
-
* 4. Environment variables:
|
|
48
|
+
* 4. Environment variables: NUXT_BETTER_STACK_API_KEY, BETTER_STACK_API_KEY (or legacy `*_SOURCE_TOKEN`)
|
|
35
49
|
*
|
|
36
50
|
* @example
|
|
37
51
|
* ```ts
|
|
38
|
-
*
|
|
39
|
-
* nitroApp.hooks.hook('evlog:drain', createBetterStackDrain())
|
|
52
|
+
* initLogger({ drain: createBetterStackDrain() })
|
|
40
53
|
*
|
|
41
|
-
*
|
|
42
|
-
* nitroApp.hooks.hook('evlog:drain', createBetterStackDrain({
|
|
43
|
-
* sourceToken: 'my-token',
|
|
44
|
-
* }))
|
|
54
|
+
* initLogger({ drain: createBetterStackDrain({ apiKey: 'my-key' }) })
|
|
45
55
|
* ```
|
|
46
56
|
*/
|
|
47
57
|
function createBetterStackDrain(overrides) {
|
|
48
|
-
return
|
|
58
|
+
return defineHttpDrain({
|
|
49
59
|
name: "better-stack",
|
|
50
60
|
resolve: async () => {
|
|
51
|
-
const config = await resolveAdapterConfig("betterStack", BETTER_STACK_FIELDS, overrides);
|
|
52
|
-
if (!config.
|
|
53
|
-
console.error("[evlog/better-stack] Missing
|
|
61
|
+
const config = applyApiKeyAlias(await resolveAdapterConfig("betterStack", BETTER_STACK_FIELDS, overrides));
|
|
62
|
+
if (!config.apiKey) {
|
|
63
|
+
console.error("[evlog/better-stack] Missing apiKey. Set NUXT_BETTER_STACK_API_KEY env var or pass to createBetterStackDrain()");
|
|
54
64
|
return null;
|
|
55
65
|
}
|
|
56
66
|
return config;
|
|
57
67
|
},
|
|
58
|
-
|
|
68
|
+
encode: (events, config) => ({
|
|
69
|
+
url: (config.endpoint ?? "https://in.logs.betterstack.com").replace(/\/+$/, ""),
|
|
70
|
+
headers: {
|
|
71
|
+
"Content-Type": "application/json",
|
|
72
|
+
"Authorization": `Bearer ${config.apiKey}`
|
|
73
|
+
},
|
|
74
|
+
body: JSON.stringify(events.map(toBetterStackEvent))
|
|
75
|
+
})
|
|
59
76
|
});
|
|
60
77
|
}
|
|
61
78
|
/**
|
|
62
79
|
* Send a single event to Better Stack.
|
|
63
|
-
*
|
|
64
|
-
* @example
|
|
65
|
-
* ```ts
|
|
66
|
-
* await sendToBetterStack(event, {
|
|
67
|
-
* sourceToken: process.env.BETTER_STACK_SOURCE_TOKEN!,
|
|
68
|
-
* })
|
|
69
|
-
* ```
|
|
70
80
|
*/
|
|
71
81
|
async function sendToBetterStack(event, config) {
|
|
72
82
|
await sendBatchToBetterStack([event], config);
|
|
73
83
|
}
|
|
74
84
|
/**
|
|
75
85
|
* Send a batch of events to Better Stack.
|
|
76
|
-
*
|
|
77
|
-
* @example
|
|
78
|
-
* ```ts
|
|
79
|
-
* await sendBatchToBetterStack(events, {
|
|
80
|
-
* sourceToken: process.env.BETTER_STACK_SOURCE_TOKEN!,
|
|
81
|
-
* })
|
|
82
|
-
* ```
|
|
83
86
|
*/
|
|
84
87
|
async function sendBatchToBetterStack(events, config) {
|
|
88
|
+
const apiKey = config.apiKey ?? config.sourceToken;
|
|
89
|
+
if (!apiKey) throw new Error("[evlog/better-stack] Missing apiKey");
|
|
85
90
|
await httpPost({
|
|
86
91
|
url: (config.endpoint ?? "https://in.logs.betterstack.com").replace(/\/+$/, ""),
|
|
87
92
|
headers: {
|
|
88
93
|
"Content-Type": "application/json",
|
|
89
|
-
"Authorization": `Bearer ${
|
|
94
|
+
"Authorization": `Bearer ${apiKey}`
|
|
90
95
|
},
|
|
91
96
|
body: JSON.stringify(events.map(toBetterStackEvent)),
|
|
92
97
|
timeout: config.timeout ?? 5e3,
|
|
@@ -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 '
|
|
1
|
+
{"version":3,"file":"better-stack.mjs","names":[],"sources":["../../src/adapters/better-stack.ts"],"sourcesContent":["import type { WideEvent } from '../types'\nimport type { ConfigField } from '../shared/config'\nimport { resolveAdapterConfig } from '../shared/config'\nimport { defineHttpDrain } from '../shared/drain'\nimport { httpPost } from '../shared/http'\n\nexport interface BetterStackConfig {\n /** Better Stack API key (replaces deprecated `sourceToken`). */\n apiKey: string\n /**\n * @deprecated Renamed to {@link BetterStackConfig.apiKey}. Will be removed\n * in the next major version. Pass `apiKey` instead.\n */\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: 'apiKey', env: ['NUXT_BETTER_STACK_API_KEY', 'BETTER_STACK_API_KEY'] },\n // Deprecated env var names — resolved as a fallback for `apiKey` below.\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\nlet warnedAboutSourceToken = false\n\nfunction applyApiKeyAlias(config: BetterStackConfig): BetterStackConfig {\n if (!config.apiKey && config.sourceToken) {\n if (!warnedAboutSourceToken) {\n warnedAboutSourceToken = true\n console.warn('[evlog/better-stack] `sourceToken` is deprecated, use `apiKey` instead. (Env: NUXT_BETTER_STACK_SOURCE_TOKEN → NUXT_BETTER_STACK_API_KEY.)')\n }\n config.apiKey = config.sourceToken\n }\n return config\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_API_KEY, BETTER_STACK_API_KEY (or legacy `*_SOURCE_TOKEN`)\n *\n * @example\n * ```ts\n * initLogger({ drain: createBetterStackDrain() })\n *\n * initLogger({ drain: createBetterStackDrain({ apiKey: 'my-key' }) })\n * ```\n */\nexport function createBetterStackDrain(overrides?: Partial<BetterStackConfig>) {\n return defineHttpDrain<BetterStackConfig>({\n name: 'better-stack',\n resolve: async () => {\n const resolved = await resolveAdapterConfig<BetterStackConfig>('betterStack', BETTER_STACK_FIELDS, overrides)\n const config = applyApiKeyAlias(resolved as BetterStackConfig)\n if (!config.apiKey) {\n console.error('[evlog/better-stack] Missing apiKey. Set NUXT_BETTER_STACK_API_KEY env var or pass to createBetterStackDrain()')\n return null\n }\n return config\n },\n encode: (events, config) => ({\n url: (config.endpoint ?? 'https://in.logs.betterstack.com').replace(/\\/+$/, ''),\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${config.apiKey}`,\n },\n body: JSON.stringify(events.map(toBetterStackEvent)),\n }),\n })\n}\n\n/**\n * Send a single event to Better Stack.\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 */\nexport async function sendBatchToBetterStack(events: WideEvent[], config: BetterStackConfig): Promise<void> {\n const apiKey = config.apiKey ?? config.sourceToken\n if (!apiKey) throw new Error('[evlog/better-stack] Missing apiKey')\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 ${apiKey}`,\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":";;AAsBA,MAAM,sBAAwD;CAC5D;EAAE,KAAK;EAAU,KAAK,CAAC,6BAA6B,uBAAuB;EAAE;CAE7E;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;AAED,IAAI,yBAAyB;AAE7B,SAAS,iBAAiB,QAA8C;AACtE,KAAI,CAAC,OAAO,UAAU,OAAO,aAAa;AACxC,MAAI,CAAC,wBAAwB;AAC3B,4BAAyB;AACzB,WAAQ,KAAK,6IAA6I;;AAE5J,SAAO,SAAS,OAAO;;AAEzB,QAAO;;;;;;AAOT,SAAgB,mBAAmB,OAA2C;CAC5E,MAAM,EAAE,WAAW,GAAG,SAAS;AAC/B,QAAO;EAAE,GAAG;EAAM,IAAI;EAAW;;;;;;;;;;;;;;;;;;AAmBnC,SAAgB,uBAAuB,WAAwC;AAC7E,QAAO,gBAAmC;EACxC,MAAM;EACN,SAAS,YAAY;GAEnB,MAAM,SAAS,iBAAiB,MADT,qBAAwC,eAAe,qBAAqB,UAAU,CAC/C;AAC9D,OAAI,CAAC,OAAO,QAAQ;AAClB,YAAQ,MAAM,iHAAiH;AAC/H,WAAO;;AAET,UAAO;;EAET,SAAS,QAAQ,YAAY;GAC3B,MAAM,OAAO,YAAY,mCAAmC,QAAQ,QAAQ,GAAG;GAC/E,SAAS;IACP,gBAAgB;IAChB,iBAAiB,UAAU,OAAO;IACnC;GACD,MAAM,KAAK,UAAU,OAAO,IAAI,mBAAmB,CAAC;GACrD;EACF,CAAC;;;;;AAMJ,eAAsB,kBAAkB,OAAkB,QAA0C;AAClG,OAAM,uBAAuB,CAAC,MAAM,EAAE,OAAO;;;;;AAM/C,eAAsB,uBAAuB,QAAqB,QAA0C;CAC1G,MAAM,SAAS,OAAO,UAAU,OAAO;AACvC,KAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,sCAAsC;AAGnE,OAAM,SAAS;EACb,MAHgB,OAAO,YAAY,mCAAmC,QAAQ,QAAQ,GAGzE;EACb,SAAS;GACP,gBAAgB;GAChB,iBAAiB,UAAU;GAC5B;EACD,MAAM,KAAK,UAAU,OAAO,IAAI,mBAAmB,CAAC;EACpD,SAAS,OAAO,WAAW;EAC3B,SAAS,OAAO;EAChB,OAAO;EACR,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as DrainContext, it as WideEvent } from "../audit-
|
|
1
|
+
import { F as DrainContext, it as WideEvent } from "../audit-X1uUukm3.mjs";
|
|
2
2
|
//#region src/adapters/datadog.d.ts
|
|
3
3
|
interface DatadogConfig {
|
|
4
4
|
/** Datadog API key with Logs intake permission */
|
|
@@ -1 +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;;;;;;;;;;;;;;;;;;
|
|
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;;;;;;;;;;;;;;;;;;AAoDrD;;iBAzBgB,kBAAA,CAAmB,SAAA,GAAY,OAAA,CAAQ,aAAA,KAAc,GAAA,EAAf,YAAA,GAAe,YAAA,OAAA,OAAA;;;;iBAyB/C,aAAA,CAAc,KAAA,EAAO,SAAA,EAAW,MAAA,EAAQ,aAAA,GAAgB,OAAA;;;;iBAOxD,kBAAA,CAAmB,MAAA,EAAQ,SAAA,IAAa,MAAA,EAAQ,aAAA,GAAgB,OAAA"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as defineDrain } from "../_drain-CmCtsuF6.mjs";
|
|
1
|
+
import { a as resolveAdapterConfig, n as defineHttpDrain, r as httpPost } from "../drain-ByWUeOQC.mjs";
|
|
3
2
|
//#region src/adapters/datadog.ts
|
|
4
3
|
const DATADOG_FIELDS = [
|
|
5
4
|
{
|
|
@@ -130,7 +129,7 @@ function resolveDatadogIntakeUrl(config) {
|
|
|
130
129
|
* ```
|
|
131
130
|
*/
|
|
132
131
|
function createDatadogDrain(overrides) {
|
|
133
|
-
return
|
|
132
|
+
return defineHttpDrain({
|
|
134
133
|
name: "datadog",
|
|
135
134
|
resolve: async () => {
|
|
136
135
|
const config = await resolveAdapterConfig("datadog", DATADOG_FIELDS, overrides);
|
|
@@ -140,7 +139,14 @@ function createDatadogDrain(overrides) {
|
|
|
140
139
|
}
|
|
141
140
|
return config;
|
|
142
141
|
},
|
|
143
|
-
|
|
142
|
+
encode: (events, config) => ({
|
|
143
|
+
url: resolveDatadogIntakeUrl(config),
|
|
144
|
+
headers: {
|
|
145
|
+
"Content-Type": "application/json",
|
|
146
|
+
"DD-API-KEY": config.apiKey
|
|
147
|
+
},
|
|
148
|
+
body: JSON.stringify(events.map(toDatadogLog))
|
|
149
|
+
})
|
|
144
150
|
});
|
|
145
151
|
}
|
|
146
152
|
/**
|
|
@@ -1 +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":"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"}
|
|
1
|
+
{"version":3,"file":"datadog.mjs","names":[],"sources":["../../src/adapters/datadog.ts"],"sourcesContent":["import type { WideEvent } from '../types'\nimport type { ConfigField } from '../shared/config'\nimport { resolveAdapterConfig } from '../shared/config'\nimport { defineHttpDrain } from '../shared/drain'\nimport { httpPost } from '../shared/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 defineHttpDrain<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 encode: (events, config) => ({\n url: resolveDatadogIntakeUrl(config),\n headers: {\n 'Content-Type': 'application/json',\n 'DD-API-KEY': config.apiKey,\n },\n body: JSON.stringify(events.map(toDatadogLog)),\n }),\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":"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"}
|
package/dist/adapters/fs.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { F as DrainContext, it as WideEvent } from "../audit-
|
|
1
|
+
import { F as DrainContext, it as WideEvent } from "../audit-X1uUukm3.mjs";
|
|
2
2
|
//#region src/adapters/fs.d.ts
|
|
3
3
|
interface FsConfig {
|
|
4
|
-
/** Directory for log files */
|
|
4
|
+
/** Directory for log files. Default: `.evlog/logs` */
|
|
5
5
|
dir: string;
|
|
6
6
|
/** Max number of log files to keep (auto-deletes oldest when exceeded) */
|
|
7
7
|
maxFiles?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.d.mts","names":[],"sources":["../../src/adapters/fs.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"fs.d.mts","names":[],"sources":["../../src/adapters/fs.ts"],"mappings":";;UAOiB,QAAA;;EAEf,GAAA;EAFe;EAIf,QAAA;;EAEA,cAAA;EAJA;EAMA,MAAA;AAAA;AAAA,iBA+EoB,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"}
|