MM-58281 Allow client metrics to be floats and round timestamps (#27027)
* MM-58281 Allow client metrics to be floats and round timestamps * MM-58281 Fix report version * Ensure reports can contain a single timestamp * Round timestamps in unit tests
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
617053e206
Коммит
6cf93ea480
@@ -18,7 +18,7 @@ func (a *App) RegisterPerformanceReport(rctx request.CTX, report *model.Performa
|
|||||||
for _, c := range report.Counters {
|
for _, c := range report.Counters {
|
||||||
switch c.Metric {
|
switch c.Metric {
|
||||||
case model.ClientLongTasks:
|
case model.ClientLongTasks:
|
||||||
a.Metrics().IncrementClientLongTasks(commonLabels["platform"], commonLabels["agent"], float64(c.Value))
|
a.Metrics().IncrementClientLongTasks(commonLabels["platform"], commonLabels["agent"], c.Value)
|
||||||
default:
|
default:
|
||||||
// we intentionally skip unknown metrics
|
// we intentionally skip unknown metrics
|
||||||
}
|
}
|
||||||
@@ -27,21 +27,21 @@ func (a *App) RegisterPerformanceReport(rctx request.CTX, report *model.Performa
|
|||||||
for _, h := range report.Histograms {
|
for _, h := range report.Histograms {
|
||||||
switch h.Metric {
|
switch h.Metric {
|
||||||
case model.ClientTimeToFirstByte:
|
case model.ClientTimeToFirstByte:
|
||||||
a.Metrics().ObserveClientTimeToFirstByte(commonLabels["platform"], commonLabels["agent"], float64(h.Value))
|
a.Metrics().ObserveClientTimeToFirstByte(commonLabels["platform"], commonLabels["agent"], h.Value)
|
||||||
case model.ClientFirstContentfulPaint:
|
case model.ClientFirstContentfulPaint:
|
||||||
a.Metrics().ObserveClientFirstContentfulPaint(commonLabels["platform"], commonLabels["agent"], float64(h.Value))
|
a.Metrics().ObserveClientFirstContentfulPaint(commonLabels["platform"], commonLabels["agent"], h.Value)
|
||||||
case model.ClientLargestContentfulPaint:
|
case model.ClientLargestContentfulPaint:
|
||||||
a.Metrics().ObserveClientLargestContentfulPaint(commonLabels["platform"], commonLabels["agent"], float64(h.Value))
|
a.Metrics().ObserveClientLargestContentfulPaint(commonLabels["platform"], commonLabels["agent"], h.Value)
|
||||||
case model.ClientInteractionToNextPaint:
|
case model.ClientInteractionToNextPaint:
|
||||||
a.Metrics().ObserveClientInteractionToNextPaint(commonLabels["platform"], commonLabels["agent"], float64(h.Value))
|
a.Metrics().ObserveClientInteractionToNextPaint(commonLabels["platform"], commonLabels["agent"], h.Value)
|
||||||
case model.ClientCumulativeLayoutShift:
|
case model.ClientCumulativeLayoutShift:
|
||||||
a.Metrics().ObserveClientCumulativeLayoutShift(commonLabels["platform"], commonLabels["agent"], float64(h.Value))
|
a.Metrics().ObserveClientCumulativeLayoutShift(commonLabels["platform"], commonLabels["agent"], h.Value)
|
||||||
case model.ClientChannelSwitchDuration:
|
case model.ClientChannelSwitchDuration:
|
||||||
a.Metrics().ObserveClientChannelSwitchDuration(commonLabels["platform"], commonLabels["agent"], float64(h.Value))
|
a.Metrics().ObserveClientChannelSwitchDuration(commonLabels["platform"], commonLabels["agent"], h.Value)
|
||||||
case model.ClientTeamSwitchDuration:
|
case model.ClientTeamSwitchDuration:
|
||||||
a.Metrics().ObserveClientTeamSwitchDuration(commonLabels["platform"], commonLabels["agent"], float64(h.Value))
|
a.Metrics().ObserveClientTeamSwitchDuration(commonLabels["platform"], commonLabels["agent"], h.Value)
|
||||||
case model.ClientRHSLoadDuration:
|
case model.ClientRHSLoadDuration:
|
||||||
a.Metrics().ObserveClientRHSLoadDuration(commonLabels["platform"], commonLabels["agent"], float64(h.Value))
|
a.Metrics().ObserveClientRHSLoadDuration(commonLabels["platform"], commonLabels["agent"], h.Value)
|
||||||
default:
|
default:
|
||||||
// we intentionally skip unknown metrics
|
// we intentionally skip unknown metrics
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ var (
|
|||||||
|
|
||||||
type MetricSample struct {
|
type MetricSample struct {
|
||||||
Metric MetricType `json:"metric"`
|
Metric MetricType `json:"metric"`
|
||||||
Value int64 `json:"value"`
|
Value float64 `json:"value"`
|
||||||
Timestamp int64 `json:"timestamp,omitempty"`
|
Timestamp int64 `json:"timestamp,omitempty"`
|
||||||
Labels map[string]string `json:"labels,omitempty"`
|
Labels map[string]string `json:"labels,omitempty"`
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,7 @@ func (r *PerformanceReport) IsValid() error {
|
|||||||
return fmt.Errorf("report version is not supported: server version: %s, report version: %s", performanceReportVersion.String(), r.Version)
|
return fmt.Errorf("report version is not supported: server version: %s, report version: %s", performanceReportVersion.String(), r.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Start >= r.End {
|
if r.Start > r.End {
|
||||||
return fmt.Errorf("report timestamps are erroneous")
|
return fmt.Errorf("report timestamps are erroneous")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,23 +52,23 @@ describe('PerformanceReporter', () => {
|
|||||||
expect(sendBeacon.mock.calls[0][0]).toEqual(siteUrl + '/api/v4/client_perf');
|
expect(sendBeacon.mock.calls[0][0]).toEqual(siteUrl + '/api/v4/client_perf');
|
||||||
const report = JSON.parse(sendBeacon.mock.calls[0][1]);
|
const report = JSON.parse(sendBeacon.mock.calls[0][1]);
|
||||||
expect(report).toMatchObject({
|
expect(report).toMatchObject({
|
||||||
start: performance.timeOrigin + testMarkA.startTime,
|
start: Math.round(performance.timeOrigin + testMarkA.startTime),
|
||||||
end: performance.timeOrigin + testMarkB.startTime,
|
end: Math.round(performance.timeOrigin + testMarkB.startTime),
|
||||||
histograms: [
|
histograms: [
|
||||||
{
|
{
|
||||||
metric: 'testMeasureA',
|
metric: 'testMeasureA',
|
||||||
value: testMarkB.startTime - testMarkA.startTime,
|
value: testMarkB.startTime - testMarkA.startTime,
|
||||||
timestamp: performance.timeOrigin + testMarkA.startTime,
|
timestamp: Math.round(performance.timeOrigin + testMarkA.startTime),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
metric: 'testMeasureB',
|
metric: 'testMeasureB',
|
||||||
value: testMarkC.startTime - testMarkA.startTime,
|
value: testMarkC.startTime - testMarkA.startTime,
|
||||||
timestamp: performance.timeOrigin + testMarkA.startTime,
|
timestamp: Math.round(performance.timeOrigin + testMarkA.startTime),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
metric: 'testMeasureC',
|
metric: 'testMeasureC',
|
||||||
value: testMarkC.startTime - testMarkB.startTime,
|
value: testMarkC.startTime - testMarkB.startTime,
|
||||||
timestamp: performance.timeOrigin + testMarkB.startTime,
|
timestamp: Math.round(performance.timeOrigin + testMarkB.startTime),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@@ -94,7 +94,7 @@ describe('PerformanceReporter', () => {
|
|||||||
|
|
||||||
expect(reporter.handleObservations).toHaveBeenCalled();
|
expect(reporter.handleObservations).toHaveBeenCalled();
|
||||||
|
|
||||||
const timestamp = performance.timeOrigin + performance.now();
|
const timestamp = Math.round(performance.timeOrigin + performance.now());
|
||||||
|
|
||||||
await waitForReport();
|
await waitForReport();
|
||||||
|
|
||||||
|
|||||||
@@ -17,13 +17,28 @@ import type {PlatformLabel, UserAgentLabel} from './platform_detection';
|
|||||||
import {getPlatformLabel, getUserAgentLabel} from './platform_detection';
|
import {getPlatformLabel, getUserAgentLabel} from './platform_detection';
|
||||||
|
|
||||||
type PerformanceReportMeasure = {
|
type PerformanceReportMeasure = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* metric is the name of a counter or histogram metric which must match a MetricType constant as defined in
|
||||||
|
* model/metrics.go on the server.
|
||||||
|
*/
|
||||||
metric: string;
|
metric: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* value is the floating point value of the metric. It's often a millisecond duration, but it's meaning depends
|
||||||
|
* on which metric this is.
|
||||||
|
*/
|
||||||
value: number;
|
value: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* timestamp is an integer value representing when the metric was measured as a millisecond value. Some browsers
|
||||||
|
* use floating point numbers for performance timestamps, so we need to make sure to round this.
|
||||||
|
*/
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
type PerformanceReport = {
|
type PerformanceReport = {
|
||||||
version: '1.0';
|
version: '0.1.0';
|
||||||
|
|
||||||
labels: {
|
labels: {
|
||||||
platform: PlatformLabel;
|
platform: PlatformLabel;
|
||||||
@@ -126,7 +141,7 @@ export default class PerformanceReporter {
|
|||||||
this.histogramMeasures.push({
|
this.histogramMeasures.push({
|
||||||
metric: entry.name,
|
metric: entry.name,
|
||||||
value: entry.duration,
|
value: entry.duration,
|
||||||
timestamp: performance.timeOrigin + entry.startTime,
|
timestamp: Math.round(performance.timeOrigin + entry.startTime),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +166,7 @@ export default class PerformanceReporter {
|
|||||||
this.histogramMeasures.push({
|
this.histogramMeasures.push({
|
||||||
metric: metric.name,
|
metric: metric.name,
|
||||||
value: metric.value,
|
value: metric.value,
|
||||||
timestamp: performance.timeOrigin + performance.now(),
|
timestamp: Math.round(performance.timeOrigin + performance.now()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,7 +227,7 @@ export default class PerformanceReporter {
|
|||||||
const counterMeasures = this.countersToMeasures(now, counters);
|
const counterMeasures = this.countersToMeasures(now, counters);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
version: '1.0',
|
version: '0.1.0',
|
||||||
|
|
||||||
labels: {
|
labels: {
|
||||||
platform: this.platformLabel,
|
platform: this.platformLabel,
|
||||||
@@ -252,7 +267,7 @@ export default class PerformanceReporter {
|
|||||||
counterMeasures.push({
|
counterMeasures.push({
|
||||||
metric: name,
|
metric: name,
|
||||||
value,
|
value,
|
||||||
timestamp: now,
|
timestamp: Math.round(now),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user