feat(server): 新增统一 logger 入口与 /health liveness 端点

- src/server/logger.ts 包一层 console.*,给后续 pino/otel 迁移留单点
- interceptors.ts 的 logError 改走 logger.error,业务侧禁止直接 console.*
- /health 返回 'ok',纯 liveness(不查 DB),DB 挂时探活仍绿
This commit is contained in:
2026-04-25 13:31:25 +08:00
parent 830c908712
commit 8f7744ca0d
4 changed files with 38 additions and 5 deletions
+21 -3
View File
@@ -9,10 +9,16 @@
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
import { Route as rootRouteImport } from './routes/__root'
import { Route as HealthRouteImport } from './routes/health'
import { Route as IndexRouteImport } from './routes/index'
import { Route as ApiSplatRouteImport } from './routes/api/$'
import { Route as ApiRpcSplatRouteImport } from './routes/api/rpc.$'
const HealthRoute = HealthRouteImport.update({
id: '/health',
path: '/health',
getParentRoute: () => rootRouteImport,
} as any)
const IndexRoute = IndexRouteImport.update({
id: '/',
path: '/',
@@ -31,36 +37,47 @@ const ApiRpcSplatRoute = ApiRpcSplatRouteImport.update({
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/health': typeof HealthRoute
'/api/$': typeof ApiSplatRoute
'/api/rpc/$': typeof ApiRpcSplatRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/health': typeof HealthRoute
'/api/$': typeof ApiSplatRoute
'/api/rpc/$': typeof ApiRpcSplatRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/health': typeof HealthRoute
'/api/$': typeof ApiSplatRoute
'/api/rpc/$': typeof ApiRpcSplatRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/api/$' | '/api/rpc/$'
fullPaths: '/' | '/health' | '/api/$' | '/api/rpc/$'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/api/$' | '/api/rpc/$'
id: '__root__' | '/' | '/api/$' | '/api/rpc/$'
to: '/' | '/health' | '/api/$' | '/api/rpc/$'
id: '__root__' | '/' | '/health' | '/api/$' | '/api/rpc/$'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
HealthRoute: typeof HealthRoute
ApiSplatRoute: typeof ApiSplatRoute
ApiRpcSplatRoute: typeof ApiRpcSplatRoute
}
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/health': {
id: '/health'
path: '/health'
fullPath: '/health'
preLoaderRoute: typeof HealthRouteImport
parentRoute: typeof rootRouteImport
}
'/': {
id: '/'
path: '/'
@@ -87,6 +104,7 @@ declare module '@tanstack/react-router' {
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
HealthRoute: HealthRoute,
ApiSplatRoute: ApiSplatRoute,
ApiRpcSplatRoute: ApiRpcSplatRoute,
}
+9
View File
@@ -0,0 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/health')({
server: {
handlers: {
GET: () => new Response('ok', { status: 200, headers: { 'content-type': 'text/plain' } }),
},
},
})
+3 -2
View File
@@ -1,13 +1,14 @@
import { ORPCError, ValidationError } from '@orpc/server'
import { z } from 'zod'
import { logger } from '@/server/logger'
export const logError = (error: unknown) => {
console.error(error)
logger.error(error)
}
export const handleValidationError = (error: unknown) => {
if (error instanceof ORPCError && error.code === 'BAD_REQUEST' && error.cause instanceof ValidationError) {
// If you only use Zod you can safely cast to ZodIssue[] (per ORPC official docs)
// ORPC ValidationError.issues are Zod issues in this app.
const zodError = new z.ZodError(error.cause.issues as z.core.$ZodIssue[])
throw new ORPCError('INPUT_VALIDATION_FAILED', {
+5
View File
@@ -0,0 +1,5 @@
export const logger = {
error: (error: unknown) => console.error(error),
warn: (...args: unknown[]) => console.warn(...args),
info: (...args: unknown[]) => console.info(...args),
}