Skip to content

Commit 87dee2b

Browse files
author
Benjamin Pasero
committed
storage - telemetry around errors and perf
1 parent 87adbff commit 87dee2b

4 files changed

Lines changed: 112 additions & 13 deletions

File tree

src/vs/base/node/storage.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ export class Storage extends Disposable {
5858
this.pendingScheduler = new RunOnceScheduler(() => this.flushPending(), Storage.FLUSH_DELAY);
5959
}
6060

61+
get size(): number {
62+
return this.cache.size;
63+
}
64+
6165
init(): Promise<void> {
6266
if (this.state !== StorageState.None) {
6367
return Promise.resolve(); // either closed or already initialized

src/vs/platform/storage/electron-browser/storageService.ts

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { startsWith } from 'vs/base/common/strings';
1515
import { Action } from 'vs/base/common/actions';
1616
import { IWindowService } from 'vs/platform/windows/common/windows';
1717
import { localize } from 'vs/nls';
18+
import { mark } from 'vs/base/common/performance';
1819

1920
export class StorageService extends Disposable implements IStorageService {
2021
_serviceBrand: any;
@@ -27,6 +28,23 @@ export class StorageService extends Disposable implements IStorageService {
2728
private _onWillSaveState: Emitter<void> = this._register(new Emitter<void>());
2829
get onWillSaveState(): Event<void> { return this._onWillSaveState.event; }
2930

31+
private bufferedStorageErrors: (string | Error)[] = [];
32+
private _onStorageError: Emitter<string | Error> = this._register(new Emitter<string | Error>());
33+
get onStorageError(): Event<string | Error> {
34+
if (Array.isArray(this.bufferedStorageErrors)) {
35+
if (this.bufferedStorageErrors.length > 0) {
36+
const bufferedStorageErrors = this.bufferedStorageErrors;
37+
setTimeout(() => {
38+
this._onStorageError.fire(`[startup errors] ${bufferedStorageErrors.join('\n')}`);
39+
}, 0);
40+
}
41+
42+
this.bufferedStorageErrors = void 0;
43+
}
44+
45+
return this._onStorageError.event;
46+
}
47+
3048
private globalStorage: Storage;
3149
private workspaceStorage: Storage;
3250

@@ -40,7 +58,15 @@ export class StorageService extends Disposable implements IStorageService {
4058
const loggingOptions: IStorageLoggingOptions = {
4159
info: environmentService.verbose || environmentService.logStorage,
4260
infoLogger: msg => logService.info(msg),
43-
errorLogger: error => logService.error(error)
61+
errorLogger: error => {
62+
logService.error(error);
63+
64+
if (Array.isArray(this.bufferedStorageErrors)) {
65+
this.bufferedStorageErrors.push(error);
66+
} else {
67+
this._onStorageError.fire(error);
68+
}
69+
}
4470
};
4571

4672
const useInMemoryStorage = !!environmentService.extensionTestsPath; // never keep any state when running extension tests
@@ -61,7 +87,13 @@ export class StorageService extends Disposable implements IStorageService {
6187
}
6288

6389
init(): Promise<void> {
64-
return Promise.all([this.globalStorage.init(), this.workspaceStorage.init()]).then(() => void 0);
90+
mark('willInitGlobalStorage');
91+
mark('willInitWorkspaceStorage');
92+
93+
return Promise.all([
94+
this.globalStorage.init().then(() => mark('didInitGlobalStorage')),
95+
this.workspaceStorage.init().then(() => mark('didInitWorkspaceStorage'))
96+
]).then(() => void 0);
6597
}
6698

6799
get(key: string, scope: StorageScope, fallbackValue?: any): string {
@@ -100,6 +132,10 @@ export class StorageService extends Disposable implements IStorageService {
100132
return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage;
101133
}
102134

135+
getSize(scope: StorageScope): number {
136+
return scope === StorageScope.GLOBAL ? this.globalStorage.size : this.workspaceStorage.size;
137+
}
138+
103139
logStorage(): Promise<void> {
104140
return Promise.all([this.globalStorage.getItems(), this.workspaceStorage.getItems()]).then(items => {
105141
const safeParse = (value: string) => {
@@ -154,7 +190,7 @@ export class LogStorageAction extends Action {
154190
}
155191

156192
run(): Thenable<void> {
157-
this.storageService.logStorage();
193+
this.storageService.storage.logStorage();
158194

159195
return this.windowService.openDevTools();
160196
}
@@ -174,8 +210,7 @@ export class DelegatingStorageService extends Disposable implements IStorageServ
174210
constructor(
175211
@IStorageService private storageService: StorageService,
176212
@IStorageLegacyService private storageLegacyService: IStorageLegacyService,
177-
@ILogService private logService: ILogService,
178-
@IEnvironmentService environmentService: IEnvironmentService
213+
@ILogService private logService: ILogService
179214
) {
180215
super();
181216

@@ -196,6 +231,10 @@ export class DelegatingStorageService extends Disposable implements IStorageServ
196231
}));
197232
}
198233

234+
get storage(): StorageService {
235+
return this.storageService;
236+
}
237+
199238
get(key: string, scope: StorageScope, fallbackValue?: any): string {
200239
const localStorageValue = this.storageLegacyService.get(key, this.convertScope(scope), fallbackValue);
201240
const dbValue = this.storageService.get(key, scope, localStorageValue);
@@ -268,8 +307,4 @@ export class DelegatingStorageService extends Disposable implements IStorageServ
268307
private convertScope(scope: StorageScope): StorageLegacyScope {
269308
return scope === StorageScope.GLOBAL ? StorageLegacyScope.GLOBAL : StorageLegacyScope.WORKSPACE;
270309
}
271-
272-
logStorage(): Promise<void> {
273-
return this.storageService.logStorage();
274-
}
275310
}

src/vs/workbench/electron-browser/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ function openWorkbench(configuration: IWindowConfiguration): Promise<void> {
103103
]).then(services => {
104104
const workspaceService = services[0];
105105
const storageLegacyService = createStorageLegacyService(workspaceService, environmentService);
106-
const storageService = new DelegatingStorageService(services[1], storageLegacyService, logService, environmentService);
106+
const storageService = new DelegatingStorageService(services[1], storageLegacyService, logService);
107107

108108
return domContentLoaded().then(() => {
109109
perf.mark('willStartWorkbench');

src/vs/workbench/electron-browser/shell.ts

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ import { IBroadcastService, BroadcastService } from 'vs/platform/broadcast/elect
7575
import { HashService } from 'vs/workbench/services/hash/node/hashService';
7676
import { IHashService } from 'vs/workbench/services/hash/common/hashService';
7777
import { ILogService } from 'vs/platform/log/common/log';
78-
import { IStorageService } from 'vs/platform/storage/common/storage';
78+
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
79+
import { DelegatingStorageService } from 'vs/platform/storage/electron-browser/storageService';
7980
import { Event, Emitter } from 'vs/base/common/event';
8081
import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme';
8182
import { ILocalizationsChannel, LocalizationsChannelClient } from 'vs/platform/localizations/node/localizationsIpc';
@@ -109,7 +110,7 @@ export interface ICoreServices {
109110
environmentService: IEnvironmentService;
110111
logService: ILogService;
111112
storageLegacyService: IStorageLegacyService;
112-
storageService: IStorageService;
113+
storageService: DelegatingStorageService;
113114
}
114115

115116
/**
@@ -122,7 +123,7 @@ export class WorkbenchShell extends Disposable {
122123
get onShutdown(): Event<ShutdownEvent> { return this._onShutdown.event; }
123124

124125
private storageLegacyService: IStorageLegacyService;
125-
private storageService: IStorageService;
126+
private storageService: DelegatingStorageService;
126127
private environmentService: IEnvironmentService;
127128
private logService: ILogService;
128129
private configurationService: IConfigurationService;
@@ -217,6 +218,11 @@ export class WorkbenchShell extends Disposable {
217218
// Startup Telemetry
218219
this.logStartupTelemetry(startupInfos);
219220

221+
// Storage Telemetry (TODO@Ben remove me later, including storage errors)
222+
if (!this.environmentService.extensionTestsPath) {
223+
this.logStorageTelemetry();
224+
}
225+
220226
// Set lifecycle phase to `Runnning For A Bit` after a short delay
221227
let eventuallPhaseTimeoutHandle = runWhenIdle(() => {
222228
eventuallPhaseTimeoutHandle = void 0;
@@ -277,6 +283,60 @@ export class WorkbenchShell extends Disposable {
277283
perf.mark('didStartWorkbench');
278284
}
279285

286+
private logStorageTelemetry(): void {
287+
const globalStorageInitDuration = perf.getDuration('willInitGlobalStorage', 'didInitGlobalStorage');
288+
const workspaceStorageInitDuration = perf.getDuration('willInitWorkspaceStorage', 'didInitWorkspaceStorage');
289+
const workbenchLoadDuration = perf.getDuration('willLoadWorkbenchMain', 'didLoadWorkbenchMain');
290+
291+
/* __GDPR__
292+
"sqliteStorageTimers" : {
293+
"globalReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
294+
"workspaceReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
295+
"globalKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
296+
"workspaceKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
297+
"startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
298+
}
299+
*/
300+
this.telemetryService.publicLog('sqliteStorageTimers', {
301+
'globalReadTime': globalStorageInitDuration,
302+
'workspaceReadTime': workspaceStorageInitDuration,
303+
'workbenchRequireTime': workbenchLoadDuration,
304+
'globalKeys': this.storageService.storage.getSize(StorageScope.GLOBAL),
305+
'workspaceKeys': this.storageService.storage.getSize(StorageScope.WORKSPACE),
306+
'startupKind': this.lifecycleService.startupKind
307+
});
308+
309+
// Handle errors (avoid duplicates to reduce spam)
310+
const loggedStorageErrors = new Set<string>();
311+
this._register(this.storageService.storage.onStorageError(error => {
312+
const errorStr = `${error}`;
313+
314+
if (!loggedStorageErrors.has(errorStr)) {
315+
loggedStorageErrors.add(errorStr);
316+
317+
/* __GDPR__
318+
"sqliteStorageError" : {
319+
"globalReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
320+
"workspaceReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
321+
"globalKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
322+
"workspaceKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
323+
"startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
324+
"storageError": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
325+
}
326+
*/
327+
this.telemetryService.publicLog('sqliteStorageError', {
328+
'globalReadTime': globalStorageInitDuration,
329+
'workspaceReadTime': workspaceStorageInitDuration,
330+
'workbenchRequireTime': workbenchLoadDuration,
331+
'globalKeys': this.storageService.storage.getSize(StorageScope.GLOBAL),
332+
'workspaceKeys': this.storageService.storage.getSize(StorageScope.WORKSPACE),
333+
'startupKind': this.lifecycleService.startupKind,
334+
'storageError': errorStr
335+
});
336+
}
337+
}));
338+
}
339+
280340
private initServiceCollection(container: HTMLElement): [IInstantiationService, ServiceCollection] {
281341
const serviceCollection = new ServiceCollection();
282342
serviceCollection.set(IWorkspaceContextService, this.contextService);

0 commit comments

Comments
 (0)