chore: add Copy request to alerting chart (#121)

* chore: add `Copy request` to alerting chart

* chore: update release notes

---------

Co-authored-by: yaojiping <yaojiping@infini.ltd>
This commit is contained in:
yaojp123 2025-02-13 12:18:36 +08:00 committed by GitHub
parent 9d120276d1
commit 4e8215482c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 122 additions and 39 deletions

View File

@ -18,7 +18,9 @@ Information about release notes of INFINI Console is provided here.
### Improvements
- add Buckets Diff to alerting rule
- add Copy request to alerting chart
- add credential settings for agent in enrolling agent
- add collection mode to cluster editing
## 1.28.1 (2025-01-24)

View File

@ -17,7 +17,7 @@ title: "版本历史"
### Improvements
- 告警规则新增分桶对比
- 告警图表新增复制请求
- 在注册 Agent 中新增 Agent 凭据设置
- 在集群编辑中新增采集模式

View File

@ -54,7 +54,8 @@ export default (props) => {
isEdit,
fetchParamsCache,
handleContextMenu,
isFullScreen
isFullScreen,
onResultChange
} = props;
const { series = [] } = record;
@ -99,6 +100,7 @@ export default (props) => {
if (!refresh) {
setLoading(true)
setData()
onResultChange && onResultChange()
}
if (isTimeSeries && !range) {
@ -189,8 +191,10 @@ export default (props) => {
}
res.hits.hits = res.hits.hits || [];
setData(res.hits)
onResultChange && onResultChange(res)
}
} else {
const index_pattern = indices.join(',')
const bodys = metrics.map((item) => {
const { groups = [] } = item;
let newGroups = cloneDeep(groups);
@ -204,7 +208,7 @@ export default (props) => {
return {
cluster_id,
filter,
index_pattern: indices.join(','),
index_pattern,
time_field: time_field,
...item,
items: item.items || [],
@ -219,6 +223,7 @@ export default (props) => {
if (res) {
if (res.some((item) => item.status === 403)) {
setData({ error: 403 })
onResultChange && onResultChange()
setLoading(false);
return;
}
@ -269,8 +274,15 @@ export default (props) => {
}
}
setData(newData)
onResultChange && onResultChange(res.map((item) => (
{
...item,
request: item.request ? `GET ${index_pattern}/_search\n${item.request}` : undefined
}
)))
} else {
setData([])
onResultChange && onResultChange()
}
}
setLoading(false);

View File

@ -41,7 +41,8 @@ export default (props) => {
queriesBarParams,
isFullScreen,
hideHeader,
displayOptions={}
displayOptions={},
onResultChange,
} = props;
const [cacheRecord, setCacheRecord] = useState(record)
@ -252,6 +253,7 @@ export default (props) => {
queriesBarParams={queriesBarParams}
handleContextMenu={handleContextMenu}
isFullScreen={isFullScreen}
onResultChange={onResultChange}
/>
</Spin>
<WidgetConfigDrawer

View File

@ -1,17 +1,20 @@
import { useEffect, useMemo, useState } from "react"
import Widget from "./Widget"
import styles from "./WidgetLoader.less"
import { Empty, Spin } from "antd"
import { Empty, Icon, message, Spin, Tooltip } from "antd"
import request from "@/utils/request"
import { generateFilter, mergeFilters } from "./components/QueriesBar/generate_filters"
import { calculateBounds } from "@/components/vendor/data/common/query/timefilter";
import moment from "moment"
import { getTimezone } from "@/utils/utils"
import { CopyToClipboard } from "react-copy-to-clipboard";
import { formatMessage } from "umi/locale";
export const WidgetRender = (props) => {
const { widget, range, query, queryParams = {}, highlightRange = {}, refresh } = props;
const { widget, range, query, queryParams = {}, highlightRange = {}, refresh, showCopy = true } = props;
const [globalRangeCache, setGlobalRangeCache] = useState()
const [requests, setRequests] = useState([])
const filters = useMemo(() => {
const newFilters = []
@ -41,6 +44,18 @@ export const WidgetRender = (props) => {
return (
widget ? (
<div className={styles.content}>
{
showCopy && requests.length > 0 && (
<CopyToClipboard text={requests.join('\n')}>
<Tooltip placement="left" title={formatMessage({id: "cluster.metrics.request.copy"})}>
<div className={styles.copy} onClick={() => message.success(formatMessage({id: "cluster.metrics.request.copy.success"}))}>
<Icon type="copy" />
</div>
</Tooltip>
</CopyToClipboard>
)
}
<Widget
record={widget}
globalQueries={{
@ -66,7 +81,11 @@ export const WidgetRender = (props) => {
clusterList={[]}
highlightRange={highlightRange}
onHighlightRangeChange={() => {}}
onResultChange={(res) => {
setRequests(Array.isArray(res) ? res.filter((item) => !!item.request).map((item) => item.request) : [])
}}
/>
</div>
) : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
)
}

View File

@ -11,3 +11,50 @@
}
}
}
.content {
width: 100%;
height: 100%;
position: relative;
.copy {
position: absolute;
display: none;
right: 0px;
top: 0px;
width: 24px;
height: 24px;
border-radius: 4px;
line-height: 24px;
text-align: center;
font-size: 16px;
box-shadow: rgba(0, 0, 0, 0.16) 0px 0px 5px 0px;
background: #fff;
z-index: 11;
cursor: pointer;
color: rgba(0, 0, 0, 0.45);
transition: all 0.3s ease;
&:hover {
color: #1890ff;
box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 5px 0px;
}
}
&:hover {
.copy {
display: block;
}
}
}
.copyTooltip {
:global {
.ant-tooltip-inner {
width: 100px;
}
}
}

View File

@ -179,11 +179,11 @@ class ClusterForm extends React.Component {
agent_credential_id:
values.agent_credential_id !== MANUAL_VALUE && isAgentMode
? values.agent_credential_id
: agent_credential_id,
agent_basic_auth: isAgentMode ? {
: undefined,
agent_basic_auth: {
username: values.agent_username,
password: values.agent_password,
} : agent_basic_auth,
},
metric_collection_mode: values.metric_collection_mode || 'agentless',
description: values.description,
@ -204,6 +204,7 @@ class ClusterForm extends React.Component {
if (this.clusterUUID) {
newVals.cluster_uuid = this.clusterUUID;
}
if (clusterConfig.editMode === "NEW") {
dispatch({
type: "clusterConfig/addCluster",

View File

@ -90,7 +90,7 @@ export default {
},
*updateCluster({ payload }, { call, put, select }) {
let res = yield call(updateClusterConfig, payload);
if (res.error) {
if (res?.error) {
return false;
}
let { data } = yield select((state) => state.clusterConfig);