Skip to content

Commit

Permalink
Add ability to search for nodes in TraceDiffGraph and TraceGraph (jae…
Browse files Browse the repository at this point in the history
…gertracing#307)

Signed-off-by: Everett Ross <reverett@uber.com>

Signed-off-by: vvvprabhakar <vvvprabhakar@gmail.com>
  • Loading branch information
everett980 committed Jan 16, 2019
1 parent 3608969 commit c671079
Show file tree
Hide file tree
Showing 11 changed files with 247 additions and 59 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
Copyright (c) 2019 The Jaeger Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

.GraphSearch {
position: absolute;
right: 20px;
bottom: 20px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// @flow

// Copyright (c) 2019 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import * as React from 'react';
import { Icon, Input } from 'antd';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import queryString from 'query-string';

import type { Location, /* Match, */ RouterHistory } from 'react-router-dom';

import prefixUrl from '../../../utils/prefix-url';

import type { ReduxState } from '../../../types/index';

import './GraphSearch.css';

type propsType = {
graphSearch?: string,
history: RouterHistory,
location: Location,
};

export function UnconnectedGraphSearch(props: propsType) {
function inputOnChange(evt) {
const { graphSearch, ...queryParams } = queryString.parse(props.location.search);
const { value } = evt.target;
if (value) {
queryParams.graphSearch = value;
}
props.history.replace(prefixUrl(`?${queryString.stringify(queryParams)}`));
}
return (
<div className="GraphSearch">
<Input onChange={inputOnChange} value={props.graphSearch} />
<Icon type="search" />
</div>
);
}

UnconnectedGraphSearch.defaultProps = {
graphSearch: null,
};

export function mapStateToProps(state: ReduxState): { graphSearch?: string } {
const { graphSearch } = queryString.parse(state.router.location.search);
return { graphSearch };
}

export default withRouter(connect(mapStateToProps)(UnconnectedGraphSearch));
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import * as React from 'react';
import { DirectedGraph, LayoutManager } from '@jaegertracing/plexus';

import drawNode from './drawNode';
import GraphSearch from './GraphSearch';
import ErrorMessage from '../../common/ErrorMessage';
import LoadingIndicator from '../../common/LoadingIndicator';
import { fetchedState } from '../../../constants';
Expand Down Expand Up @@ -112,6 +113,7 @@ export default class TraceDiffGraph extends React.PureComponent<Props> {
edges={edges}
vertices={vertices}
/>
<GraphSearch />
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ limitations under the License.
color: #fff;
}

.DiffNode.is-graph-search-match {
outline-style: solid;
outline-color: green;
outline-width: 1px;
}

.DiffNode--metricCell {
padding: 0.3rem 0.5rem;
background: rgba(255, 255, 255, 0.3);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,22 @@
import * as React from 'react';
import { Popover } from 'antd';
import cx from 'classnames';
import _map from 'lodash/map';
import _memoize from 'lodash/memoize';
import queryString from 'query-string';
import { connect } from 'react-redux';

import filterSpans from '../../../utils/filter-spans';
import type { PVertex } from '../../../model/trace-dag/types';
import type { ReduxState } from '../../../types/index';

import './drawNode.css';

type Props = {
a: number,
b: number,
graphSearch?: string,
members: any[],
operation: string,
service: string,
};
Expand All @@ -34,9 +42,18 @@ const max = Math.max;

class DiffNode extends React.PureComponent<Props> {
props: Props;
filterSpans: typeof filterSpans;
static defaultProps = {
graphSearch: '',
};

constructor(props: Props) {
super(props);
this.filterSpans = _memoize(filterSpans);
}

render() {
const { a, b, operation, service } = this.props;
const { a, b, graphSearch, operation, service } = this.props;
const isSame = a === b;
const className = cx({
'is-same': isSame,
Expand All @@ -45,6 +62,7 @@ class DiffNode extends React.PureComponent<Props> {
'is-added': a === 0,
'is-less': a > b && b > 0,
'is-removed': b === 0,
'is-graph-search-match': this.filterSpans(graphSearch, _map(this.props.members, 'span')).size,
});
const chgSign = a < b ? '+' : '-';
const table = (
Expand Down Expand Up @@ -81,7 +99,15 @@ class DiffNode extends React.PureComponent<Props> {
}
}

// TODO: This mapStateToProps is duplicative in three components
export function mapStateToProps(state: ReduxState): { graphSearch?: string } {
const { graphSearch } = queryString.parse(state.router.location.search);
return { graphSearch };
}

const ConnectedDiffNode = connect(mapStateToProps)(DiffNode);

export default function drawNode<T>(vertex: PVertex<T>) {
const { data, operation, service } = vertex.data;
return <DiffNode {...data} operation={operation} service={service} />;
const { data, members, operation, service } = vertex.data;
return <ConnectedDiffNode {...data} members={members} operation={operation} service={service} />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ limitations under the License.
.OpNode th {
border: none;
}
.OpNode.is-graph-search-match {
outline-style: solid;
outline-color: green;
outline-width: 1px;
}

.OpMode--mode-service {
background: #bbb;
Expand Down
51 changes: 47 additions & 4 deletions packages/jaeger-ui/src/components/TracePage/TraceGraph/OpNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,17 @@

import * as React from 'react';
import { Popover } from 'antd';
import cx from 'classnames';
import _map from 'lodash/map';
import _memoize from 'lodash/memoize';
import queryString from 'query-string';
import { connect } from 'react-redux';

import filterSpans from '../../../utils/filter-spans';
import colorGenerator from '../../../utils/color-generator';

import type { PVertex } from '../../../model/trace-dag/types';
import type { ReduxState } from '../../../types/index';

import './OpNode.css';

Expand All @@ -32,6 +40,8 @@ type Props = {
operation: string,
service: string,
mode: string,
graphSearch?: string,
members: any[],
};

export const MODE_SERVICE = 'service';
Expand Down Expand Up @@ -63,9 +73,29 @@ export function round2(percent: number) {

export default class OpNode extends React.PureComponent<Props> {
props: Props;
filterSpans: typeof filterSpans;
static defaultProps = {
graphSearch: '',
};

constructor(props: Props) {
super(props);
this.filterSpans = _memoize(filterSpans);
}

render() {
const { count, errors, time, percent, selfTime, percentSelfTime, operation, service, mode } = this.props;
const {
count,
errors,
time,
percent,
selfTime,
percentSelfTime,
operation,
service,
mode,
graphSearch,
} = this.props;

// Spans over 20 % time are full red - we have probably to reconsider better approach
let backgroundColor;
Expand All @@ -81,8 +111,12 @@ export default class OpNode extends React.PureComponent<Props> {
.join();
}

const className = cx('OpNode', `OpNode--mode-${mode}`, {
'is-graph-search-match': this.filterSpans(graphSearch, _map(this.props.members, 'span')).size,
});

const table = (
<table className={`OpNode OpNode--mode-${mode}`} cellSpacing="0">
<table className={className} cellSpacing="0">
<tbody
style={{
background: `rgba(${backgroundColor})`,
Expand Down Expand Up @@ -118,9 +152,18 @@ export default class OpNode extends React.PureComponent<Props> {
}
}

export function mapStateToProps(state: ReduxState): { graphSearch?: string } {
const { graphSearch } = queryString.parse(state.router.location.search);
return { graphSearch };
}

const ConnectedOpNode = connect(mapStateToProps)(OpNode);

export function getNodeDrawer(mode: string) {
return function drawNode<T>(vertex: PVertex<T>) {
const { data, operation, service } = vertex.data;
return <OpNode {...data} mode={mode} operation={operation} service={service} />;
const { data, members, operation, service } = vertex.data;
return (
<ConnectedOpNode {...data} members={members} mode={mode} operation={operation} service={service} />
);
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { DirectedGraph, LayoutManager } from '@jaegertracing/plexus';
import DRange from 'drange';

import { getNodeDrawer, MODE_SERVICE, MODE_TIME, MODE_SELFTIME, HELP_TABLE } from './OpNode';
// TODO: Location implies only used by diff, need to move
import GraphSearch from '../../TraceDiff/TraceDiffGraph/GraphSearch';
import convPlexus from '../../../model/trace-dag/convPlexus';
import TraceDag from '../../../model/trace-dag/TraceDag';

Expand Down Expand Up @@ -336,6 +338,7 @@ export default class TraceGraph extends React.PureComponent<Props, State> {
</section>
)}
</div>
<GraphSearch />
</div>
);
}
Expand Down
56 changes: 4 additions & 52 deletions packages/jaeger-ui/src/components/TracePage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import * as React from 'react';
import { Input } from 'antd';
import _clamp from 'lodash/clamp';
import _get from 'lodash/get';
import _mapValues from 'lodash/mapValues';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
Expand All @@ -38,13 +39,14 @@ import ErrorMessage from '../common/ErrorMessage';
import LoadingIndicator from '../common/LoadingIndicator';
import * as jaegerApiActions from '../../actions/jaeger-api';
import { fetchedState } from '../../constants';
import filterSpans from '../../utils/filter-spans';

import type { CombokeysHandler, ShortcutCallbacks } from './keyboard-shortcuts';
import type { ViewRange, ViewRangeTimeUpdate } from './types';
import type { FetchedTrace, ReduxState } from '../../types';
import type { TraceArchive } from '../../types/archive';
import type { EmbeddedState } from '../../types/embedded';
import type { KeyValuePair, Span } from '../../types/trace';
// import type { KeyValuePair, Span } from '../../types/trace';

import './index.css';

Expand Down Expand Up @@ -217,60 +219,10 @@ export class TracePageImpl extends React.PureComponent<TracePageProps, TracePage
}
};

filterSpans = (textFilter: string) => {
const spans = this.props.trace && this.props.trace.data && this.props.trace.data.spans;
if (!spans) return null;

// if a span field includes at least one filter in includeFilters, the span is a match
const includeFilters = [];

// values with keys that include text in any one of the excludeKeys will be ignored
const excludeKeys = [];

// split textFilter by whitespace, remove empty strings, and extract includeFilters and excludeKeys
textFilter
.split(' ')
.map(s => s.trim())
.filter(s => s)
.forEach(w => {
if (w[0] === '-') {
excludeKeys.push(w.substr(1).toLowerCase());
} else {
includeFilters.push(w.toLowerCase());
}
});

const isTextInFilters = (filters: Array<string>, text: string) =>
filters.some(filter => text.toLowerCase().includes(filter));

const isTextInKeyValues = (kvs: Array<KeyValuePair>) =>
kvs
? kvs.some(kv => {
// ignore checking key and value for a match if key is in excludeKeys
if (isTextInFilters(excludeKeys, kv.key)) return false;
// match if key or value matches an item in includeFilters
return (
isTextInFilters(includeFilters, kv.key) || isTextInFilters(includeFilters, kv.value.toString())
);
})
: false;

const isSpanAMatch = (span: Span) =>
isTextInFilters(includeFilters, span.operationName) ||
isTextInFilters(includeFilters, span.process.serviceName) ||
isTextInKeyValues(span.tags) ||
span.logs.some(log => isTextInKeyValues(log.fields)) ||
isTextInKeyValues(span.process.tags);

// declare as const because need to disambiguate the type
const rv: Set<string> = new Set(spans.filter(isSpanAMatch).map((span: Span) => span.spanID));
return rv;
};

updateTextFilter = (textFilter: string) => {
let findMatchesIDs;
if (textFilter.trim()) {
findMatchesIDs = this.filterSpans(textFilter);
findMatchesIDs = filterSpans(textFilter, _get(this.props, 'trace.data.spans'));
} else {
findMatchesIDs = null;
}
Expand Down
1 change: 1 addition & 0 deletions packages/jaeger-ui/src/model/trace-dag/TraceDag.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export default class TraceDag<T = void> {
});
const { data } = node;
data[key] = src.count;
node.members.push(...src.members);
node.count = data.b - data.a;
if (!node.parentID) {
dt.rootIDs.add(node.id);
Expand Down
Loading

0 comments on commit c671079

Please sign in to comment.