如何让NextJS输出访问日志?是否有对应开启配置?
如何让Next.js输出Apache风格的访问日志?
默认情况下,Next.js 使用 yarn start 启动服务时不会输出访问日志,仅会提示服务就绪:
yarn run v1.22.19 $ next start ready - started server on 0.0.0.0:3000, url: http://localhost:3000
Next.js 没有直接的配置项可以一键开启访问日志,但可以通过以下两种方式实现类似Apache的access.log风格日志输出:
方法一:自定义服务器 + Morgan日志中间件
这是最简便的方案,借助Express和Morgan(专门生成HTTP日志的工具)快速生成Apache格式的日志。
- 安装依赖:
yarn add express morgan # 或 npm install express morgan
- 在项目根目录创建
server.js文件:
const express = require('express'); const next = require('next'); const morgan = require('morgan'); const fs = require('fs'); const path = require('path'); const dev = process.env.NODE_ENV !== 'production'; const app = next({ dev }); const handle = app.getRequestHandler(); // 创建日志写入流(可选,若只需控制台输出可跳过) const accessLogStream = fs.createWriteStream( path.join(__dirname, 'access.log'), { flags: 'a' } // 追加模式写入 ); app.prepare().then(() => { const server = express(); // 配置Morgan输出Apache Combined格式日志 // 若只需控制台输出,去掉{ stream: accessLogStream }参数即可 server.use(morgan('combined', { stream: accessLogStream })); // 接管所有Next.js请求 server.all('*', (req, res) => { return handle(req, res); }); server.listen(3000, (err) => { if (err) throw err; console.log('> Ready on http://localhost:3000'); }); });
- 修改
package.json的启动脚本:
"scripts": { "dev": "node server.js", "build": "next build", "start": "NODE_ENV=production node server.js" }
启动服务后,控制台或access.log文件中就会生成类似Apache的日志:
192.168.2.20 - - [28/Jul/2006:10:27:10 -0300] "GET /cgi-bin/try/ HTTP/1.0" 200 3395 127.0.0.1 - - [28/Jul/2006:10:22:04 -0300] "GET / HTTP/1.0" 200 2216
方法二:使用Next.js中间件手动记录日志
如果不想搭建自定义服务器,可以通过Next.js的内置中间件手动构造日志内容。
在项目根目录创建middleware.js(或middleware.ts)文件:
import { NextResponse } from 'next/server'; import fs from 'fs/promises'; import path from 'path'; export async function middleware(request) { // 构造Apache风格的日志字段 const clientIp = request.ip || request.headers.get('x-forwarded-for') || 'unknown'; const dateStr = new Date().toLocaleString('en-US', { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit', timeZoneName: 'short' }).replace(', ', ':').replace(' ', '/'); const method = request.method; const url = request.nextUrl.pathname; const protocol = request.nextUrl.protocol.replace(':', ''); const status = 200; // 若需获取实际响应状态,需额外处理响应对象 const contentLength = '-'; // 实际内容长度需通过响应拦截获取 const logLine = `${clientIp} - - [${dateStr}] "${method} ${url} HTTP/${protocol}" ${status} ${contentLength}\n`; // 输出到控制台 console.log(logLine.trim()); // 写入日志文件(可选) await fs.appendFile(path.join(process.cwd(), 'access.log'), logLine); return NextResponse.next(); } // 指定中间件生效的路径(这里匹配所有路径) export const config = { matcher: ['/:path*'], };
这种方案无需额外安装依赖,但要获取准确的响应状态码和内容长度,需要对响应进行拦截包装,相对复杂一些,适合轻量日志需求场景。
内容的提问来源于stack exchange,提问作者Diogo Melo
相关产品推荐
相关产品推荐

