evlog 1.4.0 → 1.5.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.
@@ -0,0 +1,72 @@
1
+ import { DrainContext, WideEvent } from '../types.mjs';
2
+
3
+ interface PostHogConfig {
4
+ /** PostHog project API key */
5
+ apiKey: string;
6
+ /** PostHog host URL. Default: https://us.i.posthog.com */
7
+ host?: string;
8
+ /** PostHog event name. Default: evlog_wide_event */
9
+ eventName?: string;
10
+ /** Override distinct_id (defaults to event.service) */
11
+ distinctId?: string;
12
+ /** Request timeout in milliseconds. Default: 5000 */
13
+ timeout?: number;
14
+ }
15
+ /** PostHog event structure for the batch API */
16
+ interface PostHogEvent {
17
+ event: string;
18
+ distinct_id: string;
19
+ timestamp: string;
20
+ properties: Record<string, unknown>;
21
+ }
22
+ /**
23
+ * Convert a WideEvent to a PostHog event format.
24
+ */
25
+ declare function toPostHogEvent(event: WideEvent, config: PostHogConfig): PostHogEvent;
26
+ /**
27
+ * Create a drain function for sending logs to PostHog.
28
+ *
29
+ * Configuration priority (highest to lowest):
30
+ * 1. Overrides passed to createPostHogDrain()
31
+ * 2. runtimeConfig.evlog.posthog
32
+ * 3. runtimeConfig.posthog
33
+ * 4. Environment variables: NUXT_POSTHOG_*, POSTHOG_*
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * // Zero config - just set NUXT_POSTHOG_API_KEY env var
38
+ * nitroApp.hooks.hook('evlog:drain', createPostHogDrain())
39
+ *
40
+ * // With overrides
41
+ * nitroApp.hooks.hook('evlog:drain', createPostHogDrain({
42
+ * apiKey: 'phc_...',
43
+ * host: 'https://eu.i.posthog.com',
44
+ * }))
45
+ * ```
46
+ */
47
+ declare function createPostHogDrain(overrides?: Partial<PostHogConfig>): (ctx: DrainContext) => Promise<void>;
48
+ /**
49
+ * Send a single event to PostHog.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * await sendToPostHog(event, {
54
+ * apiKey: process.env.POSTHOG_API_KEY!,
55
+ * })
56
+ * ```
57
+ */
58
+ declare function sendToPostHog(event: WideEvent, config: PostHogConfig): Promise<void>;
59
+ /**
60
+ * Send a batch of events to PostHog.
61
+ *
62
+ * @example
63
+ * ```ts
64
+ * await sendBatchToPostHog(events, {
65
+ * apiKey: process.env.POSTHOG_API_KEY!,
66
+ * })
67
+ * ```
68
+ */
69
+ declare function sendBatchToPostHog(events: WideEvent[], config: PostHogConfig): Promise<void>;
70
+
71
+ export { createPostHogDrain, sendBatchToPostHog, sendToPostHog, toPostHogEvent };
72
+ export type { PostHogConfig, PostHogEvent };
@@ -0,0 +1,72 @@
1
+ import { DrainContext, WideEvent } from '../types.js';
2
+
3
+ interface PostHogConfig {
4
+ /** PostHog project API key */
5
+ apiKey: string;
6
+ /** PostHog host URL. Default: https://us.i.posthog.com */
7
+ host?: string;
8
+ /** PostHog event name. Default: evlog_wide_event */
9
+ eventName?: string;
10
+ /** Override distinct_id (defaults to event.service) */
11
+ distinctId?: string;
12
+ /** Request timeout in milliseconds. Default: 5000 */
13
+ timeout?: number;
14
+ }
15
+ /** PostHog event structure for the batch API */
16
+ interface PostHogEvent {
17
+ event: string;
18
+ distinct_id: string;
19
+ timestamp: string;
20
+ properties: Record<string, unknown>;
21
+ }
22
+ /**
23
+ * Convert a WideEvent to a PostHog event format.
24
+ */
25
+ declare function toPostHogEvent(event: WideEvent, config: PostHogConfig): PostHogEvent;
26
+ /**
27
+ * Create a drain function for sending logs to PostHog.
28
+ *
29
+ * Configuration priority (highest to lowest):
30
+ * 1. Overrides passed to createPostHogDrain()
31
+ * 2. runtimeConfig.evlog.posthog
32
+ * 3. runtimeConfig.posthog
33
+ * 4. Environment variables: NUXT_POSTHOG_*, POSTHOG_*
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * // Zero config - just set NUXT_POSTHOG_API_KEY env var
38
+ * nitroApp.hooks.hook('evlog:drain', createPostHogDrain())
39
+ *
40
+ * // With overrides
41
+ * nitroApp.hooks.hook('evlog:drain', createPostHogDrain({
42
+ * apiKey: 'phc_...',
43
+ * host: 'https://eu.i.posthog.com',
44
+ * }))
45
+ * ```
46
+ */
47
+ declare function createPostHogDrain(overrides?: Partial<PostHogConfig>): (ctx: DrainContext) => Promise<void>;
48
+ /**
49
+ * Send a single event to PostHog.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * await sendToPostHog(event, {
54
+ * apiKey: process.env.POSTHOG_API_KEY!,
55
+ * })
56
+ * ```
57
+ */
58
+ declare function sendToPostHog(event: WideEvent, config: PostHogConfig): Promise<void>;
59
+ /**
60
+ * Send a batch of events to PostHog.
61
+ *
62
+ * @example
63
+ * ```ts
64
+ * await sendBatchToPostHog(events, {
65
+ * apiKey: process.env.POSTHOG_API_KEY!,
66
+ * })
67
+ * ```
68
+ */
69
+ declare function sendBatchToPostHog(events: WideEvent[], config: PostHogConfig): Promise<void>;
70
+
71
+ export { createPostHogDrain, sendBatchToPostHog, sendToPostHog, toPostHogEvent };
72
+ export type { PostHogConfig, PostHogEvent };
@@ -0,0 +1,79 @@
1
+ function getRuntimeConfig() {
2
+ try {
3
+ const { useRuntimeConfig } = require("nitropack/runtime");
4
+ return useRuntimeConfig();
5
+ } catch {
6
+ return void 0;
7
+ }
8
+ }
9
+ function toPostHogEvent(event, config) {
10
+ const { timestamp, level, service, ...rest } = event;
11
+ return {
12
+ event: config.eventName ?? "evlog_wide_event",
13
+ distinct_id: config.distinctId ?? service,
14
+ timestamp,
15
+ properties: {
16
+ level,
17
+ service,
18
+ ...rest
19
+ }
20
+ };
21
+ }
22
+ function createPostHogDrain(overrides) {
23
+ return async (ctx) => {
24
+ const runtimeConfig = getRuntimeConfig();
25
+ const evlogPostHog = runtimeConfig?.evlog?.posthog;
26
+ const rootPostHog = runtimeConfig?.posthog;
27
+ const config = {
28
+ apiKey: overrides?.apiKey ?? evlogPostHog?.apiKey ?? rootPostHog?.apiKey ?? process.env.NUXT_POSTHOG_API_KEY ?? process.env.POSTHOG_API_KEY,
29
+ host: overrides?.host ?? evlogPostHog?.host ?? rootPostHog?.host ?? process.env.NUXT_POSTHOG_HOST ?? process.env.POSTHOG_HOST,
30
+ eventName: overrides?.eventName ?? evlogPostHog?.eventName ?? rootPostHog?.eventName,
31
+ distinctId: overrides?.distinctId ?? evlogPostHog?.distinctId ?? rootPostHog?.distinctId,
32
+ timeout: overrides?.timeout ?? evlogPostHog?.timeout ?? rootPostHog?.timeout
33
+ };
34
+ if (!config.apiKey) {
35
+ console.error("[evlog/posthog] Missing apiKey. Set NUXT_POSTHOG_API_KEY/POSTHOG_API_KEY env var or pass to createPostHogDrain()");
36
+ return;
37
+ }
38
+ try {
39
+ await sendToPostHog(ctx.event, config);
40
+ } catch (error) {
41
+ console.error("[evlog/posthog] Failed to send event:", error);
42
+ }
43
+ };
44
+ }
45
+ async function sendToPostHog(event, config) {
46
+ await sendBatchToPostHog([event], config);
47
+ }
48
+ async function sendBatchToPostHog(events, config) {
49
+ if (events.length === 0) return;
50
+ const host = (config.host ?? "https://us.i.posthog.com").replace(/\/$/, "");
51
+ const timeout = config.timeout ?? 5e3;
52
+ const url = `${host}/batch/`;
53
+ const batch = events.map((event) => toPostHogEvent(event, config));
54
+ const payload = {
55
+ api_key: config.apiKey,
56
+ batch
57
+ };
58
+ const controller = new AbortController();
59
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
60
+ try {
61
+ const response = await fetch(url, {
62
+ method: "POST",
63
+ headers: {
64
+ "Content-Type": "application/json"
65
+ },
66
+ body: JSON.stringify(payload),
67
+ signal: controller.signal
68
+ });
69
+ if (!response.ok) {
70
+ const text = await response.text().catch(() => "Unknown error");
71
+ const safeText = text.length > 200 ? `${text.slice(0, 200)}...[truncated]` : text;
72
+ throw new Error(`PostHog API error: ${response.status} ${response.statusText} - ${safeText}`);
73
+ }
74
+ } finally {
75
+ clearTimeout(timeoutId);
76
+ }
77
+ }
78
+
79
+ export { createPostHogDrain, sendBatchToPostHog, sendToPostHog, toPostHogEvent };
@@ -63,9 +63,9 @@ function callDrainHook(nitroApp, emittedEvent, event) {
63
63
  }).catch((err) => {
64
64
  console.error("[evlog] drain failed:", err);
65
65
  });
