Skip to content

Commit e01ecdc

Browse files
InfantLabclaude
andcommitted
feat: structured logging audit — request IDs, log levels, and console.error migration
Add request ID middleware (X-Request-Id header), LOG_LEVEL env var filtering, request duration tracking for slow/failed requests, and migrate all 34 server files from raw console.error to structured logger with userId/requestId context. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c72bdeb commit e01ecdc

35 files changed

Lines changed: 358 additions & 48 deletions

app/server/api/entries/[id].delete.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { defineEventHandler, getRouterParam, createError } from "h3";
22
import { db } from "~/server/db";
33
import { entries } from "~/server/db/schema";
44
import { eq, and } from "drizzle-orm";
5+
import { createLogger } from "~/server/utils/logger";
6+
7+
const logger = createLogger("api:entries:delete");
58

69
export default defineEventHandler(async (event) => {
710
try {
@@ -49,7 +52,7 @@ export default defineEventHandler(async (event) => {
4952

5053
return { success: true, id };
5154
} catch (error: unknown) {
52-
console.error("Failed to delete entry:", error);
55+
logger.error("Failed to delete entry", error, { entryId: id });
5356

5457
if (error && typeof error === "object" && "statusCode" in error) {
5558
throw error;

app/server/api/newsletter/subscribe.post.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { eq } from "drizzle-orm";
22
import { db } from "~/server/db";
33
import { newsletterSubscribers } from "~/server/db/schema";
4+
import { createLogger } from "~/server/utils/logger";
5+
6+
const logger = createLogger("api:newsletter");
47

58
/**
69
* POST /api/newsletter/subscribe
@@ -84,7 +87,7 @@ export default defineEventHandler(async (event) => {
8487
message: "Thanks for subscribing!",
8588
};
8689
} catch (error) {
87-
console.error("Newsletter subscription error:", error);
90+
logger.error("Newsletter subscription error", error, { email });
8891
throw createError({
8992
statusCode: 500,
9093
message: "Something went wrong. Please try again.",

app/server/api/v1/auth/keys.get.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
import { success, apiError } from "~/server/utils/response";
1010
import { listApiKeys } from "~/server/utils/api-key";
11+
import { createLogger } from "~/server/utils/logger";
12+
13+
const logger = createLogger("api:v1:auth:keys:list");
1114

1215
export default defineEventHandler(async (event) => {
1316
const auth = event.context['auth']!;
@@ -34,7 +37,7 @@ export default defineEventHandler(async (event) => {
3437
// Return success response
3538
return success(event, keys);
3639
} catch (error) {
37-
console.error("Error listing API keys:", error);
40+
logger.error("Error listing API keys", error instanceof Error ? error : new Error(String(error)), { userId: event.context.user?.id, requestId: event.context.requestId });
3841
throw createError(
3942
apiError(
4043
event,

app/server/api/v1/auth/keys.post.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99
import { z } from "zod";
1010
import { created, apiError, validationError } from "~/server/utils/response";
1111
import { createApiKey } from "~/server/utils/api-key";
12+
import { createLogger } from "~/server/utils/logger";
1213
import type { Permission } from "~/types/api";
1314

15+
const logger = createLogger("api:v1:auth:keys:create");
16+
1417
// Validation schema for API key creation
1518
const createKeySchema = z.object({
1619
name: z.string().min(1).max(100),
@@ -88,7 +91,7 @@ export default defineEventHandler(async (event) => {
8891
"Save this API key now. You won't be able to see it again. If you lose it, you'll need to generate a new one.",
8992
});
9093
} catch (error: unknown) {
91-
console.error("Error creating API key:", error);
94+
logger.error("Error creating API key", error instanceof Error ? error : new Error(String(error)), { userId: event.context.user?.id, requestId: event.context.requestId });
9295

9396
// Surface useful detail for common failures
9497
const message =

app/server/api/v1/auth/keys/[id].delete.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
import { success, apiError } from "~/server/utils/response";
1010
import { revokeApiKey } from "~/server/utils/api-key";
11+
import { createLogger } from "~/server/utils/logger";
12+
13+
const logger = createLogger("api:v1:auth:keys:delete");
1114

1215
export default defineEventHandler(async (event) => {
1316
const auth = event.context['auth']!;
@@ -42,7 +45,7 @@ export default defineEventHandler(async (event) => {
4245
// Return success response
4346
return success(event, { id: keyId }, { revoked: true });
4447
} catch (error) {
45-
console.error("Error revoking API key:", error);
48+
logger.error("Error revoking API key", error instanceof Error ? error : new Error(String(error)), { userId: event.context.user?.id, requestId: event.context.requestId });
4649
throw createError(
4750
apiError(
4851
event,

app/server/api/v1/entries/[id].delete.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import { requirePermission } from "~/server/utils/permissions";
1010
import { success, apiError } from "~/server/utils/response";
1111
import { deleteEntry } from "~/server/services/entries";
1212
import { triggerWebhooks } from "~/server/services/webhooks";
13+
import { createLogger } from "~/server/utils/logger";
14+
15+
const logger = createLogger("api:v1:entries:delete");
1316

1417
export default defineEventHandler(async (event) => {
1518
// Require entries:write permission
@@ -41,7 +44,7 @@ export default defineEventHandler(async (event) => {
4144
triggerWebhooks(userId, "entry.deleted", {
4245
id: entryId,
4346
}).catch((error) => {
44-
console.error("Error triggering webhooks:", error);
47+
logger.error("Error triggering webhooks", error instanceof Error ? error : new Error(String(error)), { userId: event.context.user?.id, requestId: event.context.requestId });
4548
});
4649

4750
// Return success response
@@ -52,7 +55,7 @@ export default defineEventHandler(async (event) => {
5255
throw error;
5356
}
5457

55-
console.error("Error deleting entry:", error);
58+
logger.error("Error deleting entry", error instanceof Error ? error : new Error(String(error)), { userId: event.context.user?.id, requestId: event.context.requestId });
5659
throw createError(
5760
apiError(
5861
event,

app/server/api/v1/entries/[id].get.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import { requirePermission } from "~/server/utils/permissions";
1010
import { success, apiError, notFound } from "~/server/utils/response";
1111
import { getEntryById } from "~/server/services/entries";
1212
import { computeContentHash } from "~/server/utils/contentHash";
13+
import { createLogger } from "~/server/utils/logger";
14+
15+
const logger = createLogger("api:v1:entries:get");
1316

1417
export default defineEventHandler(async (event) => {
1518
// Require entries:read permission
@@ -46,7 +49,7 @@ export default defineEventHandler(async (event) => {
4649
throw error;
4750
}
4851

49-
console.error("Error fetching entry:", error);
52+
logger.error("Error fetching entry", error instanceof Error ? error : new Error(String(error)), { userId: event.context.user?.id, requestId: event.context.requestId });
5053
throw createError(
5154
apiError(
5255
event,

app/server/api/v1/entries/[id].patch.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import { requirePermission } from "~/server/utils/permissions";
1111
import { success, apiError, notFound, validationError } from "~/server/utils/response";
1212
import { updateEntry } from "~/server/services/entries";
1313
import { triggerWebhooks } from "~/server/services/webhooks";
14+
import { createLogger } from "~/server/utils/logger";
15+
16+
const logger = createLogger("api:v1:entries:update");
1417

1518
// Schema for partial updates (all fields optional except what's not updatable)
1619
const updateSchema = z.object({
@@ -79,7 +82,7 @@ export default defineEventHandler(async (event) => {
7982
timestamp: entry.timestamp,
8083
updates,
8184
}).catch((error) => {
82-
console.error("Error triggering webhooks:", error);
85+
logger.error("Error triggering webhooks", error instanceof Error ? error : new Error(String(error)), { userId: event.context.user?.id, requestId: event.context.requestId });
8386
});
8487

8588
// Return success response
@@ -90,7 +93,7 @@ export default defineEventHandler(async (event) => {
9093
throw error;
9194
}
9295

93-
console.error("Error updating entry:", error);
96+
logger.error("Error updating entry", error instanceof Error ? error : new Error(String(error)), { userId: event.context.user?.id, requestId: event.context.requestId });
9497
throw createError(
9598
apiError(
9699
event,

app/server/api/v1/entries/bulk.post.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ import {
1414
bulkUpdateEntries,
1515
bulkDeleteEntries,
1616
} from "~/server/services/entries";
17+
import { createLogger } from "~/server/utils/logger";
1718
import type { NewEntry } from "~/server/db/schema";
1819

20+
const logger = createLogger("api:v1:entries:bulk");
21+
1922
// Schema for entry data (same as POST /entries)
2023
const entryDataSchema = z.object({
2124
type: z.string().min(1),
@@ -210,7 +213,7 @@ export default defineEventHandler(async (event) => {
210213
results,
211214
});
212215
} catch (error) {
213-
console.error("Error performing bulk operations:", error);
216+
logger.error("Error performing bulk operations", error instanceof Error ? error : new Error(String(error)), { userId: event.context.user?.id, requestId: event.context.requestId });
214217
throw createError(
215218
apiError(
216219
event,

app/server/api/v1/entries/index.get.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ import { requirePermission } from "~/server/utils/permissions";
1111
import { paginated, apiError, validationError } from "~/server/utils/response";
1212
import { getEntries } from "~/server/services/entries";
1313
import { computeContentHash } from "~/server/utils/contentHash";
14+
import { createLogger } from "~/server/utils/logger";
1415
import type { EntryQueryParams } from "~/types/api";
1516

17+
const logger = createLogger("api:v1:entries:list");
18+
1619
// Query parameter validation schema
1720
const querySchema = z.object({
1821
// Date filters
@@ -89,7 +92,7 @@ export default defineEventHandler(async (event) => {
8992
queryParams.offset ?? 0,
9093
);
9194
} catch (error) {
92-
console.error("Error fetching entries:", error);
95+
logger.error("Error fetching entries", error instanceof Error ? error : new Error(String(error)), { userId: event.context.user?.id, requestId: event.context.requestId });
9396
throw createError(
9497
apiError(
9598
event,

0 commit comments

Comments
 (0)