feat: add structured pino logging and validated log-level config
This commit is contained in:
13
src/app.js
13
src/app.js
@@ -49,7 +49,12 @@ function sanitizeReturnTo(value, fallback = "/app") {
|
||||
return value;
|
||||
}
|
||||
|
||||
function buildApp({ config, initialState = null, onMutation = null }) {
|
||||
function buildApp({
|
||||
config,
|
||||
initialState = null,
|
||||
onMutation = null,
|
||||
logger = console,
|
||||
}) {
|
||||
const engine = new XArtAudioEngine({
|
||||
creditConfig: config.credit,
|
||||
initialState: initialState && initialState.engine ? initialState.engine : null,
|
||||
@@ -79,8 +84,8 @@ function buildApp({ config, initialState = null, onMutation = null }) {
|
||||
updatedAt: new Date().toISOString(),
|
||||
engine: engine.exportState(),
|
||||
});
|
||||
} catch {
|
||||
// avoid breaking request flow on persistence callback issues
|
||||
} catch (error) {
|
||||
logger.error({ err: error }, "failed to persist mutation");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,6 +170,7 @@ function buildApp({ config, initialState = null, onMutation = null }) {
|
||||
publicLink: result.reply ? result.reply.publicLink : `/audio/${result.job.assetId}`,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.warn({ err: error }, "x webhook request failed");
|
||||
return json(400, { error: error.message });
|
||||
}
|
||||
}
|
||||
@@ -192,6 +198,7 @@ function buildApp({ config, initialState = null, onMutation = null }) {
|
||||
persistMutation();
|
||||
return json(200, { status: "credited" });
|
||||
} catch (error) {
|
||||
logger.warn({ err: error }, "polar webhook request failed");
|
||||
return json(400, { error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ function strFromEnv(name, fallback) {
|
||||
const parsed = {
|
||||
port: intFromEnv("PORT", 3000),
|
||||
stateFilePath: strFromEnv("STATE_FILE_PATH", "./data/state.json"),
|
||||
logLevel: strFromEnv("LOG_LEVEL", "info"),
|
||||
xWebhookSecret: process.env.X_WEBHOOK_SECRET || "dev-x-secret",
|
||||
polarWebhookSecret: process.env.POLAR_WEBHOOK_SECRET || "dev-polar-secret",
|
||||
rateLimits: {
|
||||
@@ -41,6 +42,7 @@ const parsed = {
|
||||
const ConfigSchema = z.object({
|
||||
port: z.number().int().positive(),
|
||||
stateFilePath: z.string().min(1),
|
||||
logLevel: z.enum(["fatal", "error", "warn", "info", "debug", "trace", "silent"]),
|
||||
xWebhookSecret: z.string().min(1),
|
||||
polarWebhookSecret: z.string().min(1),
|
||||
rateLimits: z.object({
|
||||
|
||||
18
src/lib/logger.js
Normal file
18
src/lib/logger.js
Normal file
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
|
||||
const pino = require("pino");
|
||||
|
||||
function createLogger({ level = "info", name = "xartaudio" } = {}) {
|
||||
return pino({
|
||||
name,
|
||||
level,
|
||||
base: {
|
||||
service: name,
|
||||
},
|
||||
timestamp: pino.stdTimeFunctions.isoTime,
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createLogger,
|
||||
};
|
||||
@@ -4,6 +4,7 @@ const http = require("node:http");
|
||||
const { buildApp } = require("./app");
|
||||
const { config } = require("./config");
|
||||
const { JsonFileStateStore } = require("./lib/state-store");
|
||||
const { createLogger } = require("./lib/logger");
|
||||
|
||||
function readBody(req) {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -68,7 +69,7 @@ function createMutationPersister({ stateStore, logger = console }) {
|
||||
queue = queue
|
||||
.then(() => stateStore.save(state))
|
||||
.catch((error) => {
|
||||
logger.error("failed to persist state", error);
|
||||
logger.error({ err: error }, "failed to persist state");
|
||||
});
|
||||
|
||||
return queue;
|
||||
@@ -87,6 +88,7 @@ async function createRuntime({ runtimeConfig = config, logger = console } = {})
|
||||
const app = buildApp({
|
||||
config: runtimeConfig,
|
||||
initialState,
|
||||
logger,
|
||||
onMutation(state) {
|
||||
void persister.enqueue(state);
|
||||
},
|
||||
@@ -102,7 +104,11 @@ async function createRuntime({ runtimeConfig = config, logger = console } = {})
|
||||
}
|
||||
|
||||
async function start() {
|
||||
const runtime = await createRuntime({ runtimeConfig: config });
|
||||
const logger = createLogger({
|
||||
level: config.logLevel,
|
||||
name: "xartaudio",
|
||||
});
|
||||
const runtime = await createRuntime({ runtimeConfig: config, logger });
|
||||
const { server, persister } = runtime;
|
||||
|
||||
let shuttingDown = false;
|
||||
@@ -112,7 +118,7 @@ async function start() {
|
||||
}
|
||||
|
||||
shuttingDown = true;
|
||||
console.log(`received ${signal}, shutting down`);
|
||||
logger.info({ signal }, "received shutdown signal");
|
||||
|
||||
server.close();
|
||||
await persister.flush();
|
||||
@@ -126,13 +132,17 @@ async function start() {
|
||||
});
|
||||
|
||||
server.listen(config.port, () => {
|
||||
console.log(`xartaudio server listening on :${config.port}`);
|
||||
logger.info({ port: config.port }, "xartaudio server listening");
|
||||
});
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
start().catch((error) => {
|
||||
console.error("failed to start server", error);
|
||||
const logger = createLogger({
|
||||
level: config.logLevel,
|
||||
name: "xartaudio",
|
||||
});
|
||||
logger.error({ err: error }, "failed to start server");
|
||||
process.exitCode = 1;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user