Skip to content

Commit

Permalink
Implement Collect all logs command
Browse files Browse the repository at this point in the history
  • Loading branch information
jeanp413 committed Apr 26, 2022
1 parent f59d688 commit 2e8070a
Show file tree
Hide file tree
Showing 11 changed files with 262 additions and 42 deletions.
24 changes: 16 additions & 8 deletions extensions/gitpod-remote/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ export async function activate(context: vscode.ExtensionContext) {
if (!gitpodContext) {
return;
}

if (vscode.extensions.getExtension('gitpod.gitpod')) {
try {
await util.promisify(cp.exec)('code --uninstall-extension gitpod.gitpod');
vscode.commands.executeCommand('workbench.action.reloadWindow');
} catch (e) {
gitpodContext.output.appendLine('failed to uninstall gitpod.gitpod: ' + e);
gitpodContext.logger.error('failed to uninstall gitpod.gitpod:', e);
}
return;
}
Expand All @@ -43,6 +44,11 @@ export async function activate(context: vscode.ExtensionContext) {
// and gitpod.gitpod to disable auto tunneling from the current local machine.
vscode.commands.executeCommand('gitpod.api.autoTunnel', gitpodContext.info.getGitpodHost(), gitpodContext.info.getInstanceId(), false);

// For collecting logs, will be called by gitpod-desktop extension;
context.subscriptions.push(vscode.commands.registerCommand('__gitpod.getGitpodRemoteLogsUri', () => {
return context.logUri;
}));

// TODO
// - auth?
// - .gitpod.yml validations
Expand Down Expand Up @@ -87,7 +93,7 @@ export function openWorkspaceLocation(context: GitpodExtensionContext): boolean
}

export async function installInitialExtensions(context: GitpodExtensionContext): Promise<void> {
context.output.appendLine('installing initial extensions...');
context.logger.info('installing initial extensions...');
const extensions: (vscode.Uri | string)[] = [];
try {
const workspaceContextUri = vscode.Uri.parse(context.info.getWorkspaceContextUrl());
Expand Down Expand Up @@ -125,10 +131,10 @@ export async function installInitialExtensions(context: GitpodExtensionContext):
}
}
} catch (e) {
context.output.appendLine('failed to detect workspace context dependent extensions:' + e);
context.logger.error('failed to detect workspace context dependent extensions:', e);
console.error('failed to detect workspace context dependent extensions:', e);
}
context.output.appendLine('initial extensions: ' + extensions);
context.logger.info('initial extensions:', extensions);
if (extensions.length) {
let cause;
try {
Expand All @@ -138,11 +144,11 @@ export async function installInitialExtensions(context: GitpodExtensionContext):
cause = e;
}
if (cause) {
context.output.appendLine('failed to install initial extensions: ' + cause);
context.logger.error('failed to install initial extensions:', cause);
console.error('failed to install initial extensions: ', cause);
}
}
context.output.appendLine('initial extensions installed');
context.logger.info('initial extensions installed');
}

