Add structured backend logging with pino
Wires leveled, structured logging (pretty in dev, JSON in prod) through the server lifecycle, HTTP/WS request handling, run engine, auth, db, and config loading. CLI command output is left untouched since it's user-facing terminal UX, not backend logs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,9 @@ import {
|
||||
recordFailedAttempt,
|
||||
clearAttempts,
|
||||
} from "@/lib/auth/rate-limit";
|
||||
import { logger } from "@/lib/logger";
|
||||
|
||||
const log = logger.child({ mod: "auth" });
|
||||
|
||||
const loginSchema = z.object({
|
||||
username: z.string().min(1),
|
||||
@@ -20,6 +23,7 @@ const loginSchema = z.object({
|
||||
export async function POST(request: Request) {
|
||||
const rateLimitKey = request.headers.get("x-forwarded-for") ?? "local";
|
||||
if (isRateLimited(rateLimitKey)) {
|
||||
log.warn({ from: rateLimitKey }, "login rate-limited");
|
||||
return Response.json(
|
||||
{ error: "Too many attempts, try again later." },
|
||||
{ status: 429 },
|
||||
@@ -44,6 +48,10 @@ export async function POST(request: Request) {
|
||||
!(await verifyPassword(user.passwordHash, parsed.data.password))
|
||||
) {
|
||||
recordFailedAttempt(rateLimitKey);
|
||||
log.warn(
|
||||
{ from: rateLimitKey, username: parsed.data.username },
|
||||
"login failed: invalid credentials",
|
||||
);
|
||||
return Response.json({ error: "Invalid credentials" }, { status: 401 });
|
||||
}
|
||||
|
||||
@@ -58,5 +66,7 @@ export async function POST(request: Request) {
|
||||
session.username = user.username;
|
||||
await session.save();
|
||||
|
||||
log.info({ from: rateLimitKey, username: user.username }, "login succeeded");
|
||||
|
||||
return Response.json({ user: { username: user.username } });
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ import { requireAuth, unauthorizedResponse } from "@/lib/auth/guard";
|
||||
import { getDb } from "@/lib/db/client";
|
||||
import { runs } from "@/lib/db/schema";
|
||||
import { cancelRun } from "@/lib/runner/registry";
|
||||
import { logger } from "@/lib/logger";
|
||||
|
||||
const log = logger.child({ mod: "api" });
|
||||
|
||||
export async function POST(
|
||||
request: Request,
|
||||
@@ -27,11 +30,16 @@ export async function POST(
|
||||
|
||||
const cancelled = cancelRun(runId);
|
||||
if (!cancelled) {
|
||||
log.warn(
|
||||
{ runId },
|
||||
"cancel requested for a run not tracked by this process",
|
||||
);
|
||||
return Response.json(
|
||||
{ error: "Run is not active in this server process" },
|
||||
{ status: 409 },
|
||||
);
|
||||
}
|
||||
|
||||
log.info({ runId, requestedBy: auth.identity }, "cancel requested via API");
|
||||
return Response.json({ status: "cancelling" }, { status: 202 });
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ import { requireAuth, unauthorizedResponse } from "@/lib/auth/guard";
|
||||
import { getScript } from "@/lib/config/load";
|
||||
import { buildVariableSchema } from "@/lib/validation/variable-schema";
|
||||
import { startRun } from "@/lib/runner/engine";
|
||||
import { logger } from "@/lib/logger";
|
||||
|
||||
const log = logger.child({ mod: "api" });
|
||||
|
||||
export async function POST(
|
||||
request: Request,
|
||||
@@ -26,11 +29,13 @@ export async function POST(
|
||||
const variableSchema = buildVariableSchema(script);
|
||||
const parsed = variableSchema.safeParse(variablesInput);
|
||||
if (!parsed.success) {
|
||||
const fieldErrors = parsed.error.flatten().fieldErrors;
|
||||
log.warn(
|
||||
{ scriptId: script.id, fields: Object.keys(fieldErrors) },
|
||||
"run request failed variable validation",
|
||||
);
|
||||
return Response.json(
|
||||
{
|
||||
error: "Validation failed",
|
||||
fieldErrors: parsed.error.flatten().fieldErrors,
|
||||
},
|
||||
{ error: "Validation failed", fieldErrors },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user