Skip to content

Commit

Permalink
Add db sidepanel (#211)
Browse files Browse the repository at this point in the history
<img width="1805" alt="image" src="https://github.com/hyperdxio/hyperdx/assets/2781687/94254c7c-b9a8-4a89-81f9-678394c1f157">

Also updates the db slowest query chart to be togglable between list bar chart and table chart

<img width="1727" alt="image" src="https://github.com/hyperdxio/hyperdx/assets/2781687/73d2b458-65b9-47a5-976a-7f0db7a32d3d">
  • Loading branch information
MikeShi42 committed Jan 10, 2024
1 parent ee2ad9b commit 9003c0c
Show file tree
Hide file tree
Showing 6 changed files with 433 additions and 189 deletions.
156 changes: 156 additions & 0 deletions packages/app/src/DBQuerySidePanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import * as React from 'react';
import Drawer from 'react-modern-drawer';
import { StringParam, useQueryParam, withDefault } from 'use-query-params';
import { Box, Card, Grid, Text } from '@mantine/core';

import {
convertDateRangeToGranularityString,
INTEGER_NUMBER_FORMAT,
MS_NUMBER_FORMAT,
} from './ChartUtils';
import HDXMultiSeriesTimeChart from './HDXMultiSeriesTimeChart';
import SlowestEventsTile from './SlowestEventsTile';
import { parseTimeQuery, useTimeQuery } from './timeQuery';
import { useZIndex, ZIndexContext } from './zIndex';

import styles from '../styles/LogSidePanel.module.scss';

const defaultTimeRange = parseTimeQuery('Past 1h', false);

const CHART_HEIGHT = 300;

const DB_STATEMENT_PROPERTY = 'db.statement';

export default function DBQuerySidePanel() {
const [service] = useQueryParam('service', withDefault(StringParam, ''), {
updateType: 'replaceIn',
});

const [dbQuery, setDbQuery] = useQueryParam(
'db_query',
withDefault(StringParam, ''),
{ updateType: 'replaceIn' },
);

const { searchedTimeRange: dateRange } = useTimeQuery({
isUTC: false,
defaultValue: 'Past 1h',
defaultTimeRange: [
defaultTimeRange?.[0]?.getTime() ?? -1,
defaultTimeRange?.[1]?.getTime() ?? -1,
],
});

const scopeWhereQuery = React.useCallback(
(where: string) => {
const spanNameQuery = dbQuery
? `${DB_STATEMENT_PROPERTY}:"${dbQuery}" `
: '';
const whereQuery = where ? `(${where})` : '';
const serviceQuery = service ? `service:"${service}" ` : '';
return `${spanNameQuery}${serviceQuery}${whereQuery}`.trim();
},
[dbQuery, service],
);
const contextZIndex = useZIndex();
const drawerZIndex = contextZIndex + 10;

return (
<Drawer
enableOverlay
overlayOpacity={0.1}
duration={0}
open={!!dbQuery}
onClose={() => {
setDbQuery(undefined);
}}
direction="right"
size={'80vw'}
zIndex={drawerZIndex}
>
<ZIndexContext.Provider value={drawerZIndex}>
<div className={styles.panel}>
<Box p="md">
<Text size="md">Details for {dbQuery}</Text>
</Box>
<Box className="w-100 overflow-auto" px="sm">
<Grid>
<Grid.Col span={6}>
<Card p="md">
<Card.Section p="md" py="xs" withBorder>
Total Query Time
</Card.Section>
<Card.Section p="md" py="sm" h={CHART_HEIGHT}>
<HDXMultiSeriesTimeChart
config={{
dateRange,
granularity: convertDateRangeToGranularityString(
dateRange,
60,
),
series: [
{
displayName: 'Total Query Time',
table: 'logs',
type: 'time',
aggFn: 'sum',
field: 'duration',
where: scopeWhereQuery(''),
groupBy: [],
numberFormat: MS_NUMBER_FORMAT,
},
],
seriesReturnType: 'column',
}}
/>
</Card.Section>
</Card>
</Grid.Col>
<Grid.Col span={6}>
<Card p="md">
<Card.Section p="md" py="xs" withBorder>
Query Throughput
</Card.Section>
<Card.Section p="md" py="sm" h={CHART_HEIGHT}>
<HDXMultiSeriesTimeChart
config={{
dateRange,
granularity: convertDateRangeToGranularityString(
dateRange,
60,
),
series: [
{
displayName: 'Queries',
table: 'logs',
type: 'time',
aggFn: 'count',
where: scopeWhereQuery(''),
groupBy: [],
numberFormat: {
...INTEGER_NUMBER_FORMAT,
unit: 'queries',
},
},
],
seriesReturnType: 'column',
}}
/>
</Card.Section>
</Card>
</Grid.Col>
<Grid.Col span={12}>
<SlowestEventsTile
dateRange={dateRange}
height={CHART_HEIGHT}
scopeWhereQuery={scopeWhereQuery}
title={<Text>Slowest 10% of Queries</Text>}
/>
</Grid.Col>
</Grid>
</Box>
</div>
</ZIndexContext.Provider>
</Drawer>
);
}
91 changes: 11 additions & 80 deletions packages/app/src/EndpointSidePanel.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import * as React from 'react';
import Drawer from 'react-modern-drawer';
import { StringParam, useQueryParam, withDefault } from 'use-query-params';
import { Box, Card, Flex, Grid, Text } from '@mantine/core';
import { Box, Card, Grid, Text } from '@mantine/core';

import api from './api';
import {
convertDateRangeToGranularityString,
ERROR_RATE_PERCENTAGE_NUMBER_FORMAT,
Expand All @@ -12,7 +11,7 @@ import {
import EndpointLatencyTile from './EndpointLatencyTile';
import { HDXSpanPerformanceBarChart } from './HDXListBarChart';
import HDXMultiSeriesTimeChart from './HDXMultiSeriesTimeChart';
import { LogTableWithSidePanel } from './LogTableWithSidePanel';
import SlowestEventsTile from './SlowestEventsTile';
import { parseTimeQuery, useTimeQuery } from './timeQuery';
import { useZIndex, ZIndexContext } from './zIndex';

Expand All @@ -29,7 +28,7 @@ export default function EndpointSidePanel() {

const [endpoint, setEndpoint] = useQueryParam(
'endpoint',
withDefault(StringParam, ''), // TODO: CHANGE
withDefault(StringParam, ''),
{ updateType: 'replaceIn' },
);

Expand All @@ -46,9 +45,10 @@ export default function EndpointSidePanel() {
(where: string) => {
const spanNameQuery = endpoint ? `span_name:"${endpoint}" ` : '';
const whereQuery = where ? `(${where})` : '';
return `${spanNameQuery}${whereQuery}`;
const serviceQuery = service ? `service:"${service}" ` : '';
return `${spanNameQuery}${serviceQuery}${whereQuery} span.kind:"server"`.trim();
},
[endpoint],
[endpoint, service],
);
const contextZIndex = useZIndex();
const drawerZIndex = contextZIndex + 10;
Expand Down Expand Up @@ -92,9 +92,7 @@ export default function EndpointSidePanel() {
table: 'logs',
type: 'time',
aggFn: 'count',
where: scopeWhereQuery(
'span.kind:"server" level:"error"',
),
where: scopeWhereQuery('level:"error"'),
groupBy: [],
numberFormat: ERROR_RATE_PERCENTAGE_NUMBER_FORMAT,
},
Expand All @@ -103,7 +101,7 @@ export default function EndpointSidePanel() {
type: 'time',
aggFn: 'count',
field: '',
where: scopeWhereQuery('span.kind:"server"'),
where: scopeWhereQuery(''),
groupBy: [],
numberFormat: ERROR_RATE_PERCENTAGE_NUMBER_FORMAT,
},
Expand Down Expand Up @@ -133,7 +131,7 @@ export default function EndpointSidePanel() {
table: 'logs',
type: 'time',
aggFn: 'count',
where: scopeWhereQuery('span.kind:"server"'),
where: scopeWhereQuery(''),
groupBy: [],
numberFormat: {
...INTEGER_NUMBER_FORMAT,
Expand Down Expand Up @@ -173,10 +171,11 @@ export default function EndpointSidePanel() {
/>
</Grid.Col>
<Grid.Col span={12}>
<SlowestTransactionsTile
<SlowestEventsTile
dateRange={dateRange}
height={CHART_HEIGHT}
scopeWhereQuery={scopeWhereQuery}
title={<Text>Slowest 10% of Transactions</Text>}
/>
</Grid.Col>
</Grid>
Expand All @@ -186,71 +185,3 @@ export default function EndpointSidePanel() {
</Drawer>
);
}

function SlowestTransactionsTile({
dateRange,
height,
scopeWhereQuery,
}: {
dateRange: [Date, Date];
height: number;
scopeWhereQuery: (where: string) => string;
}) {
const { data, isError, isLoading } = api.useMultiSeriesChart({
series: [
{
type: 'table',
aggFn: 'p95',
field: 'duration',
groupBy: [],
table: 'logs',
where: scopeWhereQuery(''),
},
],
endDate: dateRange[1] ?? new Date(),
startDate: dateRange[0] ?? new Date(),
seriesReturnType: 'column',
});

const p95 = data?.data?.[0]?.['series_0.data'];

const roundedP95 = Math.round(p95 ?? 0);

return (
<Card p="md">
<Card.Section p="md" py="xs" withBorder>
<Flex justify="space-between">
<Text>Slowest 10% of Transactions</Text>
<Text size="xs" c="dark.2">
(Slower than {roundedP95}ms)
</Text>
</Flex>
</Card.Section>
<Card.Section p="md" py="sm" h={height}>
{isLoading ? (
<div className="d-flex h-100 w-100 align-items-center justify-content-center text-muted">
Calculating Slow Transactions...
</div>
) : isError || p95 == null ? (
<div className="d-flex h-100 w-100 align-items-center justify-content-center text-muted">
Error Calculating Slow Transactions
</div>
) : (
<LogTableWithSidePanel
config={{
dateRange,
where: scopeWhereQuery(
`span.kind:"server" duration:>${roundedP95}`,
),
columns: ['duration'],
}}
isLive={false}
isUTC={false}
setIsUTC={() => {}}
onPropertySearchClick={() => {}}
/>
)}
</Card.Section>
</Card>
);
}
Loading

0 comments on commit 9003c0c

Please sign in to comment.