Strip ANSI sequences from container logs for clean display.
Release rolling / release (push) Successful in 8m28s
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:
+46
-4
@@ -8,9 +8,12 @@ import logger from '../utils/logger.js'
|
||||
|
||||
/**
|
||||
* Incremental demux of docker log multiplex protocol.
|
||||
* Frame: 1 byte stream type (0–2) + 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)
|
||||
|
||||
Reference in New Issue
Block a user