export function registerHearbeat(context: GitpodExtensionContext): void {
Expand All @@ -152,11 +158,13 @@ export function registerHearbeat(context: GitpodExtensionContext): void {
};
const sendHeartBeat = async (wasClosed?: true) => {
const suffix = wasClosed ? 'was closed heartbeat' : 'heartbeat';
if (wasClosed) {
context.logger.trace('sending ' + suffix);
}
try {
context.output.appendLine('sending ' + suffix);
await context.gitpod.server.sendHeartBeat({ instanceId: context.info.getInstanceId(), wasClosed });
} catch (err) {
context.output.appendLine(`failed to send ${suffix}: ` + err);
context.logger.error(`failed to send ${suffix}:`, err);
console.error(`failed to send ${suffix}`, err);
}
};
Expand Down
67 changes: 67 additions & 0 deletions extensions/gitpod-shared/src/common/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Gitpod. All rights reserved.
*--------------------------------------------------------------------------------------------*/

import * as vscode from 'vscode';

type LogLevel = 'Trace' | 'Info' | 'Error' | 'Warn' | 'Log';

export default class Log {
private output: vscode.OutputChannel;

constructor(name: string) {
this.output = vscode.window.createOutputChannel(name);
}

private data2String(data: any): string {
if (data instanceof Error) {
return data.stack || data.message;
}
if (data.success === false && data.message) {
return data.message;
}
return data.toString();
}

public trace(message: string, data?: any): void {
this.logLevel('Trace', message, data);
}

public info(message: string, data?: any): void {
this.logLevel('Info', message, data);
}

public error(message: string, data?: any): void {
this.logLevel('Error', message, data);
}

public warn(message: string, data?: any): void {
this.logLevel('Warn', message, data);
}

public log(message: string, data?: any): void {
this.logLevel('Log', message, data);
}

public logLevel(level: LogLevel, message: string, data?: any): void {
this.output.appendLine(`[${level} - ${this.now()}] ${message}`);
if (data) {
this.output.appendLine(this.data2String(data));
}
}

private now(): string {
const now = new Date();
return padLeft(now.getUTCHours() + '', 2, '0')
+ ':' + padLeft(now.getMinutes() + '', 2, '0')
+ ':' + padLeft(now.getUTCSeconds() + '', 2, '0') + '.' + now.getMilliseconds();
}

public show() {
this.output.show();
}
}

function padLeft(s: string, n: number, pad = ' ') {
return pad.repeat(Math.max(0, n - s.length)) + s;
}
23 changes: 16 additions & 7 deletions extensions/gitpod-shared/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,15 @@ export async function setupGitpodContext(context: vscode.ExtensionContext): Prom
}

const gitpodContext = await createGitpodExtensionContext(context);
vscode.commands.executeCommand('setContext', 'gitpod.inWorkspace', !!gitpodContext);
if (!gitpodContext) {
vscode.commands.executeCommand('setContext', 'gitpod.inWorkspace', false);
return undefined;
}
vscode.commands.executeCommand('setContext', 'gitpod.inWorkspace', true);

logContextInfo(gitpodContext);

vscode.commands.executeCommand('setContext', 'gitpod.ideAlias', gitpodContext.info.getIdeAlias());
if (vscode.env.uiKind === vscode.UIKind.Web) {
vscode.commands.executeCommand('setContext', 'gitpod.UIKind', 'web');
} else if (vscode.env.uiKind === vscode.UIKind.Desktop) {
vscode.commands.executeCommand('setContext', 'gitpod.UIKind', 'desktop');
}
vscode.commands.executeCommand('setContext', 'gitpod.UIKind', vscode.env.uiKind === vscode.UIKind.Web ? 'web' : 'desktop');

registerUsageAnalytics(gitpodContext);
registerWorkspaceCommands(gitpodContext);
Expand All @@ -40,3 +37,15 @@ function registerUsageAnalytics(context: GitpodExtensionContext): void {
context.fireAnalyticsEvent({ eventName: 'vscode_session', properties: {} });
}

function logContextInfo(context: GitpodExtensionContext) {
context.logger.info(`VSCODE_MACHINE_ID: ${vscode.env.machineId}`);
context.logger.info(`VSCODE_SESSION_ID: ${vscode.env.sessionId}`);
context.logger.info(`VSCODE_VERSION: ${vscode.version}`);
context.logger.info(`VSCODE_APP_NAME: ${vscode.env.appName}`);
context.logger.info(`VSCODE_APP_HOST: ${vscode.env.appHost}`);
context.logger.info(`VSCODE_UI_KIND: ${vscode.env.uiKind === vscode.UIKind.Web ? 'web' : 'desktop'}`);

context.logger.info(`GITPOD_WORKSPACE_CONTEXT_URL: ${context.info.getWorkspaceContextUrl()}`);
context.logger.info(`GITPOD_INSTANCE_ID: ${context.info.getInstanceId()}`);
context.logger.info(`GITPOD_WORKSPACE_URL: ${context.info.getWorkspaceUrl()}`);
}
44 changes: 32 additions & 12 deletions extensions/gitpod-shared/src/features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import WebSocket = require('ws');
import { BaseGitpodAnalyticsEventPropeties, GitpodAnalyticsEvent } from './analytics';
import * as uuid from 'uuid';
import { RemoteTrackMessage } from '@gitpod/gitpod-protocol/lib/analytics';
import Log from './common/logger';

