使用EventSource实现SSE实时日志展示,需服务端返回text/event-stream格式、每条data: JSON日志以双换行结尾;前端监听message事件解析渲染,利用内置重连与可选id断点续传,并优化DOM更新性能。

用 EventSource 接收服务端发送事件(SSE)来展示实时日志,核心是建立长连接、正确处理事件流、容错重连,并解析服务端推送的结构化日志数据。
服务端需按 SSE 格式输出日志流
SSE 要求响应头必须包含 Content-Type: text/event-stream,且每条消息以 data: 开头,用空行分隔。日志建议用 JSON 字符串,便于前端解析:
HTTP/1.1 200 OK Content-Type: text/event-stream Cache-Control: no-cache Connection: keep-alive
每次推送示例:
data: {"level":"INFO","timestamp":"2024-06-15T10:22:33Z","message":"User login succeeded","service":"auth-api"}
data: {"level":"ERROR","timestamp":"2024-06-15T10:22:35Z","message":"DB timeout","service":"order-service"}
- 每条日志必须以
data:开头,后跟一个空格和 JSON 字符串 - 末尾必须有两个换行(
\n\n),否则浏览器不会触发message事件 - 避免输出
id:或event:字段,除非需要自定义事件类型或断线续传
前端用 EventSource 建立连接并监听日志
创建 EventSource 实例,监听 message 事件,对每条日志做渲染或过滤:
立即学习“Java免费学习笔记(深入)”;
const es = new EventSource('/api/logs/stream');
es.onmessage = (event) => {
try {
const log = JSON.parse(event.data);
renderLogEntry(log); // 自定义渲染函数,如追加到 <pre class="brush:php;toolbar:false;"> 或 <div>
} catch (e) {
console.warn('Invalid log JSON:', event.data);
}
};
es.onerror = (err) => {
console.error('EventSource failed:', err);
// 连接异常时会自动重试(默认 3s),无需手动调用 es.close() + new
};
-
event.data是字符串,需JSON.parse()才能获取对象 - 不要在
onerror中主动close()并重建 ——EventSource内置重连机制,频繁手动干预反而导致重复连接 - 若需带认证,可用
new EventSource(url, { withCredentials: true })
支持断线续传(可选但推荐)
服务端在每次推送时带上 id: 字段(递增数字或时间戳),客户端断开重连时浏览器会自动在请求头带上 Last-Event-ID。服务端据此从断点继续推送:
id: 12345
data: {"level":"WARN","message":"High memory usage"}
id: 12346
data: {"level":"INFO","message":"Cache refreshed"}
- 服务端需识别
req.headers['last-event-id'](Node.js/Express 示例),并从该 ID 后续日志开始流式返回 - 前端无需额外代码,
EventSource自动处理 ID 传递与恢复 - 注意:ID 必须为纯数字或字符串,不能含空格或特殊字符
控制日志显示与性能优化
实时日志量大时,避免无限制追加 DOM 节点导致卡顿:
- 限制最大显示条数(如只保留最近 1000 条),旧日志用
logContainer.removeChild(logs[0])移除 - 对高频日志(如心跳、debug)添加前端过滤开关:
if (log.level !== 'DEBUG') renderLog(log) - 使用
requestIdleCallback或节流方式批量渲染,避免阻塞主线程 - 考虑用
<textarea readonly></textarea>替代<pre class="brush:php;toolbar:false;"></pre>,滚动性能更稳定


















