Skip to content

Commit

Permalink
Add GA event tracking for actions in trace view (#191)
Browse files Browse the repository at this point in the history
* Track errors in GA w raven-js; TODO: tests, readme

Signed-off-by: Joe Farro <joef@uber.com>

* Include CSS selector with last error breadcrumb

Signed-off-by: Joe Farro <joef@uber.com>

* README for GA error tracking

Signed-off-by: Joe Farro <joef@uber.com>

* Misc cleanup

Signed-off-by: Joe Farro <joef@uber.com>

* README info on GA Application Tracking

Signed-off-by: Joe Farro <joef@uber.com>

* Misc fix to tracking README

Signed-off-by: Joe Farro <joef@uber.com>

* Misc cleanup to raven message conversion to GA

Signed-off-by: Joe Farro <joef@uber.com>

* Tests for tracking

Signed-off-by: Joe Farro <joef@uber.com>

* Apply prettier to markdown files

Signed-off-by: Joe Farro <joef@uber.com>

* Error tracking fn name fallback, CSS import order

Signed-off-by: Joe Farro <joef@uber.com>

* GA event tracking in trace view, tests are TODO

Signed-off-by: Joe Farro <joef@uber.com>

* Fix broken tests

Signed-off-by: Joe Farro <joef@uber.com>

* Tests for TracePage tracking

Signed-off-by: Joe Farro <joef@uber.com>

* Additional tests for trace GA event tracking

Signed-off-by: Joe Farro <joef@uber.com>

* Better names for tracking functions

Signed-off-by: Joe Farro <joef@uber.com>

* Make GA event tracking more concise (PR feedback)

Signed-off-by: Joe Farro <joef@uber.com>

* Revert the "more concise" changes

Signed-off-by: Joe Farro <joef@uber.com>

* Update changelog

Signed-off-by: Joe Farro <joef@uber.com>
  • Loading branch information
tiffon committed Mar 26, 2018
1 parent 00ee282 commit c622330
Show file tree
Hide file tree
Showing 31 changed files with 641 additions and 84 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changes merged into master

### [#191](https://github.com/jaegertracing/jaeger-ui/pull/191) Add GA event tracking for actions in trace view

* Partially addresses [#157](https://github.com/jaegertracing/jaeger-ui/issues/157) - Enhanced Google Analytics integration

### [#198](https://github.com/jaegertracing/jaeger-ui/pull/198) Use `<base>` and config webpack at runtime to allow path prefix

* Fix [#42](https://github.com/jaegertracing/jaeger-ui/issues/42) - No support for Jaeger behind a reverse proxy

### [#195](https://github.com/jaegertracing/jaeger-ui/pull/195) Handle Error stored in redux trace.traces

* Fix [#166](https://github.com/jaegertracing/jaeger-ui/issues/166) - JS error on search page after viewing 404 trace

### [#192](https://github.com/jaegertracing/jaeger-ui/pull/192) Change fallback trace name to be more informative

* Fix [#190](https://github.com/jaegertracing/jaeger-ui/issues/190) - Change `cannot-find-trace-name` to `trace-without-root-span`
Expand Down
1 change: 0 additions & 1 deletion src/components/TracePage/KeyboardShortcutsHelp.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ limitations under the License.
border: 1px solid #e8e8e8;
border-bottom: 1px solid #ddd;
color: #000;
margin-right: 0.4em;
font-family: monospace;
padding: 0.25em 0.3em;
}
4 changes: 3 additions & 1 deletion src/components/TracePage/KeyboardShortcutsHelp.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import React from 'react';
import { Button, Modal, Table } from 'antd';

import { kbdMappings } from './keyboard-shortcuts';
import track from './KeyboardShortcutsHelp.track';

import './KeyboardShortcutsHelp.css';

Expand Down Expand Up @@ -56,13 +57,14 @@ function convertKeys(keyConfig: string | string[]): string[][] {
}

function helpModal() {
track();
const data = [];
Object.keys(kbdMappings).forEach(title => {
const keyConfigs = convertKeys(kbdMappings[title]);
data.push(
...keyConfigs.map(config => ({
key: String(config),
kbds: config.map(s => <kbd key={s}>{s}</kbd>),
kbds: <kbd>{config.join(' ')}</kbd>,
description: descriptions[title],
}))
);
Expand Down
22 changes: 22 additions & 0 deletions src/components/TracePage/KeyboardShortcutsHelp.track.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// @flow

// Copyright (c) 2017 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 { OPEN } from '../../utils/tracking/common';
import { trackEvent } from '../../utils/tracking';

const CATEGORY = 'jaeger/ux/trace/kbd-modal';

export default trackEvent.bind(null, CATEGORY, OPEN);
6 changes: 3 additions & 3 deletions src/components/TracePage/SpanGraph/ViewingLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import './ViewingLayer.css';
type ViewingLayerProps = {
height: number,
numTicks: number,
updateViewRangeTime: (number, number) => void,
updateViewRangeTime: (number, number, ?string) => void,
updateNextViewRangeTime: ViewRangeTimeUpdate => void,
viewRange: ViewRange,
};
Expand Down Expand Up @@ -188,7 +188,7 @@ export default class ViewingLayer extends React.PureComponent<ViewingLayerProps,
const anchor = time.reframe ? time.reframe.anchor : value;
const [start, end] = value < anchor ? [value, anchor] : [anchor, value];
manager.resetBounds();
this.props.updateViewRangeTime(start, end);
this.props.updateViewRangeTime(start, end, 'minimap');
};

_handleScrubberEnterLeave = ({ type }: DraggingUpdate) => {
Expand Down Expand Up @@ -220,7 +220,7 @@ export default class ViewingLayer extends React.PureComponent<ViewingLayerProps,
}
manager.resetBounds();
this.setState({ preventCursorLine: false });
this.props.updateViewRangeTime(...update);
this.props.updateViewRangeTime(update[0], update[1], 'minimap');
};

/**
Expand Down
8 changes: 4 additions & 4 deletions src/components/TracePage/SpanGraph/ViewingLayer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ describe('<SpanGraph>', () => {
wrapper.instance()._handleReframeDragEnd({ manager, value });
expect(manager.resetBounds.mock.calls).toEqual([[]]);
const calls = props.updateViewRangeTime.mock.calls;
expect(calls).toEqual([[value, value]]);
expect(calls).toEqual([[value, value, 'minimap']]);
});

it('handles dragged left (anchor is greater)', () => {
Expand All @@ -154,7 +154,7 @@ describe('<SpanGraph>', () => {

expect(manager.resetBounds.mock.calls).toEqual([[]]);
const calls = props.updateViewRangeTime.mock.calls;
expect(calls).toEqual([[value, anchor]]);
expect(calls).toEqual([[value, anchor, 'minimap']]);
});

it('handles dragged right (anchor is less)', () => {
Expand All @@ -167,7 +167,7 @@ describe('<SpanGraph>', () => {

expect(manager.resetBounds.mock.calls).toEqual([[]]);
const calls = props.updateViewRangeTime.mock.calls;
expect(calls).toEqual([[anchor, value]]);
expect(calls).toEqual([[anchor, value, 'minimap']]);
});
});
});
Expand Down Expand Up @@ -251,7 +251,7 @@ describe('<SpanGraph>', () => {
instance._handleScrubberDragEnd(_case.dragUpdate);
expect(wrapper.state('preventCursorLine')).toBe(false);
expect(manager.resetBounds.mock.calls).toEqual([[]]);
expect(props.updateViewRangeTime).lastCalledWith(..._case.viewRangeUpdate);
expect(props.updateViewRangeTime).lastCalledWith(..._case.viewRangeUpdate, 'minimap');
});
});
});
Expand Down
15 changes: 13 additions & 2 deletions src/components/TracePage/TracePageHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import IoIosFilingOutline from 'react-icons/lib/io/ios-filing-outline';
import { Link } from 'react-router-dom';

import * as markers from './TracePageHeader.markers';
import { trackAltViewOpen } from './TracePageHeader.track';
import KeyboardShortcutsHelp from './KeyboardShortcutsHelp';
import LabeledList from '../common/LabeledList';
import { FALLBACK_TRACE_NAME } from '../../constants';
Expand Down Expand Up @@ -109,12 +110,22 @@ export default function TracePageHeader(props: TracePageHeaderProps) {
const viewMenu = (
<Menu>
<Menu.Item>
<Link to={prefixUrl(`/api/traces/${traceID}`)} rel="noopener noreferrer" target="_blank">
<Link
to={prefixUrl(`/api/traces/${traceID}`)}
rel="noopener noreferrer"
target="_blank"
onClick={trackAltViewOpen}
>
Trace JSON
</Link>
</Menu.Item>
<Menu.Item>
<Link to={prefixUrl(`/api/traces/${traceID}?raw=true`)} rel="noopener noreferrer" target="_blank">
<Link
to={prefixUrl(`/api/traces/${traceID}?raw=true`)}
rel="noopener noreferrer"
target="_blank"
onClick={trackAltViewOpen}
>
Trace JSON (unadjusted)
</Link>
</Menu.Item>
Expand Down
27 changes: 27 additions & 0 deletions src/components/TracePage/TracePageHeader.track.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// @flow

// Copyright (c) 2017 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 { getToggleValue, OPEN } from '../../utils/tracking/common';
import { trackEvent } from '../../utils/tracking';

const CATEGORY_ALT_VIEW = 'jaeger/ux/trace/alt-view';
const CATEGORY_SLIM_HEADER = 'jaeger/ux/trace/slim-header';

// use a closure instead of bind to prevent forwarding any arguments to trackEvent()
export const trackAltViewOpen = () => trackEvent(CATEGORY_ALT_VIEW, OPEN);

export const trackSlimHeaderToggle = (isOpen: boolean) =>
trackEvent(CATEGORY_SLIM_HEADER, getToggleValue(isOpen));
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import KeyValuesTable from './KeyValuesTable';
import './AccordianKeyValues.css';

type AccordianKeyValuesProps = {
className: ?string,
className?: ?string,
data: { key: string, value: any }[],
highContrast?: boolean,
isOpen: boolean,
Expand Down Expand Up @@ -86,5 +86,6 @@ export default function AccordianKeyValues(props: AccordianKeyValuesProps) {
}

AccordianKeyValues.defaultProps = {
className: null,
highContrast: false,
};
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// @flow

// Copyright (c) 2017 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type TimelineHeaderRowProps = {
numTicks: number,
onColummWidthChange: number => void,
updateNextViewRangeTime: ViewRangeTimeUpdate => void,
updateViewRangeTime: (number, number) => void,
updateViewRangeTime: (number, number, ?string) => void,
viewRangeTime: ViewRangeTime,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type TimelineViewingLayerProps = {
*/
boundsInvalidator: ?any,
updateNextViewRangeTime: ViewRangeTimeUpdate => void,
updateViewRangeTime: (number, number) => void,
updateViewRangeTime: (number, number, ?string) => void,
viewRangeTime: ViewRangeTime,
};

Expand Down Expand Up @@ -179,7 +179,7 @@ export default class TimelineViewingLayer extends React.PureComponent<TimelineVi
const anchor = reframe ? reframe.anchor : shift;
const [start, end] = shift < anchor ? [shift, anchor] : [anchor, shift];
manager.resetBounds();
this.props.updateViewRangeTime(start, end);
this.props.updateViewRangeTime(start, end, 'timeline-header');
};

render() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ describe('<TimelineViewingLayer>', () => {
wrapper.setProps({ viewRangeTime });
instance._draggerReframe._onDragEnd({ manager, value });
expect(manager.resetBounds.mock.calls).toEqual([[]]);
expect(props.updateViewRangeTime.mock.calls).toEqual([[anchor, shift]]);
expect(props.updateViewRangeTime.mock.calls).toEqual([[anchor, shift, 'timeline-header']]);
});
});

Expand Down
2 changes: 1 addition & 1 deletion src/components/TracePage/TraceTimelineViewer/duck.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export function newInitialState({ spanNameColumnWidth = null, traceID = null } =
};
}

