在Next.js中使用@elastic/elasticsearch收集日志时遇node:console错误
错误详情
⨯ node:console
Module build failed: UnhandledSchemeError: Reading from "node:console" is not handled by plugins (Unhandled scheme).
Webpack supports "data:" and "file:" URIs by default.
You may need an additional plugin to handle "node:" URIs.
Import trace for requested module:
node:console
./node_modules/undici/lib/mock/pending-interceptors-formatter.js
./node_modules/undici/lib/mock/mock-agent.js
./node_modules/undici/index.js
./node_modules/@elastic/transport/lib/connection/UndiciConnection.js
./node_modules/@elastic/transport/lib/connection/index.js
./node_modules/@elastic/transport/index.js
./node_modules/@elastic/elasticsearch/index.js
./src/lib/logger.ts
相关代码
src/lib/logger.ts
import { Client } from "@elastic/elasticsearch"; const client = new Client({ node: "http://localhost:9200/", }); export default class Logger { static async log(index: string, body: object) { await client.index({ index, body }); } }
src/middleware.ts
import { NextRequest } from "next/server"; import Logger from "@/lib/logger"; export async function middleware(request: NextRequest) { // ... Logger.log("cb-request-logs", { user: session.user._id, url: request.url, userAgent: request.headers.get("User-Agent"), }); // ... }
环境信息
- Node.js v20
- Next.js v14.2.7
- @elastic/elasticsearch ^8.15.0
解决方法
错误根源是Next.js的Middleware默认运行在边缘运行时(Edge Runtime),而@elastic/elasticsearch客户端依赖Node.js专属内置模块(如node:console),边缘运行时不支持这类模块,同时Webpack无法解析node:协议的导入路径。提供三种可行方案:
方案1:将日志逻辑转移到API路由
把Elasticsearch调用放到Node.js环境的API路由中,Middleware通过请求该API发送日志:
- 创建
src/app/api/log/route.ts:
import { Client } from "@elastic/elasticsearch"; import { NextRequest, NextResponse } from "next/server"; const client = new Client({ node: "http://localhost:9200/" }); export async function POST(request: NextRequest) { const { index, body } = await request.json(); try { await client.index({ index, body }); return NextResponse.json({ success: true }); } catch (err) { return NextResponse.json({ success: false, error: (err as Error).message }, { status: 500 }); } }
- 修改Middleware中的日志调用逻辑:
import { NextRequest } from "next/server"; export async function middleware(request: NextRequest) { // ... 其他逻辑 await fetch(new URL("/api/log", request.url), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ index: "cb-request-logs", body: { user: session.user._id, url: request.url, userAgent: request.headers.get("User-Agent"), }, }), credentials: "same-origin", }); // ... 其他逻辑 }
方案2:让Middleware使用Node.js运行时
强制Middleware切换到Node.js运行时,直接兼容Elasticsearch客户端:
- 在
middleware.ts顶部添加运行时声明:
export const runtime = "nodejs"; import { NextRequest } from "next/server"; import Logger from "@/lib/logger"; export async function middleware(request: NextRequest) { // ... 原有逻辑保持不变 }
- 若仍有Webpack报错,配置
next.config.js处理node:协议:
/** @type {import('next').NextConfig} */ const nextConfig = { webpack: (config) => { // 忽略node:相关模块的解析 config.resolve.fallback = { ...config.resolve.fallback, "node:console": false }; config.module.rules.push({ test: /node:.+/, use: "ignore-loader", }); return config; }, }; module.exports = nextConfig;
执行安装依赖命令:
npm install ignore-loader --save-dev
方案3:直接调用Elasticsearch REST API
不依赖官方客户端,用原生fetch调用Elasticsearch的REST接口,完全兼容边缘运行时:
修改src/lib/logger.ts:
export default class Logger { static async log(index: string, body: object) { await fetch(`http://localhost:9200/${index}/_doc`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); } }
内容的提问来源于stack exchange,提问作者그저 사람

