Skip to content

Commit b7acd65

Browse files
author
Akos Kitta
committed
fix: forward log from electron to backend process
Otherwise log messages from the electron-main process will not be in the log files when the app is running in production. Signed-off-by: Akos Kitta <[email protected]>
1 parent 73ddbef commit b7acd65

File tree

2 files changed

+89
-20
lines changed

2 files changed

+89
-20
lines changed

Diff for: arduino-ide-extension/src/electron-main/theia/electron-main-application.ts

+71-20
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
1-
import { inject, injectable } from '@theia/core/shared/inversify';
1+
import type { FrontendApplicationConfig } from '@theia/application-package/lib/application-props';
2+
import { environment } from '@theia/application-package/lib/environment';
23
import {
34
app,
45
BrowserWindow,
56
contentTracing,
6-
ipcMain,
77
Event as ElectronEvent,
8+
ipcMain,
89
} from '@theia/core/electron-shared/electron';
9-
import { fork } from 'node:child_process';
10-
import { AddressInfo } from 'node:net';
11-
import { join, isAbsolute, resolve } from 'node:path';
12-
import { promises as fs, rm, rmSync } from 'node:fs';
10+
import {
11+
Disposable,
12+
DisposableCollection,
13+
} from '@theia/core/lib/common/disposable';
14+
import { Emitter, Event } from '@theia/core/lib/common/event';
15+
import { isOSX } from '@theia/core/lib/common/os';
16+
import { Deferred } from '@theia/core/lib/common/promise-util';
1317
import type { MaybePromise, Mutable } from '@theia/core/lib/common/types';
1418
import { ElectronSecurityToken } from '@theia/core/lib/electron-common/electron-token';
15-
import { FrontendApplicationConfig } from '@theia/application-package/lib/application-props';
16-
import { environment } from '@theia/application-package/lib/environment';
1719
import {
1820
ElectronMainApplication as TheiaElectronMainApplication,
1921
ElectronMainExecutionParams,
2022
} from '@theia/core/lib/electron-main/electron-main-application';
21-
import { URI } from '@theia/core/shared/vscode-uri';
22-
import { Deferred } from '@theia/core/lib/common/promise-util';
23-
import * as os from '@theia/core/lib/common/os';
24-
import { TheiaBrowserWindowOptions } from '@theia/core/lib/electron-main/theia-electron-window';
25-
import { IsTempSketch } from '../../node/is-temp-sketch';
26-
import { ErrnoException } from '../../node/utils/errors';
27-
import { isAccessibleSketchPath } from '../../node/sketches-service-impl';
23+
import type { TheiaBrowserWindowOptions } from '@theia/core/lib/electron-main/theia-electron-window';
2824
import { FileUri } from '@theia/core/lib/node/file-uri';
29-
import {
30-
Disposable,
31-
DisposableCollection,
32-
} from '@theia/core/lib/common/disposable';
25+
import { inject, injectable } from '@theia/core/shared/inversify';
26+
import { URI } from '@theia/core/shared/vscode-uri';
27+
import type { ChildProcess } from 'node:child_process';
28+
import { fork } from 'node:child_process';
29+
import { promises as fs, rm, rmSync } from 'node:fs';
30+
import type { AddressInfo } from 'node:net';
31+
import { isAbsolute, join, resolve } from 'node:path';
32+
import { format as formatLog } from 'node:util';
3333
import { Sketch } from '../../common/protocol';
3434
import {
3535
AppInfo,
@@ -39,6 +39,54 @@ import {
3939
CHANNEL_SHOW_PLOTTER_WINDOW,
4040
isShowPlotterWindowParams,
4141
} from '../../electron-common/electron-arduino';
42+
import { IsTempSketch } from '../../node/is-temp-sketch';
43+
import { isAccessibleSketchPath } from '../../node/sketches-service-impl';
44+
import { ErrnoException } from '../../node/utils/errors';
45+
46+
const consoleLogFunctionNames = [
47+
'log',
48+
'trace',
49+
'debug',
50+
'info',
51+
'warn',
52+
'error',
53+
] as const;
54+
type LogSeverity = (typeof consoleLogFunctionNames)[number];
55+
interface LogMessage {
56+
readonly severity: LogSeverity;
57+
readonly message: string;
58+
}
59+
60+
function forwardLog(onDidFork: Event<ChildProcess>): void {
61+
if (environment.electron.isDevMode()) {
62+
return;
63+
}
64+
const buffer: LogMessage[] = [];
65+
let backendProcess: ChildProcess | undefined = undefined;
66+
for (const name of consoleLogFunctionNames) {
67+
console[name] = function () {
68+
// eslint-disable-next-line prefer-rest-params
69+
const messages = Object.values(arguments);
70+
const message = formatLog(...messages);
71+
const logMessage: LogMessage = { severity: name, message };
72+
if (backendProcess) {
73+
backendProcess.send(logMessage);
74+
} else {
75+
buffer.push(logMessage);
76+
}
77+
};
78+
}
79+
onDidFork((cp) => {
80+
backendProcess = cp;
81+
let logMessage = buffer.shift();
82+
while (logMessage) {
83+
cp.send(logMessage);
84+
logMessage = buffer.shift();
85+
}
86+
});
87+
}
88+
const onDidForkEmitter = new Emitter<ChildProcess>();
89+
forwardLog(onDidForkEmitter.event);
4290

4391
app.commandLine.appendSwitch('disable-http-cache');
4492

@@ -185,7 +233,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
185233

186234
private attachFileAssociations(cwd: string): void {
187235
// OSX: register open-file event
188-
if (os.isOSX) {
236+
if (isOSX) {
189237
app.on('open-file', async (event, path) => {
190238
event.preventDefault();
191239
const resolvedPath = await this.resolvePath(path, cwd);
@@ -493,6 +541,9 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
493541
args,
494542
await this.getForkOptions()
495543
);
544+
// In production, the electron main's `console` must be rebind, and delegate all log messages to the Theia backend via IPC.
545+
// Otherwise, any error that happens in this process won't be in the log files.
546+
onDidForkEmitter.fire(backendProcess);
496547
console.log(`Starting backend process. PID: ${backendProcess.pid}`);
497548
return new Promise((resolve, reject) => {
498549
// The backend server main file is also supposed to send the resolved http(s) server port via IPC.

Diff for: electron-app/arduino-ide-backend-main.js

+18
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,24 @@ function enableFileLogger() {
3333
log(message);
3434
};
3535
}
36+
37+
function forward(message) {
38+
if (
39+
Boolean(message) &&
40+
typeof message === 'object' &&
41+
'severity' in message &&
42+
'message' in message
43+
) {
44+
const severity = message.severity;
45+
const logMessage = message.message;
46+
const logFunction = console[severity];
47+
if (typeof logFunction === 'function' && typeof logMessage === 'string') {
48+
logFunction(logMessage);
49+
}
50+
}
51+
}
52+
53+
process.on('message', forward);
3654
}
3755

3856
if (process.env.IDE2_FILE_LOGGER === 'true') {

0 commit comments

Comments
 (0)