Skip to content

Commit 0018a30

Browse files
committed
Implement Collect all logs command
1 parent f59d688 commit 0018a30

File tree

11 files changed

+262
-42
lines changed

11 files changed

+262
-42
lines changed

extensions/gitpod-remote/src/extension.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@ export async function activate(context: vscode.ExtensionContext) {
1616
if (!gitpodContext) {
1717
return;
1818
}
19+
1920
if (vscode.extensions.getExtension('gitpod.gitpod')) {
2021
try {
2122
await util.promisify(cp.exec)('code --uninstall-extension gitpod.gitpod');
2223
vscode.commands.executeCommand('workbench.action.reloadWindow');
2324
} catch (e) {
24-
gitpodContext.output.appendLine('failed to uninstall gitpod.gitpod: ' + e);
25+
gitpodContext.logger.error('failed to uninstall gitpod.gitpod:', e);
2526
}
2627
return;
2728
}
@@ -43,6 +44,11 @@ export async function activate(context: vscode.ExtensionContext) {
4344
// and gitpod.gitpod to disable auto tunneling from the current local machine.
4445
vscode.commands.executeCommand('gitpod.api.autoTunnel', gitpodContext.info.getGitpodHost(), gitpodContext.info.getInstanceId(), false);
4546

47+
// For collecting logs, will be called by gitpod-desktop extension;
48+
context.subscriptions.push(vscode.commands.registerCommand('__gitpod.getGitpodRemoteLogsUri', () => {
49+
return context.logUri;
50+
}));
51+
4652
// TODO
4753
// - auth?
4854
// - .gitpod.yml validations
@@ -87,7 +93,7 @@ export function openWorkspaceLocation(context: GitpodExtensionContext): boolean
8793
}
8894

8995
export async function installInitialExtensions(context: GitpodExtensionContext): Promise<void> {
90-
context.output.appendLine('installing initial extensions...');
96+
context.logger.info('installing initial extensions...');
9197
const extensions: (vscode.Uri | string)[] = [];
9298
try {
9399
const workspaceContextUri = vscode.Uri.parse(context.info.getWorkspaceContextUrl());
@@ -125,10 +131,10 @@ export async function installInitialExtensions(context: GitpodExtensionContext):
125131
}
126132
}
127133
} catch (e) {
128-
context.output.appendLine('failed to detect workspace context dependent extensions:' + e);
134+
context.logger.error('failed to detect workspace context dependent extensions:', e);
129135
console.error('failed to detect workspace context dependent extensions:', e);
130136
}
131-
context.output.appendLine('initial extensions: ' + extensions);
137+
context.logger.info('initial extensions:', extensions);
132138
if (extensions.length) {
133139
let cause;
134140
try {
@@ -138,11 +144,11 @@ export async function installInitialExtensions(context: GitpodExtensionContext):
138144
cause = e;
139145
}
140146
if (cause) {
141-
context.output.appendLine('failed to install initial extensions: ' + cause);
147+
context.logger.error('failed to install initial extensions:', cause);
142148
console.error('failed to install initial extensions: ', cause);
143149
}
144150
}
145-
context.output.appendLine('initial extensions installed');
151+
context.logger.info('initial extensions installed');
146152
}
147153

