Skip to content

Commit dc4700c

Browse files
Bamiehmikecote
authored andcommitted
[Telemetry] Server side fetcher (#50015) (#50455)
* initial push * self code review * ignore node-fetch type * usageFetcher api * user agent metric * telemetry plugin collector * remove extra unused method * remove unused import * type check * fix collections tests * pass kfetch as dep * add ui metrics integration test for user agent * dont start ui metrics when not authenticated * user agent count always 1 * fix broken ui-metric integration tests * try using config.get * avoid fetching configs if sending * type unknown -> string * check if fetcher is causing the issue * disable ui_metric from functional tests * enable ui_metric back again * ignore keyword above 256 * check requesting app first * clean up after all the debugging :) * fix tests * always return 200 for ui metric reporting * remove boom import * logout after removing role/user * undo some changes in tests * inside try catch * prevent potential race conditions in priorities with = * use snake_case for telemetry plugin collection * usageFetcher -> sendUsageFrom * more replacements * remove extra unused route * config() -> config * Update src/legacy/core_plugins/telemetry/index.ts Co-Authored-By: Mike Côté <[email protected]> * Update src/legacy/core_plugins/ui_metric/server/routes/api/ui_metric.ts Co-Authored-By: Mike Côté <[email protected]> * config() -> config * fix SO update logic given the current changes * fix opt in check * triple check * check for non boolean * take into account older settings * import TelemetryOptInProvider * update test case
1 parent ddc265d commit dc4700c

File tree

53 files changed

+1237
-505
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1237
-505
lines changed

packages/kbn-analytics/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@
1717
"@babel/cli": "7.5.5",
1818
"@kbn/dev-utils": "1.0.0",
1919
"@kbn/babel-preset": "1.0.0",
20-
"typescript": "3.5.1"
20+
"typescript": "3.5.3"
2121
}
2222
}

packages/kbn-analytics/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@
1717
* under the License.
1818
*/
1919

20-
export { createReporter, ReportHTTP, Reporter, ReporterConfig } from './reporter';
20+
export { ReportHTTP, Reporter, ReporterConfig } from './reporter';
2121
export { UiStatsMetricType, METRIC_TYPE } from './metrics';
2222
export { Report, ReportManager } from './report';

packages/kbn-analytics/src/metrics/index.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,17 @@
1717
* under the License.
1818
*/
1919

20-
import { UiStatsMetric, UiStatsMetricType } from './ui_stats';
20+
import { UiStatsMetric } from './ui_stats';
21+
import { UserAgentMetric } from './user_agent';
2122

22-
export {
23-
UiStatsMetric,
24-
createUiStatsMetric,
25-
UiStatsMetricReport,
26-
UiStatsMetricType,
27-
} from './ui_stats';
23+
export { UiStatsMetric, createUiStatsMetric, UiStatsMetricType } from './ui_stats';
2824
export { Stats } from './stats';
25+
export { trackUsageAgent } from './user_agent';
2926

30-
export type Metric = UiStatsMetric<UiStatsMetricType>;
31-
export type MetricType = keyof typeof METRIC_TYPE;
32-
27+
export type Metric = UiStatsMetric | UserAgentMetric;
3328
export enum METRIC_TYPE {
3429
COUNT = 'count',
3530
LOADED = 'loaded',
3631
CLICK = 'click',
32+
USER_AGENT = 'user_agent',
3733
}

packages/kbn-analytics/src/metrics/ui_stats.ts

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,37 +17,33 @@
1717
* under the License.
1818
*/
1919

20-
import { Stats } from './stats';
2120
import { METRIC_TYPE } from './';
2221

