refactor(backend): route startup logs through logger
This commit is contained in:
parent
21aefb589d
commit
7016fc1ad5
@ -1,5 +1,6 @@
|
||||
import { initializeAllContainers, registerContainers } from '@bytelyst/cosmos';
|
||||
import type { ContainerConfig } from '@bytelyst/cosmos';
|
||||
import type { Logger } from '@bytelyst/logger';
|
||||
import { config } from './config.js';
|
||||
|
||||
const CONTAINER_DEFS: Record<string, ContainerConfig> = {
|
||||
@ -27,7 +28,9 @@ const CONTAINER_DEFS: Record<string, ContainerConfig> = {
|
||||
palace_diaries: { partitionKeyPath: '/userId' },
|
||||
};
|
||||
|
||||
export async function initCosmosIfNeeded(): Promise<void> {
|
||||
type CosmosInitLogger = Pick<Logger, 'error' | 'info'>;
|
||||
|
||||
export async function initCosmosIfNeeded(logger?: CosmosInitLogger): Promise<void> {
|
||||
registerContainers(CONTAINER_DEFS);
|
||||
|
||||
const shouldInit = config.NODE_ENV !== 'production' || process.env.COSMOS_AUTO_INIT === 'true';
|
||||
@ -37,9 +40,8 @@ export async function initCosmosIfNeeded(): Promise<void> {
|
||||
|
||||
try {
|
||||
await initializeAllContainers();
|
||||
process.stdout.write('[notelett-backend] Cosmos containers ensured\n');
|
||||
logger?.info('Cosmos containers ensured');
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
process.stderr.write(`[notelett-backend] Cosmos init failed: ${msg}\n`);
|
||||
logger?.error('Cosmos init failed', err);
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,6 +22,16 @@ import { stripHtmlForEmbedding } from '../../lib/embeddings.js';
|
||||
|
||||
// ── Types ──────────────────────────────────────────────────────────
|
||||
|
||||
type SchedulerLogger = {
|
||||
error: (details: Record<string, unknown>, message: string) => void;
|
||||
};
|
||||
|
||||
let schedulerLogger: SchedulerLogger = { error: () => undefined };
|
||||
|
||||
export function setSchedulerLogger(logger: SchedulerLogger): void {
|
||||
schedulerLogger = logger;
|
||||
}
|
||||
|
||||
export interface PromptScheduleDoc {
|
||||
id: string;
|
||||
productId: string;
|
||||
@ -216,15 +226,17 @@ export async function runSchedulerTick(): Promise<number> {
|
||||
trackEvent('scheduled_action_fired', schedule.userId, { scheduleId: schedule.id, templateSlug: template?.slug ?? schedule.templateId });
|
||||
ran++;
|
||||
} catch (err: unknown) {
|
||||
const msg = err instanceof Error ? err.message : 'Unknown scheduler error';
|
||||
process.stderr.write(`[scheduler] Failed to run schedule ${schedule.id}: ${msg}\n`);
|
||||
schedulerLogger.error({ err, scheduleId: schedule.id }, 'Failed to run scheduled prompt');
|
||||
}
|
||||
}
|
||||
|
||||
return ran;
|
||||
}
|
||||
|
||||
export function startSchedulerLoop(intervalMs = 60_000): void {
|
||||
export function startSchedulerLoop(intervalMs = 60_000, logger?: SchedulerLogger): void {
|
||||
if (logger) {
|
||||
setSchedulerLogger(logger);
|
||||
}
|
||||
if (schedulerInterval) return;
|
||||
schedulerInterval = setInterval(() => {
|
||||
void runSchedulerTick();
|
||||
|
||||
@ -6,12 +6,18 @@ const startServiceMock = vi.fn(async () => undefined);
|
||||
const initCosmosIfNeededMock = vi.fn(async () => undefined);
|
||||
const initDatastoreMock = vi.fn(() => undefined);
|
||||
const diagnosticsRoutesMock = vi.fn(async () => undefined);
|
||||
const startSchedulerLoopMock = vi.fn();
|
||||
const stopSchedulerLoopMock = vi.fn();
|
||||
|
||||
const appMock = {
|
||||
register: vi.fn(async () => undefined),
|
||||
get: vi.fn(),
|
||||
post: vi.fn(),
|
||||
addHook: vi.fn(),
|
||||
log: {
|
||||
error: vi.fn(),
|
||||
info: vi.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
vi.mock('@bytelyst/fastify-core', () => ({
|
||||
@ -36,8 +42,8 @@ vi.mock('./modules/ecosystem-phase3/routes.js', () => ({ ecosystemPhase3Routes:
|
||||
vi.mock('./modules/note-prompts/routes.js', () => ({ notePromptRoutes: vi.fn() }));
|
||||
vi.mock('./modules/note-prompts/scheduler.js', () => ({
|
||||
promptSchedulerRoutes: vi.fn(),
|
||||
startSchedulerLoop: vi.fn(),
|
||||
stopSchedulerLoop: vi.fn(),
|
||||
startSchedulerLoop: startSchedulerLoopMock,
|
||||
stopSchedulerLoop: stopSchedulerLoopMock,
|
||||
}));
|
||||
vi.mock('./modules/intake/routes.js', () => ({ intakeRoutes: vi.fn() }));
|
||||
vi.mock('./modules/note-collaborators/routes.js', () => ({ noteCollaboratorRoutes: vi.fn() }));
|
||||
@ -51,6 +57,7 @@ vi.mock('./lib/config.js', () => ({
|
||||
PORT: 4016,
|
||||
HOST: '0.0.0.0',
|
||||
JWT_SECRET: 'test-secret',
|
||||
NODE_ENV: 'test',
|
||||
},
|
||||
}));
|
||||
vi.mock('./lib/product-config.js', () => ({
|
||||
@ -78,12 +85,16 @@ describe('server bootstrap', () => {
|
||||
it('initializes app, routes, and starts service', async () => {
|
||||
await import('./server.js');
|
||||
|
||||
expect(initCosmosIfNeededMock).toHaveBeenCalledOnce();
|
||||
expect(initCosmosIfNeededMock).toHaveBeenCalledWith(expect.objectContaining({
|
||||
error: expect.any(Function),
|
||||
info: expect.any(Function),
|
||||
}));
|
||||
expect(initDatastoreMock).toHaveBeenCalledOnce();
|
||||
expect(createServiceAppMock).toHaveBeenCalledOnce();
|
||||
expect(registerOptionalJwtContextMock).toHaveBeenCalledOnce();
|
||||
expect(appMock.register).toHaveBeenCalledTimes(14);
|
||||
expect(diagnosticsRoutesMock).toHaveBeenCalledWith(appMock);
|
||||
expect(startSchedulerLoopMock).toHaveBeenCalledWith(60_000, appMock.log);
|
||||
expect(startServiceMock).toHaveBeenCalledWith(appMock, { port: 4016, host: '0.0.0.0' });
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { createServiceApp, registerOptionalJwtContext, startService } from '@bytelyst/fastify-core';
|
||||
import { createLogger } from '@bytelyst/logger';
|
||||
import { jwtVerify } from 'jose';
|
||||
import { noteAgentActionRoutes } from './modules/note-agent-actions/routes.js';
|
||||
import { ecosystemPhase1Routes } from './modules/ecosystem-phase1/routes.js';
|
||||
@ -27,8 +28,12 @@ import { findShareByToken } from './modules/note-shares/repository.js';
|
||||
import * as noteRepo from './modules/notes/repository.js';
|
||||
|
||||
const jwtSecret = new TextEncoder().encode(config.JWT_SECRET);
|
||||
const startupLogger = createLogger({
|
||||
service: config.SERVICE_NAME,
|
||||
isDev: config.NODE_ENV !== 'production',
|
||||
});
|
||||
|
||||
await initCosmosIfNeeded();
|
||||
await initCosmosIfNeeded(startupLogger);
|
||||
initDatastore();
|
||||
|
||||
const app = await createServiceApp({
|
||||
@ -74,7 +79,7 @@ await registerApiPlugin(noteCollaboratorRoutes);
|
||||
await registerApiPlugin(palaceRoutes);
|
||||
|
||||
// ── Start scheduler loop (F25) ────────────────────────────────────
|
||||
startSchedulerLoop();
|
||||
startSchedulerLoop(60_000, app.log);
|
||||
initWebhookSubscriber();
|
||||
app.addHook('onClose', async () => { stopSchedulerLoop(); stopWebhookSubscriber(); });
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user