148154
export function registerHearbeat(context: GitpodExtensionContext): void {
@@ -152,11 +158,13 @@ export function registerHearbeat(context: GitpodExtensionContext): void {
152158
};
153159
const sendHeartBeat = async (wasClosed?: true) => {
154160
const suffix = wasClosed ? 'was closed heartbeat' : 'heartbeat';
161+
if (wasClosed) {
162+
context.logger.trace('sending ' + suffix);
163+
}
155164
try {
156-
context.output.appendLine('sending ' + suffix);
157165
await context.gitpod.server.sendHeartBeat({ instanceId: context.info.getInstanceId(), wasClosed });
158166
} catch (err) {
159-
context.output.appendLine(`failed to send ${suffix}: ` + err);
167+
context.logger.error(`failed to send ${suffix}:`, err);
160168
console.error(`failed to send ${suffix}`, err);
161169
}
162170
};
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Gitpod. All rights reserved.
3+
*--------------------------------------------------------------------------------------------*/
4+
5+
import * as vscode from 'vscode';
6+
7+
type LogLevel = 'Trace' | 'Info' | 'Error' | 'Warn' | 'Log';
8+
9+
export default class Log {
10+
private output: vscode.OutputChannel;
11+
12+
constructor(name: string) {
13+
this.output = vscode.window.createOutputChannel(name);
14+
}
15+
16+
private data2String(data: any): string {
17+
if (data instanceof Error) {
18+
return data.stack || data.message;
19+
}
20+
if (data.success === false && data.message) {
21+
return data.message;
22+
}
23+
return data.toString();
24+
}
25+
26+
public trace(message: string, data?: any): void {
27+
this.logLevel('Trace', message, data);
28+
}
29+
30+
public info(message: string, data?: any): void {
31+
this.logLevel('Info', message, data);
32+
}
33+
34+
public error(message: string, data?: any): void {
35+
this.logLevel('Error', message, data);
36+
}
37+
38+
public warn(message: string, data?: any): void {
39+
this.logLevel('Warn', message, data);
40+
}
41+
42+
public log(message: string, data?: any): void {
43+
this.logLevel('Log', message, data);
44+
}
45+
46+
public logLevel(level: LogLevel, message: string, data?: any): void {
47+
this.output.appendLine(`[${level} - ${this.now()}] ${message}`);
48+
if (data) {
49+
this.output.appendLine(this.data2String(data));
50+
}
51+
}
52+
53+
private now(): string {
54+
const now = new Date();
55+
return padLeft(now.getUTCHours() + '', 2, '0')
56+
+ ':' + padLeft(now.getMinutes() + '', 2, '0')
57+
+ ':' + padLeft(now.getUTCSeconds() + '', 2, '0') + '.' + now.getMilliseconds();
58+
}
59+
60+
public show() {
61+
this.output.show();
62+
}
63+
}
64+
65+
function padLeft(s: string, n: number, pad = ' ') {
66+
return pad.repeat(Math.max(0, n - s.length)) + s;
67+
}

extensions/gitpod-shared/src/extension.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,15 @@ export async function setupGitpodContext(context: vscode.ExtensionContext): Prom
1414
}
1515

1616
const gitpodContext = await createGitpodExtensionContext(context);
17+
vscode.commands.executeCommand('setContext', 'gitpod.inWorkspace', !!gitpodContext);
1718
if (!gitpodContext) {
18-
vscode.commands.executeCommand('setContext', 'gitpod.inWorkspace', false);
1919
return undefined;
2020
}
21-
vscode.commands.executeCommand('setContext', 'gitpod.inWorkspace', true);
21+
22+
logContextInfo(gitpodContext);
2223

2324
vscode.commands.executeCommand('setContext', 'gitpod.ideAlias', gitpodContext.info.getIdeAlias());
24-
if (vscode.env.uiKind === vscode.UIKind.Web) {
25-
vscode.commands.executeCommand('setContext', 'gitpod.UIKind', 'web');
26-
} else if (vscode.env.uiKind === vscode.UIKind.Desktop) {
27-
vscode.commands.executeCommand('setContext', 'gitpod.UIKind', 'desktop');
28-
}
25+
vscode.commands.executeCommand('setContext', 'gitpod.UIKind', vscode.env.uiKind === vscode.UIKind.Web ? 'web' : 'desktop');
2926

3027
registerUsageAnalytics(gitpodContext);
3128
registerWorkspaceCommands(gitpodContext);
@@ -40,3 +37,15 @@ function registerUsageAnalytics(context: GitpodExtensionContext): void {
4037
context.fireAnalyticsEvent({ eventName: 'vscode_session', properties: {} });
4138
}
4239

40+
function logContextInfo(context: GitpodExtensionContext) {
41+
context.logger.info(`VSCODE_MACHINE_ID: ${vscode.env.machineId}`);
42+
context.logger.info(`VSCODE_SESSION_ID: ${vscode.env.sessionId}`);
43+
context.logger.info(`VSCODE_VERSION: ${vscode.version}`);
44+
context.logger.info(`VSCODE_APP_NAME: ${vscode.env.appName}`);
45+
context.logger.info(`VSCODE_APP_HOST: ${vscode.env.appHost}`);
46+
context.logger.info(`VSCODE_UI_KIND: ${vscode.env.uiKind === vscode.UIKind.Web ? 'web' : 'desktop'}`);
47+
48+
context.logger.info(`GITPOD_WORKSPACE_CONTEXT_URL: ${context.info.getWorkspaceContextUrl()}`);
49+
context.logger.info(`GITPOD_INSTANCE_ID: ${context.info.getInstanceId()}`);
50+
context.logger.info(`GITPOD_WORKSPACE_URL: ${context.info.getWorkspaceUrl()}`);
51+
}

extensions/gitpod-shared/src/features.ts

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import WebSocket = require('ws');
3535
import { BaseGitpodAnalyticsEventPropeties, GitpodAnalyticsEvent } from './analytics';
3636
import * as uuid from 'uuid';
3737
import { RemoteTrackMessage } from '@gitpod/gitpod-protocol/lib/analytics';
38+
import Log from './common/logger';
3839

