Skip to content
Permalink
Browse files

Fix: Use VS Code language ID to reduce file type telemetry noise

Early telemetry shows a large number of different file extensions can be
mapped to one of the language types webhint registers for. Switching to
use the language ID from VS Code should help simplify this metric.

- - - - - - - - - -

Close #3289
  • Loading branch information
antross committed Nov 8, 2019
1 parent dc3d825 commit 1c6e51b8315ee168db26ea774b1214326f968477
@@ -55,7 +55,7 @@ documents.onDidClose(({ document }) => {

// Report deltas in cached results when a document is saved.
documents.onDidSave(({ document }) => {
trackSave(document.uri);
trackSave(document.uri, document.languageId);
});

// Listen on the text document manager and connection.
@@ -1,5 +1,3 @@
import { extname } from 'path';

import { trackEvent } from './app-insights';

export type ResultData = {
@@ -27,10 +25,11 @@ type ProblemCountMap = {
// Remember per-document results for analytics.
const prevProblems = new Map<string, ProblemCountMap>();
const nextProblems = new Map<string, ProblemCountMap>();
const languageIds = new Map<string, string>();
const lastSaveTimes = new Map<string, number>();
const twoMinutes = 1000 * 60 * 2;

const determineHintStatus = (prev: ProblemCountMap, next: ProblemCountMap, uri: string) => {
const determineHintStatus = (prev: ProblemCountMap, next: ProblemCountMap, languageId: string) => {
const status: HintStatusMap = {};

for (const id of Object.keys(next)) {
@@ -51,9 +50,7 @@ const determineHintStatus = (prev: ProblemCountMap, next: ProblemCountMap, uri:
}
}

const fileExtension = extname(uri);

return { fileExtension, ...status };
return { languageId, ...status };
};

const toTrackedResult = (data: ResultData) => {
@@ -71,8 +68,8 @@ const toTrackedResult = (data: ResultData) => {
return result;
};

const trackOpen = (result: ProblemCountMap, uri: string) => {
trackEvent('vscode-open', determineHintStatus({}, result, uri));
const trackOpen = (result: ProblemCountMap, languageId: string) => {
trackEvent('vscode-open', determineHintStatus({}, result, languageId));
};

export const trackOptIn = (telemetryEnabled: TelemetryState, everEnabledTelemetry: boolean) => {
@@ -84,21 +81,24 @@ export const trackOptIn = (telemetryEnabled: TelemetryState, everEnabledTelemetr
export const trackClose = (uri: string) => {
prevProblems.delete(uri);
nextProblems.delete(uri);
languageIds.delete(uri);
lastSaveTimes.delete(uri);
};

export const trackResult = (uri: string, result: ResultData) => {
export const trackResult = (uri: string, languageId: string, result: ResultData) => {
const problems = toTrackedResult(result);

languageIds.set(uri, languageId);

if (prevProblems.has(uri)) {
nextProblems.set(uri, problems);
} else {
prevProblems.set(uri, problems);
trackOpen(problems, uri);
trackOpen(problems, languageId);
}
};

export const trackSave = (uri: string) => {
export const trackSave = (uri: string, languageId: string) => {
const prev = prevProblems.get(uri);
const next = nextProblems.get(uri);
const lastSave = lastSaveTimes.get(uri);
@@ -116,7 +116,7 @@ export const trackSave = (uri: string) => {
prevProblems.set(uri, next);
nextProblems.delete(uri);

trackEvent('vscode-save', determineHintStatus(prev, next, uri));
trackEvent('vscode-save', determineHintStatus(prev, next, languageId));

lastSaveTimes.set(uri, now);
};
@@ -8,14 +8,17 @@ import { loadWebhint, updateSharedWebhint } from './webhint-packages';
import { problemToDiagnostic } from './problems';
import { promptAddWebhint, promptRetry } from './prompts';

const analyze = async (uri: string, content: string, webhint: import('hint').Analyzer): Promise<PublishDiagnosticsParams> => {
const analyze = async (textDocument: TextDocument, webhint: import('hint').Analyzer): Promise<PublishDiagnosticsParams> => {
const { languageId, uri } = textDocument;
const content = textDocument.getText();

// In VSCode on Windows, the `:` is escaped after the drive letter in `textDocument.uri`.
const url = new URL(unescape(uri));

// Pass content directly to validate unsaved changes.
const results = await webhint.analyze({ content, url });

trackResult(uri, {
trackResult(uri, languageId, {
hints: webhint.resources.hints,
problems: results.length > 0 ? results[0].problems : []
});
@@ -125,7 +128,7 @@ export class Analyzer {
return;
}

const diagnostics = await analyze(textDocument.uri, textDocument.getText(), this.webhint);
const diagnostics = await analyze(textDocument, this.webhint);

this.connection.sendDiagnostics(diagnostics);

@@ -44,7 +44,7 @@ test('It tracks the first result for each document when opened', (t) => {
const trackEventSpy = sandbox.spy(stubs['./app-insights'], 'trackEvent');

// First result should be tracked.
module.trackResult('test.html', {
module.trackResult('test.html', 'html', {
hints: [
{ meta: { id: 'foo' } } as IHintConstructor
],
@@ -57,15 +57,15 @@ test('It tracks the first result for each document when opened', (t) => {
});

// Second result should not be tracked (will be cached for 'onSave').
module.trackResult('test.html', {
module.trackResult('test.html', 'html', {
hints: [
{ meta: { id: 'foo' } } as IHintConstructor
],
problems: []
});

// First result for another document should be tracked.
module.trackResult('test.css', {
module.trackResult('test.css', 'css', {
hints: [
{ meta: { id: 'bar' } } as IHintConstructor
],
@@ -75,13 +75,13 @@ test('It tracks the first result for each document when opened', (t) => {
t.true(trackEventSpy.calledTwice);
t.is(trackEventSpy.firstCall.args[0], 'vscode-open');
t.deepEqual(trackEventSpy.firstCall.args[1], {
fileExtension: '.html',
'hint-foo': 'failed'
'hint-foo': 'failed',
languageId: 'html'
});
t.is(trackEventSpy.secondCall.args[0], 'vscode-open');
t.deepEqual(trackEventSpy.secondCall.args[1], {
fileExtension: '.css',
'hint-bar': 'passed'
'hint-bar': 'passed',
languageId: 'css'
});

sandbox.restore();
@@ -93,7 +93,7 @@ test('It tracks the delta between the first and last results on save', (t) => {
const trackEventSpy = sandbox.spy(stubs['./app-insights'], 'trackEvent');

// First result should be tracked.
module.trackResult('test.html', {
module.trackResult('test.html', 'html', {
hints: [
{ meta: { id: 'bar' } } as IHintConstructor,
{ meta: { id: 'baz' } } as IHintConstructor,
@@ -109,7 +109,7 @@ test('It tracks the delta between the first and last results on save', (t) => {
});

// Second result should not be tracked (will be cached for 'onSave').
module.trackResult('test.html', {
module.trackResult('test.html', 'html', {
hints: [
{ meta: { id: 'bar' } } as IHintConstructor,
{ meta: { id: 'baz' } } as IHintConstructor,
@@ -122,7 +122,7 @@ test('It tracks the delta between the first and last results on save', (t) => {
});

// First result for another document should be tracked.
module.trackResult('test.html', {
module.trackResult('test.html', 'html', {
hints: [
{ meta: { id: 'bar' } } as IHintConstructor,
{ meta: { id: 'baz' } } as IHintConstructor,
@@ -133,25 +133,25 @@ test('It tracks the delta between the first and last results on save', (t) => {
]
});

module.trackSave('test.html');
module.trackSave('test.html', 'html');

// Verify multiple saves only submit once with no new results.
module.trackSave('test.html');
module.trackSave('test.html', 'html');

t.true(trackEventSpy.calledTwice);
t.is(trackEventSpy.firstCall.args[0], 'vscode-open');
t.deepEqual(trackEventSpy.firstCall.args[1], {
fileExtension: '.html',
'hint-bar': 'failed',
'hint-baz': 'passed',
'hint-foo': 'failed'
'hint-foo': 'failed',
languageId: 'html'
});
t.is(trackEventSpy.secondCall.args[0], 'vscode-save');
t.deepEqual(trackEventSpy.secondCall.args[1], {
fileExtension: '.html',
'hint-bar': 'fixed',
'hint-baz': 'passed',
'hint-foo': 'fixing'
'hint-foo': 'fixing',
languageId: 'html'
});

sandbox.restore();
@@ -163,7 +163,7 @@ test('It tracks results again for a document when re-opened', (t) => {
const trackEventSpy = sandbox.spy(stubs['./app-insights'], 'trackEvent');

// First result should be tracked.
module.trackResult('test.html', {
module.trackResult('test.html', 'html', {
hints: [
{ meta: { id: 'foo' } } as IHintConstructor
],
@@ -179,7 +179,7 @@ test('It tracks results again for a document when re-opened', (t) => {
module.trackClose('test.html');

// Second result should be tracked because document was re-opened.
module.trackResult('test.html', {
module.trackResult('test.html', 'html', {
hints: [
{ meta: { id: 'foo' } } as IHintConstructor
],
@@ -189,12 +189,12 @@ test('It tracks results again for a document when re-opened', (t) => {
t.true(trackEventSpy.calledTwice);
t.is(trackEventSpy.firstCall.args[0], 'vscode-open');
t.deepEqual(trackEventSpy.firstCall.args[1], {
fileExtension: '.html',
'hint-foo': 'failed'
'hint-foo': 'failed',
languageId: 'html'
});
t.is(trackEventSpy.secondCall.args[0], 'vscode-open');
t.deepEqual(trackEventSpy.secondCall.args[1], {
fileExtension: '.html',
'hint-foo': 'passed'
'hint-foo': 'passed',
languageId: 'html'
});
});

0 comments on commit 1c6e51b

Please sign in to comment.
You can’t perform that action at this time.