feat(api): adopt pino request logging

This commit is contained in:
William Valentin
2025-10-17 09:54:46 -07:00
parent 37411bc890
commit 994da9a4e1
6 changed files with 69 additions and 55 deletions

View File

@@ -1,12 +1,15 @@
import express from 'express';
import { setRoutes } from './routes/index';
import { errorHandler } from './middlewares/errorHandler';
import requestLogger from './middlewares/requestLogger';
import express from "express";
import { setRoutes } from "./routes/index";
import { errorHandler } from "./middlewares/errorHandler";
import pino from "pino";
import pinoHttp from "pino-http";
const logger = pino({ level: process.env.LOG_LEVEL || "info" });
const app = express();
const PORT = process.env.PORT || 3000;
app.use(requestLogger);
app.set("logger", logger);
app.use(pinoHttp({ logger }));
app.use(express.json());
setRoutes(app);
@@ -14,5 +17,5 @@ setRoutes(app);
app.use(errorHandler);
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
logger.info(`Server is running on port ${PORT}`);
});

View File

@@ -1,23 +1,34 @@
import { Request, Response } from 'express';
import StreamService from '../services/streamService';
import { Request, Response } from "express";
import StreamService from "../services/streamService";
import { Logger } from "pino";
export default class StreamController {
constructor(private streamService: StreamService) { }
constructor(
private streamService: StreamService,
private logger: Logger,
) {}
// GET /api/streams/:key
async streamAudio(req: Request, res: Response) {
const { fileName: key } = req.params as { fileName: string };
const range = req.headers.range as string | undefined;
if (!key) return res.status(400).send('Missing file key');
// GET /api/streams/:key
async streamAudio(req: Request, res: Response) {
const { fileName: key } = req.params as { fileName: string };
const range = req.headers.range as string | undefined;
if (!key) return res.status(400).send("Missing file key");
try {
const result = await this.streamService.streamFromS3({ key, range });
res.writeHead(result.status, result.headers);
result.body.pipe(res);
} catch (err: any) {
const message = err?.message || 'Error streaming audio';
const status = err?.statusCode || 500;
res.status(status).send(message);
}
try {
this.logger.info(
`Attempting to stream file: ${key}, range: ${range || "none"}`,
);
const result = await this.streamService.streamFromS3({ key, range });
this.logger.info(
`Streaming successful for ${key}, status: ${result.status}, content-length: ${result.headers["Content-Length"]}`,
);
res.writeHead(result.status, result.headers);
result.body.pipe(res);
} catch (err: any) {
this.logger.error({ err, key }, `Error streaming audio file ${key}`);
const message = err?.message || "Error streaming audio";
const status = err?.statusCode || 500;
res.status(status).send(message);
}
}
}
}

View File

@@ -1,8 +0,0 @@
import { Request, Response, NextFunction } from 'express';
const requestLogger = (req: Request, res: Response, next: NextFunction) => {
console.log(`${req.method} ${req.url}`);
next();
};
export default requestLogger;

View File

@@ -1,18 +1,28 @@
import { Application, Router, Request, Response } from 'express';
import StreamController from '../controllers/streamController';
import StreamService from '../services/streamService';
import { Application, Router, Request, Response } from "express";
import StreamController from "../controllers/streamController";
import StreamService from "../services/streamService";
const router = Router();
const streamService = new StreamService();
const streamController = new StreamController(streamService);
export function setRoutes(app: Application) {
// Health endpoints
app.get('/', (_req: Request, res: Response) => res.status(200).send('OK'));
app.get('/healthz', (_req: Request, res: Response) => res.status(200).json({ status: 'ok' }));
// Health endpoints
app.get("/", (_req: Request, res: Response) => res.status(200).send("OK"));
app.get("/healthz", (_req: Request, res: Response) =>
res.status(200).json({ status: "ok" }),
);
app.use('/api/streams', router);
router.get('/:fileName', streamController.streamAudio.bind(streamController));
const streamService = new StreamService(
app.get("logger"),
process.env.S3_BUCKET,
process.env.S3_PREFIX,
);
const streamController = new StreamController(
streamService,
app.get("logger"),
);
router.get("/:fileName", streamController.streamAudio.bind(streamController));
app.use("/api/streams", router);
}
export default router;
export default router;

View File

@@ -1,11 +0,0 @@
import { Router } from 'express';
import StreamController from '../controllers/streamController';
import StreamService from '../services/streamService';
const router = Router();
const streamService = new StreamService();
const streamController = new StreamController(streamService);
router.get('/:fileName', streamController.streamAudio.bind(streamController));
export default router;

9
s3-nodejs-api/src/types/pino.d.ts vendored Normal file
View File

@@ -0,0 +1,9 @@
import { Logger } from 'pino';
declare global {
namespace Express {
interface Request {
log: Logger;
}
}
}