feat(observability): structured logs, W3C trace IDs, Sentry hooks (#18)

- TS: pino + pino-http; every HTTP request log includes traceId from
  W3C traceparent header (generated if absent); forwarded to ml/serving
  on all /score, /generate, /reward, and /api/ml proxy calls
- Python: structlog JSON; FastAPI middleware binds trace_id via
  contextvars so every log line within a request carries it
- Sentry: optional SENTRY_DSN init in both runtimes (no-op if unset)
- Replace all console.* calls across services/api with pino logger
- Update tests to spy on logger instead of console

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-26 03:37:28 +00:00
parent 7281af83a4
commit c4960d0601
18 changed files with 1041 additions and 64 deletions

View File

@@ -13,6 +13,7 @@ import { db } from '../db/index.js';
import { integrationTokens } from '../db/schema.js';
import { eq } from 'drizzle-orm';
import { todoistSource } from './todoist.js';
import { logger } from '../logger.js';
const DEFAULT_INTERVAL_MS = 15 * 60 * 1000;
@@ -25,7 +26,7 @@ export function startTodoistSyncScheduler(intervalMs = DEFAULT_INTERVAL_MS): Nod
.from(integrationTokens)
.where(eq(integrationTokens.tokenStatus, 'active'));
} catch (err: any) {
console.error(`[scheduler] failed to query users: ${err.message}`);
logger.error({ err }, 'scheduler: failed to query users');
return;
}
@@ -39,10 +40,10 @@ export function startTodoistSyncScheduler(intervalMs = DEFAULT_INTERVAL_MS): Nod
let failed = 0;
for (const r of results) {
if (r.status === 'fulfilled') ok++;
else { failed++; console.error(`[scheduler] sync error:`, r.reason); }
else { failed++; logger.error({ err: r.reason }, 'scheduler: sync error'); }
}
console.log(`[scheduler] todoist sync: ${ok} ok, ${failed} failed (${users.length} users)`);
logger.info({ ok, failed, total: users.length }, 'scheduler: todoist sync');
}
// Run once shortly after startup, then on interval