mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
vmui/logs: add markdown support (#6292)
Add support for markdown format and emoji for the `_msg` field in the "Group" view. Add markdown rendering toggle. Disabled by default. Value is stored in `localStorage`.
This commit is contained in:
parent
0b7c47a40c
commit
84088e5a2d
16 changed files with 2048 additions and 21 deletions
25
app/vmui/packages/vmui/package-lock.json
generated
25
app/vmui/packages/vmui/package-lock.json
generated
|
@ -11,7 +11,6 @@
|
||||||
"@types/lodash.debounce": "^4.0.6",
|
"@types/lodash.debounce": "^4.0.6",
|
||||||
"@types/lodash.get": "^4.4.6",
|
"@types/lodash.get": "^4.4.6",
|
||||||
"@types/lodash.throttle": "^4.1.6",
|
"@types/lodash.throttle": "^4.1.6",
|
||||||
"@types/marked": "^5.0.0",
|
|
||||||
"@types/node": "^20.4.0",
|
"@types/node": "^20.4.0",
|
||||||
"@types/qs": "^6.9.7",
|
"@types/qs": "^6.9.7",
|
||||||
"@types/react-input-mask": "^3.0.2",
|
"@types/react-input-mask": "^3.0.2",
|
||||||
|
@ -22,7 +21,8 @@
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"marked": "^5.1.0",
|
"marked": "^12.0.2",
|
||||||
|
"marked-emoji": "^1.4.0",
|
||||||
"preact": "^10.7.1",
|
"preact": "^10.7.1",
|
||||||
"qs": "^6.10.3",
|
"qs": "^6.10.3",
|
||||||
"react-input-mask": "^2.0.4",
|
"react-input-mask": "^2.0.4",
|
||||||
|
@ -4272,11 +4272,6 @@
|
||||||
"@types/lodash": "*"
|
"@types/lodash": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/marked": {
|
|
||||||
"version": "5.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/marked/-/marked-5.0.2.tgz",
|
|
||||||
"integrity": "sha512-OucS4KMHhFzhz27KxmWg7J+kIYqyqoW5kdIEI319hqARQQUTqhao3M/F+uFnDXD0Rg72iDDZxZNxq5gvctmLlg=="
|
|
||||||
},
|
|
||||||
"node_modules/@types/mime": {
|
"node_modules/@types/mime": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
|
||||||
|
@ -13598,14 +13593,22 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/marked": {
|
"node_modules/marked": {
|
||||||
"version": "5.1.2",
|
"version": "12.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/marked/-/marked-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz",
|
||||||
"integrity": "sha512-ahRPGXJpjMjwSOlBoTMZAK7ATXkli5qCPxZ21TG44rx1KEo44bii4ekgTDQPNRQ4Kh7JMb9Ub1PVk1NxRSsorg==",
|
"integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==",
|
||||||
"bin": {
|
"bin": {
|
||||||
"marked": "bin/marked.js"
|
"marked": "bin/marked.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 16"
|
"node": ">= 18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/marked-emoji": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/marked-emoji/-/marked-emoji-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-/2TJfGzXpiBBq+X3akHHbTrAjZPJDwR+7FV6SyQLECnQEfaoVkrpKZJzHhPTAq3Sl/A1l2frMT0u6b38VBBlNg==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"marked": ">=4 <13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mdn-data": {
|
"node_modules/mdn-data": {
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"@types/lodash.debounce": "^4.0.6",
|
"@types/lodash.debounce": "^4.0.6",
|
||||||
"@types/lodash.get": "^4.4.6",
|
"@types/lodash.get": "^4.4.6",
|
||||||
"@types/lodash.throttle": "^4.1.6",
|
"@types/lodash.throttle": "^4.1.6",
|
||||||
"@types/marked": "^5.0.0",
|
|
||||||
"@types/node": "^20.4.0",
|
"@types/node": "^20.4.0",
|
||||||
"@types/qs": "^6.9.7",
|
"@types/qs": "^6.9.7",
|
||||||
"@types/react-input-mask": "^3.0.2",
|
"@types/react-input-mask": "^3.0.2",
|
||||||
|
@ -18,7 +17,8 @@
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"marked": "^5.1.0",
|
"marked": "^12.0.2",
|
||||||
|
"marked-emoji": "^1.4.0",
|
||||||
"preact": "^10.7.1",
|
"preact": "^10.7.1",
|
||||||
"qs": "^6.10.3",
|
"qs": "^6.10.3",
|
||||||
"react-input-mask": "^2.0.4",
|
"react-input-mask": "^2.0.4",
|
||||||
|
|
|
@ -4,6 +4,7 @@ import AppContextProvider from "./contexts/AppContextProvider";
|
||||||
import ThemeProvider from "./components/Main/ThemeProvider/ThemeProvider";
|
import ThemeProvider from "./components/Main/ThemeProvider/ThemeProvider";
|
||||||
import ExploreLogs from "./pages/ExploreLogs/ExploreLogs";
|
import ExploreLogs from "./pages/ExploreLogs/ExploreLogs";
|
||||||
import LogsLayout from "./layouts/LogsLayout/LogsLayout";
|
import LogsLayout from "./layouts/LogsLayout/LogsLayout";
|
||||||
|
import "./constants/markedPlugins";
|
||||||
|
|
||||||
const AppLogs: FC = () => {
|
const AppLogs: FC = () => {
|
||||||
const [loadedTheme, setLoadedTheme] = useState(false);
|
const [loadedTheme, setLoadedTheme] = useState(false);
|
||||||
|
|
1915
app/vmui/packages/vmui/src/constants/emojis.ts
Normal file
1915
app/vmui/packages/vmui/src/constants/emojis.ts
Normal file
File diff suppressed because it is too large
Load diff
5
app/vmui/packages/vmui/src/constants/markedPlugins.ts
Normal file
5
app/vmui/packages/vmui/src/constants/markedPlugins.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { markedEmoji } from "marked-emoji";
|
||||||
|
import { marked } from "marked";
|
||||||
|
import emojis from "./emojis";
|
||||||
|
|
||||||
|
marked.use(markedEmoji({ emojis, renderer: (token) => token.emoji }));
|
|
@ -54,7 +54,7 @@ const useGetMetricsQL = () => {
|
||||||
|
|
||||||
const processMarkdown = (text: string) => {
|
const processMarkdown = (text: string) => {
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.innerHTML = marked(text);
|
div.innerHTML = marked(text) as string;
|
||||||
const groups = div.querySelectorAll(`${CATEGORY_TAG}, ${FUNCTION_TAG}`);
|
const groups = div.querySelectorAll(`${CATEGORY_TAG}, ${FUNCTION_TAG}`);
|
||||||
return processGroups(groups);
|
return processGroups(groups);
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,6 +26,7 @@ const ExploreLogs: FC = () => {
|
||||||
const { logs, isLoading, error, fetchLogs } = useFetchLogs(serverUrl, query, limit);
|
const { logs, isLoading, error, fetchLogs } = useFetchLogs(serverUrl, query, limit);
|
||||||
const [queryError, setQueryError] = useState<ErrorTypes | string>("");
|
const [queryError, setQueryError] = useState<ErrorTypes | string>("");
|
||||||
const [loaded, isLoaded] = useState(false);
|
const [loaded, isLoaded] = useState(false);
|
||||||
|
const [markdownParsing, setMarkdownParsing] = useState(getFromStorage("LOGS_MARKDOWN") === "true");
|
||||||
|
|
||||||
const handleRunQuery = () => {
|
const handleRunQuery = () => {
|
||||||
if (!query) {
|
if (!query) {
|
||||||
|
@ -51,6 +52,11 @@ const ExploreLogs: FC = () => {
|
||||||
saveToStorage("LOGS_LIMIT", `${limit}`);
|
saveToStorage("LOGS_LIMIT", `${limit}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleChangeMarkdownParsing = (val: boolean) => {
|
||||||
|
saveToStorage("LOGS_MARKDOWN", `${val}`);
|
||||||
|
setMarkdownParsing(val);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (query) handleRunQuery();
|
if (query) handleRunQuery();
|
||||||
}, [period]);
|
}, [period]);
|
||||||
|
@ -65,15 +71,18 @@ const ExploreLogs: FC = () => {
|
||||||
query={query}
|
query={query}
|
||||||
error={queryError}
|
error={queryError}
|
||||||
limit={limit}
|
limit={limit}
|
||||||
|
markdownParsing={markdownParsing}
|
||||||
onChange={setQuery}
|
onChange={setQuery}
|
||||||
onChangeLimit={handleChangeLimit}
|
onChangeLimit={handleChangeLimit}
|
||||||
onRun={handleRunQuery}
|
onRun={handleRunQuery}
|
||||||
|
onChangeMarkdownParsing={handleChangeMarkdownParsing}
|
||||||
/>
|
/>
|
||||||
{isLoading && <Spinner />}
|
{isLoading && <Spinner />}
|
||||||
{error && <Alert variant="error">{error}</Alert>}
|
{error && <Alert variant="error">{error}</Alert>}
|
||||||
<ExploreLogsBody
|
<ExploreLogsBody
|
||||||
data={logs}
|
data={logs}
|
||||||
loaded={loaded}
|
loaded={loaded}
|
||||||
|
markdownParsing={markdownParsing}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -15,10 +15,12 @@ import useBoolean from "../../../hooks/useBoolean";
|
||||||
import TableLogs from "./TableLogs";
|
import TableLogs from "./TableLogs";
|
||||||
import GroupLogs from "../GroupLogs/GroupLogs";
|
import GroupLogs from "../GroupLogs/GroupLogs";
|
||||||
import { DATE_TIME_FORMAT } from "../../../constants/date";
|
import { DATE_TIME_FORMAT } from "../../../constants/date";
|
||||||
|
import { marked } from "marked";
|
||||||
|
|
||||||
export interface ExploreLogBodyProps {
|
export interface ExploreLogBodyProps {
|
||||||
data: Logs[];
|
data: Logs[];
|
||||||
loaded?: boolean;
|
loaded?: boolean;
|
||||||
|
markdownParsing: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum DisplayType {
|
enum DisplayType {
|
||||||
|
@ -33,7 +35,7 @@ const tabs = [
|
||||||
{ label: "JSON", value: DisplayType.json, icon: <CodeIcon/> },
|
{ label: "JSON", value: DisplayType.json, icon: <CodeIcon/> },
|
||||||
];
|
];
|
||||||
|
|
||||||
const ExploreLogsBody: FC<ExploreLogBodyProps> = ({ data, loaded }) => {
|
const ExploreLogsBody: FC<ExploreLogBodyProps> = ({ data, loaded, markdownParsing }) => {
|
||||||
const { isMobile } = useDeviceDetect();
|
const { isMobile } = useDeviceDetect();
|
||||||
const { timezone } = useTimeState();
|
const { timezone } = useTimeState();
|
||||||
const { setSearchParamsFromKeys } = useSearchParamsFromObject();
|
const { setSearchParamsFromKeys } = useSearchParamsFromObject();
|
||||||
|
@ -46,11 +48,12 @@ const ExploreLogsBody: FC<ExploreLogBodyProps> = ({ data, loaded }) => {
|
||||||
...item,
|
...item,
|
||||||
_vmui_time: item._time ? dayjs(item._time).tz().format(`${DATE_TIME_FORMAT}.SSS`) : "",
|
_vmui_time: item._time ? dayjs(item._time).tz().format(`${DATE_TIME_FORMAT}.SSS`) : "",
|
||||||
_vmui_data: JSON.stringify(item, null, 2),
|
_vmui_data: JSON.stringify(item, null, 2),
|
||||||
|
_vmui_markdown: marked(item._msg.replace(/```/g, "\n```\n")) as string,
|
||||||
})) as Logs[], [data, timezone]);
|
})) as Logs[], [data, timezone]);
|
||||||
|
|
||||||
const columns = useMemo(() => {
|
const columns = useMemo(() => {
|
||||||
if (!logs?.length) return [];
|
if (!logs?.length) return [];
|
||||||
const hideColumns = ["_vmui_data", "_vmui_time"];
|
const hideColumns = ["_vmui_data", "_vmui_time", "_vmui_markdown"];
|
||||||
const keys = new Set<string>();
|
const keys = new Set<string>();
|
||||||
for (const item of logs) {
|
for (const item of logs) {
|
||||||
for (const key in item) {
|
for (const key in item) {
|
||||||
|
@ -125,6 +128,7 @@ const ExploreLogsBody: FC<ExploreLogBodyProps> = ({ data, loaded }) => {
|
||||||
<GroupLogs
|
<GroupLogs
|
||||||
logs={logs}
|
logs={logs}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
markdownParsing={markdownParsing}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{activeTab === DisplayType.json && (
|
{activeTab === DisplayType.json && (
|
||||||
|
|
|
@ -6,17 +6,29 @@ import useDeviceDetect from "../../../hooks/useDeviceDetect";
|
||||||
import Button from "../../../components/Main/Button/Button";
|
import Button from "../../../components/Main/Button/Button";
|
||||||
import QueryEditor from "../../../components/Configurators/QueryEditor/QueryEditor";
|
import QueryEditor from "../../../components/Configurators/QueryEditor/QueryEditor";
|
||||||
import TextField from "../../../components/Main/TextField/TextField";
|
import TextField from "../../../components/Main/TextField/TextField";
|
||||||
|
import Switch from "../../../components/Main/Switch/Switch";
|
||||||
|
|
||||||
export interface ExploreLogHeaderProps {
|
export interface ExploreLogHeaderProps {
|
||||||
query: string;
|
query: string;
|
||||||
limit: number;
|
limit: number;
|
||||||
error?: string;
|
error?: string;
|
||||||
|
markdownParsing: boolean;
|
||||||
onChange: (val: string) => void;
|
onChange: (val: string) => void;
|
||||||
onChangeLimit: (val: number) => void;
|
onChangeLimit: (val: number) => void;
|
||||||
onRun: () => void;
|
onRun: () => void;
|
||||||
|
onChangeMarkdownParsing: (val: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ExploreLogsHeader: FC<ExploreLogHeaderProps> = ({ query, limit, error, onChange, onChangeLimit, onRun }) => {
|
const ExploreLogsHeader: FC<ExploreLogHeaderProps> = ({
|
||||||
|
query,
|
||||||
|
limit,
|
||||||
|
error,
|
||||||
|
markdownParsing,
|
||||||
|
onChange,
|
||||||
|
onChangeLimit,
|
||||||
|
onRun,
|
||||||
|
onChangeMarkdownParsing,
|
||||||
|
}) => {
|
||||||
const { isMobile } = useDeviceDetect();
|
const { isMobile } = useDeviceDetect();
|
||||||
|
|
||||||
const [errorLimit, setErrorLimit] = useState("");
|
const [errorLimit, setErrorLimit] = useState("");
|
||||||
|
@ -66,6 +78,14 @@ const ExploreLogsHeader: FC<ExploreLogHeaderProps> = ({ query, limit, error, onC
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="vm-explore-logs-header-bottom">
|
<div className="vm-explore-logs-header-bottom">
|
||||||
|
<div className="vm-explore-logs-header-bottom-contols">
|
||||||
|
<Switch
|
||||||
|
label={"Markdown parsing"}
|
||||||
|
value={markdownParsing}
|
||||||
|
onChange={onChangeMarkdownParsing}
|
||||||
|
fullWidth={isMobile}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="vm-explore-logs-header-bottom-helpful">
|
<div className="vm-explore-logs-header-bottom-helpful">
|
||||||
<a
|
<a
|
||||||
className="vm-link vm-link_with-icon"
|
className="vm-link vm-link_with-icon"
|
||||||
|
|
|
@ -25,6 +25,10 @@
|
||||||
justify-content: normal;
|
justify-content: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-contols {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
&__execute {
|
&__execute {
|
||||||
display: grid;
|
display: grid;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,10 @@ import GroupLogsItem from "./GroupLogsItem";
|
||||||
interface TableLogsProps {
|
interface TableLogsProps {
|
||||||
logs: Logs[];
|
logs: Logs[];
|
||||||
columns: string[];
|
columns: string[];
|
||||||
|
markdownParsing: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GroupLogs: FC<TableLogsProps> = ({ logs }) => {
|
const GroupLogs: FC<TableLogsProps> = ({ logs, markdownParsing }) => {
|
||||||
const copyToClipboard = useCopyToClipboard();
|
const copyToClipboard = useCopyToClipboard();
|
||||||
|
|
||||||
const [copied, setCopied] = useState<string | null>(null);
|
const [copied, setCopied] = useState<string | null>(null);
|
||||||
|
@ -77,6 +78,7 @@ const GroupLogs: FC<TableLogsProps> = ({ logs }) => {
|
||||||
<GroupLogsItem
|
<GroupLogsItem
|
||||||
key={`${value._msg}${value._time}`}
|
key={`${value._msg}${value._time}`}
|
||||||
log={value}
|
log={value}
|
||||||
|
markdownParsing={markdownParsing}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,15 +10,16 @@ import classNames from "classnames";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
log: Logs;
|
log: Logs;
|
||||||
|
markdownParsing: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GroupLogsItem: FC<Props> = ({ log }) => {
|
const GroupLogsItem: FC<Props> = ({ log, markdownParsing }) => {
|
||||||
const {
|
const {
|
||||||
value: isOpenFields,
|
value: isOpenFields,
|
||||||
toggle: toggleOpenFields,
|
toggle: toggleOpenFields,
|
||||||
} = useBoolean(false);
|
} = useBoolean(false);
|
||||||
|
|
||||||
const excludeKeys = ["_stream", "_msg", "_time", "_vmui_time", "_vmui_data"];
|
const excludeKeys = ["_stream", "_msg", "_time", "_vmui_time", "_vmui_data", "_vmui_markdown"];
|
||||||
const fields = Object.entries(log).filter(([key]) => !excludeKeys.includes(key));
|
const fields = Object.entries(log).filter(([key]) => !excludeKeys.includes(key));
|
||||||
const hasFields = fields.length > 0;
|
const hasFields = fields.length > 0;
|
||||||
|
|
||||||
|
@ -71,6 +72,7 @@ const GroupLogsItem: FC<Props> = ({ log }) => {
|
||||||
"vm-group-logs-row-content__msg": true,
|
"vm-group-logs-row-content__msg": true,
|
||||||
"vm-group-logs-row-content__msg_missing": !log._msg
|
"vm-group-logs-row-content__msg_missing": !log._msg
|
||||||
})}
|
})}
|
||||||
|
dangerouslySetInnerHTML={markdownParsing && log._vmui_markdown ? { __html: log._vmui_markdown } : undefined}
|
||||||
>
|
>
|
||||||
{log._msg || "message missing"}
|
{log._msg || "message missing"}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -96,6 +96,65 @@
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* styles for markdown */
|
||||||
|
p, pre, code {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
code:not(pre code), pre {
|
||||||
|
display: inline-block;
|
||||||
|
background: $color-hover-black;
|
||||||
|
border: 1px solid $color-hover-black;
|
||||||
|
border-radius: $border-radius-small;
|
||||||
|
tab-size: 4;
|
||||||
|
font-variant-ligatures: none;
|
||||||
|
margin: calc($padding-small/4) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-family: $font-family-global;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
padding: $padding-small;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-size: $font-size-small;
|
||||||
|
padding: calc($padding-small / 4) calc($padding-small / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $color-primary;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
em {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
border-left: 4px solid $color-hover-black;
|
||||||
|
margin: calc($padding-small/2) $padding-small;
|
||||||
|
padding: calc($padding-small/2) $padding-small;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul, ol {
|
||||||
|
list-style-position: inside;
|
||||||
|
}
|
||||||
|
/* end styles for markdown */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ const PredefinedPanel: FC<PredefinedPanelsProps> = ({
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
<span>Description:</span>
|
<span>Description:</span>
|
||||||
<div dangerouslySetInnerHTML={{ __html: marked.parse(description) }}/>
|
<div dangerouslySetInnerHTML={{ __html: marked(description) as string }}/>
|
||||||
</div>
|
</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -7,6 +7,7 @@ export type StorageKeys = "AUTOCOMPLETE"
|
||||||
| "DISABLED_DEFAULT_TIMEZONE"
|
| "DISABLED_DEFAULT_TIMEZONE"
|
||||||
| "THEME"
|
| "THEME"
|
||||||
| "LOGS_LIMIT"
|
| "LOGS_LIMIT"
|
||||||
|
| "LOGS_MARKDOWN"
|
||||||
| "EXPLORE_METRICS_TIPS"
|
| "EXPLORE_METRICS_TIPS"
|
||||||
| "QUERY_HISTORY"
|
| "QUERY_HISTORY"
|
||||||
| "QUERY_FAVORITES"
|
| "QUERY_FAVORITES"
|
||||||
|
|
|
@ -19,6 +19,8 @@ according to [these docs](https://docs.victoriametrics.com/victorialogs/quicksta
|
||||||
|
|
||||||
## tip
|
## tip
|
||||||
|
|
||||||
|
* FEATURE: [web UI](https://docs.victoriametrics.com/VictoriaLogs/querying/#web-ui): add markdown support to the `Group` view. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/6292).
|
||||||
|
|
||||||
## [v0.18.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v0.18.0-victorialogs)
|
## [v0.18.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v0.18.0-victorialogs)
|
||||||
|
|
||||||
Released at 2024-06-06
|
Released at 2024-06-06
|
||||||
|
|
Loading…
Reference in a new issue