Skip to content

Commit 79caab0

Browse files
author
Mykhailo Semenchenko
committed
ATM Monitor UI
Signed-off-by: Mykhailo Semenchenko <mykhailo.semenchenko@logz.io>
1 parent be446bc commit 79caab0

Some content is hidden

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

44 files changed

+7525
-9
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = async () => {
2+
process.env.TZ = 'UTC';
3+
};

packages/jaeger-ui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
"test": "CI=1 react-app-rewired test --env=jsdom --color"
117117
},
118118
"jest": {
119+
"globalSetup": "./jest.global-setup.js",
119120
"collectCoverageFrom": [
120121
"!src/setup*.js",
121122
"!src/utils/DraggableManager/demo/*.tsx",

packages/jaeger-ui/src/actions/jaeger-api.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,24 @@
1515
import { createAction } from 'redux-actions';
1616
import JaegerAPI from '../api/jaeger';
1717

18+
const metricType = {
19+
latencies: 'latencies',
20+
calls: 'calls',
21+
errors: 'errors',
22+
};
23+
24+
// export for tests
25+
// TODO use native `allSetteled` once #818 is done
26+
export function allSettled(promises) {
27+
const wrappedPromises = promises.map(p =>
28+
Promise.resolve(p).then(
29+
val => ({ status: 'fulfilled', value: val }),
30+
err => ({ status: 'rejected', reason: err })
31+
)
32+
);
33+
return Promise.all(wrappedPromises);
34+
}
35+
1836
export const fetchTrace = createAction(
1937
'@JAEGER_API/FETCH_TRACE',
2038
id => JaegerAPI.fetchTrace(id),
@@ -62,3 +80,28 @@ export const fetchDeepDependencyGraph = createAction(
6280
export const fetchDependencies = createAction('@JAEGER_API/FETCH_DEPENDENCIES', () =>
6381
JaegerAPI.fetchDependencies()
6482
);
83+
84+
export const fetchAllServiceMetrics = createAction(
85+
'@JAEGER_API/FETCH_ALL_SERVICE_METRICS',
86+
(serviceName, query) => {
87+
return allSettled([
88+
JaegerAPI.fetchMetrics(metricType.latencies, [serviceName], { ...query, quantile: 0.5 }),
89+
JaegerAPI.fetchMetrics(metricType.latencies, [serviceName], { ...query, quantile: 0.75 }),
90+
JaegerAPI.fetchMetrics(metricType.latencies, [serviceName], query),
91+
JaegerAPI.fetchMetrics(metricType.calls, [serviceName], query),
92+
JaegerAPI.fetchMetrics(metricType.errors, [serviceName], query),
93+
]);
94+
}
95+
);
96+
97+
export const fetchAggregatedServiceMetrics = createAction(
98+
'@JAEGER_API/FETCH_AGGREGATED_SERVICE_METRICS',
99+
(serviceName, queryParams) => {
100+
const query = { ...queryParams, groupByOperation: true };
101+
return allSettled([
102+
JaegerAPI.fetchMetrics(metricType.latencies, [serviceName], query),
103+
JaegerAPI.fetchMetrics(metricType.calls, [serviceName], query),
104+
JaegerAPI.fetchMetrics(metricType.errors, [serviceName], query),
105+
]);
106+
}
107+
);

packages/jaeger-ui/src/actions/jaeger-api.test.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,49 @@ describe('actions/jaeger-api', () => {
149149
jaegerApiActions.fetchDependencies();
150150
expect(called.verify()).toBeTruthy();
151151
});
152+
153+
it('@JAEGER_API/FETCH_ALL_SERVICE_METRICS should return the promise', () => {
154+
const { payload } = jaegerApiActions.fetchAllServiceMetrics('serviceName', query);
155+
expect(isPromise(payload)).toBeTruthy();
156+
});
157+
158+
it('@JAEGER_API/FETCH_ALL_SERVICE_METRICS should fetch service metrics by name', () => {
159+
mock.expects('fetchMetrics');
160+
mock.expects('fetchMetrics');
161+
mock.expects('fetchMetrics');
162+
mock.expects('fetchMetrics');
163+
mock.expects('fetchMetrics');
164+
jaegerApiActions.fetchAllServiceMetrics('serviceName', query);
165+
expect(() => mock.verify()).not.toThrow();
166+
});
167+
168+
it('@JAEGER_API/FETCH_AGGREGATED_SERVICE_METRICS should return the promise', () => {
169+
const { payload } = jaegerApiActions.fetchAggregatedServiceMetrics('serviceName', query);
170+
expect(isPromise(payload)).toBeTruthy();
171+
});
172+
173+
it('@JAEGER_API/FETCH_AGGREGATED_SERVICE_METRICS should fetch service metrics by name', () => {
174+
mock.expects('fetchMetrics');
175+
mock.expects('fetchMetrics');
176+
mock.expects('fetchMetrics');
177+
jaegerApiActions.fetchAggregatedServiceMetrics('serviceName', query);
178+
expect(() => mock.verify()).not.toThrow();
179+
});
180+
});
181+
182+
describe('allSettled', () => {
183+
it('validate responses', async () => {
184+
const res = await jaegerApiActions.allSettled([
185+
Promise.resolve(1),
186+
// eslint-disable-next-line prefer-promise-reject-errors
187+
Promise.reject(2),
188+
Promise.resolve(3),
189+
]);
190+
191+
expect(res).toEqual([
192+
{ status: 'fulfilled', value: 1 },
193+
{ status: 'rejected', reason: 2 },
194+
{ status: 'fulfilled', value: 3 },
195+
]);
196+
});
152197
});

packages/jaeger-ui/src/api/jaeger.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ export function getMessageFromError(errData, status) {
3636
function getJSON(url, options = {}) {
3737
const { query = null, ...init } = options;
3838
init.credentials = 'same-origin';
39-
const queryStr = query ? `?${queryString.stringify(query)}` : '';
39+
let queryStr = '';
40+
41+
if (query) {
42+
queryStr = `?${typeof query === 'string' ? query : queryString.stringify(query)}`;
43+
}
44+
4045
return fetch(`${url}${queryStr}`, init).then(response => {
4146
if (response.status < 400) {
4247
return response.json();
@@ -112,6 +117,13 @@ const JaegerAPI = {
112117
searchTraces(query) {
113118
return getJSON(`${this.apiRoot}traces`, { query });
114119
},
120+
fetchMetrics(metricType, serviceNameList, query) {
121+
const servicesName = serviceNameList.map(serviceName => `service=${serviceName}`).join(',');
122+
123+
return getJSON(`${this.apiRoot}metrics/${metricType}`, {
124+
query: `${servicesName}&${queryString.stringify(query)}`,
125+
}).then(d => ({ ...d, quantile: query.quantile }));
126+
},
115127
};
116128

117129
export default JaegerAPI;

packages/jaeger-ui/src/api/jaeger.test.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,44 @@ describe('getMessageFromError()', () => {
201201
});
202202
});
203203
});
204+
205+
describe('fetchMetrics', () => {
206+
it('GETs metrics query', () => {
207+
fetchMock.mockReset();
208+
const metricsType = 'latencies';
209+
const serviceName = ['serviceName'];
210+
const query = {
211+
quantile: 95,
212+
};
213+
fetchMock.mockReturnValue(
214+
Promise.resolve({
215+
status: 200,
216+
json: () => Promise.resolve({ someObj: {} }),
217+
})
218+
);
219+
220+
JaegerAPI.fetchMetrics(metricsType, serviceName, query);
221+
expect(fetchMock).toHaveBeenLastCalledWith(
222+
`${DEFAULT_API_ROOT}metrics/${metricsType}?service=${serviceName}&${queryString.stringify(query)}`,
223+
defaultOptions
224+
);
225+
});
226+
227+
it('fetchMetrics() should add quantile to response', async () => {
228+
const metricsType = 'latencies';
229+
const serviceName = ['serviceName'];
230+
const query = {
231+
quantile: 95,
232+
};
233+
234+
fetchMock.mockReturnValue(
235+
Promise.resolve({
236+
status: 200,
237+
json: () => Promise.resolve({ someObj: {} }),
238+
})
239+
);
240+
241+
const resp = await JaegerAPI.fetchMetrics(metricsType, serviceName, query);
242+
expect(resp.quantile).toBe(query.quantile);
243+
});
244+
});

packages/jaeger-ui/src/components/App/TopNav.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import * as deepDependencies from '../DeepDependencies/url';
2424
import * as qualityMetrics from '../QualityMetrics/url';
2525
import * as searchUrl from '../SearchTracePage/url';
2626
import * as diffUrl from '../TraceDiff/url';
27+
import * as monitorATMUrl from '../Monitor/url';
2728
import { ReduxState } from '../../types';
2829
import { ConfigMenuItem, ConfigMenuGroup } from '../../types/config';
2930
import { getConfigValue } from '../../utils/config/get-config';
@@ -68,13 +69,22 @@ if (getConfigValue('qualityMetrics.menuEnabled')) {
6869
});
6970
}
7071

72+
if (getConfigValue('monitor.menuEnabled')) {
73+
NAV_LINKS.push({
74+
to: monitorATMUrl.getUrl(),
75+
matches: monitorATMUrl.matches,
76+
text: 'Monitor',
77+
});
78+
}
79+
7180
function getItem(item: ConfigMenuItem) {
7281
const { label, anchorTarget, url } = item;
7382
const link = (
7483
<a href={url} target={anchorTarget || '_blank'} rel="noopener noreferrer">
7584
{label}
7685
</a>
7786
);
87+
7888
return (
7989
<Menu.Item key={label} disabled={!url}>
8090
{url ? link : label}

packages/jaeger-ui/src/components/App/__snapshots__/index.test.js.snap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ exports[`JaegerUIApp does not explode 1`] = `
6060
component={[Function]}
6161
path="/quality-metrics"
6262
/>
63+
<Route
64+
component={[Function]}
65+
path="/monitor"
66+
/>
6367
<Redirect
6468
exact={true}
6569
path="/"

packages/jaeger-ui/src/components/App/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import TraceDiff from '../TraceDiff';
3232
import { ROUTE_PATH as traceDiffPath } from '../TraceDiff/url';
3333
import TracePage from '../TracePage';
3434
import { ROUTE_PATH as tracePath } from '../TracePage/url';
35+
import MonitorATMPage from '../Monitor';
36+
import { ROUTE_PATH as monitorATMPath } from '../Monitor/url';
3537
import JaegerAPI, { DEFAULT_API_ROOT } from '../../api/jaeger';
3638
import configureStore from '../../utils/configure-store';
3739
import processScripts from '../../utils/config/process-scripts';
@@ -63,6 +65,7 @@ export default class JaegerUIApp extends Component {
6365
<Route path={dependenciesPath} component={DependencyGraph} />
6466
<Route path={deepDependenciesPath} component={DeepDependencies} />
6567
<Route path={qualityMetricsPath} component={QualityMetrics} />
68+
<Route path={monitorATMPath} component={MonitorATMPage} />
6669

6770
<Redirect exact path="/" to={searchPath} />
6871
<Redirect exact path={prefixUrl()} to={searchPath} />

0 commit comments

Comments
 (0)