Skip to content

Commit ec28623

Browse files
authored
fix: forward backend logging to electron (#2236)
Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
1 parent 73ddbef commit ec28623

File tree

5 files changed

+106
-59
lines changed

5 files changed

+106
-59
lines changed

.vscode/tasks.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
{
55
"label": "Rebuild App",
66
"type": "shell",
7-
"command": "yarn rebuild:browser && yarn rebuild:electron",
7+
"command": "yarn rebuild",
88
"group": "build",
9+
"options": {
10+
"cwd": "${workspaceFolder}/electron-app"
11+
},
912
"presentation": {
1013
"reveal": "always",
1114
"panel": "new",

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

Lines changed: 96 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,33 @@
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';
13-
import type { MaybePromise, Mutable } from '@theia/core/lib/common/types';
10+
import {
11+
Disposable,
12+
DisposableCollection,
13+
} from '@theia/core/lib/common/disposable';
14+
import { isOSX } from '@theia/core/lib/common/os';
15+
import { Deferred } from '@theia/core/lib/common/promise-util';
16+
import { isObject, MaybePromise, Mutable } from '@theia/core/lib/common/types';
1417
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';
1718
import {
1819
ElectronMainApplication as TheiaElectronMainApplication,
1920
ElectronMainExecutionParams,
2021
} 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';
22+
import type { TheiaBrowserWindowOptions } from '@theia/core/lib/electron-main/theia-electron-window';
2823
import { FileUri } from '@theia/core/lib/node/file-uri';
29-
import {
30-
Disposable,
31-
DisposableCollection,
32-
} from '@theia/core/lib/common/disposable';
24+
import { inject, injectable } from '@theia/core/shared/inversify';
25+
import { URI } from '@theia/core/shared/vscode-uri';
26+
import { log as logToFile, setup as setupFileLog } from 'node-log-rotate';
27+
import { fork } from 'node:child_process';
28+
import { promises as fs, rm, rmSync } from 'node:fs';
29+
import type { AddressInfo } from 'node:net';
30+
import { isAbsolute, join, resolve } from 'node:path';
3331
import { Sketch } from '../../common/protocol';
3432
import {
3533
AppInfo,
@@ -39,9 +37,71 @@ import {
3937
CHANNEL_SHOW_PLOTTER_WINDOW,
4038
isShowPlotterWindowParams,
4139
} from '../../electron-common/electron-arduino';
40+
import { IsTempSketch } from '../../node/is-temp-sketch';
41+
import { isAccessibleSketchPath } from '../../node/sketches-service-impl';
42+
import { ErrnoException } from '../../node/utils/errors';
4243

4344
app.commandLine.appendSwitch('disable-http-cache');
4445

46+
const consoleLogFunctionNames = [
47+
'log',
48+
'trace',
49+
'debug',
50+
'info',
51+
'warn',
52+
'error',
53+
] as const;
54+
type ConsoleLogSeverity = (typeof consoleLogFunctionNames)[number];
55+
interface ConsoleLogParams {
56+
readonly severity: ConsoleLogSeverity;
57+
readonly message: string;
58+
}
59+
function isConsoleLogParams(arg: unknown): arg is ConsoleLogParams {
60+
return (
61+
isObject<ConsoleLogParams>(arg) &&
62+
typeof arg.message === 'string' &&
63+
typeof arg.severity === 'string' &&
64+
consoleLogFunctionNames.includes(arg.severity as ConsoleLogSeverity)
65+
);
66+
}
67+
68+
// Patch for on Linux when `XDG_CONFIG_HOME` is not available, `node-log-rotate` creates the folder with `undefined` name.
69+
// See https://github.com/lemon-sour/node-log-rotate/issues/23 and https://github.com/arduino/arduino-ide/issues/394.
70+
// If the IDE2 is running on Linux, and the `XDG_CONFIG_HOME` variable is not available, set it to avoid the `undefined` folder.
71+
// From the specs: https://specifications.freedesktop.org/basedir-spec/latest/ar01s03.html
72+
// "If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME/.config should be used."
73+
function enableFileLogger() {
74+
const os = require('os');
75+
const util = require('util');
76+
if (os.platform() === 'linux' && !process.env['XDG_CONFIG_HOME']) {
77+
const { join } = require('path');
78+
const home = process.env['HOME'];
79+
const xdgConfigHome = home
80+
? join(home, '.config')
81+
: join(os.homedir(), '.config');
82+
process.env['XDG_CONFIG_HOME'] = xdgConfigHome;
83+
}
84+
setupFileLog({
85+
appName: 'Arduino IDE',
86+
maxSize: 10 * 1024 * 1024,
87+
});
88+
for (const name of consoleLogFunctionNames) {
89+
const original = console[name];
90+
console[name] = function () {
91+
// eslint-disable-next-line prefer-rest-params
92+
const messages = Object.values(arguments);
93+
const message = util.format(...messages);
94+
original(message);
95+
logToFile(message);
96+
};
97+
}
98+
}
99+
100+
const isProductionMode = !environment.electron.isDevMode();
101+
if (isProductionMode) {
102+
enableFileLogger();
103+
}
104+
45105
interface WorkspaceOptions {
46106
file: string;
47107
x: number;
@@ -185,7 +245,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
185245

186246
private attachFileAssociations(cwd: string): void {
187247
// OSX: register open-file event
188-
if (os.isOSX) {
248+
if (isOSX) {
189249
app.on('open-file', async (event, path) => {
190250
event.preventDefault();
191251
const resolvedPath = await this.resolvePath(path, cwd);
@@ -495,9 +555,14 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
495555
);
496556
console.log(`Starting backend process. PID: ${backendProcess.pid}`);
497557
return new Promise((resolve, reject) => {
498-
// The backend server main file is also supposed to send the resolved http(s) server port via IPC.
499-
backendProcess.on('message', (address: AddressInfo) => {
500-
resolve(address.port);
558+
// The forked backend process sends the resolved http(s) server port via IPC, and forwards the log messages.
559+
backendProcess.on('message', (arg: unknown) => {
560+
if (isConsoleLogParams(arg)) {
561+
const { message, severity } = arg;
562+
console[severity](message);
563+
} else if (isAddressInfo(arg)) {
564+
resolve(arg.port);
565+
}
501566
});
502567
backendProcess.on('error', (error) => {
503568
reject(error);
@@ -703,7 +768,7 @@ class InterruptWorkspaceRestoreError extends Error {
703768
async function updateFrontendApplicationConfigFromPackageJson(
704769
config: FrontendApplicationConfig
705770
): Promise<FrontendApplicationConfig> {
706-
if (environment.electron.isDevMode()) {
771+
if (!isProductionMode) {
707772
console.debug(
708773
'Skipping frontend application configuration customizations. Running in dev mode.'
709774
);
@@ -777,3 +842,9 @@ function updateAppInfo(
777842
});
778843
return toUpdate;
779844
}
845+
846+
function isAddressInfo(arg: unknown): arg is Pick<AddressInfo, 'port'> {
847+
// Cannot do the type-guard on all properties, but the port is sufficient as the address is always `localhost`.
848+
// For example, the `family` might be absent if the address is IPv6.
849+
return isObject<AddressInfo>(arg) && typeof arg.port === 'number';
850+
}
Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,17 @@
11
// @ts-check
22
'use strict';
33

4-
// Patch for on Linux when `XDG_CONFIG_HOME` is not available, `node-log-rotate` creates the folder with `undefined` name.
5-
// See https://github.com/lemon-sour/node-log-rotate/issues/23 and https://github.com/arduino/arduino-ide/issues/394.
6-
// If the IDE2 is running on Linux, and the `XDG_CONFIG_HOME` variable is not available, set it to avoid the `undefined` folder.
7-
// From the specs: https://specifications.freedesktop.org/basedir-spec/latest/ar01s03.html
8-
// "If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME/.config should be used."
9-
function enableFileLogger() {
10-
const os = require('os');
4+
// `true` if the this (backend main) process has been forked.
5+
if (process.send) {
116
const util = require('util');
12-
if (os.platform() === 'linux' && !process.env['XDG_CONFIG_HOME']) {
13-
const { join } = require('path');
14-
const home = process.env['HOME'];
15-
const xdgConfigHome = home
16-
? join(home, '.config')
17-
: join(os.homedir(), '.config');
18-
process.env['XDG_CONFIG_HOME'] = xdgConfigHome;
19-
}
20-
const { setup, log } = require('node-log-rotate');
21-
22-
setup({
23-
appName: 'Arduino IDE',
24-
maxSize: 10 * 1024 * 1024,
25-
});
267
for (const name of ['log', 'trace', 'debug', 'info', 'warn', 'error']) {
27-
const original = console[name];
288
console[name] = function () {
299
// eslint-disable-next-line prefer-rest-params
30-
const messages = Object.values(arguments);
31-
const message = util.format(...messages);
32-
original(message);
33-
log(message);
10+
const args = Object.values(arguments);
11+
const message = util.format(...args);
12+
process.send?.({ severity: name, message }); // send the log message to the parent process (electron main)
3413
};
3514
}
3615
}
3716

38-
if (process.env.IDE2_FILE_LOGGER === 'true') {
39-
enableFileLogger();
40-
}
41-
4217
require('./src-gen/backend/main');

electron-app/arduino-ide-electron-main.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ if (config.buildDate) {
1818
]
1919
.filter(Boolean)
2020
.join(',');
21-
// Enables the file logger in the backend process.
22-
process.env.IDE2_FILE_LOGGER = 'true';
2321
}
2422

2523
require('./lib/backend/electron-main');

electron-app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
"prepackage": "rimraf dist",
5454
"package": "node ./scripts/package.js",
5555
"postpackage": "node ./scripts/post-package.js",
56-
"rebuild": "theia rebuild:browser && theia rebuild:electron"
56+
"rebuild": "theia rebuild:browser --cacheRoot ../.. && theia rebuild:electron --cacheRoot ../.."
5757
},
5858
"theia": {
5959
"target": "electron",

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy