chore: log rank-check live call failures with error code and keyword, warn on upstream flakes (#542)
This commit is contained in:
parent
824e914eb1
commit
c7ff28c30a
@ -138,10 +138,11 @@ function createAuthenticatedFetch(
|
|||||||
);
|
);
|
||||||
error.name = "DataForSEOHttpError";
|
error.name = "DataForSEOHttpError";
|
||||||
// UPSTREAM_UNAVAILABLE is non-reportable, and the error handlers only log
|
// UPSTREAM_UNAVAILABLE is non-reportable, and the error handlers only log
|
||||||
// what they capture, so log here to keep the provider's failure rate
|
// what they capture, so warn here to keep the provider's failure rate
|
||||||
// visible in Workers Observability.
|
// visible in Workers Observability. Warn, not error: there is nothing in
|
||||||
|
// the app to fix.
|
||||||
if (code === "UPSTREAM_UNAVAILABLE")
|
if (code === "UPSTREAM_UNAVAILABLE")
|
||||||
console.error("dataforseo.upstream-http-failed", {
|
console.warn("dataforseo.upstream-http-failed", {
|
||||||
path,
|
path,
|
||||||
status: response.status,
|
status: response.status,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -246,10 +246,11 @@ export function assertOk<T extends DataforseoTaskLike>(
|
|||||||
const detailedMessage = describeInvalidField(message, task);
|
const detailedMessage = describeInvalidField(message, task);
|
||||||
const isUpstreamFailure = isUpstreamServerErrorTask(task);
|
const isUpstreamFailure = isUpstreamServerErrorTask(task);
|
||||||
// UPSTREAM_UNAVAILABLE is non-reportable, and the error handlers only log
|
// UPSTREAM_UNAVAILABLE is non-reportable, and the error handlers only log
|
||||||
// what they capture, so log here to keep the provider's failure rate — and
|
// what they capture, so warn here to keep the provider's failure rate — and
|
||||||
// the only remaining record of the message — visible in Workers Observability.
|
// the only remaining record of the message — visible in Workers
|
||||||
|
// Observability. Warn, not error: there is nothing in the app to fix.
|
||||||
if (isUpstreamFailure)
|
if (isUpstreamFailure)
|
||||||
console.error("dataforseo.upstream-task-failed", {
|
console.warn("dataforseo.upstream-task-failed", {
|
||||||
path,
|
path,
|
||||||
status: task.status_code,
|
status: task.status_code,
|
||||||
message: task.status_message,
|
message: task.status_message,
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import type {
|
|||||||
RankCheckResult,
|
RankCheckResult,
|
||||||
RankCheckTaskInput,
|
RankCheckTaskInput,
|
||||||
} from "@/server/lib/dataforseo";
|
} from "@/server/lib/dataforseo";
|
||||||
|
import { AppError } from "@/server/lib/errors";
|
||||||
import type { RankTrackingConfig } from "@/types/schemas/rank-tracking";
|
import type { RankTrackingConfig } from "@/types/schemas/rank-tracking";
|
||||||
import { KEYWORDS_PER_BATCH } from "@/shared/rank-tracking";
|
import { KEYWORDS_PER_BATCH } from "@/shared/rank-tracking";
|
||||||
import { pgStep } from "@/server/workflows/pgStep";
|
import { pgStep } from "@/server/workflows/pgStep";
|
||||||
@ -100,16 +101,23 @@ async function checkBatchLive(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
const results: RankCheckResultWithDevice[] = [];
|
const results: RankCheckResultWithDevice[] = [];
|
||||||
for (const outcome of settled) {
|
settled.forEach((outcome, index) => {
|
||||||
if (outcome.status === "fulfilled") {
|
if (outcome.status === "fulfilled") {
|
||||||
results.push(outcome.value);
|
results.push(outcome.value);
|
||||||
} else {
|
return;
|
||||||
console.error(
|
|
||||||
`[rank-check] ${ctx.runId} live call failed:`,
|
|
||||||
outcome.reason,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
const reason = outcome.reason;
|
||||||
|
const code = reason instanceof AppError ? reason.code : "UNKNOWN";
|
||||||
|
const message = reason instanceof Error ? reason.message : String(reason);
|
||||||
|
// DataForSEO erring on its own side is a provider flake, not our bug: the
|
||||||
|
// keyword just misses this run and finalize reports it to the user. Every
|
||||||
|
// other rejection (no credits, bad API key) is ours and stays at error.
|
||||||
|
const log = code === "UPSTREAM_UNAVAILABLE" ? console.warn : console.error;
|
||||||
|
const task = tasks[index];
|
||||||
|
log(
|
||||||
|
`[rank-check] ${ctx.runId} live call failed (${code}) keyword="${task.keyword}" device=${task.device}: ${message}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
if (results.length > 0) {
|
if (results.length > 0) {
|
||||||
await RankTrackingRepository.insertSnapshots(
|
await RankTrackingRepository.insertSnapshots(
|
||||||
mapResultsToSnapshotRows(ctx.runId, results),
|
mapResultsToSnapshotRows(ctx.runId, results),
|
||||||
|
|||||||
@ -39,8 +39,8 @@ const NON_REPORTABLE_ERROR_CODES = new Set<ErrorCode>([
|
|||||||
"AUDIT_ALREADY_RUNNING",
|
"AUDIT_ALREADY_RUNNING",
|
||||||
// An external provider (DataForSEO) failing on its own side. Nothing in the
|
// An external provider (DataForSEO) failing on its own side. Nothing in the
|
||||||
// app to fix, and it drowned real exceptions. Note the error handlers only
|
// app to fix, and it drowned real exceptions. Note the error handlers only
|
||||||
// log what they capture, so every throw site logs its own line to keep the
|
// log what they capture, so every throw site warns on its own line to keep
|
||||||
// failure rate visible in Workers Observability.
|
// the failure rate visible in Workers Observability.
|
||||||
"UPSTREAM_UNAVAILABLE",
|
"UPSTREAM_UNAVAILABLE",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user