Debt - Add dedicated Edit Sessions output channel (#154190)

* Create a separate log channel for edit sessions

* Move edit sessions services into contrib since they are not accessed from outside the contrib

* Remove redundant log message prefix

* Update test
This commit is contained in:
Joyce Er
2022-07-05 12:54:14 -07:00
committed by GitHub
parent 2c7201670f
commit 3e59037fa1
10 changed files with 95 additions and 30 deletions

View File

@@ -66,6 +66,7 @@ export interface IEnvironmentService {
// --- continue edit session
editSessionId?: string;
editSessionsLogResource: URI;
// --- extension development
debugExtensionHost: IExtensionHostDebugParams;

View File

@@ -80,6 +80,9 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron
@memoize
get userDataSyncLogResource(): URI { return URI.file(join(this.logsPath, 'userDataSync.log')); }
@memoize
get editSessionsLogResource(): URI { return URI.file(join(this.logsPath, 'editSessions.log')); }
@memoize
get sync(): 'on' | 'off' | undefined { return this.args.sync; }

View File

@@ -9,5 +9,6 @@ export const rendererLogChannelId = 'rendererLog';
export const extHostLogChannelId = 'extHostLog';
export const telemetryLogChannelId = 'telemetryLog';
export const userDataSyncLogChannelId = 'userDataSyncLog';
export const editSessionsLogChannelId = 'editSessionsSyncLog';
export const showWindowLogActionId = 'workbench.action.showWindowLog';

View File

@@ -38,6 +38,7 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution {
private registerCommonContributions(): void {
this.registerLogChannel(Constants.userDataSyncLogChannelId, nls.localize('userDataSyncLog', "Settings Sync"), this.environmentService.userDataSyncLogResource);
this.registerLogChannel(Constants.editSessionsLogChannelId, nls.localize('editSessionsLog', "Edit Sessions"), this.environmentService.editSessionsLogResource);
this.registerLogChannel(Constants.rendererLogChannelId, nls.localize('rendererLog', "Window"), this.environmentService.logFile);
const registerTelemetryChannel = () => {

View File

@@ -10,7 +10,7 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle
import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { localize } from 'vs/nls';
import { ISessionSyncWorkbenchService, Change, ChangeType, Folder, EditSession, FileType, EDIT_SESSION_SYNC_CATEGORY, EditSessionSchemaVersion } from 'vs/workbench/services/sessionSync/common/sessionSync';
import { ISessionSyncWorkbenchService, Change, ChangeType, Folder, EditSession, FileType, EDIT_SESSION_SYNC_CATEGORY, EditSessionSchemaVersion, IEditSessionsLogService } from 'vs/workbench/contrib/sessionSync/common/sessionSync';
import { ISCMRepository, ISCMService } from 'vs/workbench/contrib/scm/common/scm';
import { IFileService } from 'vs/platform/files/common/files';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
@@ -19,13 +19,12 @@ import { joinPath, relativePath } from 'vs/base/common/resources';
import { VSBuffer } from 'vs/base/common/buffer';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
import { SessionSyncWorkbenchService } from 'vs/workbench/services/sessionSync/browser/sessionSyncWorkbenchService';
import { SessionSyncWorkbenchService } from 'vs/workbench/contrib/sessionSync/browser/sessionSyncWorkbenchService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { UserDataSyncErrorCode, UserDataSyncStoreError } from 'vs/platform/userDataSync/common/userDataSync';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IDialogService, IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@@ -39,7 +38,9 @@ import { getVirtualWorkspaceLocation } from 'vs/platform/workspace/common/virtua
import { Schemas } from 'vs/base/common/network';
import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys';
import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
import { EditSessionsLogService } from 'vs/workbench/contrib/sessionSync/common/editSessionsLogService';
registerSingleton(IEditSessionsLogService, EditSessionsLogService);
registerSingleton(ISessionSyncWorkbenchService, SessionSyncWorkbenchService);
const resumeLatestCommand = {
@@ -61,6 +62,7 @@ const openLocalFolderCommand = {
title: { value: localize('continue edit session in local folder', "Open In Local Folder"), original: 'Open In Local Folder' },
};
const queryParamName = 'editSessionId';
const experimentalSettingName = 'workbench.experimental.editSessions.enabled';
export class SessionSyncContribution extends Disposable implements IWorkbenchContribution {
@@ -76,7 +78,7 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon
@ISCMService private readonly scmService: ISCMService,
@INotificationService private readonly notificationService: INotificationService,
@IDialogService private readonly dialogService: IDialogService,
@ILogService private readonly logService: ILogService,
@IEditSessionsLogService private readonly logService: IEditSessionsLogService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IProductService private readonly productService: IProductService,
@IConfigurationService private configurationService: IConfigurationService,
@@ -93,7 +95,7 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon
}
this.configurationService.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration('workbench.experimental.editSessions.enabled')) {
if (e.affectsConfiguration(experimentalSettingName)) {
this.registerActions();
}
});
@@ -129,7 +131,8 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon
}
private registerActions() {
if (this.registered || this.configurationService.getValue('workbench.experimental.editSessions.enabled') !== true) {
if (this.registered || this.configurationService.getValue(experimentalSettingName) !== true) {
this.logService.info(`Skipping registering edit sessions actions as edit sessions are currently disabled. Set ${experimentalSettingName} to enable edit sessions.`);
return;
}
@@ -167,11 +170,11 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon
query: uri.query.length > 0 ? (uri + `&${queryParamName}=${encodedRef}`) : `${queryParamName}=${encodedRef}`
});
} else {
that.logService.warn(`Edit Sessions: Failed to store edit session when invoking ${continueEditSessionCommand.id}.`);
that.logService.warn(`Failed to store edit session when invoking ${continueEditSessionCommand.id}.`);
}
// Open the URI
that.logService.info(`Edit Sessions: opening ${uri.toString()}`);
that.logService.info(`Opening ${uri.toString()}`);
await that.openerService.open(uri, { openExternal: true });
}
}));
@@ -221,7 +224,7 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon
async applyEditSession(ref?: string): Promise<void> {
if (ref !== undefined) {
this.logService.info(`Edit Sessions: Applying edit session with ref ${ref}.`);
this.logService.info(`Applying edit session with ref ${ref}.`);
}
const data = await this.sessionSyncWorkbenchService.read(ref);
@@ -231,7 +234,7 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon
} else {
this.notificationService.warn(localize('no edit session content for ref', 'Could not apply edit session contents for ID {0}.', ref));
}
this.logService.info(`Edit Sessions: Aborting applying edit session as no edit session content is available to be applied from ref ${ref}.`);
this.logService.info(`Aborting applying edit session as no edit session content is available to be applied from ref ${ref}.`);
return;
}
const editSession = data.editSession;
@@ -249,7 +252,7 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon
for (const folder of editSession.folders) {
const folderRoot = this.contextService.getWorkspace().folders.find((f) => f.name === folder.name);
if (!folderRoot) {
this.logService.info(`Edit Sessions: Skipping applying ${folder.workingChanges.length} changes from edit session with ref ${ref} as no corresponding workspace folder named ${folder.name} is currently open.`);
this.logService.info(`Skipping applying ${folder.workingChanges.length} changes from edit session with ref ${ref} as no corresponding workspace folder named ${folder.name} is currently open.`);
continue;
}
@@ -289,11 +292,11 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon
}
}
this.logService.info(`Edit Sessions: Deleting edit session with ref ${ref} after successfully applying it to current workspace...`);
this.logService.info(`Deleting edit session with ref ${ref} after successfully applying it to current workspace...`);
await this.sessionSyncWorkbenchService.delete(ref);
this.logService.info(`Edit Sessions: Deleted edit session with ref ${ref}.`);
this.logService.info(`Deleted edit session with ref ${ref}.`);
} catch (ex) {
this.logService.error('Edit Sessions: Failed to apply edit session, reason: ', (ex as Error).toString());
this.logService.error('Failed to apply edit session, reason: ', (ex as Error).toString());
this.notificationService.error(localize('apply failed', "Failed to apply your edit session."));
}
}
@@ -312,7 +315,7 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon
for (const uri of trackedUris) {
const workspaceFolder = this.contextService.getWorkspaceFolder(uri);
if (!workspaceFolder) {
this.logService.info(`Edit Sessions: Skipping working change ${uri.toString()} as no associated workspace folder was found.`);
this.logService.info(`Skipping working change ${uri.toString()} as no associated workspace folder was found.`);
continue;
}
@@ -341,7 +344,7 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon
}
if (!hasEdits) {
this.logService.info('Edit Sessions: Skipping storing edit session as there are no edits to store.');
this.logService.info('Skipping storing edit session as there are no edits to store.');
if (fromStoreCommand) {
this.notificationService.info(localize('no edits to store', 'Skipped storing edit session as there are no edits to store.'));
}
@@ -351,12 +354,12 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon
const data: EditSession = { folders, version: 1 };
try {
this.logService.info(`Edit Sessions: Storing edit session...`);
this.logService.info(`Storing edit session...`);
const ref = await this.sessionSyncWorkbenchService.write(data);
this.logService.info(`Edit Sessions: Stored edit session with ref ${ref}.`);
this.logService.info(`Stored edit session with ref ${ref}.`);
return ref;
} catch (ex) {
this.logService.error(`Edit Sessions: Failed to store edit session, reason: `, (ex as Error).toString());
this.logService.error(`Failed to store edit session, reason: `, (ex as Error).toString());
type UploadFailedEvent = { reason: string };
type UploadFailedClassification = {

View File

@@ -10,7 +10,6 @@ import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/act
import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IFileService } from 'vs/platform/files/common/files';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
import { IRequestService } from 'vs/platform/request/common/request';
@@ -19,7 +18,7 @@ import { IAuthenticationProvider } from 'vs/platform/userDataSync/common/userDat
import { UserDataSyncStoreClient } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
import { AuthenticationSession, AuthenticationSessionsChangeEvent, IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { EDIT_SESSIONS_SIGNED_IN, EditSession, EDIT_SESSION_SYNC_CATEGORY, ISessionSyncWorkbenchService, EDIT_SESSIONS_SIGNED_IN_KEY } from 'vs/workbench/services/sessionSync/common/sessionSync';
import { EDIT_SESSIONS_SIGNED_IN, EditSession, EDIT_SESSION_SYNC_CATEGORY, ISessionSyncWorkbenchService, EDIT_SESSIONS_SIGNED_IN_KEY, IEditSessionsLogService } from 'vs/workbench/contrib/sessionSync/common/sessionSync';
type ExistingSession = IQuickPickItem & { session: AuthenticationSession & { providerId: string } };
type AuthenticationProviderOption = IQuickPickItem & { provider: IAuthenticationProvider };
@@ -44,7 +43,7 @@ export class SessionSyncWorkbenchService extends Disposable implements ISessionS
@IAuthenticationService private readonly authenticationService: IAuthenticationService,
@IExtensionService private readonly extensionService: IExtensionService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@ILogService private readonly logService: ILogService,
@IEditSessionsLogService private readonly logService: IEditSessionsLogService,
@IProductService private readonly productService: IProductService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IRequestService private readonly requestService: IRequestService,
@@ -144,7 +143,7 @@ export class SessionSyncWorkbenchService extends Disposable implements ISessionS
if (!this.storeClient) {
this.storeClient = new UserDataSyncStoreClient(URI.parse(this.serverConfiguration.url), this.productService, this.requestService, this.logService, this.environmentService, this.fileService, this.storageService);
this._register(this.storeClient.onTokenFailed(() => {
this.logService.info('Edit Sessions: clearing edit sessions authentication preference because of successive token failures.');
this.logService.info('Clearing edit sessions authentication preference because of successive token failures.');
this.clearAuthenticationPreference();
}));
}
@@ -157,10 +156,10 @@ export class SessionSyncWorkbenchService extends Disposable implements ISessionS
// If the user signed in previously and the session is still available, reuse that without prompting the user again
const existingSessionId = this.existingSessionId;
if (existingSessionId) {
this.logService.trace(`Edit Sessions: Searching for existing authentication session with ID ${existingSessionId}`);
this.logService.trace(`Searching for existing authentication session with ID ${existingSessionId}`);
const existing = await this.getExistingSession();
if (existing !== undefined) {
this.logService.trace(`Edit Sessions: Found existing authentication session with ID ${existingSessionId}`);
this.logService.trace(`Found existing authentication session with ID ${existingSessionId}`);
this.#authenticationInfo = { sessionId: existing.session.id, token: existing.session.accessToken, providerId: existing.session.providerId };
this.storeClient.setAuthToken(this.#authenticationInfo.token, this.#authenticationInfo.providerId);
return true;
@@ -173,7 +172,7 @@ export class SessionSyncWorkbenchService extends Disposable implements ISessionS
this.#authenticationInfo = { sessionId: session.id, token: session.accessToken, providerId: session.providerId };
this.storeClient.setAuthToken(this.#authenticationInfo.token, this.#authenticationInfo.providerId);
this.existingSessionId = session.id;
this.logService.trace(`Edit Sessions: Saving authentication session preference for ID ${session.id}.`);
this.logService.trace(`Saving authentication session preference for ID ${session.id}.`);
return true;
}
@@ -311,7 +310,7 @@ export class SessionSyncWorkbenchService extends Disposable implements ISessionS
const previousSessionId = this.#authenticationInfo?.sessionId;
if (previousSessionId !== newSessionId) {
this.logService.trace(`Edit Sessions: resetting authentication state because authentication session ID preference changed from ${previousSessionId} to ${newSessionId}.`);
this.logService.trace(`Resetting authentication state because authentication session ID preference changed from ${previousSessionId} to ${newSessionId}.`);
this.#authenticationInfo = undefined;
this.initialized = false;
}

View File

@@ -0,0 +1,50 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { AbstractLogger, ILogger, ILoggerService } from 'vs/platform/log/common/log';
import { IEditSessionsLogService } from 'vs/workbench/contrib/sessionSync/common/sessionSync';
export class EditSessionsLogService extends AbstractLogger implements IEditSessionsLogService {
declare readonly _serviceBrand: undefined;
private readonly logger: ILogger;
constructor(
@ILoggerService loggerService: ILoggerService,
@IEnvironmentService environmentService: IEnvironmentService
) {
super();
this.logger = this._register(loggerService.createLogger(environmentService.editSessionsLogResource, { name: 'editsessions' }));
}
trace(message: string, ...args: any[]): void {
this.logger.trace(message, ...args);
}
debug(message: string, ...args: any[]): void {
this.logger.debug(message, ...args);
}
info(message: string, ...args: any[]): void {
this.logger.info(message, ...args);
}
warn(message: string, ...args: any[]): void {
this.logger.warn(message, ...args);
}
error(message: string | Error, ...args: any[]): void {
this.logger.error(message, ...args);
}
critical(message: string | Error, ...args: any[]): void {
this.logger.critical(message, ...args);
}
flush(): void {
this.logger.flush();
}
}

View File

@@ -7,6 +7,7 @@ import { localize } from 'vs/nls';
import { ILocalizedString } from 'vs/platform/action/common/action';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
export const EDIT_SESSION_SYNC_CATEGORY: ILocalizedString = {
original: 'Edit Sessions',
@@ -22,6 +23,9 @@ export interface ISessionSyncWorkbenchService {
delete(ref: string): Promise<void>;
}
export const IEditSessionsLogService = createDecorator<IEditSessionsLogService>('IEditSessionsLogService');
export interface IEditSessionsLogService extends ILogService { }
export enum ChangeType {
Addition = 1,
Deletion = 2,

View File

@@ -9,7 +9,7 @@ import { FileService } from 'vs/platform/files/common/fileService';
import { Schemas } from 'vs/base/common/network';
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { NullLogService, ILogService } from 'vs/platform/log/common/log';
import { NullLogService } from 'vs/platform/log/common/log';
import { SessionSyncContribution } from 'vs/workbench/contrib/sessionSync/browser/sessionSync.contribution';
import { ProgressService } from 'vs/workbench/services/progress/browser/progressService';
import { IProgressService } from 'vs/platform/progress/common/progress';
@@ -21,7 +21,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace
import { mock } from 'vs/base/test/common/mock';
import * as sinon from 'sinon';
import * as assert from 'assert';
import { ChangeType, FileType, ISessionSyncWorkbenchService } from 'vs/workbench/services/sessionSync/common/sessionSync';
import { ChangeType, FileType, IEditSessionsLogService, ISessionSyncWorkbenchService } from 'vs/workbench/contrib/sessionSync/common/sessionSync';
import { URI } from 'vs/base/common/uri';
import { joinPath } from 'vs/base/common/resources';
import { INotificationService } from 'vs/platform/notification/common/notification';
@@ -52,7 +52,7 @@ suite('Edit session sync', () => {
fileService.registerProvider(Schemas.file, fileSystemProvider);
// Stub out all services
instantiationService.stub(ILogService, logService);
instantiationService.stub(IEditSessionsLogService, logService);
instantiationService.stub(IFileService, fileService);
instantiationService.stub(INotificationService, new TestNotificationService());
instantiationService.stub(ISessionSyncWorkbenchService, new class extends mock<ISessionSyncWorkbenchService>() { });

View File

@@ -83,6 +83,9 @@ export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvi
@memoize
get userDataSyncLogResource(): URI { return joinPath(this.logsHome, 'userDataSync.log'); }
@memoize
get editSessionsLogResource(): URI { return joinPath(this.logsHome, 'editSessions.log'); }
@memoize
get sync(): 'on' | 'off' | undefined { return undefined; }