chore: govern rmbg service logging

This commit is contained in:
Your Name 2026-05-27 13:06:01 +08:00
parent 36f3de8321
commit 8e91c00c74
2 changed files with 95 additions and 13 deletions

View File

@ -6,8 +6,11 @@ import {
} from "@/lib/db/queries";
import { db } from "@/lib/db";
import { generatedImage } from "@/lib/db/schema";
import { createLogger } from "@/lib/observability/logger";
import { uploadFile } from "@/lib/storage/service";
const logger = createLogger("rmbg-service");
export type RmbgProvider = "rmbg" | "photoroom" | "aoscdn";
type RmbgConfig = {
@ -73,7 +76,7 @@ async function createThumbnail(buffer: Buffer, mimeType: string): Promise<Buffer
.png({ quality: 80 })
.toBuffer();
} catch (error) {
console.error("[RMBG] Failed to create thumbnail:", error);
logger.warn("thumbnail_creation_failed", error);
return buffer;
}
}
@ -105,12 +108,20 @@ async function uploadBufferToS3(
uploadFile(originalFilename, originalArrayBuffer),
]);
logger.info("storage_upload_completed", {
mimeType,
originalBytes: buffer.length,
originalUrl: originalResult.url,
thumbnailBytes: thumbnailBuffer.length,
thumbnailUrl: thumbnailResult.url,
});
return {
thumbnail: thumbnailResult.url,
original: originalResult.url,
};
} catch (error) {
console.error("[RMBG] Failed to upload to S3:", error);
logger.error("storage_upload_failed", error);
throw new RmbgServiceError(
"图片上传到存储桶失败",
500,
@ -190,7 +201,7 @@ async function prepareImageForAos(
filename: `image.${filenameExt}`,
};
} catch (error) {
console.error("[AOS RMBG] 图片预处理失败,继续使用原图:", error);
logger.warn("aos_image_preprocess_failed", error);
return { buffer: imageBuffer, mimeType: "image/png", filename: "image.png" };
}
}
@ -228,7 +239,10 @@ async function processWithRMBG(
if (!response.ok) {
const errorText = await response.text();
console.error("[RMBG API] 请求失败:", { status: response.status, error: errorText });
logger.warn("rmbg_provider_request_failed", {
errorLength: errorText.length,
status: response.status,
});
return { success: false, error: errorText || response.statusText };
}
@ -247,6 +261,7 @@ async function processWithRMBG(
if (error instanceof Error && error.name === "AbortError") {
return { success: false, error: "请求超时(30秒)" };
}
logger.warn("rmbg_provider_request_error", error);
return { success: false, error: error instanceof Error ? error.message : "未知错误" };
}
}
@ -282,7 +297,10 @@ async function processWithPhotoRoom(
if (!response.ok) {
const errorText = await response.text();
console.error("[PhotoRoom API] 请求失败:", { status: response.status, error: errorText });
logger.warn("photoroom_provider_request_failed", {
errorLength: errorText.length,
status: response.status,
});
return { success: false, error: `PhotoRoom API错误: ${response.status} - ${errorText}` };
}
@ -296,7 +314,7 @@ async function processWithPhotoRoom(
if (error instanceof Error && error.name === "AbortError") {
return { success: false, error: "PhotoRoom请求超时(60秒)" };
}
console.error("[PhotoRoom] Error:", error);
logger.warn("photoroom_provider_request_error", error);
return { success: false, error: error instanceof Error ? error.message : "未知错误" };
}
}
@ -319,7 +337,9 @@ async function processWithAosSegmentation(
try {
const normalizedImage = await prepareImageForAos(imageBuffer);
const createForm = new FormData();
const imageBlob = new Blob([new Uint8Array(normalizedImage.buffer)], { type: normalizedImage.mimeType });
const imageBlob = new Blob([new Uint8Array(normalizedImage.buffer)], {
type: normalizedImage.mimeType,
});
createForm.append("image_file", imageBlob, normalizedImage.filename);
createForm.append("sync", "0");
createForm.append("return_type", "1");
@ -343,6 +363,10 @@ async function processWithAosSegmentation(
const createPayload = await createResponse.json().catch(() => null);
if (!createResponse.ok || !createPayload?.data?.task_id) {
logger.warn("aos_task_create_failed", {
hasPayload: createPayload != null,
status: createResponse.status,
});
return {
success: false,
error: `AOS任务创建失败: ${createPayload?.message || createResponse.statusText}`,
@ -368,6 +392,10 @@ async function processWithAosSegmentation(
const pollPayload = await pollResponse.json().catch(() => null);
if (!pollResponse.ok || !pollPayload?.data) {
logger.warn("aos_task_poll_failed", {
hasPayload: pollPayload != null,
status: pollResponse.status,
});
return {
success: false,
error: `AOS任务查询失败: ${pollPayload?.message || pollResponse.statusText}`,
@ -380,6 +408,10 @@ async function processWithAosSegmentation(
const errMessage = String(pollPayload.data.err_message || pollPayload.data.err_info || "");
if (state === 1 && imageUrl) {
logger.debug("aos_task_completed", {
pollCount,
taskId,
});
const imageResponse = await withTimeout(
(signal) =>
fetch(imageUrl, {
@ -390,6 +422,10 @@ async function processWithAosSegmentation(
);
if (!imageResponse.ok) {
logger.warn("aos_result_download_failed", {
status: imageResponse.status,
taskId,
});
return {
success: false,
error: `AOS结果下载失败: ${imageResponse.status} ${imageResponse.statusText}`,
@ -405,6 +441,11 @@ async function processWithAosSegmentation(
}
if (state < 0) {
logger.warn("aos_task_failed", {
hasErrorMessage: Boolean(errMessage),
state,
taskId,
});
return {
success: false,
error: `AOS任务失败: ${errMessage || stateDetail || pollPayload?.message || "状态异常"}`,
@ -412,11 +453,13 @@ async function processWithAosSegmentation(
}
}
logger.warn("aos_task_timeout", { maxPollCount });
return { success: false, error: "AOS任务超时(最多60秒)" };
} catch (error) {
if (error instanceof Error && error.name === "AbortError") {
return { success: false, error: "AOS请求超时,请稍后重试" };
}
logger.warn("aos_provider_request_error", error);
return { success: false, error: error instanceof Error ? error.message : "未知错误" };
}
}
@ -468,7 +511,7 @@ export async function fetchImageBufferFromUrl(imageUrl: string): Promise<Buffer>
if (error instanceof RmbgServiceError) {
throw error;
}
console.error("[RMBG] 获取图片失败:", error);
logger.warn("source_image_fetch_failed", error);
throw new RmbgServiceError("无法获取图片,请检查图片链接是否有效", 400);
}
}
@ -488,7 +531,14 @@ export async function executeRmbgJob(
} = params;
const { provider, config } = await getValidatedRmbgConfig();
console.log(`[RMBG] Using provider: ${provider}`);
logger.info("job_started", {
hasChatId: Boolean(chatId),
hasMessageId: Boolean(messageId),
hasSourceImageUrl: Boolean(sourceImageUrl),
inputBytes: imageBuffer.length,
provider,
skipGallerySave,
});
let resultBuffer: Buffer;
let resultBase64: string;
@ -497,6 +547,10 @@ export async function executeRmbgJob(
if (provider === "photoroom") {
const result = await processWithPhotoRoom(imageBuffer, config.photoroomApiKey!);
if (!result.success || !result.imageBuffer) {
logger.warn("job_provider_failed", {
provider,
reasonLength: result.error?.length ?? 0,
});
throw new RmbgServiceError("抠图处理失败", 500, result.error);
}
resultBuffer = result.imageBuffer;
@ -514,7 +568,16 @@ export async function executeRmbgJob(
if (!result.success || !result.imageBuffer) {
const detail = result.error || "AOS处理失败";
const friendlyMessage = getAosFriendlyErrorMessage(detail);
throw new RmbgServiceError("抠图处理失败", friendlyMessage ? 400 : 500, friendlyMessage || detail);
logger.warn("job_provider_failed", {
friendly: Boolean(friendlyMessage),
provider,
reasonLength: detail.length,
});
throw new RmbgServiceError(
"抠图处理失败",
friendlyMessage ? 400 : 500,
friendlyMessage || detail
);
}
resultBuffer = result.imageBuffer;
@ -527,6 +590,10 @@ export async function executeRmbgJob(
});
if (!result.success || !result.image_base64) {
logger.warn("job_provider_failed", {
provider,
reasonLength: result.error?.length ?? 0,
});
throw new RmbgServiceError("抠图处理失败", 500, result.error);
}
@ -575,7 +642,7 @@ export async function executeRmbgJob(
}
if (!saved) {
console.error("[RMBG] ❌ 保存到数据库失败:", saveError);
logger.error("gallery_save_failed", saveError);
throw new RmbgServiceError("图片处理成功但保存到相册失败,请稍后重试", 500);
}
@ -628,9 +695,17 @@ export async function executeRmbgJob(
}
}
} catch (assetError) {
console.error("[RMBG] ⚠️ MediaAsset 注册失败:", assetError);
logger.warn("media_asset_registration_failed", assetError);
}
logger.info("job_completed", {
generatedImageId: savedImage.id,
mediaAssetId,
processingSeconds: Number(processingTime.toFixed(2)),
provider,
savedToGallery: true,
});
return {
image_base64: resultBase64,
thumbnail_url: thumbnailUrl,
@ -642,9 +717,15 @@ export async function executeRmbgJob(
};
}
} else {
console.log("[RMBG] Skip gallery save by request flag");
logger.info("gallery_save_skipped", { provider });
}
logger.info("job_completed", {
processingSeconds: Number(processingTime.toFixed(2)),
provider,
savedToGallery: false,
});
return {
image_base64: resultBase64,
thumbnail_url: thumbnailUrl,

View File

@ -24,6 +24,7 @@ const files = [
"lib/ai/providers/gemini-image.ts",
"lib/ai/providers/volcano-vision.ts",
"lib/ai/providers/qianwen-video.ts",
"lib/ai/rmbg/service.ts",
"lib/ai/dynamic-provider.ts",
"services/canvas/index.ts",
"components/image-annotator.tsx",