Skip to content

Commit 51650d7

Browse files
committed
Gate backend console.log behind SHINYELECTRON_DEBUG
Adds logDebug() to inst/electron/backends/utils.js; all diagnostic console.log in container.js, native-r.js, native-py.js, shinylive.js and runtime-downloader.js now route through it. Warnings and errors still print unconditionally. Set SHINYELECTRON_DEBUG=1 to see runtime discovery, spawn commands, and server-ready events. Documented in vignettes/troubleshooting.qmd; the e2e launch test also sets this flag so the log lines it greps for are visible.
1 parent c5dc796 commit 51650d7

8 files changed

Lines changed: 94 additions & 55 deletions

File tree

inst/electron/backends/container.js

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const { spawn, execFileSync } = require('child_process');
44
const fs = require('fs');
55
const os = require('os');
66
const path = require('path');
7-
const { waitForServer } = require('./utils');
7+
const { waitForServer, logDebug } = require('./utils');
88

99
class ContainerBackend extends EventEmitter {
1010
constructor() {
@@ -26,7 +26,7 @@ class ContainerBackend extends EventEmitter {
2626
});
2727
const host = ctx.trim();
2828
if (host) {
29-
console.log(`Docker endpoint from context: ${host}`);
29+
logDebug(`Docker endpoint from context: ${host}`);
3030
return host;
3131
}
3232
} catch { /* fall through */ }
@@ -40,7 +40,7 @@ class ContainerBackend extends EventEmitter {
4040
for (const pipe of pipes) {
4141
try {
4242
execFileSync('docker', ['-H', pipe, 'info'], { stdio: 'ignore', timeout: 5000 });
43-
console.log(`Docker endpoint found: ${pipe}`);
43+
logDebug(`Docker endpoint found: ${pipe}`);
4444
return pipe;
4545
} catch { /* try next */ }
4646
}
@@ -55,7 +55,7 @@ class ContainerBackend extends EventEmitter {
5555
for (const sock of sockets) {
5656
const sockPath = sock.replace('unix://', '');
5757
if (fs.existsSync(sockPath)) {
58-
console.log(`Docker socket found: ${sock}`);
58+
logDebug(`Docker socket found: ${sock}`);
5959
return sock;
6060
}
6161
}
@@ -144,7 +144,7 @@ class ContainerBackend extends EventEmitter {
144144
execFileSync(this.containerEngine, ['image', 'inspect', image], {
145145
stdio: 'ignore', env, timeout: 10000
146146
});
147-
console.log(`Image ${image} found locally`);
147+
logDebug(`Image ${image} found locally`);
148148
return;
149149
} catch {
150150
// Image not found locally
@@ -189,7 +189,7 @@ class ContainerBackend extends EventEmitter {
189189
buildProc.stdout.on('data', (data) => {
190190
const line = data.toString().trim();
191191
if (line) {
192-
console.log(`[docker build] ${line}`);
192+
logDebug(`[docker build] ${line}`);
193193
this.emit('status', {
194194
phase: 'downloading_runtime',
195195
message: line.substring(0, 100)
@@ -200,7 +200,7 @@ class ContainerBackend extends EventEmitter {
200200
const line = data.toString().trim();
201201
stderr += line + '\n';
202202
if (line) {
203-
console.log(`[docker build] ${line}`);
203+
logDebug(`[docker build] ${line}`);
204204
this.emit('status', {
205205
phase: 'downloading_runtime',
206206
message: line.substring(0, 100)
@@ -209,7 +209,7 @@ class ContainerBackend extends EventEmitter {
209209
});
210210
buildProc.on('close', (code) => {
211211
if (code === 0) {
212-
console.log(`Built image: ${image}`);
212+
logDebug(`Built image: ${image}`);
213213
resolve();
214214
} else {
215215
reject(new Error(
@@ -294,10 +294,10 @@ class ContainerBackend extends EventEmitter {
294294
message: `Using ${this.containerEngine === 'docker' ? 'Docker' : 'Podman'}`
295295
});
296296

297-
console.log(`Starting container with ${this.containerEngine}...`);
298-
console.log(`Image: ${image}`);
299-
console.log(`App path: ${appPath}`);
300-
console.log(`Port: ${port}`);
297+
logDebug(`Starting container with ${this.containerEngine}...`);
298+
logDebug(`Image: ${image}`);
299+
logDebug(`App path: ${appPath}`);
300+
logDebug(`Port: ${port}`);
301301

302302
// Ensure image is available (build locally or pull from registry)
303303
await this.ensureImage(image, config);
@@ -344,7 +344,7 @@ class ContainerBackend extends EventEmitter {
344344
args.push(image);
345345

346346
return new Promise((resolve, reject) => {
347-
console.log(`Running: ${this.containerEngine} ${args.join(' ')}`);
347+
logDebug(`Running: ${this.containerEngine} ${args.join(' ')}`);
348348

349349
const proc = spawn(this.containerEngine, args, {
350350
stdio: ['ignore', 'pipe', 'pipe'],
@@ -371,7 +371,7 @@ class ContainerBackend extends EventEmitter {
371371
}
372372

373373
this.containerId = stdout.trim().substring(0, 12);
374-
console.log(`Container started: ${this.containerId}`);
374+
logDebug(`Container started: ${this.containerId}`);
375375

376376
// Stream container logs while waiting for startup
377377
const logProc = spawn(this.containerEngine, ['logs', '-f', this.containerId], {
@@ -383,23 +383,23 @@ class ContainerBackend extends EventEmitter {
383383
try {
384384
const msg = data.toString().trim();
385385
if (msg) {
386-
console.log(`[container] ${msg}`);
386+
logDebug(`[container] ${msg}`);
387387
this.emit('status', { phase: 'starting_server', message: msg });
388388
}
389389
} catch { /* ignore write errors after shutdown */ }
390390
});
391391
logProc.stderr.on('data', (data) => {
392392
try {
393393
const msg = data.toString().trim();
394-
if (msg) console.log(`[container] ${msg}`);
394+
if (msg) logDebug(`[container] ${msg}`);
395395
} catch { /* ignore */ }
396396
});
397397

398398
// Wait for the server to be ready (longer timeout for container startup)
399399
waitForServer(hostPort, { timeout: 120000, interval: 1000 })
400400
.then(() => {
401401
logProc.kill();
402-
console.log(`Container server ready on http://localhost:${hostPort}`);
402+
logDebug(`Container server ready on http://localhost:${hostPort}`);
403403
this.emit('status', { phase: 'server_ready', message: 'Container ready' });
404404
resolve({ port: hostPort });
405405
})
@@ -440,7 +440,7 @@ class ContainerBackend extends EventEmitter {
440440
stop() {
441441
if (this.containerId && this.containerEngine) {
442442
this.emit('status', { phase: 'stopping_server', message: `Stopping container...` });
443-
console.log(`Stopping container ${this.containerId}...`);
443+
logDebug(`Stopping container ${this.containerId}...`);
444444
try {
445445
execFileSync(this.containerEngine, ['stop', this.containerId], { stdio: 'ignore', timeout: 10000, env: this.getDockerEnv() });
446446
} catch (err) {

inst/electron/backends/native-py.js

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const path = require('path');
66
const os = require('os');
77
const {
88
waitForServer, findAvailablePort, killProcessTree,
9-
sortCandidatesByVersion, reportRuntimeCandidates
9+
sortCandidatesByVersion, reportRuntimeCandidates, logDebug
1010
} = require('./utils');
1111

1212
class NativePyBackend extends EventEmitter {
@@ -125,7 +125,7 @@ class NativePyBackend extends EventEmitter {
125125
];
126126
for (const c of candidates) {
127127
if (fs.existsSync(c)) {
128-
console.log(`Found bundled Python: ${c}`);
128+
logDebug(`Found bundled Python: ${c}`);
129129
this.emit('status', { phase: 'runtime_found', message: `Found bundled Python` });
130130
return c;
131131
}
@@ -226,10 +226,10 @@ class NativePyBackend extends EventEmitter {
226226
});
227227
throw new Error('No internet connection for runtime download');
228228
}
229-
console.log('Python runtime not found, downloading...');
229+
logDebug('Python runtime not found, downloading...');
230230
this.emit('status', { phase: 'downloading_runtime', message: 'Downloading Python runtime...' });
231231
python = await downloadRuntime(manifest, (msg, pct) => {
232-
console.log(`[Runtime] ${msg}`);
232+
logDebug(`[Runtime] ${msg}`);
233233
this.emit('status', { phase: 'downloading_runtime', message: msg, progress: pct });
234234
});
235235
}
@@ -296,7 +296,7 @@ class NativePyBackend extends EventEmitter {
296296
const { execFileSync } = require('child_process');
297297
fs.mkdirSync(venvDir, { recursive: true });
298298
execFileSync(python, ['-m', 'venv', venvDir], { timeout: 60000, stdio: 'ignore' });
299-
console.log(`Created venv at ${venvDir}`);
299+
logDebug(`Created venv at ${venvDir}`);
300300
this.emit('status', { phase: 'checking_packages', message: 'Python environment ready' });
301301
} catch (err) {
302302
console.warn('Failed to create venv:', err.message);
@@ -369,7 +369,7 @@ class NativePyBackend extends EventEmitter {
369369

370370
// Port retry: find an available port starting from the requested one
371371
const actualPort = await findAvailablePort(port, 10, (attempted, next) => {
372-
console.log(`Port ${attempted} is in use, trying ${next}...`);
372+
logDebug(`Port ${attempted} is in use, trying ${next}...`);
373373
this.emit('status', { phase: 'port_conflict', message: `Port ${attempted} in use, trying ${next}...`, attempted, next });
374374
});
375375

@@ -386,9 +386,9 @@ class NativePyBackend extends EventEmitter {
386386
'app:app'
387387
];
388388

389-
console.log(`Starting Python Shiny server on port ${actualPort}...`);
390-
console.log(`Python command: ${python}`);
391-
console.log(`App path: ${appPath}`);
389+
logDebug(`Starting Python Shiny server on port ${actualPort}...`);
390+
logDebug(`Python command: ${python}`);
391+
logDebug(`App path: ${appPath}`);
392392
this.emit('status', { phase: 'starting_server', message: 'Starting Python Shiny server...', port: actualPort });
393393

394394
const checker3 = require('./dependency-checker');
@@ -433,13 +433,13 @@ class NativePyBackend extends EventEmitter {
433433
let stderr = '';
434434

435435
this.pyProcess.stdout.on('data', (data) => {
436-
console.log(`[Python stdout] ${data.toString().trim()}`);
436+
logDebug(`[Python stdout] ${data.toString().trim()}`);
437437
});
438438

439439
this.pyProcess.stderr.on('data', (data) => {
440440
const msg = data.toString().trim();
441441
stderr += msg + '\n';
442-
console.log(`[Python stderr] ${msg}`);
442+
logDebug(`[Python stderr] ${msg}`);
443443

444444
// Surface Python's progress as lifecycle status updates
445445
if (/Uvicorn running on/.test(msg)) {
@@ -474,7 +474,7 @@ class NativePyBackend extends EventEmitter {
474474

475475
waitForServer(actualPort, { timeout: 60000, interval: 500 })
476476
.then(() => {
477-
console.log(`Python Shiny server ready on http://localhost:${actualPort}`);
477+
logDebug(`Python Shiny server ready on http://localhost:${actualPort}`);
478478
this.emit('status', { phase: 'server_ready', message: 'Python Shiny server ready', port: actualPort });
479479
resolve({ port: actualPort });
480480
})
@@ -500,7 +500,7 @@ class NativePyBackend extends EventEmitter {
500500
*/
501501
stop() {
502502
if (this.pyProcess) {
503-
console.log('Stopping Python Shiny server...');
503+
logDebug('Stopping Python Shiny server...');
504504
this.emit('status', { phase: 'stopping_server', message: 'Stopping Python Shiny server...' });
505505
killProcessTree(this.pyProcess);
506506
this.pyProcess = null;

inst/electron/backends/native-r.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const path = require('path');
66
const os = require('os');
77
const {
88
waitForServer, findAvailablePort, killProcessTree,
9-
sortCandidatesByVersion, reportRuntimeCandidates
9+
sortCandidatesByVersion, reportRuntimeCandidates, logDebug
1010
} = require('./utils');
1111

1212
class NativeRBackend extends EventEmitter {
@@ -140,7 +140,7 @@ class NativeRBackend extends EventEmitter {
140140
const rscriptName = process.platform === 'win32' ? 'Rscript.exe' : 'Rscript';
141141
const candidate = path.join(subdir, sub, 'bin', rscriptName);
142142
if (fs.existsSync(candidate)) {
143-
console.log(`Found bundled R: ${candidate}`);
143+
logDebug(`Found bundled R: ${candidate}`);
144144
this.emit('status', { phase: 'runtime_found', message: `Found bundled R: ${sub}` });
145145
return candidate;
146146
}
@@ -149,7 +149,7 @@ class NativeRBackend extends EventEmitter {
149149
// Also check flat layout: runtime/R/{version}/bin/Rscript
150150
const flatCandidate = path.join(subdir, 'bin', process.platform === 'win32' ? 'Rscript.exe' : 'Rscript');
151151
if (fs.existsSync(flatCandidate)) {
152-
console.log(`Found bundled R: ${flatCandidate}`);
152+
logDebug(`Found bundled R: ${flatCandidate}`);
153153
this.emit('status', { phase: 'runtime_found', message: `Found bundled R` });
154154
return flatCandidate;
155155
}
@@ -239,10 +239,10 @@ class NativeRBackend extends EventEmitter {
239239
});
240240
throw new Error('No internet connection for runtime download');
241241
}
242-
console.log('R runtime not found, downloading...');
242+
logDebug('R runtime not found, downloading...');
243243
this.emit('status', { phase: 'downloading_runtime', message: 'Downloading R runtime...' });
244244
rscript = await downloadRuntime(manifest, (msg, pct) => {
245-
console.log(`[Runtime] ${msg}`);
245+
logDebug(`[Runtime] ${msg}`);
246246
this.emit('status', { phase: 'downloading_runtime', message: `[Runtime] ${msg}` });
247247
});
248248
}
@@ -395,9 +395,9 @@ class NativeRBackend extends EventEmitter {
395395
rCode = `shiny::runApp('${safeAppPath}', port = ${actualPort}, host = '127.0.0.1', launch.browser = FALSE)`;
396396
}
397397

398-
console.log(`Starting R Shiny server on port ${actualPort}...`);
399-
console.log(`Rscript command: ${rscript}`);
400-
console.log(`App path: ${appPath}`);
398+
logDebug(`Starting R Shiny server on port ${actualPort}...`);
399+
logDebug(`Rscript command: ${rscript}`);
400+
logDebug(`App path: ${appPath}`);
401401

402402
this.rProcess = spawn(rscript, ['-e', rCode], {
403403
stdio: ['ignore', 'pipe', 'pipe'],
@@ -407,13 +407,13 @@ class NativeRBackend extends EventEmitter {
407407
let stderr = '';
408408

409409
this.rProcess.stdout.on('data', (data) => {
410-
console.log(`[R stdout] ${data.toString().trim()}`);
410+
logDebug(`[R stdout] ${data.toString().trim()}`);
411411
});
412412

413413
this.rProcess.stderr.on('data', (data) => {
414414
const msg = data.toString().trim();
415415
stderr += msg + '\n';
416-
console.log(`[R stderr] ${msg}`);
416+
logDebug(`[R stderr] ${msg}`);
417417

418418
// Surface R's progress as lifecycle status updates so the splash
419419
// screen shows what's happening instead of sitting frozen.
@@ -451,7 +451,7 @@ class NativeRBackend extends EventEmitter {
451451

452452
waitForServer(actualPort, { timeout: 60000, interval: 500 })
453453
.then(() => {
454-
console.log(`R Shiny server ready on http://localhost:${actualPort}`);
454+
logDebug(`R Shiny server ready on http://localhost:${actualPort}`);
455455
this.emit('status', { phase: 'server_ready', message: 'R Shiny server ready' });
456456
resolve({ port: actualPort });
457457
})
@@ -481,7 +481,7 @@ class NativeRBackend extends EventEmitter {
481481
stop() {
482482
this.emit('status', { phase: 'stopping_server', message: 'Stopping R server...' });
483483
if (this.rProcess) {
484-
console.log('Stopping R Shiny server...');
484+
logDebug('Stopping R Shiny server...');
485485
killProcessTree(this.rProcess);
486486
this.rProcess = null;
487487
}

inst/electron/backends/runtime-downloader.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const path = require('path');
66
const os = require('os');
77
const { execFileSync } = require('child_process');
88
const crypto = require('crypto');
9+
const { logDebug } = require('./utils');
910

1011
/**
1112
* Download a file with progress reporting.
@@ -165,7 +166,7 @@ async function downloadRuntime(manifest, onProgress) {
165166
`The download may be corrupted or tampered with.`
166167
);
167168
}
168-
console.log('SHA-256 checksum verified');
169+
logDebug('SHA-256 checksum verified');
169170
} else {
170171
console.warn('No SHA-256 checksum in manifest — skipping integrity verification');
171172
}

inst/electron/backends/shinylive.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class ShinyliveBackend extends EventEmitter {
1111

1212
async start({ appPath, port, config }) {
1313
this.removeAllListeners();
14-
const { isOnline } = require('./utils');
14+
const { isOnline, logDebug } = require('./utils');
1515

1616
this.emit('status', { phase: 'starting_server', message: 'Starting server...' });
1717

@@ -42,7 +42,7 @@ class ShinyliveBackend extends EventEmitter {
4242
// simultaneously (findAvailablePort has TOCTOU race conditions).
4343
this.server = app.listen(0, '127.0.0.1', () => {
4444
const actualPort = this.server.address().port;
45-
console.log(`Shinylive server running on http://127.0.0.1:${actualPort}`);
45+
logDebug(`Shinylive server running on http://127.0.0.1:${actualPort}`);
4646
this.emit('status', { phase: 'server_ready', message: 'Server ready' });
4747
resolve({ port: actualPort });
4848
});

0 commit comments

Comments
 (0)