refactor(logging): centralize console output

This commit is contained in:
2026-06-18 13:34:19 +08:00
parent f600e11d55
commit 812a3e41b9
24 changed files with 261 additions and 166 deletions
@@ -19,6 +19,9 @@ import { SUBSCRIPTION_PLANS } from "@/data/constants/subscription-plans";
import type { SubscriptionPlanId } from "@/data/constants/subscription-plans";
import { getPlanPriceId } from "@/lib/stripe/stripe-products";
import { getStripeClient } from "@/lib/stripe/stripe-client";
import { Logger } from "@/utils";
const log = new Logger("AppApiPaymentCreateCheckoutSessionRoute");
interface CreateCheckoutRequestBody {
planId: SubscriptionPlanId;
@@ -86,7 +89,7 @@ export async function POST(req: NextRequest) {
return NextResponse.json({ url: session.url });
} catch (e) {
const err = e as { message?: string; type?: string; code?: string };
console.error("[api/create-checkout-session] Stripe error", {
log.error("[api/create-checkout-session] Stripe error", {
message: err.message,
type: err.type,
code: err.code,
+4 -1
View File
@@ -14,6 +14,9 @@
import { NextRequest, NextResponse } from "next/server";
import { getStripeClient } from "@/lib/stripe/stripe-client";
import { Logger } from "@/utils";
const log = new Logger("AppApiPaymentCustomerPortalRoute");
interface CreatePortalRequestBody {
customerId: string | null;
@@ -56,7 +59,7 @@ export async function POST(req: NextRequest) {
return NextResponse.json({ url: session.url });
} catch (e) {
const err = e as { message?: string; type?: string; code?: string };
console.error("[api/customer-portal] Stripe error", {
log.error("[api/customer-portal] Stripe error", {
customerId: body.customerId,
message: err.message,
type: err.type,
+9 -6
View File
@@ -27,11 +27,14 @@ import type Stripe from "stripe";
import { dispatchStripeEvent } from "@/lib/stripe/stripe-events";
import { getStripeClient } from "@/lib/stripe/stripe-client";
import { Logger } from "@/utils";
const log = new Logger("AppApiPaymentWebhookRoute");
export async function POST(req: NextRequest) {
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
if (!webhookSecret) {
console.error("[api/webhook] STRIPE_WEBHOOK_SECRET is not set");
log.error("[api/webhook] STRIPE_WEBHOOK_SECRET is not set");
return NextResponse.json(
{ error: "Webhook secret not configured" },
{ status: 500 },
@@ -42,7 +45,7 @@ export async function POST(req: NextRequest) {
const rawBody = await req.text();
const sigHeader = req.headers.get("stripe-signature");
if (!sigHeader) {
console.error("[api/webhook] Missing stripe-signature header");
log.error("[api/webhook] Missing stripe-signature header");
return NextResponse.json(
{ error: "Missing stripe-signature header" },
{ status: 400 },
@@ -56,7 +59,7 @@ export async function POST(req: NextRequest) {
event = stripe.webhooks.constructEvent(rawBody, sigHeader, webhookSecret);
} catch (e) {
const err = e as Error;
console.error("[api/webhook] Signature verification failed", {
log.error("[api/webhook] Signature verification failed", {
message: err.message,
});
return NextResponse.json(
@@ -66,7 +69,7 @@ export async function POST(req: NextRequest) {
}
// 分发到 handler
console.log("[api/webhook] Received event", {
log.debug("[api/webhook] Received event", {
eventId: event.id,
type: event.type,
created: new Date(event.created * 1000).toISOString(),
@@ -75,11 +78,11 @@ export async function POST(req: NextRequest) {
try {
const handled = await dispatchStripeEvent(event);
if (!handled) {
console.log(`[api/webhook] Ignored event type: ${event.type}`);
log.debug(`[api/webhook] Ignored event type: ${event.type}`);
}
} catch (e) {
const err = e as Error;
console.error("[api/webhook] Handler error", {
log.error("[api/webhook] Handler error", {
eventId: event.id,
type: event.type,
message: err.message,
+5 -2
View File
@@ -13,6 +13,9 @@ import { ROUTES } from "@/router/routes";
import { AuthBackground, AuthPanel } from "./components";
import styles from "./components/auth-screen.module.css";
import { Logger } from "@/utils";
const log = new Logger("AppAuthAuthScreen");
export function AuthScreen() {
const state = useAuthState();
@@ -23,7 +26,7 @@ export function AuthScreen() {
// 用 pendingRedirect flag(不是 useRef transition detection)——
// re-mount 时 flag 在 auth context 里持久,区分"刚按了"和"re-visit"准确无误。
useEffect(() => {
console.log("[auth-screen] useEffect (loginStatus + pendingRedirect)", {
log.debug("[auth-screen] useEffect (loginStatus + pendingRedirect)", {
pending: state.pendingRedirect,
loginStatus: state.loginStatus,
willRedirect:
@@ -31,7 +34,7 @@ export function AuthScreen() {
});
if (state.pendingRedirect && state.loginStatus !== "notLoggedIn") {
console.log(
log.debug(
"[auth-screen] useEffect → router.replace(/chat) [via pendingRedirect flag]",
);
authDispatch({ type: "AuthClearPendingRedirect" });
+6 -3
View File
@@ -14,6 +14,9 @@ import { AuthTextField } from "./auth-text-field";
import { AuthPrimaryButton } from "./auth-primary-button";
import { AuthErrorMessage } from "./auth-error-message";
import styles from "./email-form.module.css";
import { Logger } from "@/utils";
const log = new Logger("AppAuthComponentsEmailLoginForm");
export interface EmailLoginFormProps {
globalError?: string | null;
@@ -44,7 +47,7 @@ export function EmailLoginForm({
};
const submit = () => {
console.log("[email-login-form] submit ENTRY", {
log.debug("[email-login-form] submit ENTRY", {
emailLength: email.length,
emailPreview: email.slice(0, 8),
passwordLength: password.length,
@@ -53,7 +56,7 @@ export function EmailLoginForm({
const passwordErr = validatePassword(password);
if (emailErr || passwordErr) {
const firstErr = emailErr ?? passwordErr ?? null;
console.warn(
log.warn(
"[email-login-form] submit VALIDATION FAILED (silent return)",
{ emailErr, passwordErr, surfaced: firstErr },
);
@@ -61,7 +64,7 @@ export function EmailLoginForm({
return;
}
setValidationError(null);
console.log("[email-login-form] submit dispatching AuthEmailLoginSubmitted");
log.debug("[email-login-form] submit dispatching AuthEmailLoginSubmitted");
dispatch({ type: "AuthEmailLoginSubmitted", email, password });
};
@@ -25,6 +25,9 @@ import { AuthTextField } from "./auth-text-field";
import { AuthPrimaryButton } from "./auth-primary-button";
import { AuthErrorMessage } from "./auth-error-message";
import styles from "./email-form.module.css";
import { Logger } from "@/utils";
const log = new Logger("AppAuthComponentsEmailRegisterForm");
export interface EmailRegisterFormProps {
globalError?: string | null;
@@ -66,7 +69,7 @@ export function EmailRegisterForm({
};
const submit = () => {
console.log("[email-register-form] submit ENTRY", {
log.debug("[email-register-form] submit ENTRY", {
emailLength: email.length,
emailPreview: email.slice(0, 8),
passwordLength: password.length,
@@ -81,7 +84,7 @@ export function EmailRegisterForm({
// 按 username → email → password → confirm 顺序挑第一个错(与字段顺序一致)
const firstErr =
usernameErr ?? emailErr ?? passwordErr ?? confirmErr ?? null;
console.warn(
log.warn(
"[email-register-form] submit VALIDATION FAILED (silent return)",
{
emailErr,
@@ -95,7 +98,7 @@ export function EmailRegisterForm({
return;
}
setValidationError(null);
console.log(
log.debug(
"[email-register-form] submit dispatching AuthEmailRegisterSubmitted",
);
dispatch({
+4 -1
View File
@@ -27,6 +27,9 @@ import { useChatDispatch } from "@/stores/chat/chat-context";
import { ChatInputTextField } from "./chat-input-text-field";
import { ChatSendButton } from "./chat-send-button";
import styles from "./chat-input-bar.module.css";
import { Logger } from "@/utils";
const log = new Logger("AppChatComponentsChatInputBar");
export interface ChatInputBarProps {
disabled?: boolean;
@@ -42,7 +45,7 @@ export function ChatInputBar({ disabled = false }: ChatInputBarProps) {
const handleSend = () => {
if (!hasContent) return;
console.log("[chat-input-bar] handleSend", {
log.debug("[chat-input-bar] handleSend", {
contentLength: input.length,
contentPreview: input.slice(0, 50),
hasContent,
@@ -16,6 +16,9 @@ import { useRouter } from "next/navigation";
import { AuthStorage } from "@/data/storage/auth/auth_storage";
import { UserStorage } from "@/data/storage/user/user_storage";
import { ROUTES } from "@/router/routes";
import { Logger } from "@/utils";
const log = new Logger("AppChatDeviceidDeviceIdDeepLinkPersist");
interface DeepLinkPersistProps {
deviceId: string;
@@ -43,7 +46,7 @@ export default function DeepLinkPersist({
}
} catch (err) {
// 写不进 localStorage 时(例如隐私模式)也不阻塞跳转;记录到 console 即可。
console.warn("[DeepLinkPersist] failed to persist", err);
log.warn("[DeepLinkPersist] failed to persist", err);
} finally {
setDone(true);
router.replace(ROUTES.chat);
+4 -1
View File
@@ -14,6 +14,9 @@ import { useEffect } from "react";
import Link from "next/link";
import { ROUTES } from "@/router/routes";
import { Logger } from "@/utils";
const log = new Logger("AppError");
interface ErrorProps {
error: Error & { digest?: string };
@@ -23,7 +26,7 @@ interface ErrorProps {
export default function GlobalError({ error, unstable_retry }: ErrorProps) {
useEffect(() => {
// 把错误送到 console;后续接 Sentry/Datadog 时改这里。
console.error("[app/error.tsx]", error);
log.error("[app/error.tsx]", error);
}, [error]);
return (
+5 -3
View File
@@ -7,7 +7,7 @@ import { MobileShell } from "@/app/_components/core";
import { useAuthDispatch, useAuthState } from "@/stores/auth/auth-context";
import { AuthStorage } from "@/data/storage/auth/auth_storage";
import { ROUTES } from "@/router/routes";
import { pwaUtil } from "@/utils";
import { pwaUtil, Logger } from "@/utils";
import {
SplashBackground,
@@ -17,6 +17,8 @@ import {
} from "./components";
import styles from "./components/splash-screen.module.css";
const log = new Logger("AppSplashSplashScreen");
// 尽量早地缓存 beforeinstallprompt,避免用户停留 splash 时浏览器事件已触发并丢失。
if (typeof window !== "undefined") {
pwaUtil.prepareInstallPrompt();
@@ -67,7 +69,7 @@ export function SplashScreen() {
// ─────────────────────────────────────────────────────────────
useEffect(() => {
// 调试日志(保留,不删 —— 用于排查"re-mount 误跳 /chat"问题)
console.log("[splash] useEffect ② (loginStatus + pendingRedirect)", {
log.debug("[splash] useEffect ② (loginStatus + pendingRedirect)", {
pending: state.pendingRedirect,
loginStatus: state.loginStatus,
willRedirect:
@@ -75,7 +77,7 @@ export function SplashScreen() {
});
if (state.pendingRedirect && state.loginStatus !== "notLoggedIn") {
console.log(
log.debug(
"[splash] useEffect ② → router.replace(/chat) [via pendingRedirect flag]",
);
authDispatch({ type: "AuthClearPendingRedirect" });
@@ -18,6 +18,9 @@ import { ROUTES } from "@/router/routes";
import { SubscriptionCtaButton } from "./subscription-cta-button";
import styles from "./subscription-cta-button.module.css";
import { Logger } from "@/utils";
const log = new Logger("AppSubscriptionComponentsSubscriptionCheckoutButton");
export interface SubscriptionCheckoutButtonProps {
planId: SubscriptionPlanId;
@@ -64,7 +67,7 @@ export function SubscriptionCheckoutButton({
window.location.href = url;
} catch (e) {
const err = e as Error;
console.error("[subscription-checkout-button] error", err);
log.error("[subscription-checkout-button] error", err);
setError(err.message);
setIsLoading(false);
}
@@ -15,6 +15,9 @@ import { ROUTES } from "@/router/routes";
import { SubscriptionCtaButton } from "./subscription-cta-button";
import styles from "./subscription-cta-button.module.css";
import { Logger } from "@/utils";
const log = new Logger("AppSubscriptionComponentsSubscriptionManageButton");
export interface SubscriptionManageButtonProps {
/** 从 User.stripeCustomerId 取(webhook 设置) */
@@ -63,7 +66,7 @@ export function SubscriptionManageButton({ customerId }: SubscriptionManageButto
window.location.href = url;
} catch (e) {
const err = e as Error;
console.error("[subscription-manage-button] error", err);
log.error("[subscription-manage-button] error", err);
setError(err.message);
setIsLoading(false);
}