Add structured JSON logging across webhook and review flow.

Improve observability with correlation IDs, skip/success/failure lifecycle events, and retry diagnostics while documenting log level configuration.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Daan Schouteden
2026-06-02 15:00:29 +02:00
parent 7233ab3f32
commit e9aafdf8c4
6 changed files with 125 additions and 26 deletions
+45 -17
View File
@@ -8,6 +8,7 @@ import { deletePriorBotReviews, postReview } from "../gitea/review-api.js";
import { removeBotFromReviewers } from "../gitea/reviewer-api.js";
import { buildReviewPrompt } from "../prompt/build-review-prompt.js";
import { RoutedEvent } from "../types/events.js";
import { log } from "../logging/logger.js";
import { retry } from "./retry.js";
export async function runReview(input: {
@@ -28,6 +29,13 @@ export async function runReview(input: {
headSha
});
if (input.dedupe.has(dedupeKey)) {
log("info", "Review skipped: duplicate webhook", {
correlation_id: input.correlationId,
owner,
repo,
pr_number: prNumber,
head_sha: headSha
});
return "skipped";
}
input.dedupe.mark(dedupeKey);
@@ -36,7 +44,9 @@ export async function runReview(input: {
const pull = await retry({
fn: () => gitea.getPull(owner, repo, prNumber),
retries: 2,
initialDelayMs: 300
initialDelayMs: 300,
operationName: "getPull",
correlationId: input.correlationId
});
const { config: repoConfig, ruleFiles } = await retry({
fn: () =>
@@ -47,7 +57,9 @@ export async function runReview(input: {
ref: pull.head.ref
}),
retries: 2,
initialDelayMs: 300
initialDelayMs: 300,
operationName: "loadRepoConfig",
correlationId: input.correlationId
});
const should = shouldProcessEvent({
@@ -57,13 +69,23 @@ export async function runReview(input: {
botLogin: input.env.GITEA_BOT_LOGIN
});
if (!should.process) {
log("info", "Review skipped by policy", {
correlation_id: input.correlationId,
owner,
repo,
pr_number: prNumber,
head_sha: headSha,
reason: should.reason ?? "unknown"
});
return "skipped";
}
const files = await retry({
fn: () => gitea.getPullFiles(owner, repo, prNumber),
retries: 2,
initialDelayMs: 300
initialDelayMs: 300,
operationName: "getPullFiles",
correlationId: input.correlationId
});
const maxInlineComments = repoConfig.max_inline_comments ?? input.env.MAX_INLINE_COMMENTS;
const prompt = buildReviewPrompt({
@@ -83,7 +105,9 @@ export async function runReview(input: {
model: repoConfig.model
}),
retries: 2,
initialDelayMs: 500
initialDelayMs: 500,
operationName: "runCursorReview",
correlationId: input.correlationId
});
await retry({
@@ -96,7 +120,9 @@ export async function runReview(input: {
botLogin: input.env.GITEA_BOT_LOGIN
}),
retries: 2,
initialDelayMs: 300
initialDelayMs: 300,
operationName: "deletePriorBotReviews",
correlationId: input.correlationId
});
await retry({
@@ -111,7 +137,9 @@ export async function runReview(input: {
maxInlineComments
}),
retries: 2,
initialDelayMs: 300
initialDelayMs: 300,
operationName: "postReview",
correlationId: input.correlationId
});
await retry({
@@ -124,18 +152,18 @@ export async function runReview(input: {
botLogin: input.env.GITEA_BOT_LOGIN
}),
retries: 2,
initialDelayMs: 300
initialDelayMs: 300,
operationName: "removeBotFromReviewers",
correlationId: input.correlationId
});
console.log(
JSON.stringify({
correlation_id: input.correlationId,
owner,
repo,
pr_number: prNumber,
head_sha: headSha,
outcome: "success"
})
);
log("info", "Review completed", {
correlation_id: input.correlationId,
owner,
repo,
pr_number: prNumber,
head_sha: headSha,
outcome: "success"
});
return "success";
}