3940
export class SupervisorConnection {
4041
readonly deadlines = {
@@ -94,7 +95,7 @@ export class GitpodExtensionContext implements vscode.ExtensionContext {
9495
readonly user: Promise<User>,
9596
readonly instanceListener: Promise<WorkspaceInstanceUpdateListener>,
9697
readonly workspaceOwned: Promise<boolean>,
97-
readonly output: vscode.OutputChannel,
98+
readonly logger: Log,
9899
readonly ipcHookCli: string | undefined
99100
) {
100101
this.workspaceContextUrl = vscode.Uri.parse(info.getWorkspaceContextUrl());
@@ -168,7 +169,7 @@ export class GitpodExtensionContext implements vscode.ExtensionContext {
168169
await Promise.allSettled(this.pendingWillCloseSocket.map(f => f()));
169170
webSocket.close();
170171
} catch (e) {
171-
this.output.appendLine('failed to dispose context: ' + e);
172+
this.logger.error('failed to dispose context:', e);
172173
console.error('failed to dispose context:', e);
173174
}
174175
})();
@@ -193,20 +194,20 @@ export class GitpodExtensionContext implements vscode.ExtensionContext {
193194
}
194195
};
195196
if (this.devMode && vscode.env.uiKind === vscode.UIKind.Web) {
196-
this.output.appendLine(`ANALYTICS: ${JSON.stringify(msg)} `);
197+
this.logger.trace(`ANALYTICS: ${JSON.stringify(msg)} `);
197198
return Promise.resolve();
198199
}
199200
try {
200201
await this.gitpod.server.trackEvent(msg);
201202
} catch (e) {
202-
this.output.appendLine('failed to track event: ' + e);
203+
this.logger.error('failed to track event:', e);
203204
console.error('failed to track event:', e);
204205
}
205206
}
206207
}
207208

208209
export async function createGitpodExtensionContext(context: vscode.ExtensionContext): Promise<GitpodExtensionContext | undefined> {
209-
const output = vscode.window.createOutputChannel('Gitpod Workspace');
210+
const logger = new Log('Gitpod Workspace');
210211
const devMode = context.extensionMode === vscode.ExtensionMode.Development || !!process.env['VSCODE_DEV'];
211212

212213
const supervisor = new SupervisorConnection(context);
@@ -222,7 +223,7 @@ export async function createGitpodExtensionContext(context: vscode.ExtensionCont
222223
contentAvailable = result.getAvailable();
223224
} catch (e) {
224225
if (e.code === grpc.status.UNAVAILABLE) {
225-
output.appendLine('It does not look like we are running in a Gitpod workspace, supervisor is not available.');
226+
logger.info('It does not look like we are running in a Gitpod workspace, supervisor is not available.');
226227
return undefined;
227228
}
228229
console.error('cannot maintain connection to supervisor', e);
@@ -308,7 +309,7 @@ export async function createGitpodExtensionContext(context: vscode.ExtensionCont
308309
return workspaceOwned;
309310
})();
310311

311-
const ipcHookCli = installCLIProxy(context, output);
312+
const ipcHookCli = installCLIProxy(context, logger);
312313

313314
const config = await import('./gitpod-plugin-model');
314315
return new GitpodExtensionContext(
@@ -324,7 +325,7 @@ export async function createGitpodExtensionContext(context: vscode.ExtensionCont
324325
pendingGetUser,
325326
pendingInstanceListener,
326327
pendingWorkspaceOwned,
327-
output,
328+
logger,
328329
ipcHookCli
329330
);
330331
}
@@ -722,7 +723,7 @@ export function registerDefaultLayout(context: GitpodExtensionContext): void {
722723
}
723724
}
724725

725-
function installCLIProxy(context: vscode.ExtensionContext, output: vscode.OutputChannel): string | undefined {
726+
function installCLIProxy(context: vscode.ExtensionContext, logger: Log): string | undefined {
726727
const vscodeIpcHookCli = process.env['VSCODE_IPC_HOOK_CLI'];
727728
if (!vscodeIpcHookCli) {
728729
return undefined;
@@ -772,7 +773,7 @@ function installCLIProxy(context: vscode.ExtensionContext, output: vscode.Output
772773
fs.promises.unlink(ipcHookCli)
773774
));
774775
}).catch(e => {
775-
output.appendLine('failed to start cli proxy: ' + e);
776+
logger.error('failed to start cli proxy: ' + e);
776777
console.error('failed to start cli proxy:' + e);
777778
});
778779