export class SupervisorConnection {
readonly deadlines = {
Expand Down Expand Up @@ -94,7 +95,7 @@ export class GitpodExtensionContext implements vscode.ExtensionContext {
readonly user: Promise<User>,
readonly instanceListener: Promise<WorkspaceInstanceUpdateListener>,
readonly workspaceOwned: Promise<boolean>,
readonly output: vscode.OutputChannel,
readonly logger: Log,
readonly ipcHookCli: string | undefined
) {
this.workspaceContextUrl = vscode.Uri.parse(info.getWorkspaceContextUrl());
Expand Down Expand Up @@ -168,7 +169,7 @@ export class GitpodExtensionContext implements vscode.ExtensionContext {
await Promise.allSettled(this.pendingWillCloseSocket.map(f => f()));
webSocket.close();
} catch (e) {
this.output.appendLine('failed to dispose context: ' + e);
this.logger.error('failed to dispose context:', e);
console.error('failed to dispose context:', e);
}
})();
Expand All @@ -193,20 +194,20 @@ export class GitpodExtensionContext implements vscode.ExtensionContext {
}
};
if (this.devMode && vscode.env.uiKind === vscode.UIKind.Web) {
this.output.appendLine(`ANALYTICS: ${JSON.stringify(msg)} `);
this.logger.trace(`ANALYTICS: ${JSON.stringify(msg)} `);
return Promise.resolve();
}
try {
await this.gitpod.server.trackEvent(msg);
} catch (e) {
this.output.appendLine('failed to track event: ' + e);
this.logger.error('failed to track event:', e);
console.error('failed to track event:', e);
}
}
}

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

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

const ipcHookCli = installCLIProxy(context, output);
const ipcHookCli = installCLIProxy(context, logger);

const config = await import('./gitpod-plugin-model');
return new GitpodExtensionContext(
Expand All @@ -324,7 +325,7 @@ export async function createGitpodExtensionContext(context: vscode.ExtensionCont
pendingGetUser,
pendingInstanceListener,
pendingWorkspaceOwned,
output,
logger,
ipcHookCli
);
}
Expand Down Expand Up @@ -722,7 +723,7 @@ export function registerDefaultLayout(context: GitpodExtensionContext): void {
}
}

function installCLIProxy(context: vscode.ExtensionContext, output: vscode.OutputChannel): string | undefined {
function installCLIProxy(context: vscode.ExtensionContext, logger: Log): string | undefined {
const vscodeIpcHookCli = process.env['VSCODE_IPC_HOOK_CLI'];
if (!vscodeIpcHookCli) {
return undefined;
Expand Down Expand Up @@ -772,7 +773,7 @@ function installCLIProxy(context: vscode.ExtensionContext, output: vscode.Output
fs.promises.unlink(ipcHookCli)
));
}).catch(e => {
output.appendLine('failed to start cli proxy: ' + e);
logger.error('failed to start cli proxy: ' + e);
console.error('failed to start cli proxy:' + e);
});