const actionTypes = generateActionTypes('@jaeger-ui/trace-timeline-viewer', [
export const actionTypes = generateActionTypes('@jaeger-ui/trace-timeline-viewer', [
'SET_TRACE',
'SET_SPAN_NAME_COLUMN_WIDTH',
'CHILDREN_TOGGLE',
Expand Down
68 changes: 68 additions & 0 deletions src/components/TracePage/TraceTimelineViewer/duck.track.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// @flow

// Copyright (c) 2017 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 type { Store } from 'redux';

import { actionTypes as types } from './duck';
import { trackEvent } from '../../../utils/tracking';
import { getToggleValue } from '../../../utils/tracking/common';

const ACTION_RESIZE = 'resize';

const CATEGORY_BASE = 'jaeger/ux/trace/timeline';
// export for tests
export const CATEGORY_TAGS = `${CATEGORY_BASE}/tags`;
export const CATEGORY_PROCESS = `${CATEGORY_BASE}/process`;
export const CATEGORY_LOGS = `${CATEGORY_BASE}/logs`;
export const CATEGORY_LOGS_ITEM = `${CATEGORY_BASE}/logs/item`;
export const CATEGORY_COLUMN = `${CATEGORY_BASE}/column`;
export const CATEGORY_PARENT = `${CATEGORY_BASE}/parent`;
export const CATEGORY_ROW = `${CATEGORY_BASE}/row`;

function trackParent(store: Store, action: any) {
const st = store.getState();
const { spanID } = action.payload;
const traceID = st.traceTimeline.traceID;
const isHidden = st.traceTimeline.childrenHiddenIDs.has(spanID);
const span = st.trace.traces[traceID].spans.find(sp => sp.spanID === spanID);
if (span) {
trackEvent(CATEGORY_PARENT, getToggleValue(!isHidden), span.depth);
}
}

const logs = (isOpen: boolean) => trackEvent(CATEGORY_LOGS, getToggleValue(isOpen));
const logsItem = (isOpen: boolean) => trackEvent(CATEGORY_LOGS_ITEM, getToggleValue(isOpen));
const process = (isOpen: boolean) => trackEvent(CATEGORY_PROCESS, getToggleValue(isOpen));
const tags = (isOpen: boolean) => trackEvent(CATEGORY_TAGS, getToggleValue(isOpen));
const detailRow = (isOpen: boolean) => trackEvent(CATEGORY_ROW, getToggleValue(isOpen));
const columnWidth = (_, action) =>
trackEvent(CATEGORY_COLUMN, ACTION_RESIZE, Math.round(action.payload.width * 1000));

const getDetail = (store, action) => store.getState().traceTimeline.detailStates.get(action.payload.spanID);

export const middlewareHooks = {
[types.CHILDREN_TOGGLE]: trackParent,
[types.DETAIL_TOGGLE]: (store, action) => detailRow(Boolean(getDetail(store, action))),
[types.DETAIL_TAGS_TOGGLE]: (store, action) => tags(getDetail(store, action).isTagsOpen),
[types.DETAIL_PROCESS_TOGGLE]: (store, action) => process(getDetail(store, action).isProcessOpen),
[types.DETAIL_LOGS_TOGGLE]: (store, action) => logs(getDetail(store, action).logs.isOpen),
[types.DETAIL_LOG_ITEM_TOGGLE]: (store, action) => {
const detail = getDetail(store, action);
const { logItem } = action.payload;
logsItem(detail.logs.openedItems.has(logItem));
},
[types.SET_SPAN_NAME_COLUMN_WIDTH]: columnWidth,
};
Loading

0 comments on commit c622330

Please sign in to comment.