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
7 changes: 7 additions & 0 deletions .changeset/severity-class-classifier.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@hyperdx/common-utils': minor
---

Add `core/severity` with the OpenTelemetry log severity rules:
`getSeverityClass` (from SeverityNumber 1-24, falling back to a case-insensitive
severity text prefix), `getSeverityShortName` and `getSeverityLabel`.
15 changes: 15 additions & 0 deletions .changeset/severity-number-display.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'@hyperdx/app': patch
---

When a log source sets Severity Number Expression, log levels follow the
OpenTelemetry SeverityNumber (1-24) in the row side panel, the search results
and pattern table level cells, the pattern trend bars, and the trace waterfall's
log error and warning flags. Levels show the spec's short name, such as `INFO2`,
add the stored text in parentheses when it differs, as in
`INFO (Informational)`, and take their color from the number. The search results
and pattern table use the number only when the expression names a stored numeric
column. Copy and filter actions still use the stored text. Rows without a number
in 1-24, and sources without the field, show the stored text as before. Level
labels and the waterfall flags also treat `panic`, `dpanic`, `ftl` and `severe`
as errors and `wrn` as a warning.
3 changes: 1 addition & 2 deletions .changeset/source-severity-number-expression.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@

Add an optional `severityNumberExpression` to log sources, pointing at the
numeric OpenTelemetry SeverityNumber (1-24). Source auto-detection sets it when
an OTel log table has an integer `SeverityNumber` column. The field is stored
and editable only: log levels still come from the severity text.
an OTel log table has an integer `SeverityNumber` column.
1 change: 1 addition & 0 deletions packages/app/src/DBSearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3031,6 +3031,7 @@ function DBSearchPageContent() {
selectionResetKey={selectionResetKey}
onSelectedRowsChange={onSelectedRowsChange}
onResolvedColumnsChange={onResolvedColumnsChange}
colorLevelBySeverityNumber
/>
)}
</Box>
Expand Down
33 changes: 33 additions & 0 deletions packages/app/src/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import {
collapseSeverityClass,
getSeverityClass,
TEXT_PREFIX_RULES,
} from '@hyperdx/common-utils/dist/core/severity';
import {
ChartPaletteTokenSchema,
ColorCondition,
Expand All @@ -20,6 +25,7 @@ import {
formatNumber,
getAllMetricTables,
getColorFromCSSToken,
getLogLevelClass,
getLogSeverityLevel,
getMetricTableName,
isColumnInSelect,
Expand Down Expand Up @@ -1547,13 +1553,40 @@ describe('getLogSeverityLevel', () => {
[undefined, 'Warning', 'warn'],
['', 'fatal', 'error'],
[undefined, 'info', undefined],
[25, 'warn', 'warn'],
[0, 'panic', 'error'],
])('maps %p and %p to %p', (severityNumber, severityText, expected) => {
expect(getLogSeverityLevel({ severityNumber, severityText })).toBe(
expected,
);
});
});

describe('getLogLevelClass', () => {
// Log-only vocabulary that trace StatusCode and chart colors don't take.
const LOG_ONLY_PREFIXES = [
'panic',
'dpanic',
'ftl',
'severe',
'wrn',
'inf',
'dbg',
'trc',
];

it.each(TEXT_PREFIX_RULES.filter(r => !LOG_ONLY_PREFIXES.includes(r.prefix)))(
'agrees with the log severity class on $prefix',
({ prefix }) => {
const severityClass = getSeverityClass({ severityText: prefix });
expect(getLogLevelClass(prefix)).toBe(
collapseSeverityClass(severityClass) ??
(severityClass === 'UNKNOWN' ? undefined : 'info'),
);
},
);
});

describe('getColorFromCSSToken', () => {
afterEach(() => {
jest.restoreAllMocks();
Expand Down
12 changes: 12 additions & 0 deletions packages/app/src/components/DBRowDataPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Box } from '@mantine/core';
import { useQueriedChartConfig } from '@/hooks/useChartConfig';
import {
getKnownColumnsList,
getSelectableSeverityNumberExpression,
getSelectAllColumnsQuerySettings,
useSelectAllColumnsQuerySettings,
} from '@/hooks/useMaterializedAliasColumnsOption';
Expand All @@ -35,6 +36,7 @@ export enum ROW_DATA_ALIASES {
TRACE_ID = '__hdx_trace_id',
SPAN_ID = '__hdx_span_id',
SEVERITY_TEXT = '__hdx_severity_text',
SEVERITY_NUMBER = '__hdx_severity_number',
SERVICE_NAME = '__hdx_service_name',
RESOURCE_ATTRIBUTES = '__hdx_resource_attributes',
EVENT_ATTRIBUTES = '__hdx_event_attributes',
Expand Down Expand Up @@ -97,6 +99,8 @@ export function useRowData({
// select that instead of `*` when fetching full row data.
const knownColumns = getKnownColumnsList(source);

const severityNumberExpr = getSelectableSeverityNumberExpression(source);

const baseConfig = {
source: source.id,
connection: source.connection,
Expand Down Expand Up @@ -141,6 +145,14 @@ export function useRowData({
},
]
: []),
...(severityNumberExpr
? [
{
valueExpression: severityNumberExpr,
alias: ROW_DATA_ALIASES.SEVERITY_NUMBER,
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 minor — Side panel row query selects severityNumberExpression with no check, so a bad expression breaks the whole panel

The search table selects the number only for a stored numeric column ('the number isn't worth failing a query over' per getSelectableSeverityNumberExpression). The side panel's full-row query adds the raw expression unconditionally. A missing column or bad expression (e.g. a column dropped from the table) now fails the row fetch, which blanks the whole side panel just to color one badge. Use the same column check as getSeverityNumberSelect, or fetch the number in a separate query whose failure doesn't affect the row.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

]
: []),
...((isLogSource(source) || isTraceSource(source)) &&
source.serviceNameExpression
? [
Expand Down
5 changes: 4 additions & 1 deletion packages/app/src/components/DBRowSidePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ export const DBRowSidePanelInner = ({
: undefined;
const severityText: string | undefined =
normalizedRow?.['__hdx_severity_text'];
const severityNumber = normalizedRow?.['__hdx_severity_number'];

// Owns the View Trace nudge's auto-open latch. Kept here (above the loading
// gate that unmounts the row content on every navigation) so the one-time
Expand Down Expand Up @@ -915,7 +916,9 @@ export const DBRowSidePanelInner = ({
<Box px="sm" pt="sm" pb="xs">
{controls}
<Group gap="xs" wrap="wrap">
{!sourceIsTrace && severityText && <LogLevel level={severityText} />}
{!sourceIsTrace && (
<LogLevel level={severityText} severityNumber={severityNumber} />
)}
{timestampDate && !isNaN(timestampDate.getTime()) && (
<Text size="xs" c="dimmed">
<FormatTime value={timestampDate} /> ·{' '}
Expand Down
Loading
Loading