refactor(logging): centralize console output
This commit is contained in:
@@ -6,11 +6,9 @@
|
||||
*/
|
||||
import type { FetchHook } from "ofetch";
|
||||
|
||||
const TAG = "[API]";
|
||||
import { AppEnvUtil, Logger } from "@/utils";
|
||||
|
||||
function isDev(): boolean {
|
||||
return process.env.NODE_ENV !== "production";
|
||||
}
|
||||
const log = new Logger("ApiLoggingInterceptor");
|
||||
|
||||
function getRequestUrl(request: Request | string): string {
|
||||
return typeof request === "string" ? request : request.url;
|
||||
@@ -23,54 +21,32 @@ function getRequestMethod(
|
||||
return request instanceof Request ? request.method : options.method ?? "GET";
|
||||
}
|
||||
|
||||
function formatBody(body: unknown): string {
|
||||
if (body === undefined || body === null) return "";
|
||||
try {
|
||||
if (body instanceof FormData) {
|
||||
const entries: string[] = [];
|
||||
body.forEach((value, key) => {
|
||||
if (value instanceof File) {
|
||||
entries.push(`${key}=File(${value.name})`);
|
||||
} else {
|
||||
entries.push(`${key}=${String(value)}`);
|
||||
}
|
||||
});
|
||||
return `FormData{${entries.join(", ")}}`;
|
||||
}
|
||||
return JSON.stringify(body);
|
||||
} catch {
|
||||
return String(body);
|
||||
}
|
||||
}
|
||||
|
||||
export const onRequestLogging: FetchHook = async (ctx) => {
|
||||
if (!isDev()) return;
|
||||
if (AppEnvUtil.isProduction()) return;
|
||||
const method = getRequestMethod(ctx.request, ctx.options);
|
||||
const url = getRequestUrl(ctx.request);
|
||||
console.log(
|
||||
`${TAG} → ${method} ${url}`,
|
||||
ctx.options.body !== undefined ? formatBody(ctx.options.body) : ""
|
||||
);
|
||||
const body = Logger.formatValue(ctx.options.body);
|
||||
log.debug(`→ ${method} ${url}${body ? `\nrequest body:\n${body}` : ""}`);
|
||||
};
|
||||
|
||||
export const onResponseLogging: FetchHook = async (ctx) => {
|
||||
if (!isDev() || !ctx.response) return;
|
||||
if (AppEnvUtil.isProduction() || !ctx.response) return;
|
||||
const method = getRequestMethod(ctx.request, ctx.options);
|
||||
const url = getRequestUrl(ctx.request);
|
||||
console.log(
|
||||
`${TAG} ← ${ctx.response.status} ${method} ${url}`,
|
||||
formatBody(ctx.response._data)
|
||||
const body = Logger.formatValue(ctx.response._data);
|
||||
log.debug(
|
||||
`← ${ctx.response.status} ${method} ${url}${body ? `\nresponse body:\n${body}` : ""}`,
|
||||
);
|
||||
};
|
||||
|
||||
export const onErrorLogging: FetchHook = async (ctx) => {
|
||||
if (!isDev()) return;
|
||||
if (AppEnvUtil.isProduction()) return;
|
||||
const status = ctx.response?.status ?? "N/A";
|
||||
const method = getRequestMethod(ctx.request, ctx.options);
|
||||
const url = getRequestUrl(ctx.request);
|
||||
console.error(
|
||||
`${TAG} ✕ ${status} ${method} ${url}`,
|
||||
ctx.error?.message ?? "Unknown error"
|
||||
const body = Logger.formatValue(ctx.response?._data);
|
||||
log.error(
|
||||
`✕ ${status} ${method} ${url}\nerror: ${ctx.error?.message ?? "Unknown error"}${body ? `\nresponse body:\n${body}` : ""}`,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -6,9 +6,12 @@
|
||||
* 原始 Dart: lib/core/net/interceptor/token_interceptor.dart + token_paths.dart
|
||||
*/
|
||||
import type { FetchHook } from "ofetch";
|
||||
import { Logger } from "@/utils";
|
||||
import { ApiPath } from "../../../data/services/api/api_path";
|
||||
import { AuthStorage } from "../../../data/storage/auth/auth_storage";
|
||||
|
||||
const log = new Logger("TokenInterceptor");
|
||||
|
||||
/**
|
||||
* 不需要 token 的路径
|
||||
*/
|
||||
@@ -47,41 +50,41 @@ async function getAuthToken(): Promise<string | null> {
|
||||
const storage = AuthStorage.getInstance();
|
||||
const loginR = await storage.getLoginToken();
|
||||
if (loginR.success && loginR.data && loginR.data.length > 0) {
|
||||
console.log("[token-interceptor] getAuthToken: using LOGIN token", {
|
||||
log.debug({
|
||||
length: loginR.data.length,
|
||||
prefix: loginR.data.slice(0, 8),
|
||||
});
|
||||
}, "getAuthToken: using LOGIN token");
|
||||
return loginR.data;
|
||||
}
|
||||
console.log("[token-interceptor] getAuthToken: no login token", {
|
||||
log.debug({
|
||||
success: loginR.success,
|
||||
hasData: loginR.success ? !!loginR.data : false,
|
||||
});
|
||||
}, "getAuthToken: no login token");
|
||||
|
||||
const guestR = await storage.getGuestToken();
|
||||
if (guestR.success && guestR.data && guestR.data.length > 0) {
|
||||
console.log(
|
||||
"[token-interceptor] getAuthToken: using GUEST token (login priority, no login available)",
|
||||
log.debug(
|
||||
{
|
||||
length: guestR.data.length,
|
||||
prefix: guestR.data.slice(0, 8),
|
||||
},
|
||||
"getAuthToken: using GUEST token (login priority, no login available)",
|
||||
);
|
||||
return guestR.data;
|
||||
}
|
||||
console.warn("[token-interceptor] getAuthToken: NO token available (will skip Authorization)", {
|
||||
log.warn({
|
||||
loginAttempted: true,
|
||||
guestAttempted: true,
|
||||
loginSuccess: loginR.success,
|
||||
guestSuccess: guestR.success,
|
||||
});
|
||||
}, "getAuthToken: NO token available (will skip Authorization)");
|
||||
return null;
|
||||
}
|
||||
|
||||
export const onRequestToken: FetchHook = async (ctx) => {
|
||||
const path = getRequestPath(ctx.request);
|
||||
if (!needsToken(path)) {
|
||||
console.log("[token-interceptor] onRequestToken SKIP (no-token path)", { path });
|
||||
log.debug({ path }, "onRequestToken SKIP (no-token path)");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -91,13 +94,13 @@ export const onRequestToken: FetchHook = async (ctx) => {
|
||||
const headers = new Headers(ctx.options.headers);
|
||||
headers.set("Authorization", `Bearer ${token}`);
|
||||
ctx.options.headers = headers;
|
||||
console.log("[token-interceptor] onRequestToken SET Authorization header (via ctx.options.headers)", {
|
||||
log.debug({
|
||||
path,
|
||||
length: token.length,
|
||||
prefix: token.slice(0, 8),
|
||||
});
|
||||
}, "onRequestToken SET Authorization header (via ctx.options.headers)");
|
||||
} else {
|
||||
console.warn("[token-interceptor] onRequestToken NO token to set", { path });
|
||||
log.warn({ path }, "onRequestToken NO token to set");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -8,8 +8,12 @@
|
||||
* - 消费者可声明每条消息是否成功消费;不成功则重新入队
|
||||
* - 队列可在任意时刻清空(dispose)
|
||||
*/
|
||||
import { Logger } from "@/utils";
|
||||
|
||||
export type MessageConsumer = (content: string) => boolean | Promise<boolean>;
|
||||
|
||||
const log = new Logger("MessageQueue");
|
||||
|
||||
export class MessageQueue {
|
||||
private queue: string[] = [];
|
||||
private timer: ReturnType<typeof setTimeout> | null = null;
|
||||
@@ -38,7 +42,7 @@ export class MessageQueue {
|
||||
try {
|
||||
ok = await this.consumer(next);
|
||||
} catch (e) {
|
||||
console.error("[MessageQueue] consumer error", e);
|
||||
log.error({ err: e }, "consumer error");
|
||||
ok = false;
|
||||
}
|
||||
if (!ok) {
|
||||
|
||||
Reference in New Issue
Block a user