Skip to content

Commit a4de55a

Browse files
committed
Additional logging to see why this app sometimes crashes in dev
1 parent 6fa3e0d commit a4de55a

File tree

4 files changed

+104
-1
lines changed

4 files changed

+104
-1
lines changed

app.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,45 @@ const bodyParser = require("body-parser");
1111
const cors = require("cors");
1212
const HttpStatus = require("http-status-codes");
1313
const logger = require("./src/common/logger");
14+
15+
// Global error and signal handlers to improve crash visibility.
16+
// Note: SIGSEGV cannot be handled in userland, but these cover other fatal cases.
17+
process.on("uncaughtException", (err) => {
18+
try {
19+
logger.error("Uncaught exception:", err);
20+
if (process.report && typeof process.report.writeReport === "function") {
21+
const reportPath = process.report.writeReport();
22+
if (reportPath) logger.error(`Diagnostic report written: ${reportPath}`);
23+
}
24+
} finally {
25+
// Exit to avoid undefined state after an unhandled exception
26+
process.exit(1);
27+
}
28+
});
29+
30+
process.on("unhandledRejection", (reason, promise) => {
31+
logger.error("Unhandled rejection:", { reason, promise });
32+
try {
33+
if (process.report && typeof process.report.writeReport === "function") {
34+
const reportPath = process.report.writeReport();
35+
if (reportPath) logger.error(`Diagnostic report written: ${reportPath}`);
36+
}
37+
} catch (_) {}
38+
});
39+
40+
// Allow on-demand diagnostic reports (e.g., docker/ecs kill -s SIGUSR2 <container>)
41+
process.on("SIGUSR2", () => {
42+
try {
43+
if (process.report && typeof process.report.writeReport === "function") {
44+
const reportPath = process.report.writeReport();
45+
if (reportPath) logger.warn(`SIGUSR2 received. Diagnostic report: ${reportPath}`);
46+
} else {
47+
logger.warn("SIGUSR2 received, but process.report is not available.");
48+
}
49+
} catch (err) {
50+
logger.error("Error generating diagnostic report on SIGUSR2:", err);
51+
}
52+
});
1453
const interceptor = require("express-interceptor");
1554
const fileUpload = require("express-fileupload");
1655
const YAML = require("yamljs");

docker/Dockerfile

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,12 @@ WORKDIR /challenge-api
1111
RUN npm i -g pnpm
1212
RUN pnpm install
1313

14-
CMD node /challenge-api/app.js
14+
# Enable Node diagnostic reports and Prisma backtraces for deeper crash insights
15+
ENV NODE_OPTIONS="--report-on-fatalerror --report-uncaught-exception --report-signal=SIGUSR2 --report-directory=/challenge-api/reports --report-filename=report.%p.%t.json --enable-source-maps"
16+
ENV RUST_BACKTRACE=1
17+
# Optional: raise Prisma log level from the engine; keep it modest by default
18+
ENV PRISMA_LOG_LEVEL=info
19+
20+
RUN mkdir -p /challenge-api/reports
21+
22+
CMD ["node","/challenge-api/app.js"]

src/common/prisma.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const {
77
PrizeSetTypeEnum,
88
ReviewOpportunityTypeEnum,
99
} = require("@prisma/client");
10+
const logger = require("./logger");
1011

1112
const prismaClient = new PrismaClient({
1213
log: [
@@ -24,6 +25,35 @@ const prismaClient = new PrismaClient({
2425
},
2526
});
2627

28+
// Forward Prisma engine logs to the application logger. This helps diagnose
29+
// native engine panics or crashes that may lead to exit code 139.
30+
prismaClient.$on("error", (e) => {
31+
try {
32+
logger.error(`[prisma:error] ${e.message || e}`);
33+
} catch (_) {}
34+
});
35+
prismaClient.$on("warn", (e) => {
36+
try {
37+
logger.warn(`[prisma:warn] ${e.message || e}`);
38+
} catch (_) {}
39+
});
40+
prismaClient.$on("info", (e) => {
41+
try {
42+
logger.info(`[prisma:info] ${e.message || e}`);
43+
} catch (_) {}
44+
});
45+
46+
// Optional verbose query logging: enable by setting PRISMA_LOG_QUERIES=true
47+
if (process.env.PRISMA_LOG_QUERIES === "true") {
48+
prismaClient.$on("query", (e) => {
49+
try {
50+
logger.info(
51+
`[prisma:query] ${e.query} params=${e.params} duration=${e.duration}ms`
52+
);
53+
} catch (_) {}
54+
});
55+
}
56+
2757
// By running the first query, prisma calls $connect() under the hood
2858
module.exports.prismaConnect = () => {
2959
prismaClient.$connect();

src/common/review-prisma.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const { PrismaClient } = require("@prisma/client");
22
const config = require("config");
3+
const logger = require("./logger");
34

45
let reviewPrismaClient;
56

@@ -28,6 +29,31 @@ const getReviewClient = () => {
2829
}
2930
if (!reviewPrismaClient) {
3031
reviewPrismaClient = createClient();
32+
// Forward Prisma engine logs for the review DB
33+
reviewPrismaClient.$on("error", (e) => {
34+
try {
35+
logger.error(`[prisma:review:error] ${e.message || e}`);
36+
} catch (_) {}
37+
});
38+
reviewPrismaClient.$on("warn", (e) => {
39+
try {
40+
logger.warn(`[prisma:review:warn] ${e.message || e}`);
41+
} catch (_) {}
42+
});
43+
reviewPrismaClient.$on("info", (e) => {
44+
try {
45+
logger.info(`[prisma:review:info] ${e.message || e}`);
46+
} catch (_) {}
47+
});
48+
if (process.env.PRISMA_LOG_QUERIES === "true") {
49+
reviewPrismaClient.$on("query", (e) => {
50+
try {
51+
logger.info(
52+
`[prisma:review:query] ${e.query} params=${e.params} duration=${e.duration}ms`
53+
);
54+
} catch (_) {}
55+
});
56+
}
3157
}
3258
return reviewPrismaClient;
3359
};

0 commit comments

Comments
 (0)