|
15 | 15 | /* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any */ |
16 | 16 |
|
17 | 17 | import { POSTHOG_HOST, POSTHOG_PROJECT_TOKEN } from "@/config"; |
| 18 | +import logger from "@/utils/logger"; |
18 | 19 | import type { GenerationMetrics } from "./templateGen"; |
19 | 20 |
|
20 | 21 | // --------------------------------------------------------------------------- |
@@ -73,6 +74,16 @@ function getPostHogClient(): any { |
73 | 74 | PostHog: new (apiKey: string, opts: { host: string }) => any; |
74 | 75 | }; |
75 | 76 | _posthogClient = new PostHog(POSTHOG_PROJECT_TOKEN, { host: POSTHOG_HOST }); |
| 77 | + // Surface async capture failures (auth 401s on a wrong token, wrong host, |
| 78 | + // network errors). capture() is fire-and-forget so these are otherwise |
| 79 | + // invisible — the request that triggered it has already returned. Subscribe |
| 80 | + // once at client creation; client is cached for the lifetime of the process. |
| 81 | + _posthogClient.on("error", (err: unknown) => { |
| 82 | + logger.error( |
| 83 | + { err: err instanceof Error ? err.message : err }, |
| 84 | + "[posthog] async capture error", |
| 85 | + ); |
| 86 | + }); |
76 | 87 | return _posthogClient; |
77 | 88 | } |
78 | 89 |
|
@@ -129,9 +140,36 @@ export function capturePostHog(properties: PostHogCaptureProperties): void { |
129 | 140 | properties, |
130 | 141 | }); |
131 | 142 | } catch (err) { |
132 | | - console.error( |
133 | | - "[posthog] capture failed:", |
134 | | - err instanceof Error ? err.message : err, |
| 143 | + logger.error( |
| 144 | + { err: err instanceof Error ? err.message : err }, |
| 145 | + "[posthog] capture failed", |
| 146 | + ); |
| 147 | + } |
| 148 | +} |
| 149 | + |
| 150 | +// --------------------------------------------------------------------------- |
| 151 | +// Graceful shutdown |
| 152 | +// --------------------------------------------------------------------------- |
| 153 | + |
| 154 | +/** |
| 155 | + * Flush any buffered events and tear down the PostHog client. Call once |
| 156 | + * during SIGTERM so events captured in the last `flushInterval` window |
| 157 | + * (default 10s in posthog-node v5) aren't lost when the process exits. |
| 158 | + * |
| 159 | + * No-op when the client was never created (env vars unset, or test |
| 160 | + * override installed). Errors are caught and logged — never propagate |
| 161 | + * to the caller so shutdown can continue. |
| 162 | + */ |
| 163 | +export async function shutdownPostHog(timeoutMs = 5000): Promise<void> { |
| 164 | + const client = _posthogClient; |
| 165 | + if (!client) return; |
| 166 | + _posthogClient = null; |
| 167 | + try { |
| 168 | + await client.shutdown(timeoutMs); |
| 169 | + } catch (err) { |
| 170 | + logger.error( |
| 171 | + { err: err instanceof Error ? err.message : err }, |
| 172 | + "[posthog] shutdown failed", |
135 | 173 | ); |
136 | 174 | } |
137 | 175 | } |
0 commit comments