From 38c4b2937a83a810301c0387f687c576515df9f0 Mon Sep 17 00:00:00 2001 From: Risha Mars Date: Thu, 9 Aug 2018 15:23:34 -0700 Subject: [PATCH] Tap web UI: Fix latency formatting (#1429) * Nicely format tap latencies to be more readable * Various whitespace cleanups --- web/app/js/components/ErrorBanner.jsx | 1 - web/app/js/components/TapEventTable.jsx | 7 +++--- web/app/js/components/util/ApiHelpers.jsx | 8 +++---- web/app/js/components/util/Utils.js | 26 +++++++++++++++++------ web/app/js/components/util/withREST.jsx | 2 +- web/app/test/UtilsTest.js | 23 ++++++++++++++++---- 6 files changed, 47 insertions(+), 20 deletions(-) diff --git a/web/app/js/components/ErrorBanner.jsx b/web/app/js/components/ErrorBanner.jsx index 11791e52b..5568393ab 100644 --- a/web/app/js/components/ErrorBanner.jsx +++ b/web/app/js/components/ErrorBanner.jsx @@ -50,7 +50,6 @@ class ErrorMessage extends React.Component {
Error: {status} {statusText}
{ !error ? null :
Message: {error}
}
URL: {url}
-
Dismiss X
diff --git a/web/app/js/components/TapEventTable.jsx b/web/app/js/components/TapEventTable.jsx index b8cfc0d27..3ff77b4ad 100644 --- a/web/app/js/components/TapEventTable.jsx +++ b/web/app/js/components/TapEventTable.jsx @@ -1,6 +1,6 @@ import _ from 'lodash'; import BaseTable from './BaseTable.jsx'; -import { formatLatency } from './util/Utils.js'; +import { formatLatencySec } from './util/Utils.js'; import React from 'react'; import { Col, Icon, Row } from 'antd'; @@ -157,9 +157,8 @@ let tapColumns = filterOptions => [ } ]; -let formatTapLatency = str => { - let millis = parseFloat(str.replace("s", "")) * 1000; - return formatLatency(millis); +const formatTapLatency = str => { + return formatLatencySec(str.replace("s", "")); }; // hide verbose information diff --git a/web/app/js/components/util/ApiHelpers.jsx b/web/app/js/components/util/ApiHelpers.jsx index 8986b77a9..076d34f38 100644 --- a/web/app/js/components/util/ApiHelpers.jsx +++ b/web/app/js/components/util/ApiHelpers.jsx @@ -5,7 +5,9 @@ import React from 'react'; import 'whatwg-fetch'; const checkFetchOk = resp => { - if (resp.ok) { return resp; } + if (resp.ok) { + return resp; + } return resp.json().then(error => { throw { @@ -15,7 +17,6 @@ const checkFetchOk = resp => { error: error.error }; }); - }; // makeCancelable from @istarkov @@ -43,7 +44,7 @@ const makeCancelable = (promise, onSuccess) => { }; }; -export const apiErrorPropType = PropTypes.shape({ +export const apiErrorPropType = PropTypes.shape({ status: PropTypes.number, url: PropTypes.string, statusText: PropTypes.string, @@ -115,7 +116,6 @@ const ApiHelpers = (pathPrefix, defaultMetricsWindow = '1m') => { }); }; - // prefix all links in the app with `pathPrefix` class PrefixedLink extends React.Component { static defaultProps = { diff --git a/web/app/js/components/util/Utils.js b/web/app/js/components/util/Utils.js index 308b252c5..a5c739206 100644 --- a/web/app/js/components/util/Utils.js +++ b/web/app/js/components/util/Utils.js @@ -11,23 +11,37 @@ export const rowGutter = 3 * baseWidth; * Number formatters */ const successRateFormatter = d3.format(".2%"); -const latencySecFormatter = d3.format(".3f"); const latencyFormatter = d3.format(","); -export const formatLatency = m => { +export const formatLatencyMs = m => { if (_.isNil(m)) { return "---"; - } else if (m < 1000) { - return `${latencyFormatter(m)} ms`; } else { - return `${latencySecFormatter(m / 1000)} s`; + return `${formatLatencySec(m / 1000)}`; + } +}; + +const niceLatency = l => latencyFormatter(Math.round(l)); + +export const formatLatencySec = latency => { + let s = parseFloat(latency); + if (_.isNil(s)) { + return "---"; + } else if (s === parseFloat(0.0)) { + return "0 s"; + } else if (s < 0.001) { + return `${niceLatency(s * 1000 * 1000)} µs`; + } else if (s < 1.0) { + return `${niceLatency(s * 1000)} ms`; + } else { + return `${niceLatency(s)} s`; } }; export const metricToFormatter = { "REQUEST_RATE": m => _.isNil(m) ? "---" : styleNum(m, " RPS", true), "SUCCESS_RATE": m => _.isNil(m) ? "---" : successRateFormatter(m), - "LATENCY": formatLatency, + "LATENCY": formatLatencyMs, "UNTRUNCATED": m => styleNum(m, "", false) }; diff --git a/web/app/js/components/util/withREST.jsx b/web/app/js/components/util/withREST.jsx index 843a08d6c..a49f962a6 100644 --- a/web/app/js/components/util/withREST.jsx +++ b/web/app/js/components/util/withREST.jsx @@ -121,4 +121,4 @@ const withREST = (WrappedComponent, componentPromises, options={}) => { return withContext(RESTWrapper); }; -export default withREST; \ No newline at end of file +export default withREST; diff --git a/web/app/test/UtilsTest.js b/web/app/test/UtilsTest.js index c46d1a6c7..35cc356dc 100644 --- a/web/app/test/UtilsTest.js +++ b/web/app/test/UtilsTest.js @@ -1,5 +1,10 @@ import { expect } from 'chai'; -import { metricToFormatter, styleNum, toClassName } from '../js/components/util/Utils.js'; +import { + formatLatencySec, + metricToFormatter, + styleNum, + toClassName +} from '../js/components/util/Utils.js'; // introduce some binary floating point rounding errors, like ya do function float(num) { @@ -74,9 +79,9 @@ describe('Utils', () => { }); it('formats latency greater than 1s as s', () => { - expect(metricToFormatter["LATENCY"](1000)).to.equal('1.000 s'); - expect(metricToFormatter["LATENCY"](9999)).to.equal('9.999 s'); - expect(metricToFormatter["LATENCY"](99999)).to.equal('99.999 s'); + expect(metricToFormatter["LATENCY"](1000)).to.equal('1 s'); + expect(metricToFormatter["LATENCY"](9999)).to.equal('10 s'); + expect(metricToFormatter["LATENCY"](99999)).to.equal('100 s'); }); it('formats success rate', () => { @@ -86,6 +91,16 @@ describe('Utils', () => { expect(metricToFormatter["SUCCESS_RATE"](0.9999)).to.equal('99.99%'); expect(metricToFormatter["SUCCESS_RATE"](4)).to.equal('400.00%'); }); + + it('formats latencies expressed as seconds into a more appropriate display unit', () => { + expect(formatLatencySec("0.002837700")).to.equal("3 ms"); + expect(formatLatencySec("0.000")).to.equal("0 s"); + expect(formatLatencySec("0.000000797")).to.equal("1 µs"); + expect(formatLatencySec("0.000231910")).to.equal("232 µs"); + expect(formatLatencySec("0.000988600")).to.equal("989 µs"); + expect(formatLatencySec("0.005598200")).to.equal("6 ms"); + expect(formatLatencySec("3.029409200")).to.equal("3 s"); + }); }); describe('toClassName', () => {