diff --git a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/TracePageHeader.tsx b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/TracePageHeader.tsx
index ac66389f83..5fa19795e2 100644
--- a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/TracePageHeader.tsx
+++ b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/TracePageHeader.tsx
@@ -23,6 +23,7 @@ import { Link } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import AltViewOptions from './AltViewOptions';
import KeyboardShortcutsHelp from './KeyboardShortcutsHelp';
+import TracePageSettings from './TracePageSettings';
import SpanGraph from './SpanGraph';
import TracePageSearchBar from './TracePageSearchBar';
import { TUpdateViewRangeTimeFunction, IViewRange, ViewRangeTimeUpdate, ETraceViewType } from '../types';
@@ -58,6 +59,9 @@ type TracePageHeaderEmbedProps = {
showViewOptions: boolean;
slimView: boolean;
textFilter: string | TNil;
+ availableTagKeys: string[];
+ selectedMarkerColorKey: string[];
+ setMarkerColorKey: (tagKey: string[]) => void;
toSearch: string | null;
trace: Trace;
viewType: ETraceViewType;
@@ -127,6 +131,9 @@ export function TracePageHeaderFn(props: TracePageHeaderEmbedProps & { forwarded
disableJsonView,
slimView,
textFilter,
+ availableTagKeys,
+ selectedMarkerColorKey,
+ setMarkerColorKey,
toSearch,
trace,
viewType,
@@ -190,6 +197,12 @@ export function TracePageHeaderFn(props: TracePageHeaderEmbedProps & { forwarded
textFilter={textFilter}
navigable={viewType === ETraceViewType.TraceTimelineViewer}
/>
+
{showShortcutsHelp && }
{showViewOptions && (
void;
+ className: string;
+};
+
+type State = {
+ visible: boolean;
+};
+
+type DataRecord = {
+ key: string;
+ kbds: React.JSX.Element;
+ description: string;
+};
+
+function renderTagKey(tag: string) {
+ if (tag === '') {
+ return (Empty string);
+ }
+
+ return {tag};
+}
+
+function getMarkerColorKeyFormItem(props: Props) {
+ const items = [];
+ const callbacks: { [key: string]: boolean } = {}; // whether the key should be added or removed
+
+ for (const key of props.selectedMarkerColorKey) {
+ callbacks[key] = false;
+ items.push({
+ key: key,
+ label: (
+
+ {renderTagKey(key)}
+
+
+ ),
+ });
+ }
+
+ for (const key of props.availableTagKeys) {
+ if (callbacks.hasOwnProperty(key)) {
+ continue;
+ }
+
+ callbacks[key] = true;
+ items.push({
+ key: key,
+ label: renderTagKey(key),
+ });
+ }
+
+ const toggle = key => {
+ console.log(callbacks);
+ console.log(key, callbacks[key]);
+ let newList: string[];
+ if (callbacks[key]) {
+ newList = props.selectedMarkerColorKey.concat([key]);
+ newList.sort();
+ } else {
+ newList = props.selectedMarkerColorKey.filter(item => item != key);
+ }
+ console.log(newList);
+ props.onMarkerColorKeyChange(newList);
+ };
+
+ return (
+
+ <>
+ {props.selectedMarkerColorKey.map(key => (
+ }
+ onClose={e => {
+ e.preventDefault();
+ toggle(key);
+ }}
+ >
+ {renderTagKey(key)}
+
+ ))}
+ >
+ toggle(key) }}>
+
+
+
+
+
+ );
+}
+
+export default class TracePageSettings extends React.PureComponent {
+ state = {
+ visible: false,
+ };
+
+ onCtaClicked = () => {
+ track();
+ this.setState({ visible: true });
+ };
+
+ onCloserClicked = () => this.setState({ visible: false });
+
+ render() {
+ const { className } = this.props;
+ return (
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBar.css b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBar.css
index adc08aa338..c02dd399c6 100644
--- a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBar.css
+++ b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBar.css
@@ -101,7 +101,6 @@ limitations under the License.
}
.SpanBar--logMarker {
- background-color: rgba(0, 0, 0, 0.5);
cursor: pointer;
height: 60%;
min-width: 1px;
diff --git a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBar.tsx b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBar.tsx
index 7f9447ef8a..8c01484e94 100644
--- a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBar.tsx
+++ b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBar.tsx
@@ -21,6 +21,7 @@ import AccordianLogs from './SpanDetail/AccordianLogs';
import { ViewedBoundsFunctionType } from './utils';
import { TNil } from '../../../types';
import { Span, criticalPathSection } from '../../../types/trace';
+import { ColorGenerator, ColorGeneratorView, Rgb } from '../../../utils/color-generator';
import './SpanBar.css';
@@ -44,6 +45,8 @@ type TCommonProps = {
span: Span;
longLabel: string;
shortLabel: string;
+ colorGenerator: ColorGenerator;
+ markerColorKey: string[];
};
function toPercent(value: number) {
@@ -68,6 +71,8 @@ function SpanBar(props: TCommonProps) {
span,
shortLabel,
longLabel,
+ colorGenerator,
+ markerColorKey,
} = props;
// group logs based on timestamps
const logGroups = _groupBy(span.logs, log => {
@@ -124,7 +129,15 @@ function SpanBar(props: TCommonProps) {
))}
@@ -171,4 +184,33 @@ function SpanBar(props: TCommonProps) {
);
}
+function darkenRgb(rgb: Rgb): Rgb {
+ return rgb.map(comp => Math.floor((comp * 2) / 3));
+}
+
+function logMarkerImage(colorView: ColorGeneratorView, tags: string[], logs: Log[]): string {
+ if (tags.length === 0) {
+ return '#000000';
+ }
+
+ const tagSet = {};
+ for (const tag of tags) {
+ tagSet[tag] = true;
+ }
+
+ const sections = [];
+
+ for (const log of logs) {
+ const values = log.fields.filter(({ key }) => tagSet.hasOwnProperty(key)).map(({ value }) => value);
+ const color = values.length === 0 ? '#000000' : colorView.getColorByKey(JSON.stringify(values));
+
+ const start = Math.floor((sections.length * 100) / logs.length);
+ const end = Math.floor(((sections.length + 1) * 100) / logs.length);
+
+ sections.push(`${color} ${start}%`, `${color} ${end}%`);
+ }
+
+ return `linear-gradient(${sections.join(', ')})`;
+}
+
export default SpanBar;
diff --git a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBarRow.tsx b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBarRow.tsx
index 310d6aec57..45909d9dfd 100644
--- a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBarRow.tsx
+++ b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBarRow.tsx
@@ -22,6 +22,7 @@ import SpanBar from './SpanBar';
import Ticks from './Ticks';
import { TNil } from '../../../types';
+import { ColorGenerator } from '../../../utils/color-generator';
import { criticalPathSection, Span } from '../../../types/trace';
import './SpanBarRow.css';
@@ -55,6 +56,8 @@ type SpanBarRowProps = {
showErrorIcon: boolean;
getViewedBounds: ViewedBoundsFunctionType;
traceStartTime: number;
+ colorGenerator: ColorGenerator;
+ markerColorKey: string[];
span: Span;
focusSpan: (spanID: string) => void;
};
@@ -98,6 +101,8 @@ export default class SpanBarRow extends React.PureComponent {
traceStartTime,
span,
focusSpan,
+ colorGenerator,
+ markerColorKey,
} = this.props;
const {
duration,
@@ -211,6 +216,8 @@ export default class SpanBarRow extends React.PureComponent {
hintSide={hintSide}
traceStartTime={traceStartTime}
span={span}
+ colorGenerator={colorGenerator}
+ markerColorKey={markerColorKey}
/>
diff --git a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.tsx b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.tsx
index c6dafc7bb0..c094777017 100644
--- a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.tsx
+++ b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.tsx
@@ -62,6 +62,7 @@ type TVirtualizedTraceViewOwnProps = {
registerAccessors: (accesors: Accessors) => void;
trace: Trace;
criticalPath: criticalPathSection[];
+ markerColorKey: string[];
};
type TDispatchProps = {
@@ -386,6 +387,7 @@ export class VirtualizedTraceViewImpl extends React.Component
);
diff --git a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/index.tsx b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/index.tsx
index 597f77046d..24bd5c8242 100644
--- a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/index.tsx
+++ b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/index.tsx
@@ -45,6 +45,7 @@ type TProps = TDispatchProps & {
updateNextViewRangeTime: (update: ViewRangeTimeUpdate) => void;
updateViewRangeTime: TUpdateViewRangeTimeFunction;
viewRange: IViewRange;
+ markerColorKey: string[];
};
const NUM_TICKS = 5;
diff --git a/packages/jaeger-ui/src/components/TracePage/index.tsx b/packages/jaeger-ui/src/components/TracePage/index.tsx
index 1a887e713d..02d306a34b 100644
--- a/packages/jaeger-ui/src/components/TracePage/index.tsx
+++ b/packages/jaeger-ui/src/components/TracePage/index.tsx
@@ -19,6 +19,7 @@ import _clamp from 'lodash/clamp';
import _get from 'lodash/get';
import _mapValues from 'lodash/mapValues';
import _memoize from 'lodash/memoize';
+import _keys from 'lodash/keys';
import { connect } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';
@@ -97,6 +98,7 @@ type TState = {
slimView: boolean;
viewType: ETraceViewType;
viewRange: IViewRange;
+ selectedMarkerColorKey: string | null;
};
// export for tests
@@ -104,6 +106,8 @@ export const VIEW_MIN_RANGE = 0.01;
const VIEW_CHANGE_BASE = 0.005;
const VIEW_CHANGE_FAST = 0.05;
+const MARKER_COLOR_TAG_STORAGE_KEY = 'logMarkerColorKey';
+
// export for tests
export const shortcutConfig: { [name: string]: [number, number] } = {
panLeft: [-VIEW_CHANGE_BASE, -VIEW_CHANGE_BASE],
@@ -140,6 +144,12 @@ export class TracePageImpl extends React.PureComponent {
constructor(props: TProps) {
super(props);
const { embedded, trace } = props;
+
+ const selectedMarkerColorKeyJson = window.localStorage.getItem(MARKER_COLOR_TAG_STORAGE_KEY);
+ const selectedMarkerColorKey: string[] = selectedMarkerColorKeyJson
+ ? JSON.parse(selectedMarkerColorKeyJson).tagKeys
+ : [];
+
this.state = {
headerHeight: null,
slimView: Boolean(embedded && embedded.timeline.collapseTitle),
@@ -149,6 +159,7 @@ export class TracePageImpl extends React.PureComponent {
current: [0, 1],
},
},
+ selectedMarkerColorKey,
};
this._headerElm = null;
this._filterSpans = _memoize(
@@ -324,6 +335,11 @@ export class TracePageImpl extends React.PureComponent {
this._scrollManager.scrollToPrevVisibleSpan();
};
+ setMarkerColorKey = (tagKeys: string[]) => {
+ window.localStorage.setItem(MARKER_COLOR_TAG_STORAGE_KEY, JSON.stringify({ tagKeys }));
+ this.setState({ selectedMarkerColorKey: tagKeys });
+ };
+
render() {
const {
archiveEnabled,
@@ -338,7 +354,7 @@ export class TracePageImpl extends React.PureComponent {
traceGraphConfig,
location: { state: locationState },
} = this.props;
- const { slimView, viewType, headerHeight, viewRange } = this.state;
+ const { slimView, viewType, headerHeight, viewRange, selectedMarkerColorKey } = this.state;
if (!trace || trace.state === fetchedState.LOADING) {
return ;
}
@@ -391,6 +407,9 @@ export class TracePageImpl extends React.PureComponent {
trace: data,
updateNextViewRangeTime: this.updateNextViewRangeTime,
updateViewRangeTime: this.updateViewRangeTime,
+ availableTagKeys: trace ? collectTagKeys(trace.data) : [],
+ selectedMarkerColorKey,
+ setMarkerColorKey: this.setMarkerColorKey,
};
let view;
@@ -406,6 +425,7 @@ export class TracePageImpl extends React.PureComponent {
updateNextViewRangeTime={this.updateNextViewRangeTime}
updateViewRangeTime={this.updateViewRangeTime}
viewRange={viewRange}
+ markerColorKey={selectedMarkerColorKey}
/>
);
} else if (ETraceViewType.TraceGraph === viewType && headerHeight) {
@@ -440,6 +460,22 @@ export class TracePageImpl extends React.PureComponent {
}
}
+function collectTagKeys(trace: Trace): string[] {
+ const keys = {};
+
+ for (const span of trace.spans) {
+ for (const log of span.logs) {
+ for (const { key } of log.fields) {
+ keys[key] = true;
+ }
+ }
+ }
+
+ const keysArray = _keys(keys);
+ keysArray.sort();
+ return keysArray;
+}
+
// export for tests
export function mapStateToProps(state: ReduxState, ownProps: TOwnProps): TReduxProps {
const { id } = ownProps.params;
diff --git a/packages/jaeger-ui/src/utils/color-generator.tsx b/packages/jaeger-ui/src/utils/color-generator.tsx
index 7c915f4be3..af871c8c86 100644
--- a/packages/jaeger-ui/src/utils/color-generator.tsx
+++ b/packages/jaeger-ui/src/utils/color-generator.tsx
@@ -36,7 +36,7 @@ const COLORS_HEX = [
];
// TS needs the precise return type
-function strToRgb(s: string): [number, number, number] {
+function strToRgb(s: string): Rgb {
if (s.length !== 7) {
return [0, 0, 0];
}
@@ -46,9 +46,13 @@ function strToRgb(s: string): [number, number, number] {
return [parseInt(r, 16), parseInt(g, 16), parseInt(b, 16)];
}
+function rgbToStr(rgb: Rgb): string {
+ return `rgb(${rgb.map(c => c.toString()).join(', ')})`;
+}
+
export class ColorGenerator {
colorsHex: string[];
- colorsRgb: [number, number, number][];
+ colorsRgb: Rgb[];
cache: Map;
currentIdx: number;
@@ -84,7 +88,7 @@ export class ColorGenerator {
* it with a color if the key is not recognized.
* @return {number[]} An array of three ints [0, 255] representing a color.
*/
- getRgbColorByKey(key: string): [number, number, number] {
+ getRgbColorByKey(key: string): Rgb {
const i = this._getColorIndex(key);
return this.colorsRgb[i];
}
@@ -95,4 +99,31 @@ export class ColorGenerator {
}
}
+export type Rgb = [number, number, number];
+export type RgbMapper = (Rgb) => Rgb;
+
+/**
+ * An alternative view of a ColorGenerator with mapped colors.
+ * Shares the same underlying cache.
+ */
+export class ColorGeneratorView {
+ base: ColorGenerator;
+ mapper: RgbMapper;
+
+ constructor(base: ColorGenerator, mapRgb: RgbMapper) {
+ this.base = base;
+ this.mapper = mapRgb;
+ }
+
+ getRgbColorByKey(key: string): Rgb {
+ const rgb = this.base.getRgbColorByKey(key);
+ return this.mapper(rgb);
+ }
+
+ getColorByKey(key: string): string {
+ const rgb = this.getRgbColorByKey(key);
+ return rgbToStr(rgb);
+ }
+}
+
export default new ColorGenerator();