Tracing: Span link feature tracking (#61022)

* Span link feature tracking

* Update interaction name
This commit is contained in:
Joey Tawadrous 2023-01-13 14:02:25 +00:00 committed by GitHub
parent 28dbbe9f5f
commit 2b6a5e2b3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 55 additions and 8 deletions

View File

@ -328,6 +328,7 @@ export type SpanBarRowProps = {
clippingLeft?: boolean; clippingLeft?: boolean;
clippingRight?: boolean; clippingRight?: boolean;
createSpanLink?: SpanLinkFunc; createSpanLink?: SpanLinkFunc;
datasourceType: string;
}; };
/** /**
@ -377,6 +378,7 @@ export class UnthemedSpanBarRow extends React.PureComponent<SpanBarRowProps> {
clippingRight, clippingRight,
theme, theme,
createSpanLink, createSpanLink,
datasourceType,
} = this.props; } = this.props;
const { const {
duration, duration,
@ -515,7 +517,7 @@ export class UnthemedSpanBarRow extends React.PureComponent<SpanBarRowProps> {
</a> </a>
); );
} else if (links && count > 1) { } else if (links && count > 1) {
return <SpanLinksMenu links={links} />; return <SpanLinksMenu links={links} datasourceType={datasourceType} />;
} else { } else {
return null; return null;
} }

View File

@ -18,6 +18,7 @@ import React from 'react';
import IoLink from 'react-icons/lib/io/link'; import IoLink from 'react-icons/lib/io/link';
import { dateTimeFormat, GrafanaTheme2, LinkModel, TimeZone } from '@grafana/data'; import { dateTimeFormat, GrafanaTheme2, LinkModel, TimeZone } from '@grafana/data';
import { reportInteraction } from '@grafana/runtime';
import { DataLinkButton, TextArea, useStyles2 } from '@grafana/ui'; import { DataLinkButton, TextArea, useStyles2 } from '@grafana/ui';
import { autoColor } from '../../Theme'; import { autoColor } from '../../Theme';
@ -121,6 +122,7 @@ export type SpanDetailProps = {
focusedSpanId?: string; focusedSpanId?: string;
createFocusSpanLink: (traceId: string, spanId: string) => LinkModel; createFocusSpanLink: (traceId: string, spanId: string) => LinkModel;
topOfViewRefType?: TopOfViewRefType; topOfViewRefType?: TopOfViewRefType;
datasourceType: string;
}; };
export default function SpanDetail(props: SpanDetailProps) { export default function SpanDetail(props: SpanDetailProps) {
@ -140,6 +142,7 @@ export default function SpanDetail(props: SpanDetailProps) {
createSpanLink, createSpanLink,
createFocusSpanLink, createFocusSpanLink,
topOfViewRefType, topOfViewRefType,
datasourceType,
} = props; } = props;
const { const {
isTagsOpen, isTagsOpen,
@ -193,6 +196,19 @@ export default function SpanDetail(props: SpanDetailProps) {
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
const links = createSpanLink?.(span); const links = createSpanLink?.(span);
const focusSpanLink = createFocusSpanLink(traceID, spanID); const focusSpanLink = createFocusSpanLink(traceID, spanID);
const logLink = links?.logLinks?.[0]
? {
...links?.logLinks?.[0],
onClick: (event: React.MouseEvent) => {
reportInteraction('grafana_traces_trace_view_span_link_clicked', {
datasourceType: datasourceType,
type: 'log',
location: 'spanDetails',
});
links?.logLinks?.[0].onClick!(event);
},
}
: undefined;
return ( return (
<div data-testid="span-detail-component"> <div data-testid="span-detail-component">
@ -203,10 +219,9 @@ export default function SpanDetail(props: SpanDetailProps) {
</div> </div>
</div> </div>
{links?.logLinks?.[0] ? ( {links?.logLinks?.[0] ? (
<DataLinkButton <>
link={{ ...links?.logLinks?.[0], title: 'Logs for this span' } as any} <DataLinkButton link={{ ...logLink, title: 'Logs for this span' } as any} buttonProps={{ icon: 'gf-logs' }} />
buttonProps={{ icon: 'gf-logs' }} </>
/>
) : null} ) : null}
<Divider className={ubMy1} type={'horizontal'} /> <Divider className={ubMy1} type={'horizontal'} />
<div> <div>

View File

@ -96,6 +96,7 @@ export type SpanDetailRowProps = {
focusedSpanId?: string; focusedSpanId?: string;
createFocusSpanLink: (traceId: string, spanId: string) => LinkModel; createFocusSpanLink: (traceId: string, spanId: string) => LinkModel;
topOfViewRefType?: TopOfViewRefType; topOfViewRefType?: TopOfViewRefType;
datasourceType: string;
}; };
export class UnthemedSpanDetailRow extends React.PureComponent<SpanDetailRowProps> { export class UnthemedSpanDetailRow extends React.PureComponent<SpanDetailRowProps> {
@ -132,6 +133,7 @@ export class UnthemedSpanDetailRow extends React.PureComponent<SpanDetailRowProp
focusedSpanId, focusedSpanId,
createFocusSpanLink, createFocusSpanLink,
topOfViewRefType, topOfViewRefType,
datasourceType,
} = this.props; } = this.props;
const styles = getStyles(theme); const styles = getStyles(theme);
return ( return (
@ -172,6 +174,7 @@ export class UnthemedSpanDetailRow extends React.PureComponent<SpanDetailRowProp
focusedSpanId={focusedSpanId} focusedSpanId={focusedSpanId}
createFocusSpanLink={createFocusSpanLink} createFocusSpanLink={createFocusSpanLink}
topOfViewRefType={topOfViewRefType} topOfViewRefType={topOfViewRefType}
datasourceType={datasourceType}
/> />
</div> </div>
</TimelineRow.Cell> </TimelineRow.Cell>

View File

@ -1,15 +1,22 @@
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { reportInteraction } from '@grafana/runtime';
import { useStyles2, MenuGroup, MenuItem, Icon, ContextMenu } from '@grafana/ui'; import { useStyles2, MenuGroup, MenuItem, Icon, ContextMenu } from '@grafana/ui';
import { SpanLinks } from '../types/links'; import { SpanLinks } from '../types/links';
interface SpanLinksProps { interface SpanLinksProps {
links: SpanLinks; links: SpanLinks;
datasourceType: string;
} }
const renderMenuItems = (links: SpanLinks, styles: ReturnType<typeof getStyles>, closeMenu: () => void) => { const renderMenuItems = (
links: SpanLinks,
styles: ReturnType<typeof getStyles>,
closeMenu: () => void,
datasourceType: string
) => {
return ( return (
<> <>
{!!links.logLinks?.length ? ( {!!links.logLinks?.length ? (
@ -21,6 +28,11 @@ const renderMenuItems = (links: SpanLinks, styles: ReturnType<typeof getStyles>,
onClick={ onClick={
link.onClick link.onClick
? (event) => { ? (event) => {
reportInteraction('grafana_traces_trace_view_span_link_clicked', {
datasourceType: datasourceType,
type: 'log',
location: 'menu',
});
event?.preventDefault(); event?.preventDefault();
link.onClick!(event); link.onClick!(event);
closeMenu(); closeMenu();
@ -42,6 +54,11 @@ const renderMenuItems = (links: SpanLinks, styles: ReturnType<typeof getStyles>,
onClick={ onClick={
link.onClick link.onClick
? (event) => { ? (event) => {
reportInteraction('grafana_traces_trace_view_span_link_clicked', {
datasourceType: datasourceType,
type: 'metric',
location: 'menu',
});
event?.preventDefault(); event?.preventDefault();
link.onClick!(event); link.onClick!(event);
closeMenu(); closeMenu();
@ -63,6 +80,11 @@ const renderMenuItems = (links: SpanLinks, styles: ReturnType<typeof getStyles>,
onClick={ onClick={
link.onClick link.onClick
? (event) => { ? (event) => {
reportInteraction('grafana_traces_trace_view_span_link_clicked', {
datasourceType: datasourceType,
type: 'trace',
location: 'menu',
});
event?.preventDefault(); event?.preventDefault();
link.onClick!(event); link.onClick!(event);
closeMenu(); closeMenu();
@ -79,7 +101,7 @@ const renderMenuItems = (links: SpanLinks, styles: ReturnType<typeof getStyles>,
); );
}; };
export const SpanLinksMenu = ({ links }: SpanLinksProps) => { export const SpanLinksMenu = ({ links, datasourceType }: SpanLinksProps) => {
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
const [isMenuOpen, setIsMenuOpen] = useState(false); const [isMenuOpen, setIsMenuOpen] = useState(false);
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 }); const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
@ -104,7 +126,7 @@ export const SpanLinksMenu = ({ links }: SpanLinksProps) => {
{isMenuOpen ? ( {isMenuOpen ? (
<ContextMenu <ContextMenu
onClose={() => setIsMenuOpen(false)} onClose={() => setIsMenuOpen(false)}
renderMenuItems={() => renderMenuItems(links, styles, closeMenu)} renderMenuItems={() => renderMenuItems(links, styles, closeMenu, datasourceType)}
focusOnOpen={true} focusOnOpen={true}
x={menuPosition.x} x={menuPosition.x}
y={menuPosition.y} y={menuPosition.y}

View File

@ -115,6 +115,7 @@ type TVirtualizedTraceViewOwnProps = {
createFocusSpanLink: (traceId: string, spanId: string) => LinkModel; createFocusSpanLink: (traceId: string, spanId: string) => LinkModel;
topOfViewRef?: RefObject<HTMLDivElement>; topOfViewRef?: RefObject<HTMLDivElement>;
topOfViewRefType?: TopOfViewRefType; topOfViewRefType?: TopOfViewRefType;
datasourceType: string;
}; };
export type VirtualizedTraceViewProps = TVirtualizedTraceViewOwnProps & TExtractUiFindFromStateReturn & TTraceTimeline; export type VirtualizedTraceViewProps = TVirtualizedTraceViewOwnProps & TExtractUiFindFromStateReturn & TTraceTimeline;
@ -396,6 +397,7 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
focusedSpanId, focusedSpanId,
focusedSpanIdForSearch, focusedSpanIdForSearch,
theme, theme,
datasourceType,
} = this.props; } = this.props;
// to avert flow error // to avert flow error
if (!trace) { if (!trace) {
@ -461,6 +463,7 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
addHoverIndentGuideId={addHoverIndentGuideId} addHoverIndentGuideId={addHoverIndentGuideId}
removeHoverIndentGuideId={removeHoverIndentGuideId} removeHoverIndentGuideId={removeHoverIndentGuideId}
createSpanLink={createSpanLink} createSpanLink={createSpanLink}
datasourceType={datasourceType}
/> />
</div> </div>
); );
@ -492,6 +495,7 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
createFocusSpanLink, createFocusSpanLink,
topOfViewRefType, topOfViewRefType,
theme, theme,
datasourceType,
} = this.props; } = this.props;
const detailState = detailStates.get(spanID); const detailState = detailStates.get(spanID);
if (!trace || !detailState) { if (!trace || !detailState) {
@ -525,6 +529,7 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
focusedSpanId={focusedSpanId} focusedSpanId={focusedSpanId}
createFocusSpanLink={createFocusSpanLink} createFocusSpanLink={createFocusSpanLink}
topOfViewRefType={topOfViewRefType} topOfViewRefType={topOfViewRefType}
datasourceType={datasourceType}
/> />
</div> </div>
); );