2322
export type UiStatsMetricType = METRIC_TYPE.CLICK | METRIC_TYPE.LOADED | METRIC_TYPE.COUNT;
24-
export interface UiStatsMetricConfig<T extends UiStatsMetricType> {
25-
type: T;
23+
export interface UiStatsMetricConfig {
24+
type: UiStatsMetricType;
2625
appName: string;
2726
eventName: string;
2827
count?: number;
2928
}
3029

31-
export interface UiStatsMetric<T extends UiStatsMetricType = UiStatsMetricType> {
32-
type: T;
30+
export interface UiStatsMetric {
31+
type: UiStatsMetricType;
3332
appName: string;
3433
eventName: string;
3534
count: number;
3635
}
3736

38-
export function createUiStatsMetric<T extends UiStatsMetricType>({
37+
export function createUiStatsMetric({
3938
type,
4039
appName,
4140
eventName,
4241
count = 1,
43-
}: UiStatsMetricConfig<T>): UiStatsMetric<T> {
44-
return { type, appName, eventName, count };
45-
}
46-
47-
export interface UiStatsMetricReport {
48-
key: string;
49-
appName: string;
50-
eventName: string;
51-
type: UiStatsMetricType;
52-
stats: Stats;
42+
}: UiStatsMetricConfig): UiStatsMetric {
43+
return {
44+
type,
45+
appName,
46+
eventName,
47+
count,
48+
};
5349
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
import { METRIC_TYPE } from './';
20+
21+
export interface UserAgentMetric {
22+
type: METRIC_TYPE.USER_AGENT;
23+
appName: string;
24+
userAgent: string;
25+
}
26+
27+
export function trackUsageAgent(appName: string): UserAgentMetric {
28+
const userAgent = (window && window.navigator && window.navigator.userAgent) || '';
29+
30+
return {
31+
type: METRIC_TYPE.USER_AGENT,
32+
appName,
33+
userAgent,
34+
};
35+
}

packages/kbn-analytics/src/report.ts

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,47 @@
1717
* under the License.
1818
*/
1919

20-
import { UnreachableCaseError } from './util';
21-
import { Metric, Stats, UiStatsMetricReport, METRIC_TYPE } from './metrics';
20+
import { UnreachableCaseError, wrapArray } from './util';
21+
import { Metric, Stats, UiStatsMetricType, METRIC_TYPE } from './metrics';
22+
const REPORT_VERSION = 1;
2223

2324
export interface Report {
25+
reportVersion: typeof REPORT_VERSION;
2426
uiStatsMetrics: {
25-
[key: string]: UiStatsMetricReport;
27+
[key: string]: {
28+
key: string;
29+
appName: string;
30+
eventName: string;
31+
type: UiStatsMetricType;
32+
stats: Stats;
33+
};
34+
};
35+
userAgent?: {
36+
[key: string]: {
37+
userAgent: string;
38+
key: string;
39+
type: METRIC_TYPE.USER_AGENT;
40+
appName: string;
41+
};
2642
};
2743
}
2844

2945
export class ReportManager {
46+
static REPORT_VERSION = REPORT_VERSION;
3047
public report: Report;
3148
constructor(report?: Report) {
3249
this.report = report || ReportManager.createReport();
3350
}
34-
static createReport() {
35-
return { uiStatsMetrics: {} };
51+
static createReport(): Report {
52+
return { reportVersion: REPORT_VERSION, uiStatsMetrics: {} };
3653
}
3754
public clearReport() {
3855
this.report = ReportManager.createReport();
3956
}
4057
public isReportEmpty(): boolean {
41-
return Object.keys(this.report.uiStatsMetrics).length === 0;
58+
const noUiStats = Object.keys(this.report.uiStatsMetrics).length === 0;
59+
const noUserAgent = !this.report.userAgent || Object.keys(this.report.userAgent).length === 0;
60+
return noUiStats && noUserAgent;
4261
}
4362
private incrementStats(count: number, stats?: Stats): Stats {
4463
const { min = 0, max = 0, sum = 0 } = stats || {};
@@ -54,28 +73,46 @@ export class ReportManager {
5473
sum: newSum,
5574
};
5675
}
57-
assignReports(newMetrics: Metric[]) {
58-
newMetrics.forEach(newMetric => this.assignReport(this.report, newMetric));
76+
assignReports(newMetrics: Metric | Metric[]) {
77+
wrapArray(newMetrics).forEach(newMetric => this.assignReport(this.report, newMetric));
5978
}
6079
static createMetricKey(metric: Metric): string {
6180
switch (metric.type) {
81+
case METRIC_TYPE.USER_AGENT: {
82+
const { appName, type } = metric;
83+
return `${appName}-${type}`;
84+
}
6285
case METRIC_TYPE.CLICK:
6386
case METRIC_TYPE.LOADED:
6487
case METRIC_TYPE.COUNT: {
65-
const { appName, type, eventName } = metric;
88+
const { appName, eventName, type } = metric;
6689
return `${appName}-${type}-${eventName}`;
6790
}
6891
default:
69-
throw new UnreachableCaseError(metric.type);
92+
throw new UnreachableCaseError(metric);
7093
}
7194
}
7295
private assignReport(report: Report, metric: Metric) {
96+
const key = ReportManager.createMetricKey(metric);
7397
switch (metric.type) {
98+
case METRIC_TYPE.USER_AGENT: {
99+
const { appName, type, userAgent } = metric;
100+
if (userAgent) {
101+
this.report.userAgent = {
102+
[key]: {
103+
key,
104+
appName,
105+
type,
106+
userAgent: metric.userAgent,
107+
},
108+
};
109+
}
110+
return;
111+
}
74112
case METRIC_TYPE.CLICK:
75113
case METRIC_TYPE.LOADED:
76114
case METRIC_TYPE.COUNT: {
77115
const { appName, type, eventName, count } = metric;
78-
const key = ReportManager.createMetricKey(metric);
79116
const existingStats = (report.uiStatsMetrics[key] || {}).stats;
80117
this.report.uiStatsMetrics[key] = {
81118
key,
@@ -87,7 +124,7 @@ export class ReportManager {
87124
return;
88125
}
89126
default:
90-
throw new UnreachableCaseError(metric.type);
127+
throw new UnreachableCaseError(metric);
91128
}
92129
}
93130
}

packages/kbn-analytics/src/reporter.ts

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919

2020
import { wrapArray } from './util';
21-
import { Metric, UiStatsMetric, createUiStatsMetric } from './metrics';
21+
import { Metric, createUiStatsMetric, trackUsageAgent, UiStatsMetricType } from './metrics';
2222

2323
import { Storage, ReportStorageManager } from './storage';
2424
import { Report, ReportManager } from './report';
@@ -40,10 +40,11 @@ export class Reporter {
4040
private reportManager: ReportManager;
4141
private storageManager: ReportStorageManager;
4242
private debug: boolean;
43+
private retryCount = 0;
44+
private readonly maxRetries = 3;
4345

4446
constructor(config: ReporterConfig) {
45-
const { http, storage, debug, checkInterval = 10000, storageKey = 'analytics' } = config;
46-
47+
const { http, storage, debug, checkInterval = 90000, storageKey = 'analytics' } = config;
4748
this.http = http;
4849
this.checkInterval = checkInterval;
4950
this.interval = null;
@@ -59,18 +60,19 @@ export class Reporter {
5960
}
6061

6162
private flushReport() {
63+
this.retryCount = 0;
6264
this.reportManager.clearReport();
6365
this.storageManager.store(this.reportManager.report);
6466
}
6567

66-
public start() {
68+
public start = () => {
6769
if (!this.interval) {
6870
this.interval = setTimeout(() => {
6971
this.interval = null;
7072
this.sendReports();
7173
}, this.checkInterval);
7274
}
73-
}
75+
};
7476

7577
private log(message: any) {
7678
if (this.debug) {
@@ -79,36 +81,42 @@ export class Reporter {
7981
}
8082
}
8183

82-
public reportUiStats(
84+
public reportUiStats = (
8385
appName: string,
84-
type: UiStatsMetric['type'],
86+
type: UiStatsMetricType,
8587
eventNames: string | string[],
8688
count?: number
87-
) {
89+
) => {
8890
const metrics = wrapArray(eventNames).map(eventName => {
89-
if (this) this.log(`${type} Metric -> (${appName}:${eventName}):`);
91+
this.log(`${type} Metric -> (${appName}:${eventName}):`);
9092
const report = createUiStatsMetric({ type, appName, eventName, count });
9193
this.log(report);
9294
return report;
9395
});
9496
this.saveToReport(metrics);
95-
}
97+
};
98+
99+
public reportUserAgent = (appName: string) => {
100+
this.log(`Reporting user-agent.`);
101+
const report = trackUsageAgent(appName);
102+
this.saveToReport([report]);
103+
};
96104

97-
public async sendReports() {
105+
public sendReports = async () => {
98106
if (!this.reportManager.isReportEmpty()) {
99107
try {
100108
await this.http(this.reportManager.report);
101109
this.flushReport();
102110
} catch (err) {
103111
this.log(`Error Sending Metrics Report ${err}`);
112+
this.retryCount = this.retryCount + 1;
113+
const versionMismatch =
114+
this.reportManager.report.reportVersion !== ReportManager.REPORT_VERSION;
115+
if (versionMismatch || this.retryCount > this.maxRetries) {
116+
this.flushReport();
117+
}
104118
}
105119
}
106120
this.start();
107-
}
108-
}
109-
110-
export function createReporter(reportedConf: ReporterConfig) {
111-
const reporter = new Reporter(reportedConf);
112-
reporter.start();
113-
return reporter;
121+
};
114122
}

src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ kibana_vars=(
185185
xpack.security.public.hostname
186186
xpack.security.public.port
187187
telemetry.enabled
188+
telemetry.sendUsageFrom
188189
)
189190

190191
longopts=''

src/legacy/core_plugins/telemetry/common/constants.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ export const PRIVACY_STATEMENT_URL = `https://www.elastic.co/legal/telemetry-pri
5959
*/
6060
export const KIBANA_LOCALIZATION_STATS_TYPE = 'localization';
6161

62+
/**
63+
* The type name used to publish telemetry plugin stats.
64+
* @type {string}
65+
*/
66+
export const TELEMETRY_STATS_TYPE = 'telemetry';
67+
6268
/**
6369
* UI metric usage type
6470
* @type {string}

0 commit comments

Comments
 (0)