@@ -815,6 +816,7 @@ export async function registerTasks(context: GitpodExtensionContext, createTermi
815816
});
816817
} catch (err) {
817818
if (!('code' in err && err.code === grpc.status.CANCELLED)) {
819+
context.logger.error('code server: listening task updates failed:', err);
818820
console.error('code server: listening task updates failed:', err);
819821
}
820822
} finally {
@@ -824,6 +826,11 @@ export async function registerTasks(context: GitpodExtensionContext, createTermi
824826
await new Promise(resolve => setTimeout(resolve, 1000));
825827
}
826828
}
829+
context.logger.trace('Task status:', [...tasks.values()].map(status => {
830+
const stateMap = { [TaskState.OPENING]: 'CLOSED', [TaskState.RUNNING]: 'RUNNING', [TaskState.CLOSED]: 'CLOSED' };
831+
return `\t${status.getTerminal()} => ${stateMap[status.getState()]}`;
832+
}).join('\n'));
833+
827834
if (token.isCancellationRequested) {
828835
return;
829836
}
@@ -837,6 +844,7 @@ export async function registerTasks(context: GitpodExtensionContext, createTermi
837844
taskTerminals.set(term.getAlias(), term);
838845
}
839846
} catch (e) {
847+
context.logger.error('failed to list task terminals:', e);
840848
console.error('failed to list task terminals:', e);
841849
}
842850

@@ -922,6 +930,7 @@ function createTaskPty(alias: string, context: GitpodExtensionContext, contextTo
922930
} catch (e) {
923931
notFound = 'code' in e && e.code === grpc.status.NOT_FOUND;
924932
if (!token.isCancellationRequested && !notFound && !('code' in e && e.code === grpc.status.CANCELLED)) {
933+
context.logger.error(`${alias} terminal: listening failed:`, e);
925934
console.error(`${alias} terminal: listening failed:`, e);
926935
}
927936
} finally {
@@ -931,9 +940,16 @@ function createTaskPty(alias: string, context: GitpodExtensionContext, contextTo
931940
return;
932941
}
933942
if (notFound) {
943+
context.logger.trace(`${alias} terminal not found`);
934944
onDidCloseEmitter.fire();
935-
} else if (typeof exitCode === 'number') {
945+
tokenSource.cancel();
946+
return;
947+
}
948+
if (typeof exitCode === 'number') {
949+
context.logger.trace(`${alias} terminal exited with ${exitCode}`);
936950
onDidCloseEmitter.fire(exitCode);
951+
tokenSource.cancel();
952+
return;
937953
}
938954
await new Promise(resolve => setTimeout(resolve, 2000));
939955
}
@@ -958,10 +974,12 @@ function createTaskPty(alias: string, context: GitpodExtensionContext, contextTo
958974
await util.promisify(context.supervisor.terminal.shutdown.bind(context.supervisor.terminal, request, context.supervisor.metadata, {
959975
deadline: Date.now() + context.supervisor.deadlines.short
960976
}))();
977+
context.logger.trace(`${alias} terminal closed`);
961978
} catch (e) {
962979
if (e && e.code === grpc.status.NOT_FOUND) {
963980
// Swallow, the pty has already been killed
964981
} else {
982+
context.logger.error(`${alias} terminal: shutdown failed:`, e);
965983
console.error(`${alias} terminal: shutdown failed:`, e);
966984
}
967985
}
@@ -985,6 +1003,7 @@ function createTaskPty(alias: string, context: GitpodExtensionContext, contextTo
9851003
}))();
9861004
} catch (e) {
9871005
if (e && e.code !== grpc.status.NOT_FOUND) {
1006+
context.logger.error(`${alias} terminal: write failed:`, e);
9881007
console.error(`${alias} terminal: write failed:`, e);
9891008
}
9901009
}
@@ -1012,6 +1031,7 @@ function createTaskPty(alias: string, context: GitpodExtensionContext, contextTo
10121031
}))();
10131032
} catch (e) {
10141033
if (e && e.code !== grpc.status.NOT_FOUND) {
1034+
context.logger.error(`${alias} terminal: resize failed:`, e);
10151035
console.error(`${alias} terminal: resize failed:`, e);
10161036
}
10171037
}
@@ -1066,7 +1086,7 @@ async function updateIpcHookCli(context: GitpodExtensionContext): Promise<void>
10661086
req.end();
10671087
});
10681088
} catch (e) {
1069-
context.output.appendLine('Failed to update gitpod ipc hook cli: ' + e);
1089+
context.logger.error('Failed to update gitpod ipc hook cli:', e);
10701090
console.error('Failed to update gitpod ipc hook cli:', e);
10711091
}
10721092
}

extensions/gitpod-web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,6 @@
505505
"vscode-jsonrpc": "^5.0.1",
506506
"vscode-nls": "^5.0.0",
507507
"yauzl": "^2.9.2",
508-
"yazl": "^2.4.3"
508+
"yazl": "^2.5.1"
509509
}
510510
}

0 commit comments

Comments
 (0)