Skip to content

Commit e6ecc11

Browse files
committed
Strip bevy tracing prefixes from preview logs
The timestamp/level/target prefix on every engine line buries the scene's own logs, and the periodic [headless] alive heartbeat adds steady noise. Pipe the bevy child's output through a line filter that drops both while keeping WARN/ERROR markers; hammurabi output is untouched.
1 parent 0ad6164 commit e6ecc11

1 file changed

Lines changed: 44 additions & 3 deletions

File tree

packages/@dcl/sdk-commands/src/commands/start/multiplayer-server.ts

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { spawn, ChildProcess } from 'child_process'
1+
import { spawn, ChildProcess, StdioOptions } from 'child_process'
2+
import { Readable } from 'stream'
23
import { CliComponents } from '../../components'
34
import { printProgressInfo, printWarning } from '../../logic/beautiful-logs'
45
import { colors } from '../../components/log'
@@ -52,6 +53,38 @@ function registerProcessCleanup(cleanup: () => void): () => void {
5253
}
5354
}
5455

56+
// `2026-08-11T14:28:33.522058Z INFO scene_runner::renderer_context: ` — the
57+
// timestamp/level/target prefix the bevy engine's tracing puts on every line
58+
const TRACING_PREFIX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z\s+(INFO|WARN|ERROR|DEBUG|TRACE)\s+[\w:]+:\s?/
59+
const HEARTBEAT_LINE = /^\[headless\] alive:/
60+
61+
/**
62+
* Forwards a bevy child stream line by line, dropping the tracing prefix (keeping
63+
* WARN/ERROR markers) and the periodic `[headless] alive:` heartbeat.
64+
*/
65+
function forwardEngineLogs(source: Readable | null, sink: NodeJS.WriteStream) {
66+
if (!source) return
67+
let pending = ''
68+
source.setEncoding('utf8')
69+
source.on('data', (chunk: string) => {
70+
const lines = (pending + chunk).split('\n')
71+
pending = lines.pop() ?? ''
72+
for (const line of lines) {
73+
if (HEARTBEAT_LINE.test(line)) continue
74+
const match = line.match(TRACING_PREFIX)
75+
if (!match) {
76+
sink.write(line + '\n')
77+
} else {
78+
const message = line.slice(match[0].length)
79+
sink.write((match[1] === 'INFO' ? message : `${match[1]} ${message}`) + '\n')
80+
}
81+
}
82+
})
83+
source.on('end', () => {
84+
if (pending && !HEARTBEAT_LINE.test(pending)) sink.write(pending + '\n')
85+
})
86+
}
87+
5588
/**
5689
* Starts the Multiplayer Server process using npx to install and run in one step
5790
*/
@@ -84,11 +117,19 @@ export function startMultiplayerServer(
84117
env.RUST_LOG = 'warn,scene_runner::renderer_context=info'
85118
}
86119

120+
// Bevy output goes through the line filter below; hammurabi keeps the terminal directly.
121+
const stdio: StdioOptions = engine === 'bevy' ? ['inherit', 'pipe', 'pipe'] : 'inherit'
122+
87123
// If npx-cli.js was found, run it directly via process.execPath (node in regular env,
88124
// Electron Helper with ELECTRON_RUN_AS_NODE=1 in Electron). Otherwise fall back to npx binary.
89125
const serverProcess = npxCliJs
90-
? spawn(process.execPath, [npxCliJs, ...npxArgs], { cwd: workingDir, shell: false, stdio: 'inherit', env })
91-
: spawn(getNpxBin(), npxArgs, { cwd: workingDir, shell: false, stdio: 'inherit', env })
126+
? spawn(process.execPath, [npxCliJs, ...npxArgs], { cwd: workingDir, shell: false, stdio, env })
127+
: spawn(getNpxBin(), npxArgs, { cwd: workingDir, shell: false, stdio, env })
128+
129+
if (engine === 'bevy') {
130+
forwardEngineLogs(serverProcess.stdout, process.stdout)
131+
forwardEngineLogs(serverProcess.stderr, process.stderr)
132+
}
92133

93134
serverProcess.on('error', (error) => {
94135
printWarning(components.logger, `Multiplayer Server process error: ${error.message}`)

0 commit comments

Comments
 (0)