Strip ANSI sequences from container logs for clean display.
Release rolling / release (push) Successful in 8m28s

PM2 and similar tools emit VT100 color codes that browsers hide only
partially, leaving artifacts like [32m between table columns. Strip CSI/OSC
escapes when formatting log lines, and harden Docker multiplex demux on the
server so frames are not mis-parsed as text.
This commit is contained in:
Raven Scott
2026-07-13 18:53:17 -04:00
parent f9aa8d3c7e
commit b8bd4eb902
2 changed files with 77 additions and 6 deletions
+46 -4
View File
@@ -8,9 +8,12 @@ import logger from '../utils/logger.js'
/**
* Incremental demux of docker log multiplex protocol.
* Frame: 1 byte stream type (02) + 3 zero bytes + 4 byte BE size + payload.
*/
function createServerDemuxer() {
let pending = Buffer.alloc(0)
/** Once we see a valid mux frame, stay in mux mode for the stream. */
let muxMode = null // null | true | false
return {
/**
@@ -21,15 +24,54 @@ function createServerDemuxer() {
pending = Buffer.concat([pending, chunk])
const out = []
// Non-muxed (raw text) — if first byte isn't 0-2 and we have data
if (pending.length && pending[0] > 2) {
out.push(pending)
pending = Buffer.alloc(0)
if (muxMode === false) {
if (pending.length) {
out.push(pending)
pending = Buffer.alloc(0)
}
return out
}
// Detect mode from first full frame or non-mux text
if (muxMode === null && pending.length >= 1) {
const b0 = pending[0]
if (b0 > 2) {
muxMode = false
out.push(pending)
pending = Buffer.alloc(0)
return out
}
// Need header to confirm
if (pending.length >= 8) {
const size = pending.readUInt32BE(4)
// Plausible frame: type 0-2, zeros in 1-3, reasonable size
const zerosOk = pending[1] === 0 && pending[2] === 0 && pending[3] === 0
if (b0 <= 2 && zerosOk && size > 0 && size < 16 * 1024 * 1024) {
muxMode = true
} else if (b0 <= 2 && !zerosOk) {
// Not mux — treat as raw (e.g. TTY)
muxMode = false
out.push(pending)
pending = Buffer.alloc(0)
return out
}
}
}
if (muxMode !== true) {
// Wait for more bytes to decide, or emit if clearly not mux
return out
}
while (pending.length >= 8) {
const size = pending.readUInt32BE(4)
if (size < 0 || size > 16 * 1024 * 1024) {
// Corrupt header — fall back to raw remainder
out.push(pending)
pending = Buffer.alloc(0)
muxMode = false
break
}
if (pending.length < 8 + size) break
out.push(pending.subarray(8, 8 + size))
pending = pending.subarray(8 + size)