提交 324e1095 编写于 作者: R Rob Lourens

#56950 - add telemetry for non-EH search

上级 12e906cd
......@@ -155,41 +155,40 @@ export interface ISearchProgressItem extends IFileMatch, IProgress {
export interface ISearchCompleteStats {
limitHit?: boolean;
stats?: ISearchStats;
stats?: IFileSearchStats;
}
export interface ISearchComplete extends ISearchCompleteStats {
results: IFileMatch[];
}
export interface ISearchStats {
export interface IFileSearchStats {
fromCache: boolean;
cacheOrSearchEngineStats: ISearchEngineStats | ICachedSearchStats;
resultCount: number;
unsortedResultTime?: number;
sortedResultTime?: number;
workspaceFolderCount: number;
type: 'fileIndexProver' | 'fileSearchProvider' | 'searchProcess';
endToEndTime?: number;
sortingTime?: number;
}
export interface ICachedSearchStats extends ISearchStats {
cacheLookupStartTime: number;
cacheFilterStartTime: number;
cacheLookupResultTime: number;
export interface ICachedSearchStats {
cacheWasResolved: boolean;
cacheLookupTime: number;
cacheFilterTime: number;
cacheEntryCount: number;
joined?: ISearchStats;
}
export interface IUncachedSearchStats extends ISearchStats {
export interface ISearchEngineStats {
traversal: string;
errors: string[];
fileWalkStartTime: number;
fileWalkResultTime: number;
fileWalkTime: number;
directoriesWalked: number;
filesWalked: number;
cmdForkStartTime?: number;
cmdForkResultTime?: number;
cmdTime: number;
cmdResultCount?: number;
}
// ---- very simple implementation of the search model --------------------
export class FileMatch implements IFileMatch {
......
......@@ -25,7 +25,7 @@ import { EditorInput, IWorkbenchEditorConfiguration } from 'vs/workbench/common/
import { IResourceInput } from 'vs/platform/editor/common/editor';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IQueryOptions, ISearchService, ISearchStats, ISearchQuery, ISearchComplete } from 'vs/platform/search/common/search';
import { IQueryOptions, ISearchService, IFileSearchStats, ISearchQuery, ISearchComplete } from 'vs/platform/search/common/search';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IRange } from 'vs/editor/common/core/range';
......@@ -38,7 +38,7 @@ import { untildify } from 'vs/base/common/labels';
export class FileQuickOpenModel extends QuickOpenModel {
constructor(entries: QuickOpenEntry[], public stats?: ISearchStats) {
constructor(entries: QuickOpenEntry[], public stats?: IFileSearchStats) {
super(entries);
}
}
......
......@@ -16,7 +16,7 @@ import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IFileMatch, IFolderQuery, ILineMatch, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService, IUncachedSearchStats } from 'vs/platform/search/common/search';
import { IFileMatch, IFileSearchStats, IFolderQuery, ILineMatch, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService } from 'vs/platform/search/common/search';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { SearchModel } from 'vs/workbench/parts/search/common/searchModel';
......@@ -47,15 +47,19 @@ suite('SearchModel', () => {
let instantiationService: TestInstantiationService;
let restoreStubs: sinon.SinonStub[];
const testSearchStats: IUncachedSearchStats = {
const testSearchStats: IFileSearchStats = {
fromCache: false,
resultCount: 4,
traversal: 'node',
errors: [],
fileWalkStartTime: 0,
fileWalkResultTime: 1,
directoriesWalked: 2,
filesWalked: 3
resultCount: 1,
type: 'searchProcess',
workspaceFolderCount: 1,
cacheOrSearchEngineStats: {
traversal: 'node',
fileWalkTime: 0,
cmdTime: 0,
cmdResultCount: 0,
directoriesWalked: 2,
filesWalked: 3
}
};
const folderQueries: IFolderQuery[] = [
......
......@@ -6,28 +6,27 @@
'use strict';
import * as childProcess from 'child_process';
import { StringDecoder, NodeStringDecoder } from 'string_decoder';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import * as fs from 'fs';
import * as path from 'path';
import { isEqualOrParent } from 'vs/base/common/paths';
import { Readable } from 'stream';
import { TPromise } from 'vs/base/common/winjs.base';
import * as objects from 'vs/base/common/objects';
import { NodeStringDecoder, StringDecoder } from 'string_decoder';
import * as arrays from 'vs/base/common/arrays';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import * as glob from 'vs/base/common/glob';
import * as normalization from 'vs/base/common/normalization';
import * as objects from 'vs/base/common/objects';
import { isEqualOrParent } from 'vs/base/common/paths';
import * as platform from 'vs/base/common/platform';
import * as strings from 'vs/base/common/strings';
import * as normalization from 'vs/base/common/normalization';
import * as types from 'vs/base/common/types';
import * as glob from 'vs/base/common/glob';
import { IProgress, IUncachedSearchStats } from 'vs/platform/search/common/search';
import { TPromise } from 'vs/base/common/winjs.base';
import * as extfs from 'vs/base/node/extfs';
import * as flow from 'vs/base/node/flow';
import { IRawFileMatch, IRawSearch, ISearchEngine, IFolderSearch, ISerializedSearchSuccess } from './search';
import { IProgress, ISearchEngineStats } from 'vs/platform/search/common/search';
import { spawnRipgrepCmd } from './ripgrepFileSearch';
import { rgErrorMsgForDisplay } from './ripgrepTextSearch';
import { IFolderSearch, IRawFileMatch, IRawSearch, ISearchEngine, ISearchEngineSuccess } from './search';
import { StopWatch } from 'vs/base/common/stopwatch';
enum Traversal {
Node = 1,
......@@ -60,13 +59,12 @@ export class FileWalker {
private isLimitHit: boolean;
private resultCount: number;
private isCanceled: boolean;
private fileWalkStartTime: number;
private fileWalkSW: StopWatch;
private directoriesWalked: number;
private filesWalked: number;
private traversal: Traversal;
private errors: string[];
private cmdForkStartTime: number;
private cmdForkResultTime: number;
private cmdSW: StopWatch;
private cmdResultCount: number;
private folderExcludePatterns: Map<string, AbsoluteAndRelativeParsedExpression>;
......@@ -120,7 +118,7 @@ export class FileWalker {
}
public walk(folderQueries: IFolderSearch[], extraFiles: string[], onResult: (result: IRawFileMatch) => void, onMessage: (message: IProgress) => void, done: (error: Error, isLimitHit: boolean) => void): void {
this.fileWalkStartTime = Date.now();
this.fileWalkSW = StopWatch.create();
// Support that the file pattern is a full path to a file that exists
if (this.isCanceled) {
......@@ -160,7 +158,7 @@ export class FileWalker {
const isNodeTraversal = traverse === this.nodeJSTraversal;
if (!isNodeTraversal) {
this.cmdForkStartTime = Date.now();
this.cmdSW = StopWatch.create();
}
// For each root folder
......@@ -225,6 +223,7 @@ export class FileWalker {
}
process.on('exit', killCmd);
this.cmdResultCount = 0;
this.collectStdout(cmd, 'utf8', useRipgrep, onMessage, (err: Error, stdout?: string, last?: boolean) => {
if (err) {
done(err);
......@@ -360,7 +359,10 @@ export class FileWalker {
let onData = (err: Error, stdout?: string, last?: boolean) => {
if (err || last) {
onData = () => { };
this.cmdForkResultTime = Date.now();
if (this.cmdSW) {
this.cmdSW.stop();
}
}
cb(err, stdout, last);
};
......@@ -508,18 +510,13 @@ export class FileWalker {
});
}
public getStats(): IUncachedSearchStats {
public getStats(): ISearchEngineStats {
return {
fromCache: false,
cmdTime: this.cmdSW.elapsed(),
fileWalkTime: this.fileWalkSW.elapsed(),
traversal: Traversal[this.traversal],
errors: this.errors,
fileWalkStartTime: this.fileWalkStartTime,
fileWalkResultTime: Date.now(),
directoriesWalked: this.directoriesWalked,
filesWalked: this.filesWalked,
resultCount: this.resultCount,
cmdForkStartTime: this.cmdForkStartTime,
cmdForkResultTime: this.cmdForkResultTime,
cmdResultCount: this.cmdResultCount
};
}
......@@ -678,10 +675,9 @@ export class Engine implements ISearchEngine<IRawFileMatch> {
this.walker = new FileWalker(config);
}
public search(onResult: (result: IRawFileMatch) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISerializedSearchSuccess) => void): void {
public search(onResult: (result: IRawFileMatch) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISearchEngineSuccess) => void): void {
this.walker.walk(this.folderQueries, this.extraFiles, onResult, onProgress, (err: Error, isLimitHit: boolean) => {
done(err, {
type: 'success',
limitHit: isLimitHit,
stats: this.walker.getStats()
});
......
......@@ -14,16 +14,17 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { canceled, isPromiseCanceledError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import * as objects from 'vs/base/common/objects';
import { StopWatch } from 'vs/base/common/stopwatch';
import * as strings from 'vs/base/common/strings';
import { TPromise } from 'vs/base/common/winjs.base';
import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer';
import { MAX_FILE_SIZE } from 'vs/platform/files/node/files';
import { ICachedSearchStats, IProgress } from 'vs/platform/search/common/search';
import { ICachedSearchStats, IFileSearchStats, IProgress } from 'vs/platform/search/common/search';
import { Engine as FileSearchEngine, FileWalker } from 'vs/workbench/services/search/node/fileSearch';
import { RipgrepEngine } from 'vs/workbench/services/search/node/ripgrepTextSearch';
import { Engine as TextSearchEngine } from 'vs/workbench/services/search/node/textSearch';
import { TextSearchWorkerProvider } from 'vs/workbench/services/search/node/textSearchWorkerProvider';
import { IFileSearchProgressItem, IRawFileMatch, IRawSearch, IRawSearchService, ISearchEngine, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess, ITelemetryEvent } from './search';
import { IFileSearchProgressItem, IRawFileMatch, IRawSearch, IRawSearchService, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from './search';
gracefulFs.gracefulify(fs);
......@@ -38,9 +39,6 @@ export class SearchService implements IRawSearchService {
private textSearchWorkerProvider: TextSearchWorkerProvider;
private _onTelemetry = new Emitter<ITelemetryEvent>();
readonly onTelemetry: Event<ITelemetryEvent> = this._onTelemetry.event;
public fileSearch(config: IRawSearch, batchSize = SearchService.BATCH_SIZE): Event<ISerializedSearchProgressItem | ISerializedSearchComplete> {
let promise: CancelablePromise<ISerializedSearchSuccess>;
......@@ -138,10 +136,13 @@ export class SearchService implements IRawSearchService {
}
doFileSearch(EngineClass: { new(config: IRawSearch): ISearchEngine<IRawFileMatch>; }, config: IRawSearch, progressCallback: IProgressCallback, token?: CancellationToken, batchSize?: number): TPromise<ISerializedSearchSuccess> {
let resultCount = 0;
const fileProgressCallback: IFileProgressCallback = progress => {
if (Array.isArray(progress)) {
resultCount += progress.length;
progressCallback(progress.map(m => this.rawMatchToSearchItem(m)));
} else if ((<IRawFileMatch>progress).relativePath) {
resultCount++;
progressCallback(this.rawMatchToSearchItem(<IRawFileMatch>progress));
} else {
progressCallback(<IProgress>progress);
......@@ -167,7 +168,19 @@ export class SearchService implements IRawSearchService {
const engine = new EngineClass(config);
return this.doSearch(engine, fileProgressCallback, batchSize, token);
return this.doSearch(engine, fileProgressCallback, batchSize, token).then(complete => {
return <ISerializedSearchSuccess>{
limitHit: complete.limitHit,
type: 'success',
stats: {
cacheOrSearchEngineStats: complete.stats,
fromCache: false,
resultCount,
sortingTime: undefined,
workspaceFolderCount: config.folderQueries.length
}
};
});
}
private rawMatchToSearchItem(match: IRawFileMatch): ISerializedFileMatch {
......@@ -190,13 +203,7 @@ export class SearchService implements IRawSearchService {
};
return this.doSearch(engine, innerProgressCallback, -1, token)
.then<[ISerializedSearchSuccess, IRawFileMatch[]]>(result => {
// __GDPR__TODO__ classify event
this._onTelemetry.fire({
eventName: 'fileSearch',
data: result.stats
});
.then<[ISearchEngineSuccess, IRawFileMatch[]]>(result => {
return [result, results];
});
});
......@@ -204,30 +211,41 @@ export class SearchService implements IRawSearchService {
let cache: Cache;
if (config.cacheKey) {
cache = this.getOrCreateCache(config.cacheKey);
cache.resultsToSearchCache[config.filePattern] = {
const cacheRow: ICacheRow = {
promise: allResultsPromise,
event: emitter.event
event: emitter.event,
resolved: false
};
allResultsPromise.then(null, err => {
cache.resultsToSearchCache[config.filePattern] = cacheRow;
allResultsPromise.then(() => {
cacheRow.resolved = true;
}, err => {
delete cache.resultsToSearchCache[config.filePattern];
});
allResultsPromise = this.preventCancellation(allResultsPromise);
}
return toWinJsPromise<[ISerializedSearchSuccess, IRawFileMatch[]]>(
allResultsPromise.then(([result, results]) => {
const scorerCache: ScorerCache = cache ? cache.scorerCache : Object.create(null);
const unsortedResultTime = Date.now();
const sortSW = (typeof config.maxResults !== 'number' || config.maxResults > 0) && StopWatch.create();
return this.sortResults(config, results, scorerCache, token)
.then<[ISerializedSearchSuccess, IRawFileMatch[]]>(sortedResults => {
const sortedResultTime = Date.now();
// sortingTime: -1 indicates a "sorted" search that was not sorted, i.e. populating the cache when quickopen is opened.
// Contrasting with findFiles which is not sorted and will have sortingTime: undefined
const sortingTime = sortSW ? sortSW.elapsed() : -1;
return [{
type: 'success',
stats: objects.assign({}, result.stats, {
unsortedResultTime,
sortedResultTime
}),
stats: {
cacheOrSearchEngineStats: result.stats,
sortingTime,
fromCache: false,
type: 'searchProcess',
workspaceFolderCount: config.folderQueries.length,
resultCount: sortedResults.length
},
limitHit: result.limitHit || typeof config.maxResults === 'number' && results.length > config.maxResults
} as ISerializedSearchSuccess, sortedResults];
});
......@@ -249,35 +267,27 @@ export class SearchService implements IRawSearchService {
return undefined;
}
const cacheLookupStartTime = Date.now();
const cached = this.getResultsFromCache(cache, config.filePattern, progressCallback, token);
if (cached) {
return cached.then(([result, results, cacheStats]) => {
const cacheLookupResultTime = Date.now();
const sortSW = StopWatch.create();
return this.sortResults(config, results, cache.scorerCache, token)
.then<[ISerializedSearchSuccess, IRawFileMatch[]]>(sortedResults => {
const sortedResultTime = Date.now();
const stats: ICachedSearchStats = {
const sortingTime = sortSW.elapsed();
const stats: IFileSearchStats = {
fromCache: true,
cacheLookupStartTime: cacheLookupStartTime,
cacheFilterStartTime: cacheStats.cacheFilterStartTime,
cacheLookupResultTime: cacheLookupResultTime,
cacheEntryCount: cacheStats.cacheFilterResultCount,
resultCount: results.length
cacheOrSearchEngineStats: cacheStats,
type: 'searchProcess',
resultCount: results.length,
workspaceFolderCount: config.folderQueries.length,
sortingTime
};
if (config.sortByScore) {
stats.unsortedResultTime = cacheLookupResultTime;
stats.sortedResultTime = sortedResultTime;
}
if (!cacheStats.cacheWasResolved) {
stats.joined = result.stats;
}
return [
{
type: 'success',
limitHit: result.limitHit || typeof config.maxResults === 'number' && results.length > config.maxResults,
stats: stats
stats
} as ISerializedSearchSuccess,
sortedResults
];
......@@ -308,11 +318,12 @@ export class SearchService implements IRawSearchService {
}
}
private getResultsFromCache(cache: Cache, searchValue: string, progressCallback: IFileProgressCallback, token?: CancellationToken): TPromise<[ISerializedSearchSuccess, IRawFileMatch[], CacheStats]> {
private getResultsFromCache(cache: Cache, searchValue: string, progressCallback: IFileProgressCallback, token?: CancellationToken): TPromise<[ISearchEngineSuccess, IRawFileMatch[], ICachedSearchStats]> {
const cacheLookupSW = StopWatch.create();
// Find cache entries by prefix of search value
const hasPathSep = searchValue.indexOf(sep) >= 0;
let cachedRow: CacheRow;
let wasResolved: boolean;
let cachedRow: ICacheRow;
for (let previousSearch in cache.resultsToSearchCache) {
// If we narrow down, we might be able to reuse the cached results
if (strings.startsWith(searchValue, previousSearch)) {
......@@ -321,11 +332,10 @@ export class SearchService implements IRawSearchService {
}
const row = cache.resultsToSearchCache[previousSearch];
row.promise.then(() => { wasResolved = false; });
wasResolved = true;
cachedRow = {
promise: this.preventCancellation(row.promise),
event: row.event
event: row.event,
resolved: row.resolved
};
break;
}
......@@ -335,6 +345,9 @@ export class SearchService implements IRawSearchService {
return null;
}
const cacheLookupTime = cacheLookupSW.elapsed();
const cacheFilterSW = StopWatch.create();
const listener = cachedRow.event(progressCallback);
if (token) {
token.onCancellationRequested(() => {
......@@ -342,13 +355,11 @@ export class SearchService implements IRawSearchService {
});
}
return toWinJsPromise(cachedRow.promise.then<[ISerializedSearchSuccess, IRawFileMatch[], CacheStats]>(([complete, cachedEntries]) => {
return toWinJsPromise(cachedRow.promise.then<[ISearchEngineSuccess, IRawFileMatch[], ICachedSearchStats]>(([complete, cachedEntries]) => {
if (token && token.isCancellationRequested) {
throw canceled();
}
const cacheFilterStartTime = Date.now();
// Pattern match on results
let results: IRawFileMatch[] = [];
const normalizedSearchValueLowercase = strings.stripWildcards(searchValue).toLowerCase();
......@@ -364,10 +375,11 @@ export class SearchService implements IRawSearchService {
}
return [complete, results, {
cacheWasResolved: wasResolved,
cacheFilterStartTime: cacheFilterStartTime,
cacheFilterResultCount: cachedEntries.length
}] as [ISerializedSearchSuccess, IRawFileMatch[], CacheStats]; // TS?
cacheWasResolved: cachedRow.resolved,
cacheLookupTime,
cacheFilterTime: cacheFilterSW.elapsed(),
cacheEntryCount: cachedEntries.length
}];
}));
}
......@@ -388,14 +400,18 @@ export class SearchService implements IRawSearchService {
if (error) {
e(error);
} else {
c(stats);
c({
type: 'success',
limitHit: stats.limitHit,
stats: null
});
}
});
});
}
private doSearch(engine: ISearchEngine<IRawFileMatch>, progressCallback: IFileProgressCallback, batchSize: number, token?: CancellationToken): TPromise<ISerializedSearchSuccess> {
return new TPromise<ISerializedSearchSuccess>((c, e) => {
private doSearch(engine: ISearchEngine<IRawFileMatch>, progressCallback: IFileProgressCallback, batchSize: number, token?: CancellationToken): TPromise<ISearchEngineSuccess> {
return new TPromise<ISearchEngineSuccess>((c, e) => {
let batch: IRawFileMatch[] = [];
if (token) {
token.onCancellationRequested(() => engine.cancel());
......@@ -415,14 +431,15 @@ export class SearchService implements IRawSearchService {
}
}, (progress) => {
progressCallback(progress);
}, (error, stats) => {
}, (error, complete) => {
if (batch.length) {
progressCallback(batch);
}
if (error) {
e(error);
} else {
c(stats);
c(complete);
}
});
});
......@@ -452,15 +469,16 @@ export class SearchService implements IRawSearchService {
}
}
interface CacheRow {
interface ICacheRow {
// TODO@roblou - never actually canceled
promise: CancelablePromise<[ISerializedSearchSuccess, IRawFileMatch[]]>;
promise: CancelablePromise<[ISearchEngineSuccess, IRawFileMatch[]]>;
resolved: boolean;
event: Event<IFileSearchProgressItem>;
}
class Cache {
public resultsToSearchCache: { [searchValue: string]: CacheRow; } = Object.create(null);
public resultsToSearchCache: { [searchValue: string]: ICacheRow; } = Object.create(null);
public scorerCache: ScorerCache = Object.create(null);
}
......@@ -480,12 +498,6 @@ const FileMatchItemAccessor = new class implements IItemAccessor<IRawFileMatch>
}
};
interface CacheStats {
cacheWasResolved: boolean;
cacheFilterStartTime: number;
cacheFilterResultCount: number;
}
/**
* Collects items that have a size - before the cumulative size of collected items reaches START_BATCH_AFTER_COUNT, the callback is called for every
* set of items collected.
......
......@@ -7,7 +7,7 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { IExpression } from 'vs/base/common/glob';
import { IProgress, ILineMatch, IPatternInfo, ISearchStats } from 'vs/platform/search/common/search';
import { IProgress, ILineMatch, IPatternInfo, IFileSearchStats, ISearchEngineStats } from 'vs/platform/search/common/search';
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
import { Event } from 'vs/base/common/event';
......@@ -45,7 +45,6 @@ export interface IRawSearchService {
fileSearch(search: IRawSearch): Event<ISerializedSearchProgressItem | ISerializedSearchComplete>;
textSearch(search: IRawSearch): Event<ISerializedSearchProgressItem | ISerializedSearchComplete>;
clearCache(cacheKey: string): TPromise<void>;
readonly onTelemetry: Event<ITelemetryEvent>;
}
export interface IRawFileMatch {
......@@ -56,14 +55,19 @@ export interface IRawFileMatch {
}
export interface ISearchEngine<T> {
search: (onResult: (matches: T) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISerializedSearchSuccess) => void) => void;
search: (onResult: (matches: T) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISearchEngineSuccess) => void) => void;
cancel: () => void;
}
export interface ISerializedSearchSuccess {
type: 'success';
limitHit: boolean;
stats: ISearchStats;
stats: IFileSearchStats;
}
export interface ISearchEngineSuccess {
limitHit: boolean;
stats: ISearchEngineStats;
}
export interface ISerializedSearchError {
......
......@@ -5,13 +5,12 @@
'use strict';
import { Event } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { IChannel } from 'vs/base/parts/ipc/node/ipc';
import { IRawSearchService, IRawSearch, ISerializedSearchComplete, ISerializedSearchProgressItem, ITelemetryEvent } from './search';
import { Event } from 'vs/base/common/event';
import { IRawSearch, IRawSearchService, ISerializedSearchComplete, ISerializedSearchProgressItem } from './search';
export interface ISearchChannel extends IChannel {
listen(event: 'telemetry'): Event<ITelemetryEvent>;
listen(event: 'fileSearch', search: IRawSearch): Event<ISerializedSearchProgressItem | ISerializedSearchComplete>;
listen(event: 'textSearch', search: IRawSearch): Event<ISerializedSearchProgressItem | ISerializedSearchComplete>;
call(command: 'clearCache', cacheKey: string): TPromise<void>;
......@@ -24,7 +23,6 @@ export class SearchChannel implements ISearchChannel {
listen<T>(event: string, arg?: any): Event<any> {
switch (event) {
case 'telemetry': return this.service.onTelemetry;
case 'fileSearch': return this.service.fileSearch(arg);
case 'textSearch': return this.service.textSearch(arg);
}
......@@ -41,8 +39,6 @@ export class SearchChannel implements ISearchChannel {
export class SearchChannelClient implements IRawSearchService {
get onTelemetry(): Event<ITelemetryEvent> { return this.channel.listen('telemetry'); }
constructor(private channel: ISearchChannel) { }
fileSearch(search: IRawSearch): Event<ISerializedSearchProgressItem | ISerializedSearchComplete> {
......
......@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { getPathFromAmdModule } from 'vs/base/common/amd';
import * as arrays from 'vs/base/common/arrays';
import { Event } from 'vs/base/common/event';
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
......@@ -20,14 +21,13 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IDebugParams, IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ILogService } from 'vs/platform/log/common/log';
import { FileMatch, IFileMatch, IFolderQuery, IProgress, ISearchComplete, ISearchConfiguration, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, LineMatch, pathIncludedInQuery, QueryType, SearchProviderType } from 'vs/platform/search/common/search';
import { FileMatch, IFileMatch, IFolderQuery, IProgress, ISearchComplete, ISearchConfiguration, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, LineMatch, pathIncludedInQuery, QueryType, SearchProviderType, ICachedSearchStats, ISearchEngineStats } from 'vs/platform/search/common/search';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IRawSearch, IRawSearchService, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess, ITelemetryEvent } from './search';
import { IRawSearch, IRawSearchService, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess } from './search';
import { ISearchChannel, SearchChannelClient } from './searchIpc';
import { getPathFromAmdModule } from 'vs/base/common/amd';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
export class SearchService extends Disposable implements ISearchService {
public _serviceBrand: any;
......@@ -49,9 +49,6 @@ export class SearchService extends Disposable implements ISearchService {
) {
super();
this.diskSearch = new DiskSearch(!environmentService.isBuilt || environmentService.verbose, /*timeout=*/undefined, environmentService.debugSearch);
this._register(this.diskSearch.onTelemetry(event => {
this.telemetryService.publicLog(event.eventName, event.data);
}));
}
public registerSearchResultProvider(scheme: string, type: SearchProviderType, provider: ISearchResultProvider): IDisposable {
......@@ -186,7 +183,7 @@ export class SearchService extends Disposable implements ISearchService {
private searchWithProviders(query: ISearchQuery, onProviderProgress: (progress: ISearchProgressItem) => void) {
const diskSearchQueries: IFolderQuery[] = [];
const searchPs = [];
const searchPs: TPromise<ISearchComplete>[] = [];
query.folderQueries.forEach(fq => {
let provider = query.type === QueryType.File ?
......@@ -223,7 +220,72 @@ export class SearchService extends Disposable implements ISearchService {
searchPs.push(this.diskSearch.search(diskSearchQuery, onProviderProgress));
}
return TPromise.join(searchPs);
return TPromise.join(searchPs).then(completes => {
completes.forEach(complete => {
if (complete.stats) {
if (complete.stats.fromCache) {
const cacheStats: ICachedSearchStats = complete.stats.cacheOrSearchEngineStats as ICachedSearchStats;
/* __GDPR__
"cachedSearchComplete" : {
"resultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"workspaceFolderCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"endToEndTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"sortingTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"cacheWasResolved" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"cacheLookupTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"cacheFilterTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"cacheEntryCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
}
*/
this.telemetryService.publicLog('cachedSearchComplete', {
resultCount: complete.stats.resultCount,
workspaceFolderCount: complete.stats.workspaceFolderCount,
type: complete.stats.type,
endToEndTime: complete.stats.endToEndTime,
sortingTime: complete.stats.sortingTime,
cacheWasResolved: cacheStats.cacheWasResolved,
cacheLookupTime: cacheStats.cacheLookupTime,
cacheFilterTime: cacheStats.cacheFilterTime,
cacheEntryCount: cacheStats.cacheEntryCount
});
} else {
const searchEngineStats: ISearchEngineStats = complete.stats.cacheOrSearchEngineStats as ISearchEngineStats;
/* __GDPR__
"searchComplete" : {
"resultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"workspaceFolderCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"endToEndTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"sortingTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"traversal" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"fileWalkTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"directoriesWalked" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"filesWalked" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"cmdTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"cmdResultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }
}
*/
this.telemetryService.publicLog('searchComplete', {
resultCount: complete.stats.resultCount,
workspaceFolderCount: complete.stats.workspaceFolderCount,
type: complete.stats.type,
endToEndTime: complete.stats.endToEndTime,
sortingTime: complete.stats.sortingTime,
traversal: searchEngineStats.traversal,
fileWalkTime: searchEngineStats.fileWalkTime,
directoriesWalked: searchEngineStats.directoriesWalked,
filesWalked: searchEngineStats.filesWalked,
cmdTime: searchEngineStats.cmdTime,
cmdResultCount: searchEngineStats.cmdResultCount
});
}
}
});
return completes;
});
}
private getLocalResults(query: ISearchQuery): ResourceMap<IFileMatch> {
......@@ -348,10 +410,6 @@ export class DiskSearch implements ISearchResultProvider {
this.raw = new SearchChannelClient(channel);
}
public get onTelemetry(): Event<ITelemetryEvent> {
return this.raw.onTelemetry;
}
public search(query: ISearchQuery, onProgress?: (p: ISearchProgressItem) => void): TPromise<ISearchComplete> {
const folderQueries = query.folderQueries || [];
return TPromise.join(folderQueries.map(q => q.folder.scheme === Schemas.file && pfs.exists(q.folder.fsPath)))
......
......@@ -6,14 +6,12 @@
'use strict';
import * as path from 'path';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IProgress } from 'vs/platform/search/common/search';
import { FileWalker } from 'vs/workbench/services/search/node/fileSearch';
import { ISerializedFileMatch, IRawSearch, ISearchEngine, ISerializedSearchSuccess } from './search';
import { ISearchWorker } from './worker/searchWorkerIpc';
import { IRawSearch, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch } from './search';
import { ITextSearchWorkerProvider } from './textSearchWorkerProvider';
import { ISearchWorker } from './worker/searchWorkerIpc';
export class Engine implements ISearchEngine<ISerializedFileMatch[]> {
......@@ -60,7 +58,7 @@ export class Engine implements ISearchEngine<ISerializedFileMatch[]> {
});
}
search(onResult: (match: ISerializedFileMatch[]) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISerializedSearchSuccess) => void): void {
search(onResult: (match: ISerializedFileMatch[]) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISearchEngineSuccess) => void): void {
this.workers = this.workerProvider.getWorkers();
this.initializeWorkers();
......@@ -86,7 +84,6 @@ export class Engine implements ISearchEngine<ISerializedFileMatch[]> {
if (!this.isDone && this.processedBytes === this.totalBytes && this.walkerIsDone) {
this.isDone = true;
done(this.walkerError, {
type: 'success',
limitHit: this.limitReached,
stats: this.walker.getStats()
});
......
......@@ -10,9 +10,9 @@ import * as path from 'path';
import { getPathFromAmdModule } from 'vs/base/common/amd';
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
import { Emitter, Event } from 'vs/base/common/event';
import { IProgress, IUncachedSearchStats } from 'vs/platform/search/common/search';
import { IProgress, ISearchEngineStats } from 'vs/platform/search/common/search';
import { SearchService as RawSearchService } from 'vs/workbench/services/search/node/rawSearchService';
import { IFolderSearch, IRawFileMatch, IRawSearch, ISearchEngine, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from 'vs/workbench/services/search/node/search';
import { IFolderSearch, IRawFileMatch, IRawSearch, ISearchEngine, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess, ISearchEngineSuccess } from 'vs/workbench/services/search/node/search';
import { DiskSearch } from 'vs/workbench/services/search/node/searchService';
const TEST_FOLDER_QUERIES = [
......@@ -25,13 +25,10 @@ const MULTIROOT_QUERIES: IFolderSearch[] = [
{ folder: path.join(TEST_FIXTURES, 'more') }
];
const stats: IUncachedSearchStats = {
fromCache: false,
resultCount: 4,
const stats: ISearchEngineStats = {
traversal: 'node',
errors: [],
fileWalkStartTime: 0,
fileWalkResultTime: 1,
fileWalkTime: 0,
cmdTime: 1,
directoriesWalked: 2,
filesWalked: 3
};
......@@ -46,13 +43,12 @@ class TestSearchEngine implements ISearchEngine<IRawFileMatch> {
TestSearchEngine.last = this;
}
public search(onResult: (match: IRawFileMatch) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISerializedSearchSuccess) => void): void {
public search(onResult: (match: IRawFileMatch) => void, onProgress: (progress: IProgress) => void, done: (error: Error, complete: ISearchEngineSuccess) => void): void {
const self = this;
(function next() {
process.nextTick(() => {
if (self.isCanceled) {
done(null, {
type: 'success',
limitHit: false,
stats: stats
});
......@@ -61,7 +57,6 @@ class TestSearchEngine implements ISearchEngine<IRawFileMatch> {
const result = self.result();
if (!result) {
done(null, {
type: 'success',
limitHit: false,
stats: stats
});
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册