Skip to content
Merged
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
6 changes: 6 additions & 0 deletions .changeset/slow-eyes-attack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@hyperdx/common-utils": patch
"@hyperdx/app": patch
---

feat: Add custom attributes for individual rows
3 changes: 3 additions & 0 deletions packages/api/src/models/source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ export const Source = mongoose.model<ISource>(
highlightedTraceAttributeExpressions: {
type: mongoose.Schema.Types.Array,
},
highlightedRowAttributeExpressions: {
type: mongoose.Schema.Types.Array,
},

metricTables: {
type: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function DBHighlightedAttributesList({
displayedKey={displayedKey}
name={lucene ? lucene : sql}
nameLanguage={lucene ? 'lucene' : 'sql'}
value={value as string}
value={value}
key={`${displayedKey}-${value}-${source.id}`}
{...(onPropertyAddClick && contextSource?.id === source.id
? {
Expand Down
9 changes: 9 additions & 0 deletions packages/app/src/components/DBRowDataPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Box } from '@mantine/core';

import { useQueriedChartConfig } from '@/hooks/useChartConfig';
import { getDisplayedTimestampValueExpression, getEventBody } from '@/source';
import { getSelectExpressionsForHighlightedAttributes } from '@/utils/highlightedAttributes';

import { DBRowJsonViewer } from './DBRowJsonViewer';

Expand Down Expand Up @@ -37,6 +38,13 @@ export function useRowData({
const severityTextExpr =
source.severityTextExpression || source.statusCodeExpression;

const selectHighlightedRowAttributes =
source.kind === SourceKind.Trace || source.kind === SourceKind.Log
? getSelectExpressionsForHighlightedAttributes(
source.highlightedRowAttributeExpressions,
)
: [];

const queryResult = useQueriedChartConfig(
{
connection: source.connection,
Expand Down Expand Up @@ -116,6 +124,7 @@ export function useRowData({
},
]
: []),
...selectHighlightedRowAttributes,
],
where: rowId ?? '0=1',
from: source.from,
Expand Down
1 change: 0 additions & 1 deletion packages/app/src/components/DBRowOverviewPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ export function RowOverviewPanel({
<Box px="32px" pt="md">
<DBRowSidePanelHeader
date={new Date(firstRow?.__hdx_timestamp ?? 0)}
tags={{}}
mainContent={mainContent}
mainContentHeader={mainContentColumn}
severityText={firstRow?.__hdx_severity_text}
Expand Down
38 changes: 29 additions & 9 deletions packages/app/src/components/DBRowSidePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { isString } from 'lodash';
import { parseAsStringEnum, useQueryState } from 'nuqs';
import { ErrorBoundary } from 'react-error-boundary';
import { useHotkeys } from 'react-hotkeys-hook';
import { TSource } from '@hyperdx/common-utils/dist/types';
import { SourceKind, TSource } from '@hyperdx/common-utils/dist/types';
import { ChartConfigWithDateRange } from '@hyperdx/common-utils/dist/types';
import { Box, Drawer, Flex, Stack } from '@mantine/core';

Expand All @@ -26,6 +26,7 @@ import { LogSidePanelKbdShortcuts } from '@/LogSidePanelElements';
import { getEventBody } from '@/source';
import TabBar from '@/TabBar';
import { SearchConfig } from '@/types';
import { getHighlightedAttributesFromData } from '@/utils/highlightedAttributes';
import { useZIndex, ZIndexContext } from '@/zIndex';

import ServiceMapSidePanel from './ServiceMap/ServiceMapSidePanel';
Expand Down Expand Up @@ -188,15 +189,34 @@ const DBRowSidePanel = ({
: undefined;
const severityText: string | undefined =
normalizedRow?.['__hdx_severity_text'];
const serviceName = normalizedRow?.['__hdx_service_name'];

const tags = useMemo(() => {
const tags: Record<string, string> = {};
if (serviceName && source.serviceNameExpression) {
tags[source.serviceNameExpression] = serviceName;
const highlightedAttributeValues = useMemo(() => {
const attributeExpressions: TSource['highlightedRowAttributeExpressions'] =
[];
if (
(source.kind === SourceKind.Trace || source.kind === SourceKind.Log) &&
source.highlightedRowAttributeExpressions
) {
attributeExpressions.push(...source.highlightedRowAttributeExpressions);
}
return tags;
}, [serviceName, source.serviceNameExpression]);

// Add service name expression to all sources, to maintain compatibility with
// the behavior prior to the addition of highlightedRowAttributeExpressions
if (source.serviceNameExpression) {
attributeExpressions.push({
sqlExpression: source.serviceNameExpression,
});
}

return rowData
? getHighlightedAttributesFromData(
source,
attributeExpressions,
rowData.data || [],
rowData.meta || [],
)
: [];
}, [source, rowData]);

const oneHourRange = useMemo(() => {
return [
Expand Down Expand Up @@ -275,7 +295,7 @@ const DBRowSidePanel = ({
<Box p="sm">
<DBRowSidePanelHeader
date={timestampDate}
tags={tags}
attributes={highlightedAttributeValues}
mainContent={mainContent}
mainContentHeader={mainContentColumn}
severityText={severityText}
Expand Down
41 changes: 9 additions & 32 deletions packages/app/src/components/DBRowSidePanelHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ import {
UnstyledButton,
} from '@mantine/core';

import EventTag from '@/components/EventTag';
import { FormatTime } from '@/useFormatTime';
import { useUserPreferences } from '@/useUserPreferences';
import { formatDistanceToNowStrictShort } from '@/utils';

import {
DBHighlightedAttributesList,
HighlightedAttribute,
} from './DBHighlightedAttributesList';
import { RowSidePanelContext } from './DBRowSidePanel';
import LogLevel from './LogLevel';

Expand Down Expand Up @@ -119,7 +122,7 @@ function BreadcrumbNavigation({
}

export default function DBRowSidePanelHeader({
tags,
attributes = [],
mainContent = '',
mainContentHeader,
date,
Expand All @@ -130,7 +133,7 @@ export default function DBRowSidePanelHeader({
date: Date;
mainContent?: string;
mainContentHeader?: string;
tags: Record<string, string>;
attributes?: HighlightedAttribute[];
severityText?: string;
breadcrumbPath?: BreadcrumbPath;
onBreadcrumbClick?: BreadcrumbNavigationCallback;
Expand Down Expand Up @@ -264,35 +267,9 @@ export default function DBRowSidePanelHeader({
</Text>
</Paper>
)}
<Flex mt="sm">
{Object.entries(tags).map(([sqlKey, value]) => {
// Convert SQL syntax to Lucene syntax
// SQL: column['property.foo'] -> Lucene: column.property.foo
// or SQL: column -> Lucene: column
const luceneKey = sqlKey.replace(/\['([^']+)'\]/g, '.$1');

return (
<EventTag
{...(onPropertyAddClick
? {
onPropertyAddClick,
sqlExpression: sqlKey,
}
: {
onPropertyAddClick: undefined,
sqlExpression: undefined,
})}
generateSearchUrl={
generateSearchUrl ? _generateSearchUrl : undefined
}
displayedKey={luceneKey}
name={luceneKey}
value={value}
key={sqlKey}
/>
);
})}
</Flex>
<Box mt="xs">
<DBHighlightedAttributesList attributes={attributes} />
</Box>
</>
);
}
40 changes: 32 additions & 8 deletions packages/app/src/components/EventTag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { useState } from 'react';
import Link from 'next/link';
import SqlString from 'sqlstring';
import { SearchConditionLanguage } from '@hyperdx/common-utils/dist/types';
import { Button, Popover, Stack } from '@mantine/core';
import { Button, Popover, Stack, Tooltip } from '@mantine/core';
import { IconLink } from '@tabler/icons-react';

import { isLinkableUrl } from '@/utils/highlightedAttributes';

export default function EventTag({
displayedKey,
Expand Down Expand Up @@ -44,6 +47,8 @@ export default function EventTag({
);
}

const isLink = isLinkableUrl(value);

const searchCondition =
nameLanguage === 'sql'
? SqlString.format('? = ?', [SqlString.raw(name), value])
Expand All @@ -58,13 +63,32 @@ export default function EventTag({
onChange={setOpened}
>
<Popover.Target>
<div
key={name}
className="bg-highlighted px-2 py-0.5 me-1 my-1 cursor-pointer"
onClick={() => setOpened(!opened)}
>
{displayedKey || name}: {value}
</div>
{isLink ? (
<Tooltip
label={value}
withArrow
maw={400}
multiline
style={{ wordBreak: 'break-word' }}
>
<a
href={encodeURI(value)}
target="_blank"
rel="noopener noreferrer"
className="d-flex flex-row align-items-center bg-highlighted px-2 py-0.5 me-1 my-1 cursor-pointer"
>
{displayedKey || name}
<IconLink size={14} className="ms-1" />
</a>
</Tooltip>
) : (
<div
className="bg-highlighted px-2 py-0.5 me-1 my-1 cursor-pointer"
onClick={() => setOpened(!opened)}
>
{displayedKey || name}: {value}
</div>
)}
</Popover.Target>
<Popover.Dropdown p={2}>
<Stack gap={0} justify="stretch">
Expand Down
50 changes: 39 additions & 11 deletions packages/app/src/components/SourceForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,16 @@ function FormRow({
function HighlightedAttributeExpressionsFormRow({
control,
watch,
}: TableModelProps) {
name,
label,
helpText,
}: TableModelProps & {
name:
| 'highlightedTraceAttributeExpressions'
| 'highlightedRowAttributeExpressions';
label: string;
helpText?: string;
}) {
const databaseName = watch(`from.databaseName`, DEFAULT_DATABASE);
const tableName = watch(`from.tableName`);
const connectionId = watch(`connection`);
Expand All @@ -162,14 +171,11 @@ function HighlightedAttributeExpressionsFormRow({
remove: removeHighlightedAttribute,
} = useFieldArray({
control,
name: 'highlightedTraceAttributeExpressions',
name,
});

return (
<FormRow
label={'Highlighted Attributes'}
helpText="Expressions defining trace-level attributes which are displayed in the search side panel."
>
<FormRow label={label} helpText={helpText}>
<Grid columns={5}>
{highlightedAttributes.map((field, index) => (
<React.Fragment key={field.id}>
Expand All @@ -181,7 +187,7 @@ function HighlightedAttributeExpressionsFormRow({
connectionId,
}}
control={control}
name={`highlightedTraceAttributeExpressions.${index}.sqlExpression`}
name={`${name}.${index}.sqlExpression`}
disableKeywordAutocomplete
placeholder="ResourceAttributes['http.host']"
/>
Expand All @@ -191,7 +197,7 @@ function HighlightedAttributeExpressionsFormRow({
<Text c="gray">AS</Text>
<SQLInlineEditorControlled
control={control}
name={`highlightedTraceAttributeExpressions.${index}.alias`}
name={`${name}.${index}.alias`}
placeholder="Optional Alias"
disableKeywordAutocomplete
/>
Expand All @@ -208,7 +214,7 @@ function HighlightedAttributeExpressionsFormRow({
<Grid.Col span={3} pe={0}>
<InputControlled
control={control}
name={`highlightedTraceAttributeExpressions.${index}.luceneExpression`}
name={`${name}.${index}.luceneExpression`}
placeholder="ResourceAttributes.http.host (Optional) "
/>
</Grid.Col>
Expand Down Expand Up @@ -489,7 +495,18 @@ export function LogTableModelForm(props: TableModelProps) {
/>
</FormRow>
<Divider />
<HighlightedAttributeExpressionsFormRow {...props} />
<HighlightedAttributeExpressionsFormRow
{...props}
name="highlightedRowAttributeExpressions"
label="Highlighted Attributes"
helpText="Expressions defining row-level attributes which are displayed in the search side panel."
/>
<HighlightedAttributeExpressionsFormRow
{...props}
name="highlightedTraceAttributeExpressions"
label="Highlighted Trace Attributes"
helpText="Expressions defining trace-level attributes which are displayed in the search side panel."
/>
</Stack>
</>
);
Expand Down Expand Up @@ -758,7 +775,18 @@ export function TraceTableModelForm(props: TableModelProps) {
/>
</FormRow>
<Divider />
<HighlightedAttributeExpressionsFormRow {...props} />
<HighlightedAttributeExpressionsFormRow
{...props}
name="highlightedRowAttributeExpressions"
label="Highlighted Attributes"
helpText="Expressions defining row-level attributes which are displayed in the search side panel."
/>
<HighlightedAttributeExpressionsFormRow
{...props}
name="highlightedTraceAttributeExpressions"
label="Highlighted Trace Attributes"
helpText="Expressions defining trace-level attributes which are displayed in the search side panel."
/>
</Stack>
);
}
Expand Down
Loading
Loading