66
- const waitUntil = event.context.cloudflare?.context?.waitUntil ?? event.context.waitUntil;
67
- if (typeof waitUntil === "function") {
68
- waitUntil(drainPromise);
66
+ const waitUntilCtx = event.context.cloudflare?.context ?? event.context;
67
+ if (typeof waitUntilCtx?.waitUntil === "function") {
68
+ waitUntilCtx.waitUntil(drainPromise);
69
69
  }
70
70
  }
71
71
  const plugin = defineNitroPlugin((nitroApp) => {
@@ -82,10 +82,15 @@ const plugin = defineNitroPlugin((nitroApp) => {
82
82
  return;
83
83
  }
84
84
  e.context._evlogStartTime = Date.now();
85
+ let requestIdOverride = void 0;
86
+ if (globalThis.navigator?.userAgent === "Cloudflare-Workers") {
87
+ const cfRay = getSafeHeaders(event)?.["cf-ray"];
88
+ if (cfRay) requestIdOverride = cfRay;
89
+ }
85
90
  const log = createRequestLogger({
86
91
  method: e.method,
87
92
  path: e.path,
88
- requestId: e.context.requestId || crypto.randomUUID()
93
+ requestId: requestIdOverride || e.context.requestId || crypto.randomUUID()
89
94
  });
90
95
  const routeService = getServiceForPath(e.path, evlogConfig?.routes);
91
96
  if (routeService) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evlog",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Wide event logging library with structured error handling. Inspired by LoggingSucks.",
5
5
  "author": "HugoRCD <contact@hrcd.fr>",
6
6
  "homepage": "https://evlog.dev",
@@ -46,6 +46,10 @@
46
46
  "./otlp": {
47
47
  "types": "./dist/adapters/otlp.d.mts",
48
48
  "import": "./dist/adapters/otlp.mjs"
49
+ },
50
+ "./posthog": {
51
+ "types": "./dist/adapters/posthog.d.mts",
52
+ "import": "./dist/adapters/posthog.mjs"
49
53
  }
50
54
  },
51
55
  "main": "./dist/index.mjs",
@@ -69,6 +73,9 @@
69
73
  ],
70
74
  "otlp": [
71
75
  "./dist/adapters/otlp.d.mts"
76
+ ],
77
+ "posthog": [
78
+ "./dist/adapters/posthog.d.mts"
72
79
  ]
73
80
  }
74
81
  },
@@ -88,17 +95,17 @@
88
95
  "typecheck": "echo 'Typecheck handled by build'"
89
96
  },
90
97
  "dependencies": {
91
- "@nuxt/kit": "^4.3.0",
98
+ "@nuxt/kit": "^4.3.1",
92
99
  "defu": "^6.1.4"
93
100
  },
94
101
  "devDependencies": {
95
102
  "@nuxt/devtools": "^3.1.1",
96
- "@nuxt/schema": "^4.3.0",
103
+ "@nuxt/schema": "^4.3.1",
97
104
  "@nuxt/test-utils": "^3.23.0",
98
105
  "changelogen": "^0.6.2",
99
106
  "h3": "^1.15.5",
100
107
  "nitropack": "^2.13.1",
101
- "nuxt": "^4.3.0",
108
+ "nuxt": "^4.3.1",
102
109
  "typescript": "^5.9.3",
103
110
  "unbuild": "^3.6.1"
104
111
  },