Skip to content

Commit d75201e

Browse files
committed
feat(logger): wire http/cli
- add http process handlers - add NODE_ENV=test to config/schemas.ts
1 parent 19f2815 commit d75201e

File tree

4 files changed

+74
-15
lines changed

4 files changed

+74
-15
lines changed

backend/src/cli/http.ts

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { pathToFileURL } from 'node:url';
33
import OpenAI from 'openai';
44
import type { FetchPort } from '../application/ports/output/FetchPort';
55
import type { LlmPort } from '../application/ports/output/LlmPort';
6+
import type { LoggerPort } from '../application/ports/output/LoggerPort';
67
import type { PersistencePort } from '../application/ports/output/PersistencePort';
78
import { makeReportingAgentService } from '../application/usecases/agent/makeReportingAgentService';
89
import { makeSnapshotQueryService } from '../application/usecases/queries/makeSnapshotQueryService';
@@ -12,13 +13,29 @@ import { NodeFetchAdapter } from '../infrastructure/fetch/NodeFetchAdapter';
1213
import type { RedditCredentials } from '../infrastructure/items/redditAuth';
1314
import { RedditItemsAdapter } from '../infrastructure/items/RedditItemsAdapter';
1415
import { OpenAIAdapter } from '../infrastructure/llm/OpenAIAdapter';
16+
import { makeLogger } from '../infrastructure/logging/root';
1517
import { PostgresAdapter } from '../infrastructure/persistence/PostgresAdapter';
1618
import { makeReportController } from '../interface/web/ReportController';
1719

20+
const rootLogger = makeLogger();
21+
22+
process.on('unhandledRejection', (reason) => {
23+
rootLogger.error('Unhandled rejection', { error: reason });
24+
process.exit(1);
25+
});
26+
27+
process.on('uncaughtException', (err) => {
28+
rootLogger.error('Uncaught exception', { error: err });
29+
process.exit(1);
30+
});
31+
1832
type Deps = {
33+
logger: LoggerPort;
1934
port: number;
35+
2036
fetcher: FetchPort;
2137
persistence: PersistencePort;
38+
2239
llm: LlmPort;
2340
redditUrl: string;
2441
redditCreds: RedditCredentials;
@@ -33,19 +50,24 @@ export function buildServer(deps: Deps) {
3350

3451
const agent = makeReportingAgentService(provider, deps.llm, deps.persistence);
3552
const query = makeSnapshotQueryService(deps.persistence);
36-
const app = makeReportController(agent, query);
53+
const app = makeReportController(
54+
deps.logger.child({ module: 'http' }),
55+
agent,
56+
query,
57+
);
3758
return { app, port: deps.port };
3859
}
3960

40-
export function depsFromConfig(config: ReportingAgentConfig): Deps {
61+
export function buildDeps(config: ReportingAgentConfig): Deps {
4162
const { port, databaseUrl, openaiApiKey, reddit } = config;
4263

4364
return {
44-
redditUrl: reddit.url,
65+
logger: rootLogger,
4566
port,
4667
fetcher: new NodeFetchAdapter(globalThis.fetch),
4768
persistence: new PostgresAdapter(databaseUrl),
4869
llm: new OpenAIAdapter(new OpenAI({ apiKey: openaiApiKey })),
70+
redditUrl: reddit.url,
4971
redditCreds: {
5072
clientId: reddit.clientId,
5173
clientSecret: reddit.clientSecret,
@@ -57,11 +79,11 @@ export function depsFromConfig(config: ReportingAgentConfig): Deps {
5779

5880
export function runHttpServer() {
5981
const config = loadReportingAgentConfig();
60-
const { app, port } = buildServer(depsFromConfig(config));
82+
const deps = buildDeps(config);
83+
const { app, port } = buildServer(deps);
6184

62-
return app.listen(port, () => {
63-
console.log(`→ http://localhost:${port}`);
64-
});
85+
const httpLogger = rootLogger.child({ module: 'http' });
86+
return app.listen(port, () => httpLogger.info('Server listening', { port }));
6587
}
6688

6789
const entryUrl = process.argv[1]
@@ -72,7 +94,7 @@ if (import.meta.url === entryUrl) {
7294
try {
7395
runHttpServer();
7496
} catch (err) {
75-
console.error('ReportingAgentService run failed:', err);
97+
rootLogger.error('ReportingAgentService run failed:', { error: err });
7698
process.exit(1);
7799
}
78100
}

backend/src/infrastructure/config/schemas.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { z } from 'zod';
33
export const GlobalEnvSchema = z.object({
44
APP_NAME: z.string().optional().default('app'),
55
APP_VERSION: z.string().optional().default('0.0.0'),
6-
NODE_ENV: z.enum(['development', 'production']).default('development'),
6+
NODE_ENV: z
7+
.enum(['development', 'production', 'test'])
8+
.default('development'),
79
});
810

911
export const CoreEnvSchema = z.object({

backend/src/interface/web/ReportController.ts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,54 @@
11
import express, { type Express } from 'express';
22
import type { ReportingAgentPort } from '../../application/ports/input/ReportingAgentPort';
33
import type { SnapshotQueryPort } from '../../application/ports/input/SnapshotQueryPort';
4+
import type { LoggerPort } from '../../application/ports/output/LoggerPort';
5+
6+
declare module 'express-serve-static-core' {
7+
interface Request {
8+
logger?: LoggerPort;
9+
}
10+
}
411

512
export function makeReportController(
13+
logger: LoggerPort,
614
agent: ReportingAgentPort,
715
query: SnapshotQueryPort,
816
): Express {
917
const app = express();
1018
app.use(express.json());
1119

12-
app.get('/report', async (_, res) => {
13-
const report = await query.getLastReport();
14-
if (!report) return res.status(404).json({ error: 'No report found' });
15-
res.json(report);
20+
app.use((req, _res, next) => {
21+
const requestId =
22+
globalThis.crypto?.randomUUID?.() ?? Math.random().toString(36).slice(2);
23+
req.logger = logger.child({ requestId });
24+
next();
25+
});
26+
27+
app.get('/report', async (req, res) => {
28+
const reqLogger = req.logger ?? logger;
29+
try {
30+
const report = await query.getLastReport();
31+
if (!report) {
32+
reqLogger.warn('No report found');
33+
return res.status(404).json({ error: 'No report found' });
34+
}
35+
res.json(report);
36+
} catch (err) {
37+
const error = err instanceof Error ? err : new Error(String(err));
38+
reqLogger.error('Failed to load report', { error });
39+
res.status(500).json({ error: 'Failed to load report' });
40+
}
1641
});
1742

18-
app.post('/report', async (_, res) => {
43+
app.post('/report', async (req, res) => {
44+
const reqLogger = req.logger ?? logger;
1945
try {
2046
await agent.captureSnapshot();
47+
reqLogger.info('Report snapshot captured');
2148
res.json({ ok: true });
2249
} catch (err) {
23-
console.error(err);
50+
const error = err instanceof Error ? err : new Error(String(err));
51+
reqLogger.error('Failed to update report', { error });
2452
res.status(500).json({ error: 'Failed to update report' });
2553
}
2654
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type { LoggerPort } from '../../application/ports/output/LoggerPort';
2+
3+
declare module 'express-serve-static-core' {
4+
interface Request {
5+
logger?: LoggerPort;
6+
}
7+
}

0 commit comments

Comments
 (0)