寻求适用于Next.js的同构日志方案,支持日志推送至服务器
Next.js 跨端通用日志方案实现
一、封装统一日志工具类
直接写一个通用日志工具,内部自动识别运行环境,让你不用手动区分服务端还是客户端,调用同一个API就能打日志。
1. 基础日志工具代码
在项目里新建utils/logger.js(TS项目用.ts),代码如下:
class Logger { constructor() { this.isServer = typeof window === 'undefined'; // 这里填你的后端日志接收接口地址 this.logEndpoint = '/api/logs'; } // 通用日志方法,支持debug/info/warn/error四种级别 log(level, message, meta = {}) { const logData = { level, message, meta: { ...meta, timestamp: new Date().toISOString(), env: process.env.NODE_ENV, isServer: this.isServer } }; // 服务端直接打控制台,也可以顺便推送到远程 if (this.isServer) { console[level](logData); this.sendToRemote(logData); return; } // 客户端先打控制台,生产环境再推送到远程 console[level](logData); if (process.env.NODE_ENV === 'production') { this.sendToRemote(logData); } } // 封装快捷方法,调用更方便 debug(message, meta) { this.log('debug', message, meta); } info(message, meta) { this.log('info', message, meta); } warn(message, meta) { this.log('warn', message, meta); } error(message, meta) { this.log('error', message, meta); } // 远程上报逻辑,用keepalive保证页面卸载时也能发出去 async sendToRemote(logData) { try { await fetch(this.logEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(logData), keepalive: true }); } catch (err) { // 上报失败别影响主业务,悄悄打个错就行 console.error('日志上报失败:', err); } } } // 单例模式,全局只用一个实例 const logger = new Logger(); export default logger;
二、服务端接收日志的API
需要写个后端接口接收客户端发过来的日志,这里用Next.js的API路由举例:
如果是Pages Router,新建pages/api/logs.js:
export default async function handler(req, res) { if (req.method === 'POST') { const logData = req.body; // 这里可以把日志存数据库、写文件,或者转发到ELK、Datadog这类专业日志服务 console.log('收到客户端日志:', logData); res.status(200).json({ success: true }); } else { res.status(405).json({ error: 'Method Not Allowed' }); } }
如果是App Router,新建app/api/logs/route.js:
export async function POST(request) { const logData = await request.json(); console.log('收到客户端日志:', logData); return new Response(JSON.stringify({ success: true }), { headers: { 'Content-Type': 'application/json' }, }); }
三、全局使用日志工具
不管是服务端组件、客户端组件还是API路由,直接导入就能用,完全不用管环境:
// 客户端组件示例 import logger from '@/utils/logger'; export default function ClientComponent() { const handleBtnClick = () => { logger.info('用户点击了提交按钮', { buttonId: 'submit-btn', userId: 'xxx' }); }; return <button onClick={handleBtnClick}>提交</button>; }
// 服务端组件示例 import logger from '@/utils/logger'; export default async function ServerComponent() { logger.debug('服务端组件开始渲染', { componentName: 'ServerComponent' }); // 业务逻辑... return <div>服务端渲染内容</div>; }
// API路由示例 import logger from '@/utils/logger'; export async function GET() { logger.warn('请求参数缺失', { apiPath: '/api/user', ip: req.headers['x-forwarded-for'] }); return new Response('参数错误', { status: 400 }); }
四、进阶优化点
- 日志级别控制:用环境变量设置生产环境只上报warn和error,避免日志爆炸
- 敏感信息过滤:在上报前自动过滤掉密码、token这类敏感数据
- 批量上报:客户端把日志缓存起来,攒够10条或者隔30秒再批量发,减少请求次数
- 专业日志库集成:服务端可以用winston、pino这类库,把日志写入文件或者转发到专业日志平台
内容的提问来源于stack exchange,提问作者Fixing It
相关产品推荐
相关产品推荐

