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
10 changes: 8 additions & 2 deletions hack/docker-compose/docker-compose.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,17 @@ services:
- inner

log-generator:
image: quay.io/rojacob/cluster-logging-load-client:0.0.1-db25b80
image: mingrammer/flog:0.4.3
restart: unless-stopped
volumes:
- ./docker-compose-logs:/var/log
command: generate --output-format=json --destination=file --file=/var/log/generated.log --log-lines-rate=5000 --threads=3
command:
- --loop
- --format=json
- --type=log
- --number=10
- --delay=100ms
- --output=/var/log/fake.log
networks:
- inner

Expand Down
22 changes: 15 additions & 7 deletions hack/docker-compose/loki/promtail-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ clients:
scrape_configs:
- job_name: system
pipeline_stages:
- regex:
expression: '.*"lvl":"(?P<level>[a-zA-Z]+)"'
- json:
expressions:
status: status
- template:
source: level
template: '{{ if ge .status "500" }}critical{{ else if ge .status "400" }}error{{ else if ge .status "300" }}warning{{ else if ge .status "200" }}info{{ else }}debug{{ end }}'
- labels:
level:
static_configs:
Expand All @@ -29,13 +33,17 @@ scrape_configs:
__path__: /var/log/*log
- job_name: system2
pipeline_stages:
- regex:
expression: '.*"lvl":"(?P<level>[a-zA-Z]+)"'
- json:
expressions:
status: status
- template:
source: level
template: '{{ if ge .status "500" }}critical{{ else if ge .status "400" }}error{{ else if ge .status "300" }}warning{{ else if ge .status "200" }}info{{ else }}debug{{ end }}'
- template:
source: severity_text
template: '{{ .level }}'
- labels:
level:
- regex:
expression: '.*"lvl":"(?P<severity_text>[a-zA-Z]+)"'
- labels:
severity_text:
static_configs:
- targets:
Expand Down
8 changes: 6 additions & 2 deletions web/src/components/alerts/logs-alerts-metrics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,20 @@ const LOKI_TENANT_LABEL_KEY = 'tenantId';
const LogsAlertMetrics: React.FC<LogsAlertMetricsProps> = ({ rule }) => {
const { t } = useTranslation('plugin__logging-view-plugin');
const { getLogs, logsData, logsError, isLoadingLogsData } = useLogs();
const { config } = useLogsConfig();
const { config, configLoaded } = useLogsConfig();

const tenant = rule?.labels?.[config.alertingRuleTenantLabelKey ?? LOKI_TENANT_LABEL_KEY];
const [timeRange, setTimeRange] = React.useState<TimeRange | undefined>();

useEffect(() => {
if (!configLoaded) {
return;
}

if (rule?.query && tenant) {
getLogs({ query: rule.query, timeRange, tenant, schema: getSchema(config.schema) });
}
}, [rule?.query, timeRange]);
}, [rule?.query, timeRange, configLoaded, tenant, config.schema]);

const tenantError = !tenant
? new Error(
Expand Down
94 changes: 63 additions & 31 deletions web/src/components/logs-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,15 +169,15 @@ const ResourceLinkList: React.FC<{
};

type TableRowProps = {
expandedItems: Set<number>;
expandedItemsRef: React.MutableRefObject<Set<number>>;
handleRowToggle: (e: React.MouseEvent, rowIndex: number) => void;
showResources: boolean;
colSpan?: number;
};

const TableRow = ({ expandedItems, handleRowToggle, showResources, colSpan }: TableRowProps) => {
const TableRow = ({ expandedItemsRef, handleRowToggle, showResources, colSpan }: TableRowProps) => {
return function TableRowComponent({ obj, activeColumnIDs }: RowProps<LogTableData>) {
const isExpanded = expandedItems.has(obj.logIndex);
const isExpanded = expandedItemsRef.current.has(obj.logIndex);

return obj.type === 'log' ? (
<>
Expand Down Expand Up @@ -235,6 +235,8 @@ export const LogsTable: React.FC<LogsTableProps> = ({
schema,
}) => {
const [expandedItems, setExpandedItems] = React.useState<Set<number>>(new Set());
const expandedItemsRef = React.useRef(expandedItems);
expandedItemsRef.current = expandedItems;
const [prevChildrenCount, setPrevChildrenCount] = React.useState(0);
const [sortBy, setSortBy] = React.useState<ISortBy>({
index: 1,
Expand All @@ -255,14 +257,17 @@ export const LogsTable: React.FC<LogsTableProps> = ({
setPrevChildrenCount(React.Children.count(children));
}, [children]);

const handleRowToggle = (_event: React.MouseEvent, rowIndex: number) => {
if (expandedItems.has(rowIndex)) {
expandedItems.delete(rowIndex);
setExpandedItems(new Set(expandedItems));
} else {
setExpandedItems(new Set(expandedItems.add(rowIndex)));
}
};
const handleRowToggle = useCallback((_event: React.MouseEvent, rowIndex: number) => {
setExpandedItems((prev) => {
const next = new Set(prev);
if (next.has(rowIndex)) {
next.delete(rowIndex);
} else {
next.add(rowIndex);
}
return next;
});
}, []);

const getSortParams = useCallback(
(columnIndex: number): ThProps['sort'] => {
Expand Down Expand Up @@ -293,52 +298,77 @@ export const LogsTable: React.FC<LogsTableProps> = ({
[sortBy, onSortByDate],
);

const sortedData = React.useMemo(() => {
setExpandedItems(new Set());
const prevLogsDataRef = React.useRef(logsData);
if (logsData !== prevLogsDataRef.current) {
const prevData = prevLogsDataRef.current;
prevLogsDataRef.current = logsData;

const dataChanged =
!prevData ||
!logsData ||
isStreaming ||
prevData.data?.result?.length !== logsData.data?.result?.length;

if (expandedItems.size > 0 && dataChanged) {
setExpandedItems(new Set());
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const sortedData = React.useMemo(() => {
const dataCopy = [...tableData];
if (sortBy.index !== undefined && columns[sortBy.index]) {
const { sort } = columns[sortBy.index];
if (sort && typeof sort === 'function') {
return sort(
tableData,
dataCopy,
sortBy.direction === 'asc' ? SortByDirection.asc : SortByDirection.desc,
);
}
}

return tableData.sort((a, b) => numericComparator(a.timestamp, b.timestamp, -1));
}, [tableData, columns, sortBy]);
return dataCopy.sort((a, b) => numericComparator(a.timestamp, b.timestamp, -1));
}, [tableData, sortBy]);

const dataIsEmpty = sortedData.length === 0;

const handleLoadMore = () => {
onLoadMore?.(tableData[tableData.length - 1].timestamp / 1e6);
};

const RowComponent = React.useMemo(
() =>
TableRow({
expandedItemsRef,
handleRowToggle,
showResources,
colSpan: columns.length,
}),
[handleRowToggle, showResources],
);

const getRowClassName = useCallback((row: LogTableData) => {
const expanded = expandedItemsRef.current.has(row.logIndex);
let expandedClass = '';
if (expanded) {
expandedClass =
row.type === 'log'
? 'lv-plugin__table__row--expanded'
: 'lv-plugin__table__row--expanded-details';
}
return `lv-plugin__table__row ${getSeverityClass(row.severity)} ${expandedClass}`;
}, []);

return (
<div data-test={TestIds.LogsTable} className="lv-plugin__table">
{showStats && <StatsTable logsData={logsData} />}
{children}

<VirtualizedLogsTable
data={sortedData}
Row={TableRow({
expandedItems,
handleRowToggle,
showResources,
colSpan: columns.length,
})}
Row={RowComponent}
columns={columns}
getSortParams={getSortParams}
getRowClassName={(row) =>
`lv-plugin__table__row ${getSeverityClass(row.severity)} ${
expandedItems.has(row.logIndex)
? row.type === 'log'
? 'lv-plugin__table__row--expanded'
: 'lv-plugin__table__row--expanded-details'
: ''
}`
}
getRowClassName={getRowClassName}
error={error}
isLoading={isLoading}
isStreaming={isStreaming}
Expand All @@ -349,6 +379,8 @@ export const LogsTable: React.FC<LogsTableProps> = ({
shouldResize={showStats || React.Children.count(children) != prevChildrenCount}
hasNamespaceFilter={hasNamespaceFilter}
schema={schema}
expandedItems={expandedItems}
showResources={showResources}
/>
</div>
);
Expand Down
19 changes: 7 additions & 12 deletions web/src/components/refresh-interval-dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@ export const RefreshIntervalDropdown: React.FC<RefreshIntervalDropdownProps> = (
? parseInt(storedRefreshInterval.interval, 10)
: 0,
);
const [delay, setDelay] = React.useState<number>(0);
const [delay, setDelay] = React.useState<number>(refreshIntervalOptions[selectedIndex].delay);
const timer = React.useRef<NodeJS.Timer | null>(null);
const onRefreshRef = React.useRef(onRefresh);
onRefreshRef.current = onRefresh;

const clearTimer = () => {
if (timer.current) {
Expand All @@ -63,23 +65,16 @@ export const RefreshIntervalDropdown: React.FC<RefreshIntervalDropdownProps> = (
setStoredRefreshInterval({ interval: index.toString(10) });
};

const restartTimer = (callRefreshImmediately = true) => {
React.useEffect(() => {
clearTimer();

if (delay !== 0) {
if (callRefreshImmediately) {
onRefresh?.();
}
timer.current = setInterval(() => onRefresh?.(), delay);
onRefreshRef.current?.();
timer.current = setInterval(() => onRefreshRef.current?.(), delay);
}

return () => clearTimer();
};

React.useEffect(() => restartTimer(), [delay]);

// Avoid calling refresh immediately when onRefresh callback has changed
React.useEffect(() => restartTimer(false), [onRefresh]);
}, [delay]);

const toggleIsOpen = () => {
setIsOpen(!isOpen);
Expand Down
37 changes: 32 additions & 5 deletions web/src/components/virtualized-logs-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ interface VirtualizedLogsTableProps<D> {
csvData?: string;
hasNamespaceFilter?: boolean;
schema: Schema;
expandedItems?: Set<number>;
showResources?: boolean;
}

export type TableRowProps = {
Expand Down Expand Up @@ -95,6 +97,8 @@ type VirtualizedTableBodyProps<D, R = unknown> = {
getRowTitle?: (obj: D) => string;
getRowClassName?: (obj: D) => string;
scrollToIndex?: number;
expandedItems?: Set<number>;
showResources?: boolean;
};

const TableRow: React.FC<TableRowProps> = ({ id, index, trKey, style, className, ...props }) => {
Expand Down Expand Up @@ -127,13 +131,31 @@ const VirtualizedTableBody = ({
getRowTitle,
getRowClassName,
scrollToIndex,
expandedItems,
showResources,
}: // eslint-disable-next-line @typescript-eslint/no-explicit-any
VirtualizedTableBodyProps<LogTableData, any>) => {
const cellMeasurementCache = new CellMeasurerCache({
fixedWidth: true,
minHeight: 1,
keyMapper: (rowIndex) => rowIndex,
});
const cellMeasurementCache = React.useMemo(
() =>
new CellMeasurerCache({
fixedWidth: true,
minHeight: 1,
keyMapper: (rowIndex) => rowIndex,
}),
[],
);

const tableBodyRef = React.useRef<VirtualTableBody>(null);
const isFirstRender = React.useRef(true);

useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
return;
}
cellMeasurementCache.clearAll();
tableBodyRef.current?.forceUpdateVirtualGrid();
}, [expandedItems, showResources]);

const activeColumnIDs = React.useMemo(() => new Set(columns.map((c) => c.id)), [columns]);

Expand Down Expand Up @@ -186,6 +208,7 @@ VirtualizedTableBodyProps<LogTableData, any>) => {

return (
<VirtualTableBody
ref={tableBodyRef}
autoHeight
className="pf-v6-c-table pf-m-compact pf-m-border-rows pf-v6-c-virtualized pf-v6-c-window-scroller"
deferredMeasurementCache={cellMeasurementCache}
Expand Down Expand Up @@ -256,6 +279,8 @@ export const VirtualizedLogsTable = ({
shouldResize,
hasNamespaceFilter,
schema,
expandedItems,
showResources,
}: VirtualizedLogsTableProps<LogTableData>) => {
const { t } = useTranslation('plugin__logging-view-plugin');
const colSpan = columns.length + 3;
Expand Down Expand Up @@ -350,6 +375,8 @@ export const VirtualizedLogsTable = ({
width={width}
getRowClassName={getRowClassName}
scrollToIndex={scrollToIndex}
expandedItems={expandedItems}
showResources={showResources}
/>
</div>
)}
Expand Down
Loading