Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/dashboard-replay-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hyperdx/app": patch
---

feat(dashboards): add a Replay search action to log and trace dashboard tiles whose event query can be faithfully reconstructed. The action opens a new Search tab with the tile's source, query, filters, and dashboard time range preserved.
67 changes: 67 additions & 0 deletions packages/app/src/DBDashboardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
Filter,
getSampleWeightExpression,
isLogSource,
isSearchableSource,
isTraceSource,
SearchCondition,
SearchConditionLanguage,
Expand Down Expand Up @@ -684,6 +685,40 @@ const Tile = forwardRef(
);
}, [filters, queriedConfig, source]);

const replaySearchUrl = useMemo(() => {
if (!queriedConfig || !source || !isBuilderChartConfig(queriedConfig)) {
return null;
}

if (queriedConfig.metricTables != null || !isSearchableSource(source)) {
return null;
}

if (Array.isArray(queriedConfig.select)) {
const hasPerSeriesCondition = queriedConfig.select.some(
select =>
typeof select !== 'string' &&
select.aggCondition != null &&
select.aggCondition.trim().length > 0,
);
const canPromoteSingleSeriesCondition =
queriedConfig.select.length === 1 && queriedConfig.where.length === 0;

// buildEventsSearchUrl can promote one per-series condition into the
// event query, but cannot faithfully replay multiple conditions or
// combine a series condition with a global where clause.
if (hasPerSeriesCondition && !canPromoteSingleSeriesCondition) {
return null;
}
}

return buildEventsSearchUrl({
source,
config: queriedConfig,
dateRange,
});
}, [dateRange, queriedConfig, source]);
Comment thread
xob0t marked this conversation as resolved.

const hoverToolbar = useMemo(() => {
const isRawSql = isRawSqlSavedChartConfig(chart.config);
const isPromQL = isPromqlSavedChartConfig(chart.config);
Expand All @@ -702,6 +737,25 @@ const Tile = forwardRef(
key="hover-toolbar"
my={2} // Margin to ensure that the Alert Indicator doesn't clip on non-Line/Bar display types
>
{replaySearchUrl && (
<Tooltip label="Replay search" position="top" withArrow>
<ActionIcon
component={Link}
href={replaySearchUrl}
target="_blank"
rel="noopener noreferrer"
prefetch={false}
data-testid={`tile-replay-search-button-${chart.id}`}
aria-label="Replay search (opens in new tab)"
variant="subtle"
size="sm"
mr={4}
>
<IconSearch size={16} />
</ActionIcon>
</Tooltip>
)}

{displayTypeSupportsAlerts &&
(alert ? (
// Existing alert: bell with a colored status dot indicator.
Expand Down Expand Up @@ -851,6 +905,7 @@ const Tile = forwardRef(
alertIndicatorColor,
alertTooltip,
moveTargets,
replaySearchUrl,
chart.config,
chart.id,
chart.containerId,
Expand All @@ -877,6 +932,17 @@ const Tile = forwardRef(
onMoveToGroup && moveTargets && moveTargets.length > 0;
return (
<>
{replaySearchUrl && (
<Menu.Item
component={Link}
href={replaySearchUrl}
target="_blank"
rel="noopener noreferrer"
leftSection={<IconSearch size={14} />}
>
Replay search
</Menu.Item>
)}
{showAlerts && (
<>
<Menu.Item
Expand Down Expand Up @@ -982,6 +1048,7 @@ const Tile = forwardRef(
alert,
alertTooltip,
moveTargets,
replaySearchUrl,
chart.config,
chart.containerId,
chart.tabId,
Expand Down
Loading