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
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ExtraFilter } from "../types";
import { escapeForLogsQLString } from "../../../utils/regexp";
import ExtraFiltersPanelItem from "./ExtraFiltersPanelItem";
import "./style.scss";
import { isStreamFilter } from "../utils/isStreamFilter";

type Props = {
extraFilters: ExtraFilter[];
Expand All @@ -13,7 +14,7 @@ const ExtraFiltersPanel: FC<Props> = ({ extraFilters, onRemove }) => {
const getLabel = (filter: ExtraFilter) => {
const escapedValue = escapeForLogsQLString(filter.value);
const expr = `${filter.field}${filter.operator}"${escapedValue}"`;
return filter.isStream ? `{${expr}}` : expr;
return isStreamFilter(filter) ? `{${expr}}` : expr;
};

const formattedFilters = useMemo(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { FC, useEffect, useRef, useState } from "preact/compat";
import { CloseIcon } from "../../Main/Icons";
import { CloseIcon, FieldIcon, FieldStreamIcon } from "../../Main/Icons";
import { ExtraFilter } from "../types";
import "./style.scss";
import { useCallback } from "react";
import Tooltip from "../../Main/Tooltip/Tooltip";
import classNames from "classnames";
import { isStreamFilter } from "../utils/isStreamFilter";

type Props = {
filter: ExtraFilter & { label: string };
Expand All @@ -14,6 +16,8 @@ const ExtraFiltersPanelItem: FC<Props> = ({ filter, onRemove }) => {
const labelRef = useRef<HTMLDivElement>(null);
const [isOverflownLabel, setIsOverflownLabel] = useState(false);

const isStream = isStreamFilter(filter);

const handleRemove = useCallback(() => {
onRemove(filter.field, filter.value);
}, [filter, onRemove]);
Expand All @@ -24,29 +28,36 @@ const ExtraFiltersPanelItem: FC<Props> = ({ filter, onRemove }) => {
}, [filter.label, labelRef]);

return (
<Tooltip
title={<p className="vm-extra-filters-panel-item__tooltip">{filter.value}</p>}
disabled={!isOverflownLabel}
<div
key={filter.label}
className={classNames({
"vm-extra-filters-panel-item": true,
"vm-extra-filters-panel-item_stream": isStream
})}
>
<div
key={filter.label}
className="vm-extra-filters-panel-item"
<Tooltip title={`${isStream ? "Stream" : "Field"} filter`}>
<div className="vm-extra-filters-panel-item__icon">
{isStream ? <FieldStreamIcon/> : <FieldIcon/>}
</div>
</Tooltip>
<Tooltip
title={<p className="vm-extra-filters-panel-item__tooltip">{filter.value}</p>}
disabled={!isOverflownLabel}
>

<div
className="vm-extra-filters-panel-item__label"
ref={labelRef}
>
{filter.label}
</div>
<div
className="vm-extra-filters-panel-item__remove"
onClick={handleRemove}
>
<CloseIcon/>
</div>
</Tooltip>
<div
className="vm-extra-filters-panel-item__remove"
onClick={handleRemove}
>
<CloseIcon/>
</div>
</Tooltip>
</div>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,44 @@ $bg-color-dark: $color-background-body;
font-size: $font-size-small;

&-item {
position: relative;
display: flex;
align-items: center;
justify-content: center;
gap: calc($padding-small / 2);
padding-block: 1px;
padding-inline: $padding-small 2px;
padding-inline: calc($padding-small / 2) 2px;
max-width: clamp(180px, 50%, 360px);
border-radius: $border-radius-button;
color: $color-text-secondary;
background-color: $color-hover-black;
z-index: 1;

&:after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: $border-radius-button;
background-color: currentColor;
opacity: 0.1;
z-index: -1;
}

&_stream {
color: $color-dodger-blue;
}

&__icon {
Comment thread
Loori-R marked this conversation as resolved.
display: flex;
align-items: center;
justify-content: center;
padding: 4px;
min-height: 22px;
max-height: 22px;
aspect-ratio: 1;
}

&__tooltip {
max-width: calc(clamp(180px, 24vw, 360px) - ($padding-small * 2));
Expand All @@ -34,17 +62,18 @@ $bg-color-dark: $color-background-body;
}

&__label {
color: $color-text;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

&__remove {
color: $color-text-secondary;
display: flex;
align-items: center;
justify-content: center;
border-radius: calc($border-radius-medium - 2px);
color: $color-text-secondary;
background-color: transparent;
padding: calc($padding-small / 2);
cursor: pointer;
Expand Down
31 changes: 31 additions & 0 deletions app/vmui/packages/vmui/src/components/Main/Icons/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -659,3 +659,34 @@ export const SidebarCloseIcon = () => (
/>
</svg>
);

export const FieldStreamIcon = () => (
<svg
viewBox="0 0 24 24"
fill="currentColor"
>
<path
d="M21.9922 16C22.5444 15.9957 22.9956 16.44 23 16.9922C23.0031 17.3803 22.8931 17.7612 22.6836 18.0879C22.4741 18.4144 22.1741 18.6729 21.8203 18.832L13.2441 22.7305L13.2422 22.7314C12.8535 22.9074 12.4315 22.998 12.0049 22.998C11.5782 22.998 11.1563 22.9074 10.7676 22.7314L10.7666 22.7305L2.16602 18.8203L2.16016 18.8174C1.81301 18.6567 1.51908 18.3997 1.31348 18.0771C1.10797 17.7546 0.99906 17.3795 1.00001 16.9971C1.00163 16.445 1.45082 15.9986 2.00294 16C2.55483 16.0016 3.0001 16.4501 2.99903 17.002L11.5918 20.9092C11.7213 20.9678 11.8628 20.998 12.0049 20.998C12.1467 20.998 12.2868 20.9675 12.416 20.9092L20.9961 17.0098L21 17.0078C20.9957 16.4556 21.44 16.0044 21.9922 16Z"
/>
<path
d="M21.9922 11C22.5444 10.9957 22.9956 11.44 23 11.9922C23.0031 12.3803 22.8931 12.7612 22.6836 13.0879C22.4741 13.4144 22.1741 13.6729 21.8203 13.832L13.2441 17.7305L13.2422 17.7314C12.8535 17.9074 12.4315 17.998 12.0049 17.998C11.5782 17.998 11.1563 17.9074 10.7676 17.7314L10.7666 17.7305L2.16602 13.8203L2.16016 13.8174C1.81301 13.6567 1.51908 13.3997 1.31348 13.0771C1.10797 12.7546 0.99906 12.3795 1.00001 11.9971C1.00163 11.445 1.45082 10.9986 2.00294 11C2.55483 11.0016 3.0001 11.4501 2.99903 12.002L11.5918 15.9092C11.7213 15.9678 11.8628 15.998 12.0049 15.998C12.1467 15.998 12.2868 15.9675 12.416 15.9092L20.9961 12.0098L21 12.0078C20.9957 11.4556 21.44 11.0044 21.9922 11Z"
/>
<path
d="M12 1C12.4295 1 12.8544 1.09232 13.2451 1.27051L21.835 5.18066C22.1853 5.33755 22.4826 5.59137 22.6924 5.91309C22.9041 6.23783 23.0165 6.61725 23.0166 7.00488C23.0166 7.39267 22.9042 7.77281 22.6924 8.09766C22.4825 8.41956 22.1837 8.67325 21.833 8.83008L21.834 8.83105L13.2549 12.7295C12.8642 12.9076 12.4401 13.0009 12.0107 13.001C11.6347 13.001 11.2622 12.9301 10.9131 12.793L10.7656 12.7305L2.18555 8.82031C1.83582 8.6634 1.53761 8.40912 1.32813 8.08789C1.11635 7.76305 1.00396 7.3829 1.00391 6.99512C1.00396 6.60754 1.11657 6.22805 1.32813 5.90332C1.53786 5.58163 1.83619 5.32682 2.18653 5.16992L10.7559 1.27051L10.9033 1.20801C11.2522 1.0709 11.6242 1.00006 12 1ZM12 3C11.8572 3.00007 11.7159 3.03064 11.5859 3.08984L11.585 3.09082L3.00391 6.99512L3.01563 7L11.5947 10.9102C11.725 10.9695 11.8676 11.001 12.0107 11.001C12.1538 11.0009 12.2956 10.9695 12.4258 10.9102L21.0068 7.00977L21.0166 7.00488L21.0059 7.00098L12.416 3.09082L12.415 3.08984C12.2849 3.03056 12.143 3 12 3Z"
/>
</svg>
);

export const FieldIcon = () => (
<svg
viewBox="0 0 24 24"
fill="currentColor"
>
<path
d="M7.5 6C8.32843 6 9 6.67157 9 7.5C9 8.32843 8.32843 9 7.5 9C6.67157 9 6 8.32843 6 7.5C6 6.67157 6.67157 6 7.5 6Z"
/>
<path
d="M11.1719 1C11.9674 1.00017 12.7305 1.31636 13.293 1.87891L21.999 10.585C22.6368 11.2268 22.9951 12.0951 22.9951 13C22.9951 13.9049 22.6368 14.7732 21.999 15.415L15.415 21.999C14.7732 22.6368 13.9049 22.9951 13 22.9951C12.0951 22.9951 11.2268 22.6368 10.585 21.999L1.87891 13.293C1.31636 12.7305 1.00017 11.9674 1 11.1719V4C1 3.20435 1.3163 2.44152 1.87891 1.87891C2.44152 1.3163 3.20435 1 4 1H11.1719ZM4 3C3.73478 3 3.4805 3.10543 3.29297 3.29297C3.10543 3.4805 3 3.73478 3 4V11.1719L3.00488 11.2705C3.02764 11.4994 3.12884 11.7148 3.29297 11.8789L11.9951 20.5811L12.0996 20.6748C12.3529 20.881 12.6705 20.9951 13 20.9951C13.3765 20.9951 13.7378 20.8463 14.0049 20.5811L20.583 14.0029C20.8471 13.736 20.9951 13.3756 20.9951 13C20.9951 12.6244 20.8471 12.264 20.583 11.9971L11.8789 3.29297C11.7148 3.12884 11.4994 3.02764 11.2705 3.00488L11.1719 3H4Z"
/>
</svg>
);
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ import { FC, memo } from "preact/compat";
import classNames from "classnames";
import useDeviceDetect from "../../../hooks/useDeviceDetect";
import FieldRowActions from "./GroupLogsFieldRowMenu/FieldRowActions";
import { FieldStreamIcon } from "../../Main/Icons";
import Tooltip from "../../Main/Tooltip/Tooltip";

interface Props {
field: string;
value: string;
isStreamField: boolean;
hideGroupButton?: boolean;
}

const GroupLogsFieldRow: FC<Props> = ({ field, value, hideGroupButton = false }) => {
const GroupLogsFieldRow: FC<Props> = ({ field, value, isStreamField, hideGroupButton = false }) => {
const { isMobile } = useDeviceDetect();

return (
Expand All @@ -19,12 +22,18 @@ const GroupLogsFieldRow: FC<Props> = ({ field, value, hideGroupButton = false })
"vm-group-logs-row-fields-item_mobile": isMobile
})}
>
<td className="vm-group-logs-row-fields-item__icon">
<Tooltip title="Stream field">
{isStreamField ? <FieldStreamIcon/> : null}
</Tooltip>
</td>
<td className="vm-group-logs-row-fields-item__key">{field}</td>
<td className="vm-group-logs-row-fields-item__value">{value}</td>
<td className="vm-group-logs-row-fields-item-controls">
<FieldRowActions
field={field}
value={value}
isStreamField={isStreamField}
hideGroupButton={hideGroupButton}
/>
</td>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ import { useExtraFilters } from "../../../ExtraFilters/hooks/useExtraFilters";
interface Props {
field: string;
value: string;
isStreamField: boolean;
onClose: () => void;
}

const FieldFilterExclude: FC<Props> = ({ field, value, onClose }) => {
const FieldFilterExclude: FC<Props> = ({ field, value, isStreamField, onClose }) => {
const { extraFilters, addNewFilter, removeFilterByValue } = useExtraFilters();
const filtersByValue = extraFilters.filter(f => f.field === field && f.value === value);
const isExcludeFilter = filtersByValue.some(f => f.operator === ExtraFilterOperator.NotEquals);

const handleClickFilter = () => {
const newFilter = { field, value, operator: ExtraFilterOperator.NotEquals };
const newFilter = { field, value, operator: ExtraFilterOperator.NotEquals, isStream: isStreamField };
if (isExcludeFilter) {
// If the same filter already exists, we remove it by setting the value to an empty string
removeFilterByValue(field, value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ import { useExtraFilters } from "../../../ExtraFilters/hooks/useExtraFilters";
interface Props {
field: string;
value: string;
isStreamField: boolean;
}

const FieldFilterInclude: FC<Props> = ({ field, value }) => {
const FieldFilterInclude: FC<Props> = ({ field, value, isStreamField }) => {
const { extraFilters, addNewFilter, removeFilterByValue } = useExtraFilters();
const filtersByValue = extraFilters.filter(f => f.field === field && f.value === value);
const isIncludeFilter = filtersByValue.some(f => f.operator === ExtraFilterOperator.Equals);

const handleClickFilter = () => {
const newFilter = { field, value, operator: ExtraFilterOperator.Equals };
const newFilter = { field, value, operator: ExtraFilterOperator.Equals, isStream: isStreamField };
if (isIncludeFilter) {
// If the same filter already exists, we remove it by setting the value to an empty string
removeFilterByValue(field, value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import Popper from "../../../Main/Popper/Popper";
type Props = {
field: string;
value: string;
isStreamField: boolean;
hideGroupButton: boolean;
}

const FieldRowActions: FC<Props> = ({ field, value, hideGroupButton }) => {
const FieldRowActions: FC<Props> = ({ field, value, isStreamField, hideGroupButton }) => {
const {
value: openContextMenu,
setFalse: handleCloseContextMenu,
Expand All @@ -40,6 +41,7 @@ const FieldRowActions: FC<Props> = ({ field, value, hideGroupButton }) => {
<FieldFilterInclude
field={field}
value={value}
isStreamField={isStreamField}
/>

<div ref={buttonRef}>
Expand All @@ -62,6 +64,7 @@ const FieldRowActions: FC<Props> = ({ field, value, hideGroupButton }) => {
<FieldFilterExclude
field={field}
value={value}
isStreamField={isStreamField}
onClose={handleCloseContextMenu}
/>
<FieldMessageToggle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useLocalStorageBoolean } from "../../../hooks/useLocalStorageBoolean";
import useDeviceDetect from "../../../hooks/useDeviceDetect";
import TextField from "../../Main/TextField/TextField";
import { SearchIcon } from "../../Main/Icons";
import { getStreamKeys } from "../../../utils/logs";

interface Props {
log: Logs;
Expand All @@ -24,6 +25,11 @@ const GroupLogsFields: FC<Props> = ({ log, hideGroupButton }) => {
return key.toLowerCase().includes(searchLower) || String(value).toLowerCase().includes(searchLower);
}), [rawEntries, search]);

const streamFields = useMemo(() => {
if (!log._stream) return [];
return getStreamKeys(log._stream);
}, [log]);

const [disabledHovers] = useLocalStorageBoolean("LOGS_DISABLED_HOVERS");

return (
Expand Down Expand Up @@ -59,6 +65,7 @@ const GroupLogsFields: FC<Props> = ({ log, hideGroupButton }) => {
key={key}
field={key}
value={value}
isStreamField={streamFields.includes(key) || key === "_stream"}
hideGroupButton={hideGroupButton}
/>
))}
Expand Down
12 changes: 12 additions & 0 deletions app/vmui/packages/vmui/src/components/Views/GroupView/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,18 @@ $actions-width: calc(($actions-buttons * 30px) + 10px);
}
}

&__icon {
display: flex;
align-items: center;
justify-content: center;
max-width: 20px;
min-width: 20px;
padding: 4px;
aspect-ratio: 1;
color: $color-text-secondary;
margin-left: calc($padding-small / 2);
}

&__key,
&__value {
vertical-align: top;
Expand Down
2 changes: 2 additions & 0 deletions docs/victorialogs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ according to the following docs:

## tip

* FEATURE: [web UI](https://docs.victoriametrics.com/victorialogs/querying/#web-ui): visually distinguish stream fields in expanded log entries and active filters, and automatically use [stream filters](https://docs.victoriametrics.com/victorialogs/logsql/#stream-filter) for include and exclude actions on stream fields. See [#1607](https://github.com/VictoriaMetrics/VictoriaLogs/issues/1607).

## [v1.52.0](https://github.com/VictoriaMetrics/VictoriaLogs/releases/tag/v1.52.0)

Released at 2026-07-16
Expand Down
Loading