Skip to content

Commit

Permalink
ATM Monitor UI
Browse files Browse the repository at this point in the history
Signed-off-by: Mykhailo Semenchenko <mykhailo.semenchenko@logz.io>
  • Loading branch information
th3M1ke committed Nov 11, 2021
1 parent be446bc commit d403411
Show file tree
Hide file tree
Showing 44 changed files with 7,054 additions and 9 deletions.
3 changes: 3 additions & 0 deletions packages/jaeger-ui/jest.global-setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = async () => {
process.env.TZ = 'UTC';
};
1 change: 1 addition & 0 deletions packages/jaeger-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
"test": "CI=1 react-app-rewired test --env=jsdom --color"
},
"jest": {
"globalSetup": "./jest.global-setup.js",
"collectCoverageFrom": [
"!src/setup*.js",
"!src/utils/DraggableManager/demo/*.tsx",
Expand Down
43 changes: 43 additions & 0 deletions packages/jaeger-ui/src/actions/jaeger-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,24 @@
import { createAction } from 'redux-actions';
import JaegerAPI from '../api/jaeger';

const metricType = {
latencies: 'latencies',
calls: 'calls',
errors: 'errors',
};

// export for tests
// TODO use native `allSetteled` once #818 is done
export function allSettled(promises) {
const wrappedPromises = promises.map(p =>
Promise.resolve(p).then(
val => ({ status: 'fulfilled', value: val }),
err => ({ status: 'rejected', reason: err })
)
);
return Promise.all(wrappedPromises);
}

export const fetchTrace = createAction(
'@JAEGER_API/FETCH_TRACE',
id => JaegerAPI.fetchTrace(id),
Expand Down Expand Up @@ -62,3 +80,28 @@ export const fetchDeepDependencyGraph = createAction(
export const fetchDependencies = createAction('@JAEGER_API/FETCH_DEPENDENCIES', () =>
JaegerAPI.fetchDependencies()
);

export const fetchAllServiceMetrics = createAction(
'@JAEGER_API/FETCH_ALL_SERVICE_METRICS',
(serviceName, query) => {
return allSettled([
JaegerAPI.fetchMetrics(metricType.latencies, [serviceName], { ...query, quantile: 0.5 }),
JaegerAPI.fetchMetrics(metricType.latencies, [serviceName], { ...query, quantile: 0.75 }),
JaegerAPI.fetchMetrics(metricType.latencies, [serviceName], query),
JaegerAPI.fetchMetrics(metricType.calls, [serviceName], query),
JaegerAPI.fetchMetrics(metricType.errors, [serviceName], query),
]);
}
);

export const fetchAggregatedServiceMetrics = createAction(
'@JAEGER_API/FETCH_AGGREGATED_SERVICE_METRICS',
(serviceName, queryParams) => {
const query = { ...queryParams, groupByOperation: true };
return allSettled([
JaegerAPI.fetchMetrics(metricType.latencies, [serviceName], query),
JaegerAPI.fetchMetrics(metricType.calls, [serviceName], query),
JaegerAPI.fetchMetrics(metricType.errors, [serviceName], query),
]);
}
);
45 changes: 45 additions & 0 deletions packages/jaeger-ui/src/actions/jaeger-api.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,49 @@ describe('actions/jaeger-api', () => {
jaegerApiActions.fetchDependencies();
expect(called.verify()).toBeTruthy();
});

it('@JAEGER_API/FETCH_ALL_SERVICE_METRICS should return the promise', () => {
const { payload } = jaegerApiActions.fetchAllServiceMetrics('serviceName', query);
expect(isPromise(payload)).toBeTruthy();
});

it('@JAEGER_API/FETCH_ALL_SERVICE_METRICS should fetch service metrics by name', () => {
mock.expects('fetchMetrics');
mock.expects('fetchMetrics');
mock.expects('fetchMetrics');
mock.expects('fetchMetrics');
mock.expects('fetchMetrics');
jaegerApiActions.fetchAllServiceMetrics('serviceName', query);
expect(() => mock.verify()).not.toThrow();
});

it('@JAEGER_API/FETCH_AGGREGATED_SERVICE_METRICS should return the promise', () => {
const { payload } = jaegerApiActions.fetchAggregatedServiceMetrics('serviceName', query);
expect(isPromise(payload)).toBeTruthy();
});

it('@JAEGER_API/FETCH_AGGREGATED_SERVICE_METRICS should fetch service metrics by name', () => {
mock.expects('fetchMetrics');
mock.expects('fetchMetrics');
mock.expects('fetchMetrics');
jaegerApiActions.fetchAggregatedServiceMetrics('serviceName', query);
expect(() => mock.verify()).not.toThrow();
});
});

describe('allSettled', () => {
it('validate responses', async () => {
const res = await jaegerApiActions.allSettled([
Promise.resolve(1),
// eslint-disable-next-line prefer-promise-reject-errors
Promise.reject(2),
Promise.resolve(3),
]);

expect(res).toEqual([
{ status: 'fulfilled', value: 1 },
{ status: 'rejected', reason: 2 },
{ status: 'fulfilled', value: 3 },
]);
});
});
14 changes: 13 additions & 1 deletion packages/jaeger-ui/src/api/jaeger.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ export function getMessageFromError(errData, status) {
function getJSON(url, options = {}) {
const { query = null, ...init } = options;
init.credentials = 'same-origin';
const queryStr = query ? `?${queryString.stringify(query)}` : '';
let queryStr = '';

if (query) {
queryStr = `?${typeof query === 'string' ? query : queryString.stringify(query)}`;
}

return fetch(`${url}${queryStr}`, init).then(response => {
if (response.status < 400) {
return response.json();
Expand Down Expand Up @@ -112,6 +117,13 @@ const JaegerAPI = {
searchTraces(query) {
return getJSON(`${this.apiRoot}traces`, { query });
},
fetchMetrics(metricType, serviceNameList, query) {
const servicesName = serviceNameList.map(serviceName => `service=${serviceName}`).join(',');

return getJSON(`${this.apiRoot}metrics/${metricType}`, {
query: `${servicesName}&${queryString.stringify(query)}`,
}).then(d => ({ ...d, quantile: query.quantile }));
},
};

export default JaegerAPI;
41 changes: 41 additions & 0 deletions packages/jaeger-ui/src/api/jaeger.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,44 @@ describe('getMessageFromError()', () => {
});
});
});

describe('fetchMetrics', () => {
it('GETs metrics query', () => {
fetchMock.mockReset();
const metricsType = 'latencies';
const serviceName = ['serviceName'];
const query = {
quantile: 95,
};
fetchMock.mockReturnValue(
Promise.resolve({
status: 200,
json: () => Promise.resolve({ someObj: {} }),
})
);

JaegerAPI.fetchMetrics(metricsType, serviceName, query);
expect(fetchMock).toHaveBeenLastCalledWith(
`${DEFAULT_API_ROOT}metrics/${metricsType}?service=${serviceName}&${queryString.stringify(query)}`,
defaultOptions
);
});

it('fetchMetrics() should add quantile to response', async () => {
const metricsType = 'latencies';
const serviceName = ['serviceName'];
const query = {
quantile: 95,
};

fetchMock.mockReturnValue(
Promise.resolve({
status: 200,
json: () => Promise.resolve({ someObj: {} }),
})
);

const resp = await JaegerAPI.fetchMetrics(metricsType, serviceName, query);
expect(resp.quantile).toBe(query.quantile);
});
});
10 changes: 10 additions & 0 deletions packages/jaeger-ui/src/components/App/TopNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import * as deepDependencies from '../DeepDependencies/url';
import * as qualityMetrics from '../QualityMetrics/url';
import * as searchUrl from '../SearchTracePage/url';
import * as diffUrl from '../TraceDiff/url';
import * as monitorATMUrl from '../Monitor/url';
import { ReduxState } from '../../types';
import { ConfigMenuItem, ConfigMenuGroup } from '../../types/config';
import { getConfigValue } from '../../utils/config/get-config';
Expand Down Expand Up @@ -68,13 +69,22 @@ if (getConfigValue('qualityMetrics.menuEnabled')) {
});
}

if (getConfigValue('monitor.menuEnabled')) {
NAV_LINKS.push({
to: monitorATMUrl.getUrl(),
matches: monitorATMUrl.matches,
text: 'Monitor',
});
}

function getItem(item: ConfigMenuItem) {
const { label, anchorTarget, url } = item;
const link = (
<a href={url} target={anchorTarget || '_blank'} rel="noopener noreferrer">
{label}
</a>
);

return (
<Menu.Item key={label} disabled={!url}>
{url ? link : label}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ exports[`JaegerUIApp does not explode 1`] = `
component={[Function]}
path="/quality-metrics"
/>
<Route
component={[Function]}
path="/monitor"
/>
<Redirect
exact={true}
path="/"
Expand Down
3 changes: 3 additions & 0 deletions packages/jaeger-ui/src/components/App/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import TraceDiff from '../TraceDiff';
import { ROUTE_PATH as traceDiffPath } from '../TraceDiff/url';
import TracePage from '../TracePage';
import { ROUTE_PATH as tracePath } from '../TracePage/url';
import MonitorATMPage from '../Monitor';
import { ROUTE_PATH as monitorATMPath } from '../Monitor/url';
import JaegerAPI, { DEFAULT_API_ROOT } from '../../api/jaeger';
import configureStore from '../../utils/configure-store';
import processScripts from '../../utils/config/process-scripts';
Expand Down Expand Up @@ -63,6 +65,7 @@ export default class JaegerUIApp extends Component {
<Route path={dependenciesPath} component={DependencyGraph} />
<Route path={deepDependenciesPath} component={DeepDependencies} />
<Route path={qualityMetricsPath} component={QualityMetrics} />
<Route path={monitorATMPath} component={MonitorATMPage} />

<Redirect exact path="/" to={searchPath} />
<Redirect exact path={prefixUrl()} to={searchPath} />
Expand Down
Loading

0 comments on commit d403411

Please sign in to comment.