chore: govern rmbg service logging
This commit is contained in:
parent
36f3de8321
commit
8e91c00c74
@ -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,
|
||||
|
||||
@ -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",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user