Expand Down Expand Up @@ -815,6 +816,7 @@ export async function registerTasks(context: GitpodExtensionContext, createTermi
});
} catch (err) {
if (!('code' in err && err.code === grpc.status.CANCELLED)) {
context.logger.error('code server: listening task updates failed:', err);
console.error('code server: listening task updates failed:', err);
}
} finally {
Expand All @@ -824,6 +826,11 @@ export async function registerTasks(context: GitpodExtensionContext, createTermi
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
context.logger.trace('Task status:', [...tasks.values()].map(status => {
const stateMap = { [TaskState.OPENING]: 'CLOSED', [TaskState.RUNNING]: 'RUNNING', [TaskState.CLOSED]: 'CLOSED' };
return `\t${status.getTerminal()} => ${stateMap[status.getState()]}`;
}).join('\n'));

if (token.isCancellationRequested) {
return;
}
Expand All @@ -837,6 +844,7 @@ export async function registerTasks(context: GitpodExtensionContext, createTermi
taskTerminals.set(term.getAlias(), term);
}
} catch (e) {
context.logger.error('failed to list task terminals:', e);
console.error('failed to list task terminals:', e);
}

Expand Down Expand Up @@ -922,6 +930,7 @@ function createTaskPty(alias: string, context: GitpodExtensionContext, contextTo
} catch (e) {
notFound = 'code' in e && e.code === grpc.status.NOT_FOUND;
if (!token.isCancellationRequested && !notFound && !('code' in e && e.code === grpc.status.CANCELLED)) {
context.logger.error(`${alias} terminal: listening failed:`, e);
console.error(`${alias} terminal: listening failed:`, e);
}
} finally {
Expand All @@ -931,9 +940,16 @@ function createTaskPty(alias: string, context: GitpodExtensionContext, contextTo
return;
}
if (notFound) {
context.logger.trace(`${alias} terminal not found`);
onDidCloseEmitter.fire();
} else if (typeof exitCode === 'number') {
tokenSource.cancel();
return;
}
if (typeof exitCode === 'number') {
context.logger.trace(`${alias} terminal exited with ${exitCode}`);
onDidCloseEmitter.fire(exitCode);
tokenSource.cancel();
return;
}
await new Promise(resolve => setTimeout(resolve, 2000));
}
Expand All @@ -958,10 +974,12 @@ function createTaskPty(alias: string, context: GitpodExtensionContext, contextTo
await util.promisify(context.supervisor.terminal.shutdown.bind(context.supervisor.terminal, request, context.supervisor.metadata, {
deadline: Date.now() + context.supervisor.deadlines.short
}))();
context.logger.trace(`${alias} terminal closed`);
} catch (e) {
if (e && e.code === grpc.status.NOT_FOUND) {
// Swallow, the pty has already been killed
} else {
context.logger.error(`${alias} terminal: shutdown failed:`, e);
console.error(`${alias} terminal: shutdown failed:`, e);
}
}
Expand All @@ -985,6 +1003,7 @@ function createTaskPty(alias: string, context: GitpodExtensionContext, contextTo
}))();
} catch (e) {
if (e && e.code !== grpc.status.NOT_FOUND) {
context.logger.error(`${alias} terminal: write failed:`, e);
console.error(`${alias} terminal: write failed:`, e);
}
}
Expand Down Expand Up @@ -1012,6 +1031,7 @@ function createTaskPty(alias: string, context: GitpodExtensionContext, contextTo
}))();
} catch (e) {
if (e && e.code !== grpc.status.NOT_FOUND) {
context.logger.error(`${alias} terminal: resize failed:`, e);
console.error(`${alias} terminal: resize failed:`, e);
}
}
Expand Down Expand Up @@ -1066,7 +1086,7 @@ async function updateIpcHookCli(context: GitpodExtensionContext): Promise<void>
req.end();
});
} catch (e) {
context.output.appendLine('Failed to update gitpod ipc hook cli: ' + e);
context.logger.error('Failed to update gitpod ipc hook cli:', e);
console.error('Failed to update gitpod ipc hook cli:', e);
}
}
2 changes: 1 addition & 1 deletion extensions/gitpod-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,6 @@
"vscode-jsonrpc": "^5.0.1",
"vscode-nls": "^5.0.0",
"yauzl": "^2.9.2",
"yazl": "^2.4.3"
"yazl": "^2.5.1"
}
}
Loading

0 comments on commit 2e8070a

Please sign in to comment.