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:
@@ -1,8 +1,12 @@
|
||||
import { log } from "../logging/logger.js";
|
||||
|
||||
export async function retry<T>(input: {
|
||||
fn: () => Promise<T>;
|
||||
retries: number;
|
||||
initialDelayMs: number;
|
||||
shouldRetry?: (error: unknown) => boolean;
|
||||
operationName?: string;
|
||||
correlationId?: string;
|
||||
}): Promise<T> {
|
||||
let attempt = 0;
|
||||
let delay = input.initialDelayMs;
|
||||
@@ -15,6 +19,13 @@ export async function retry<T>(input: {
|
||||
if (!retryable || attempt > input.retries) {
|
||||
throw error;
|
||||
}
|
||||
log("warn", "Retrying operation after error", {
|
||||
operation: input.operationName ?? "unknown",
|
||||
attempt,
|
||||
retries: input.retries,
|
||||
correlation_id: input.correlationId,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
await sleep(delay);
|
||||
delay *= 2;
|
||||
}
|
||||
|
||||
+45
-17
@@ -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";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user