mirror of
https://github.com/dromara/hertzbeat.git
synced 2026-09-17 18:19:02 +00:00
Compare commits
94
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac99171322 | ||
|
|
2aaaf45f1e | ||
|
|
28b3b804ef | ||
|
|
3a46ffdc2a | ||
|
|
0665a10e3b | ||
|
|
1d187d3128 | ||
|
|
5d63f46929 | ||
|
|
20c945edc6 | ||
|
|
af6754ffb2 | ||
|
|
e31101cfda | ||
|
|
7dd413d0b6 | ||
|
|
8fbbdef7c0 | ||
|
|
d9fe774315 | ||
|
|
e06b145786 | ||
|
|
2417b41437 | ||
|
|
83e54716c3 | ||
|
|
6c59691135 | ||
|
|
eda80f4ada | ||
|
|
87b08620cb | ||
|
|
d4134cfbfa | ||
|
|
c85fd696c7 | ||
|
|
74ed8e4ffb | ||
|
|
6315c9bf93 | ||
|
|
50e9885ccc | ||
|
|
3a2e531b02 | ||
|
|
5894c0f8f3 | ||
|
|
d63cdfa644 | ||
|
|
ec66e8a7a0 | ||
|
|
9312a94166 | ||
|
|
2e5161c5b7 | ||
|
|
fb5cad338a | ||
|
|
6d8cdf74c6 | ||
|
|
1938a07c78 | ||
|
|
90e53d32e5 | ||
|
|
3b8273f92a | ||
|
|
236d1dcd28 | ||
|
|
24f1722517 | ||
|
|
dfb6f56f79 | ||
|
|
9f709133e0 | ||
|
|
cad4c11116 | ||
|
|
d04ee14d3f | ||
|
|
11016879af | ||
|
|
f91da0204a | ||
|
|
cf83fc1f26 | ||
|
|
002398ad72 | ||
|
|
5a4a09b2eb | ||
|
|
6a895590d3 | ||
|
|
74804b7814 | ||
|
|
e305236142 | ||
|
|
8ecd5dee38 | ||
|
|
bb47eff80d | ||
|
|
ee6f9b8a28 | ||
|
|
850c0aab90 | ||
|
|
8bfcd82eb7 | ||
|
|
4281c4247f | ||
|
|
6ce7fb1797 | ||
|
|
3c0b275d25 | ||
|
|
c07bae1f07 | ||
|
|
7ae96c6ff9 | ||
|
|
a1c479b90a | ||
|
|
403956d71d | ||
|
|
a64e37eb1a | ||
|
|
710e3b87f4 | ||
|
|
701e5259c3 | ||
|
|
f0b7b502ee | ||
|
|
726a018a66 | ||
|
|
f6880d40fc | ||
|
|
4025ef51c0 | ||
|
|
1de9236865 | ||
|
|
8291a9a1ec | ||
|
|
27a857b8d7 | ||
|
|
d04d639d0c | ||
|
|
7980f4ce11 | ||
|
|
2fe9245417 | ||
|
|
5b0f7e5c4c | ||
|
|
0951fa5280 | ||
|
|
6a5167cf0d | ||
|
|
844d664cb4 | ||
|
|
8feaca1ca3 | ||
|
|
ae0de46eb7 | ||
|
|
55e0735fdb | ||
|
|
42b92b5c0c | ||
|
|
53ff5a1e88 | ||
|
|
599b415a04 | ||
|
|
7ab10818af | ||
|
|
35b43a0ef7 | ||
|
|
e096b2de6d | ||
|
|
5b93aab11b | ||
|
|
c54dfc261d | ||
|
|
5f142ca9e0 | ||
|
|
19f1e6793d | ||
|
|
35a2c9808b | ||
|
|
c0b1b3a389 | ||
|
|
fc4dd46c0f |
@@ -1,53 +0,0 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# This workflow will build a Java project with Maven
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
||||
|
||||
name: Monitor E2E Test
|
||||
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# trigger at 01:00 everyday
|
||||
- cron: '0 1 * * *'
|
||||
push:
|
||||
branches: [ action* ]
|
||||
jobs:
|
||||
check-license-header:
|
||||
name: Monitor E2E Test
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./script/ci/github-actions/setup-deps
|
||||
- name: Build with Maven
|
||||
run: mvn clean -B package -Prelease -Dmaven.test.skip=true --file pom.xml
|
||||
- name: Build Image
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ./dist
|
||||
file: ./script/docker/server/Dockerfile
|
||||
push: false
|
||||
tags: apache/hertzbeat:test
|
||||
- name: Run K8s Monitor E2E Test
|
||||
run: |
|
||||
sudo curl -L https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
|
||||
sudo chmod u+x /usr/local/bin/docker-compose
|
||||
cd e2e/k8s
|
||||
sudo docker-compose version
|
||||
sudo docker-compose up --exit-code-from testing --remove-orphans
|
||||
@@ -13,8 +13,6 @@ package-lock.json
|
||||
jdk/
|
||||
data/
|
||||
|
||||
!/e2e/**/data/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
{
|
||||
"globs": ["home/**/*.md"],
|
||||
"config": {
|
||||
"default": true,
|
||||
"MD001": true,
|
||||
@@ -40,9 +39,9 @@
|
||||
"MD047": true,
|
||||
"MD052": true
|
||||
},
|
||||
"ignores": [
|
||||
"**/node_modules/**",
|
||||
"**/target/**",
|
||||
"**/dist/**"
|
||||
"ignore": [
|
||||
"node_modules/",
|
||||
"target/",
|
||||
"dist/"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# This workflow will build a Java project with Maven
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
||||
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkakNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUzTkRVeU5ESTROamd3SGhjTk1qVXdOREl4TVRNME1UQTRXaGNOTXpVd05ERTVNVE0wTVRBNApXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUzTkRVeU5ESTROamd3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFUR2U2SW5hQko4RGRiQVdmZTRNM09yS3ZTR1IvWkROS0JJV2lMejZXckcKYmh4Mm5ncHJFQjhRYThEWDZocGRCVkI1RmN3L1hwTXZjRnNCeFh3MThPNkdvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVWRXOEFySENLNVQ2OFdJeG1BUnlTCkJFcHJ6N1F3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnYlhwYXV0c2RlTlJHNm4zRmhBRkkzbncvWUErYTBBdi8KUE9HZzNhYWRCSDBDSUU5NW4vQW5yOXV2Z0tyczVrakpGKzAwci96SWpFdnQxNmhkOGdabFEzb1IKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
|
||||
server: https://172.29.0.11:6443
|
||||
name: default
|
||||
contexts:
|
||||
- context:
|
||||
cluster: default
|
||||
user: default
|
||||
name: default
|
||||
current-context: default
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: default
|
||||
user:
|
||||
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrVENDQVRlZ0F3SUJBZ0lJZm5OdzUyby9ybDB3Q2dZSUtvWkl6ajBFQXdJd0l6RWhNQjhHQTFVRUF3d1kKYXpOekxXTnNhV1Z1ZEMxallVQXhOelExTWpReU9EWTRNQjRYRFRJMU1EUXlNVEV6TkRFd09Gb1hEVEkyTURReQpNVEV6TkRFd09Gb3dNREVYTUJVR0ExVUVDaE1PYzNsemRHVnRPbTFoYzNSbGNuTXhGVEFUQmdOVkJBTVRESE41CmMzUmxiVHBoWkcxcGJqQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJHa3UxWDQvRGNMU1h4cXcKSzczdGhMUFd5Y3JCendrL3ZMbzkzMTZ0dXNXRWE4WnNMdytnYk5wWEVOSFNxQk8zdGcrYUFSdGhWakF3OU5GaApZWXNtaTQyalNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmCkJnTlZIU01FR0RBV2dCU1B5dEJrYlFaRmtxSEpOaHlHNjZ1K3ZqbjNhakFLQmdncWhrak9QUVFEQWdOSUFEQkYKQWlFQXZQVC9ENGs2S29tUUxaZjRNMTEvd1psT3QyZkpGTGhIY3EyOHMvUUVLcndDSUZVMWVzTGFFTnRWc2xUeAo0QTFtNkt4eEpUbmdNM0M3UXJ5OUs0Z2hzVDA0Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkekNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdFkyeHAKWlc1MExXTmhRREUzTkRVeU5ESTROamd3SGhjTk1qVXdOREl4TVRNME1UQTRXaGNOTXpVd05ERTVNVE0wTVRBNApXakFqTVNFd0h3WURWUVFEREJock0zTXRZMnhwWlc1MExXTmhRREUzTkRVeU5ESTROamd3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFSeENGL2FuNVUvS0xEUk9VUlZxdllWTHFsdWxXaE8rMzJpc1lRU1hlYUgKdnRBWHo3dFE2MkhrazNIdXZudnFmditvQlBKVUlzS3ExTyt6aytLT3JWa2dvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVWo4clFaRzBHUlpLaHlUWWNodXVyCnZyNDU5Mm93Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnWmxiRkpoZWwzQiszQ2FyZjUrdE5taDhkWkRUOC9yVDAKR01VVXlBNzlWSUVDSVFDSUQ1NkVQbmtOa0xmZExEZXloclBGWks3VWRTc2x2QWZGN2N4czF1MDBxZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
|
||||
client-key-data: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUpMOGJrS1FMbEdZWUE0Ny9Lek9CRm5iZnI0NGQ5VndUWXlPT0dJdnNLOXNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFYVM3VmZqOE53dEpmR3JBcnZlMkVzOWJKeXNIUENUKzh1ajNmWHEyNnhZUnJ4bXd2RDZCcwoybGNRMGRLb0U3ZTJENW9CRzJGV01ERDAwV0ZoaXlhTGpRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
|
||||
@@ -1 +0,0 @@
|
||||
K10800b7e6b1f6390067e433bd30dff5f925893b60074f8c36f7725bfdce6a45917::server:ca7449eb0dddd8a4bb6e5ca86e2d0cdd
|
||||
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"detected": true,
|
||||
"monitor": {
|
||||
"intervals": 10,
|
||||
"tags": [],
|
||||
"app": "kubernetes",
|
||||
"host": "172.29.0.11",
|
||||
"name": "Brave_Stingray_55yR"
|
||||
},
|
||||
"collector": "",
|
||||
"params": [
|
||||
{
|
||||
"display": true,
|
||||
"field": "host",
|
||||
"type": 1,
|
||||
"paramValue": "172.29.0.11"
|
||||
},
|
||||
{
|
||||
"display": true,
|
||||
"field": "port",
|
||||
"type": 0,
|
||||
"paramValue": 6443
|
||||
},
|
||||
{
|
||||
"display": true,
|
||||
"field": "authType",
|
||||
"type": 1,
|
||||
"paramValue": "Bearer Token"
|
||||
},
|
||||
{
|
||||
"display": true,
|
||||
"field": "token",
|
||||
"type": 1,
|
||||
"paramValue": "#TOKEN#"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
#!api-testing
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# yaml-language-server: $schema=https://linuxsuren.github.io/api-testing/api-testing-schema.json
|
||||
name: hertzbeat
|
||||
api: |
|
||||
{{default "http://localhost:1157" (env "SERVER")}}
|
||||
param:
|
||||
monitorHTTP: "{{randAlpha 6}}"
|
||||
monitorSiteMap: "{{randAlpha 6}}"
|
||||
monitorPort: "{{randAlpha 6}}"
|
||||
monitorSSL: "{{randAlpha 6}}"
|
||||
monitorPing: "{{randAlpha 6}}"
|
||||
monitorUDP: "{{randAlpha 6}}"
|
||||
monitorWebsite: "{{randAlpha 6}}"
|
||||
monitorFTP: "{{randAlpha 6}}"
|
||||
tagName: "{{randAlpha 3}}"
|
||||
items:
|
||||
- name: login
|
||||
request:
|
||||
api: /api/account/auth/form
|
||||
method: POST
|
||||
header:
|
||||
Content-type: application/json
|
||||
body: |
|
||||
{
|
||||
"type": 0,
|
||||
"identifier": "admin",
|
||||
"credential": "hertzbeat"
|
||||
}
|
||||
expect:
|
||||
bodyFieldsExpect:
|
||||
code: "0"
|
||||
- name: detectMonitor
|
||||
request:
|
||||
api: /api/monitor/detect
|
||||
method: POST
|
||||
header:
|
||||
Authorization: Bearer {{.login.data.token}}
|
||||
Content-type: application/json
|
||||
bodyFromFile: data/tmp.json
|
||||
expect:
|
||||
statusCode: 200
|
||||
- name: createMonitor
|
||||
request:
|
||||
api: /api/monitor
|
||||
method: POST
|
||||
header:
|
||||
Authorization: Bearer {{.login.data.token}}
|
||||
Content-type: application/json
|
||||
bodyFromFile: data/tmp.json
|
||||
expect:
|
||||
statusCode: 200
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
version: '3.8'
|
||||
services:
|
||||
init:
|
||||
image: bitnami/kubectl:latest
|
||||
privileged: true
|
||||
depends_on:
|
||||
k3s:
|
||||
condition: service_healthy
|
||||
entrypoint: /bin/bash
|
||||
networks:
|
||||
k3s_network:
|
||||
ipv4_address: 172.29.0.10
|
||||
volumes:
|
||||
- ./data/config/k3s.yaml:/.kube/config
|
||||
- ./data:/testcase
|
||||
command:
|
||||
- -c
|
||||
- |
|
||||
# Wait for k3s to start
|
||||
until kubectl cluster-info; do sleep 1; done
|
||||
sleep 5
|
||||
# Create serviceaccount and clusterrolebinding
|
||||
kubectl create serviceaccount -n kube-system cluster-admin-sa
|
||||
kubectl create clusterrolebinding -n kube-system cluster-admin-binding --clusterrole=cluster-admin --serviceaccount=kube-system:cluster-admin-sa
|
||||
cd /testcase
|
||||
kubectl create token cluster-admin-sa -n kube-system --duration=24h
|
||||
echo $(sed "s/#TOKEN#/$(echo $(kubectl create token cluster-admin-sa -n kube-system --duration=24h))/g" create_k8s_monitor_data.json)>tmp.json
|
||||
# install metrics server
|
||||
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
|
||||
sleep 30
|
||||
tail -f /dev/null
|
||||
k3s:
|
||||
image: rancher/k3s:v1.31.0-k3s1
|
||||
container_name: k3s
|
||||
privileged: true
|
||||
ports:
|
||||
- "6443:6443"
|
||||
- "80:80"
|
||||
command: server --bind-address 172.29.0.11
|
||||
networks:
|
||||
k3s_network:
|
||||
ipv4_address: 172.29.0.11
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL",
|
||||
"kubectl cluster-info && echo $(cat /var/lib/rancher/k3s/server/node-token)>node-token.txt" ]
|
||||
interval: 3s
|
||||
retries: 30
|
||||
start_period: 10s
|
||||
timeout: 10s
|
||||
volumes:
|
||||
- ./data/config/k3s.yaml:/etc/rancher/k3s/k3s.yaml
|
||||
- ./data/config/node-token.txt:/node-token.txt
|
||||
testing:
|
||||
image: ghcr.io/linuxsuren/api-testing:v0.0.17
|
||||
environment:
|
||||
SERVER: http://hertzbeat:1157
|
||||
container_name: e2e-testing
|
||||
volumes:
|
||||
- ./data/:/work/data/
|
||||
- ./data/testsuite.yaml:/work/testsuite.yaml
|
||||
command: atest run -p /work/testsuite.yaml --level debug --thread 3
|
||||
networks:
|
||||
k3s_network:
|
||||
ipv4_address: 172.29.0.12
|
||||
depends_on:
|
||||
checker:
|
||||
condition: service_healthy
|
||||
k3s:
|
||||
condition: service_healthy
|
||||
links:
|
||||
- hertzbeat
|
||||
- checker
|
||||
- k3s
|
||||
checker:
|
||||
image: alpine:latest
|
||||
container_name: e2e-checker
|
||||
command: sh -c "apk add --update curl && tail -f /dev/null"
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL",
|
||||
"curl -s -X POST -H \"Content-Type: application/json\" -d '{\"type\": 0,\"identifier\": \"admin\",\"credential\": \"hertzbeat\"}' http://hertzbeat:1157/api/account/auth/form | grep -q token"
|
||||
]
|
||||
interval: 3s
|
||||
timeout: 60s
|
||||
retries: 10
|
||||
start_period: 10s
|
||||
networks:
|
||||
k3s_network:
|
||||
ipv4_address: 172.29.0.13
|
||||
depends_on:
|
||||
- init
|
||||
hertzbeat:
|
||||
image: apache/hertzbeat:test
|
||||
container_name: e2e-hertzbeat
|
||||
volumes:
|
||||
- ./logs/:/opt/hertzbeat/logs/
|
||||
ports:
|
||||
- "1157:1157"
|
||||
networks:
|
||||
k3s_network:
|
||||
ipv4_address: 172.29.0.14
|
||||
networks:
|
||||
k3s_network:
|
||||
driver: bridge
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.29.0.0/16
|
||||
+2
-2
@@ -220,11 +220,11 @@ public class RealTimeAlertCalculator {
|
||||
fingerPrints.putAll(commonFingerPrints);
|
||||
for (int index = 0; index < valueRow.getColumnsList().size(); index++) {
|
||||
String valueStr = valueRow.getColumns(index);
|
||||
final CollectRep.Field field = fields.get(index);
|
||||
if (CommonConstants.NULL_VALUE.equals(valueStr)) {
|
||||
fieldValueMap.put(field.getName(), null);
|
||||
continue;
|
||||
}
|
||||
|
||||
final CollectRep.Field field = fields.get(index);
|
||||
final int fieldType = field.getType();
|
||||
|
||||
if (fieldType == CommonConstants.TYPE_NUMBER) {
|
||||
|
||||
-133
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.alert.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* uptime-kuma alert content entity
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class UptimeKumaExternAlert {
|
||||
|
||||
private Heartbeat heartbeat;
|
||||
|
||||
private Monitor monitor;
|
||||
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* Monitor information
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class Heartbeat {
|
||||
@JsonProperty("monitorID")
|
||||
private int monitorId;
|
||||
private int status;
|
||||
private String time;
|
||||
private String msg;
|
||||
private boolean important;
|
||||
private int duration;
|
||||
private String timezone;
|
||||
private String timezoneOffset;
|
||||
private String localDateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitor information
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class Monitor {
|
||||
private int id;
|
||||
private String name;
|
||||
private String description;
|
||||
private String pathName;
|
||||
private String parent;
|
||||
@JsonProperty("childrenIDs")
|
||||
private List<String> childrenIds;
|
||||
private String url;
|
||||
private String method;
|
||||
private String hostname;
|
||||
private String port;
|
||||
@JsonProperty("maxretries")
|
||||
private int maxRetries;
|
||||
private int weight;
|
||||
private boolean active;
|
||||
private boolean forceInactive;
|
||||
private String type;
|
||||
private int timeout;
|
||||
private int interval;
|
||||
private int retryInterval;
|
||||
private int resendInterval;
|
||||
private String keyword;
|
||||
private boolean invertKeyword;
|
||||
private boolean expiryNotification;
|
||||
private boolean ignoreTls;
|
||||
private boolean upsideDown;
|
||||
private int packetSize;
|
||||
@JsonProperty("maxredirects")
|
||||
private int maxRedirects;
|
||||
@JsonProperty("accepted_statuscodes")
|
||||
private List<String> acceptedStatusCodes;
|
||||
private String dnsResolveType;
|
||||
private String dnsResolveServer;
|
||||
private String dnsLastResult;
|
||||
private String dockerContainer;
|
||||
private String dockerHost;
|
||||
private String proxyId;
|
||||
private Map<String, Object> notificationIDList;
|
||||
private List<String> tags;
|
||||
private boolean maintenance;
|
||||
private String mqttTopic;
|
||||
private String mqttSuccessMessage;
|
||||
private String databaseQuery;
|
||||
private String authMethod;
|
||||
private String grpcUrl;
|
||||
private String grpcProtobuf;
|
||||
private String grpcMethod;
|
||||
private String grpcServiceName;
|
||||
private boolean grpcEnableTls;
|
||||
private String radiusCalledStationId;
|
||||
private String radiusCallingStationId;
|
||||
private String game;
|
||||
private boolean gamedigGivenPortOnly;
|
||||
private String httpBodyEncoding;
|
||||
private String jsonPath;
|
||||
private String expectedValue;
|
||||
private String kafkaProducerTopic;
|
||||
private List<String> kafkaProducerBrokers;
|
||||
private boolean kafkaProducerSsl;
|
||||
private boolean kafkaProducerAllowAutoTopicCreation;
|
||||
private String kafkaProducerMessage;
|
||||
private String screenshot;
|
||||
private boolean includeSensitiveData;
|
||||
}
|
||||
}
|
||||
+66
-64
@@ -52,84 +52,86 @@ final class DbAlertStoreHandlerImpl implements AlertStoreHandler {
|
||||
log.error("The Group Alerts is empty, ignore store");
|
||||
return groupAlert;
|
||||
}
|
||||
// Process individual alerts
|
||||
// 1. Find existing alert group
|
||||
GroupAlert existGroupAlert = groupAlertDao.findByGroupKey(groupAlert.getGroupKey());
|
||||
|
||||
// 2. Process individual alerts
|
||||
Set<String> alertFingerprints = new HashSet<>(8);
|
||||
|
||||
List<SingleAlert> originalAlerts = groupAlert.getAlerts();
|
||||
List<SingleAlert> newAlerts = new ArrayList<>();
|
||||
|
||||
|
||||
for (SingleAlert singleAlert : originalAlerts) {
|
||||
synchronized (singleAlert.getFingerprint().intern()) {
|
||||
SingleAlert existAlert = singleAlertDao.findByFingerprint(singleAlert.getFingerprint());
|
||||
if (existAlert != null) {
|
||||
// Update the existing alert with the ID and creation time from the database
|
||||
singleAlert.setId(existAlert.getId());
|
||||
singleAlert.setGmtCreate(existAlert.getGmtCreate());
|
||||
// Status transition logic
|
||||
if (CommonConstants.ALERT_STATUS_FIRING.equals(singleAlert.getStatus())) {
|
||||
// If the alert is firing and the existing alert is not resolved, update the start time and trigger times
|
||||
if (!CommonConstants.ALERT_STATUS_RESOLVED.equals(existAlert.getStatus())) {
|
||||
singleAlert.setStartAt(existAlert.getStartAt());
|
||||
int triggerTimes = Optional.ofNullable(existAlert.getTriggerTimes()).orElse(1)
|
||||
+ Optional.ofNullable(singleAlert.getTriggerTimes()).orElse(1);
|
||||
singleAlert.setTriggerTimes(triggerTimes);
|
||||
}
|
||||
} else if (CommonConstants.ALERT_STATUS_RESOLVED.equals(singleAlert.getStatus())) {
|
||||
// If the alert is resolved, set the end time (if not already set) and copy other fields from the existing alert
|
||||
if (singleAlert.getEndAt() == null) {
|
||||
singleAlert.setEndAt(System.currentTimeMillis());
|
||||
}
|
||||
SingleAlert existAlert = singleAlertDao.findByFingerprint(singleAlert.getFingerprint());
|
||||
|
||||
if (existAlert != null) {
|
||||
// Update the existing alert with the ID and creation time from the database
|
||||
singleAlert.setId(existAlert.getId());
|
||||
singleAlert.setGmtCreate(existAlert.getGmtCreate());
|
||||
|
||||
// Status transition logic
|
||||
if (CommonConstants.ALERT_STATUS_FIRING.equals(singleAlert.getStatus())) {
|
||||
// If the alert is firing and the existing alert is not resolved, update the start time and trigger times
|
||||
if (!CommonConstants.ALERT_STATUS_RESOLVED.equals(existAlert.getStatus())) {
|
||||
singleAlert.setStartAt(existAlert.getStartAt());
|
||||
singleAlert.setActiveAt(existAlert.getActiveAt());
|
||||
singleAlert.setTriggerTimes(existAlert.getTriggerTimes());
|
||||
int triggerTimes = Optional.ofNullable(existAlert.getTriggerTimes()).orElse(1)
|
||||
+ Optional.ofNullable(singleAlert.getTriggerTimes()).orElse(1);
|
||||
singleAlert.setTriggerTimes(triggerTimes);
|
||||
}
|
||||
} else if (CommonConstants.ALERT_STATUS_RESOLVED.equals(singleAlert.getStatus())) {
|
||||
// If the alert is resolved, set the end time (if not already set) and copy other fields from the existing alert
|
||||
if (singleAlert.getEndAt() == null) {
|
||||
singleAlert.setEndAt(System.currentTimeMillis());
|
||||
}
|
||||
singleAlert.setStartAt(existAlert.getStartAt());
|
||||
singleAlert.setActiveAt(existAlert.getActiveAt());
|
||||
singleAlert.setTriggerTimes(existAlert.getTriggerTimes());
|
||||
}
|
||||
SingleAlert savedSingleAlert = singleAlertDao.save(singleAlert);
|
||||
newAlerts.add(savedSingleAlert);
|
||||
alertFingerprints.add(savedSingleAlert.getFingerprint());
|
||||
}
|
||||
SingleAlert savedSingleAlert = singleAlertDao.save(singleAlert);
|
||||
newAlerts.add(savedSingleAlert);
|
||||
alertFingerprints.add(savedSingleAlert.getFingerprint());
|
||||
}
|
||||
groupAlert.setAlerts(newAlerts);
|
||||
// Find existing alert group
|
||||
synchronized (groupAlert.getGroupKey().intern()) {
|
||||
GroupAlert existGroupAlert = groupAlertDao.findByGroupKey(groupAlert.getGroupKey());
|
||||
// Process resolved alerts
|
||||
if (existGroupAlert != null) {
|
||||
List<String> existFingerprints = existGroupAlert.getAlertFingerprints();
|
||||
if (existFingerprints != null) {
|
||||
alertFingerprints.addAll(existFingerprints);
|
||||
// 3. Process resolved alerts
|
||||
if (existGroupAlert != null) {
|
||||
List<String> existFingerprints = existGroupAlert.getAlertFingerprints();
|
||||
if (existFingerprints != null) {
|
||||
alertFingerprints.addAll(existFingerprints);
|
||||
}
|
||||
// Merge group information
|
||||
groupAlert.setId(existGroupAlert.getId());
|
||||
groupAlert.setGmtCreate(existGroupAlert.getGmtCreate());
|
||||
// Merge other historical information to preserve
|
||||
Map<String, String> existCommonLabels = existGroupAlert.getCommonLabels();
|
||||
if (existCommonLabels != null) {
|
||||
Map<String, String> commonLabels = groupAlert.getCommonLabels();
|
||||
if (commonLabels != null) {
|
||||
// filter common label in commonLabels and existCommonLabels
|
||||
commonLabels = commonLabels.entrySet().stream()
|
||||
.filter(entry -> existCommonLabels.containsKey(entry.getKey()))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
groupAlert.setCommonLabels(commonLabels);
|
||||
}
|
||||
// Merge group information
|
||||
groupAlert.setId(existGroupAlert.getId());
|
||||
groupAlert.setGmtCreate(existGroupAlert.getGmtCreate());
|
||||
// Merge other historical information to preserve
|
||||
Map<String, String> existCommonLabels = existGroupAlert.getCommonLabels();
|
||||
if (existCommonLabels != null) {
|
||||
Map<String, String> commonLabels = groupAlert.getCommonLabels();
|
||||
if (commonLabels != null) {
|
||||
// filter common label in commonLabels and existCommonLabels
|
||||
commonLabels = commonLabels.entrySet().stream()
|
||||
.filter(entry -> existCommonLabels.containsKey(entry.getKey()))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
groupAlert.setCommonLabels(commonLabels);
|
||||
}
|
||||
}
|
||||
Map<String, String> existCommonAnnotations = existGroupAlert.getCommonAnnotations();
|
||||
if (existCommonAnnotations != null) {
|
||||
Map<String, String> commonAnnotations = groupAlert.getCommonAnnotations();
|
||||
if (commonAnnotations != null) {
|
||||
// filter common annotation in commonAnnotations and existCommonAnnotations
|
||||
commonAnnotations = commonAnnotations.entrySet().stream()
|
||||
.filter(entry -> existCommonAnnotations.containsKey(entry.getKey()))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
groupAlert.setCommonAnnotations(commonAnnotations);
|
||||
}
|
||||
}
|
||||
Map<String, String> existCommonAnnotations = existGroupAlert.getCommonAnnotations();
|
||||
if (existCommonAnnotations != null) {
|
||||
Map<String, String> commonAnnotations = groupAlert.getCommonAnnotations();
|
||||
if (commonAnnotations != null) {
|
||||
// filter common annotation in commonAnnotations and existCommonAnnotations
|
||||
commonAnnotations = commonAnnotations.entrySet().stream()
|
||||
.filter(entry -> existCommonAnnotations.containsKey(entry.getKey()))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
groupAlert.setCommonAnnotations(commonAnnotations);
|
||||
}
|
||||
}
|
||||
// Save alert group
|
||||
groupAlert.setAlertFingerprints(alertFingerprints.stream().toList());
|
||||
GroupAlert savedGroupAlert = groupAlertDao.save(groupAlert);
|
||||
savedGroupAlert.setAlerts(groupAlert.getAlerts());
|
||||
return savedGroupAlert;
|
||||
}
|
||||
|
||||
// 4. Save alert group
|
||||
groupAlert.setAlertFingerprints(alertFingerprints.stream().toList());
|
||||
GroupAlert savedGroupAlert = groupAlertDao.save(groupAlert);
|
||||
savedGroupAlert.setAlerts(groupAlert.getAlerts());
|
||||
return savedGroupAlert;
|
||||
}
|
||||
}
|
||||
|
||||
-118
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.alert.service.impl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.dto.UptimeKumaExternAlert;
|
||||
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
|
||||
import org.apache.hertzbeat.alert.service.ExternAlertService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* uptime-kuma external alarm service impl
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class UptimeKumaExternAlertServiceImpl implements ExternAlertService {
|
||||
|
||||
@Autowired
|
||||
private AlarmCommonReduce alarmCommonReduce;
|
||||
|
||||
@Override
|
||||
public void addExternAlert(String content) {
|
||||
UptimeKumaExternAlert alert = JsonUtil.fromJson(content, UptimeKumaExternAlert.class);
|
||||
if (alert == null) {
|
||||
log.warn("parse extern alert content failed! content: {}", content);
|
||||
return;
|
||||
}
|
||||
SingleAlert singleAlert = new UptimeKumaAlertConverter().convert(alert);
|
||||
alarmCommonReduce.reduceAndSendAlarm(singleAlert);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converter: UptimeKuma alert to SingleAlert
|
||||
*/
|
||||
public static class UptimeKumaAlertConverter {
|
||||
|
||||
/**
|
||||
* Convert UptimeKuma alert to SingleAlert
|
||||
*/
|
||||
public SingleAlert convert(UptimeKumaExternAlert alert) {
|
||||
// build basic info
|
||||
SingleAlert.SingleAlertBuilder builder = SingleAlert.builder()
|
||||
.status(convertStatus(alert.getHeartbeat().getStatus()))
|
||||
.startAt(parseTime(alert.getHeartbeat().getTime()))
|
||||
.activeAt(parseTime(alert.getHeartbeat().getTime()))
|
||||
.triggerTimes(1);
|
||||
|
||||
// build labels
|
||||
Map<String, String> labels = new HashMap<>();
|
||||
labels.put("__source__", "uptime_kuma");
|
||||
labels.put("monitor_id", String.valueOf(alert.getMonitor().getId()));
|
||||
labels.put("monitor_name", alert.getMonitor().getName());
|
||||
|
||||
// build annotations
|
||||
Map<String, String> annotations = new HashMap<>();
|
||||
annotations.put("description", alert.getMonitor().getDescription());
|
||||
annotations.put("message", alert.getHeartbeat().getMsg());
|
||||
annotations.put("important", String.valueOf(alert.getHeartbeat().isImportant()));
|
||||
|
||||
return builder
|
||||
.labels(labels)
|
||||
.annotations(annotations)
|
||||
.content(buildContent(alert))
|
||||
.build();
|
||||
}
|
||||
|
||||
private String buildContent(UptimeKumaExternAlert alert) {
|
||||
return String.format("Monitor [%s] %s: %s",
|
||||
alert.getMonitor().getName(),
|
||||
alert.getMonitor().getDescription(),
|
||||
alert.getHeartbeat().getMsg());
|
||||
}
|
||||
|
||||
private String convertStatus(int status) {
|
||||
// uptime kuma status: 1-up, 0-down, 2-pending
|
||||
return status == 1 ? CommonConstants.ALERT_STATUS_RESOLVED : CommonConstants.ALERT_STATUS_FIRING;
|
||||
}
|
||||
|
||||
private Long parseTime(String timeStr) {
|
||||
try {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
return sdf.parse(timeStr).getTime();
|
||||
} catch (ParseException e) {
|
||||
log.error("Failed to parse time: {}", timeStr);
|
||||
throw new IllegalArgumentException("Failed to parse time: " + timeStr, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String supportSource() {
|
||||
return "uptime-kuma";
|
||||
}
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.alert.service.impl;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
|
||||
import org.apache.hertzbeat.alert.service.ExternAlertService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* zabbix external alarm service impl
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ZabbixExternAlertServiceImpl implements ExternAlertService {
|
||||
|
||||
@Autowired
|
||||
private AlarmCommonReduce alarmCommonReduce;
|
||||
|
||||
@Override
|
||||
public void addExternAlert(String content) {
|
||||
SingleAlert alert = JsonUtil.fromJson(content, SingleAlert.class);
|
||||
if (alert == null) {
|
||||
log.warn("parse extern alert content failed! content: {}", content);
|
||||
return;
|
||||
}
|
||||
alarmCommonReduce.reduceAndSendAlarm(alert);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String supportSource() {
|
||||
return "zabbix";
|
||||
}
|
||||
}
|
||||
+4
-5
@@ -21,7 +21,6 @@ import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Alarm template keyword matching replacement engine tool
|
||||
@@ -39,11 +38,11 @@ public final class AlertTemplateUtil {
|
||||
private static final Pattern PATTERN = Pattern.compile("\\$\\{(\\w+)\\}");
|
||||
|
||||
public static String render(String template, Map<String, Object> replaceData) {
|
||||
if (!StringUtils.hasText(template)) {
|
||||
return template;
|
||||
if (template == null) {
|
||||
return null;
|
||||
}
|
||||
if (replaceData == null) {
|
||||
log.warn("The render replace data is null.");
|
||||
log.warn("The replaceData is null.");
|
||||
return template;
|
||||
}
|
||||
try {
|
||||
@@ -51,7 +50,7 @@ public final class AlertTemplateUtil {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
while (matcher.find()) {
|
||||
Object objectValue = replaceData.getOrDefault(matcher.group(1), "NullValue");
|
||||
String value = objectValue != null ? objectValue.toString() : "NullValue";
|
||||
String value = objectValue.toString();
|
||||
matcher.appendReplacement(builder, Matcher.quoteReplacement(value));
|
||||
}
|
||||
matcher.appendTail(builder);
|
||||
|
||||
-154
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.alert.calculate;
|
||||
|
||||
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
|
||||
import org.apache.hertzbeat.alert.service.DataSourceService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.NullAndEmptySource;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertAll;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Test case for {@link PeriodicAlertCalculator}
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PeriodicAlertCalculatorTest {
|
||||
|
||||
@Mock
|
||||
private DataSourceService dataSourceService;
|
||||
|
||||
@Mock
|
||||
private AlarmCommonReduce alarmCommonReduce;
|
||||
|
||||
@Mock
|
||||
private AlarmCacheManager alarmCacheManager;
|
||||
|
||||
@InjectMocks
|
||||
private PeriodicAlertCalculator periodicAlertCalculator;
|
||||
|
||||
private AlertDefine rule;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
rule = AlertDefine.builder()
|
||||
.id(1L)
|
||||
.name("high_cpu_usage")
|
||||
.type("periodic")
|
||||
.expr("usage>90")
|
||||
.times(1)
|
||||
.labels(Map.of("team", "test-team", "priority", "P1"))
|
||||
.annotations(Map.of("summary", "High CPU usage detected"))
|
||||
.template("High CPU usage (${__value__}%)")
|
||||
.datasource("PROMETHEUS")
|
||||
.enable(true)
|
||||
.period(300)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTriggerAlertWhenMatchThreshold() {
|
||||
reset(alarmCacheManager);
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("__value__", 95.0); // Non-null, matched with threshold
|
||||
result.put("__timestamp__", System.currentTimeMillis());
|
||||
when(dataSourceService.calculate(anyString(), anyString())).thenReturn(List.of(result));
|
||||
when(alarmCacheManager.getPending(anyString())).thenReturn(null);
|
||||
periodicAlertCalculator.calculate(rule);
|
||||
// Verify that putFiring is called
|
||||
ArgumentCaptor<String> idCaptor = ArgumentCaptor.forClass(String.class);
|
||||
ArgumentCaptor<SingleAlert> alertCaptor = ArgumentCaptor.forClass(SingleAlert.class);
|
||||
verify(alarmCacheManager).putFiring(idCaptor.capture(), alertCaptor.capture());
|
||||
// Assertion alarm status and content
|
||||
SingleAlert alert = alertCaptor.getValue();
|
||||
assertAll(() -> assertEquals(CommonConstants.ALERT_STATUS_FIRING, alert.getStatus()),
|
||||
() -> assertEquals("High CPU usage (95.0%)", alert.getContent()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNoTriggerAlertWhenNotMatchThreshold() {
|
||||
reset(alarmCacheManager);
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("__value__", null); // null, not matched with threshold
|
||||
result.put("__timestamp__", System.currentTimeMillis());
|
||||
when(dataSourceService.calculate(anyString(), anyString())).thenReturn(List.of(result));
|
||||
periodicAlertCalculator.calculate(rule);
|
||||
verify(alarmCacheManager, times(0)).putFiring(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testResolveFiringAlert() {
|
||||
reset(alarmCacheManager);
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("__value__", null); // null, not matched with threshold
|
||||
result.put("__timestamp__", System.currentTimeMillis());
|
||||
SingleAlert pendingAlert = SingleAlert.builder()
|
||||
.status(CommonConstants.ALERT_STATUS_FIRING)
|
||||
.triggerTimes(2).startAt(System.currentTimeMillis() - 60000)
|
||||
.activeAt(System.currentTimeMillis() - 30000)
|
||||
.build();
|
||||
when(alarmCacheManager.removeFiring(anyString())).thenReturn(pendingAlert);
|
||||
when(dataSourceService.calculate(anyString(), anyString())).thenReturn(List.of(result));
|
||||
periodicAlertCalculator.calculate(rule);
|
||||
ArgumentCaptor<SingleAlert> resolvedCaptor = ArgumentCaptor.forClass(SingleAlert.class);
|
||||
verify(alarmCommonReduce).reduceAndSendAlarm(resolvedCaptor.capture());
|
||||
SingleAlert resolvedAlert = resolvedCaptor.getValue();
|
||||
assertAll(() -> assertEquals(CommonConstants.ALERT_STATUS_RESOLVED, resolvedAlert.getStatus()),
|
||||
() -> assertNotNull(resolvedAlert.getEndAt()),
|
||||
() -> assertTrue(resolvedAlert.getEndAt() > resolvedAlert.getStartAt()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@NullAndEmptySource
|
||||
void testSkipWhenDataSourceResultInvalid(List<Map<String, Object>> results) {
|
||||
when(dataSourceService.calculate(anyString(), anyString())).thenReturn(results);
|
||||
periodicAlertCalculator.calculate(rule);
|
||||
verifyNoInteractions(alarmCacheManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandleDisabledRule() {
|
||||
rule.setEnable(false);
|
||||
periodicAlertCalculator.calculate(rule);
|
||||
verifyNoInteractions(dataSourceService);
|
||||
}
|
||||
}
|
||||
+1
-4
@@ -57,7 +57,6 @@ class DbAlertStoreHandlerImplTest {
|
||||
public void setUp() {
|
||||
groupAlert = new GroupAlert();
|
||||
singleAlert = new SingleAlert();
|
||||
singleAlert.setFingerprint("test-fingerprint");
|
||||
List<SingleAlert> alerts = new ArrayList<>();
|
||||
alerts.add(singleAlert);
|
||||
groupAlert.setAlerts(alerts);
|
||||
@@ -78,13 +77,11 @@ class DbAlertStoreHandlerImplTest {
|
||||
when(groupAlertDao.findByGroupKey(groupKey)).thenReturn(null);
|
||||
|
||||
SingleAlert savedSingleAlert = new SingleAlert();
|
||||
savedSingleAlert.setFingerprint("test-finger");
|
||||
when(singleAlertDao.save(any(SingleAlert.class))).thenReturn(savedSingleAlert);
|
||||
|
||||
GroupAlert savedGroupAlert = new GroupAlert();
|
||||
savedGroupAlert.setGroupKey(groupKey);
|
||||
when(groupAlertDao.save(any(GroupAlert.class))).thenReturn(savedGroupAlert);
|
||||
|
||||
|
||||
dbAlertStoreHandler.store(groupAlert);
|
||||
|
||||
verify(singleAlertDao).save(any(SingleAlert.class));
|
||||
|
||||
-2
@@ -383,8 +383,6 @@ public class JdbcCommonCollect extends AbstractCollect {
|
||||
case "oracle" -> "jdbc:oracle:thin:@" + host + ":" + port
|
||||
+ "/" + (jdbcProtocol.getDatabase() == null ? "" : jdbcProtocol.getDatabase());
|
||||
case "dm" -> "jdbc:dm://" + host + ":" + port;
|
||||
case "testcontainers" -> "jdbc:tc:" + host + ":" + port
|
||||
+ ":///" + (jdbcProtocol.getDatabase() == null ? "" : jdbcProtocol.getDatabase()) + "?user=root&password=root";
|
||||
default -> throw new IllegalArgumentException("Not support database platform: " + jdbcProtocol.getPlatform());
|
||||
};
|
||||
}
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ public class HttpSdCollectImpl extends AbstractCollect {
|
||||
@Override
|
||||
public void collect(CollectRep.MetricsData.Builder builder, Metrics metrics) {
|
||||
List<ConnectionConfig> configList = Lists.newArrayList();
|
||||
HttpUriRequest request = RequestBuilder.get().setUri(metrics.getHttp_sd().getUrl()).build();
|
||||
HttpUriRequest request = RequestBuilder.get().setUri(metrics.getSdProtocol().getSdSource()).build();
|
||||
|
||||
try (CloseableHttpResponse response = CommonHttpClient.getHttpClient().execute(request)) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
|
||||
+1
-2
@@ -281,8 +281,7 @@ public class CommonDispatcher implements MetricsTaskDispatch, CollectDataDispatc
|
||||
}
|
||||
// If it is an asynchronous periodic cyclic task, directly response the collected data
|
||||
if (job.isSd()) {
|
||||
CollectRep.MetricsData sdMetricsData = CollectRep.MetricsData.newBuilder(metricsData).build();
|
||||
commonDataQueue.sendServiceDiscoveryData(sdMetricsData);
|
||||
commonDataQueue.sendServiceDiscoveryData(metricsData);
|
||||
}
|
||||
commonDataQueue.sendMetricsData(metricsData);
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -126,7 +126,7 @@ public interface DispatchConstants {
|
||||
/**
|
||||
* protocol http sd
|
||||
*/
|
||||
String PROTOCOL_HTTP_SD = "http_sd";
|
||||
String PROTOCOL_HTTP_SD = "httpsd";
|
||||
/**
|
||||
* protocol redfish
|
||||
*/
|
||||
|
||||
+2
-2
@@ -17,10 +17,10 @@
|
||||
|
||||
package org.apache.hertzbeat.collector.dispatch.timer;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -87,7 +87,7 @@ public class TimerDispatcher implements TimerDispatch, DisposableBean {
|
||||
for (Metrics metric : addJob.getMetrics()) {
|
||||
metric.setInterval(0L);
|
||||
}
|
||||
addJob.setIntervals(new ConcurrentLinkedDeque<>(List.of(0L)));
|
||||
addJob.setIntervals(new LinkedList<>(List.of(0L)));
|
||||
Timeout timeout = wheelTimer.newTimeout(timerJob, addJob.getInterval(), TimeUnit.SECONDS);
|
||||
currentTempTaskMap.put(addJob.getId(), timeout);
|
||||
eventListeners.put(addJob.getId(), eventListener);
|
||||
|
||||
+107
-4
@@ -73,14 +73,14 @@ public interface CommonConstants {
|
||||
byte MONITOR_DOWN_CODE = 0x02;
|
||||
|
||||
/**
|
||||
* scrape type static
|
||||
* Monitor bind type, 0: monitor that auto-created by sd
|
||||
*/
|
||||
String SCRAPE_STATIC = "static";
|
||||
byte MONITOR_BIND_TYPE_SD_SUB_MONITOR = 0x00;
|
||||
|
||||
/**
|
||||
* scrape type
|
||||
* Monitor bind type, 1: the main monitor of sd
|
||||
*/
|
||||
String SCRAPE_HTTP_SD = "http_sd";
|
||||
byte MONITOR_BIND_TYPE_SD_MAIN_MONITOR = 0x01;
|
||||
|
||||
/**
|
||||
* label key: instance
|
||||
@@ -192,6 +192,11 @@ public interface CommonConstants {
|
||||
*/
|
||||
String PROM_VALUE = "value";
|
||||
|
||||
/**
|
||||
* Monitor total availability metrics
|
||||
*/
|
||||
String AVAILABILITY = "availability";
|
||||
|
||||
/**
|
||||
* Parameter Type Number
|
||||
*/
|
||||
@@ -217,6 +222,104 @@ public interface CommonConstants {
|
||||
*/
|
||||
byte PARAM_TYPE_ARRAY = 4;
|
||||
|
||||
/**
|
||||
* Authentication type Account password
|
||||
*/
|
||||
byte AUTH_TYPE_PASSWORD = 1;
|
||||
|
||||
/**
|
||||
* Authentication type GitHub three-party login
|
||||
*/
|
||||
byte AUTH_TYPE_GITHUB = 2;
|
||||
|
||||
/**
|
||||
* Authentication type WeChat three-party login
|
||||
*/
|
||||
byte AUTH_TYPE_WEIXIN = 3;
|
||||
|
||||
/**
|
||||
* Authentication type GITEE three-party login
|
||||
*/
|
||||
byte AUTH_TYPE_GITEE = 5;
|
||||
|
||||
/**
|
||||
* inner default label key __instance__
|
||||
*/
|
||||
String LABEL_INNER_KEY_INSTANCE = "__instance__";
|
||||
|
||||
/**
|
||||
* Inside the tag: monitorId Monitor task ID
|
||||
*/
|
||||
String TAG_MONITOR_ID = "monitorId";
|
||||
|
||||
/**
|
||||
* Inside the tag: monitorName Task name
|
||||
*/
|
||||
String TAG_MONITOR_NAME = "monitorName";
|
||||
|
||||
/**
|
||||
* Inside the tag: indicate that this monitor is auto-created by main monitor
|
||||
*/
|
||||
String TAG_AUTO_CREATED = "autoCreated";
|
||||
|
||||
/**
|
||||
* Inside the tag: indicate that this monitor is a main monitor which provides service discovery
|
||||
*/
|
||||
String TAG_SD_MAIN_MONITOR = "sdMainMonitor";
|
||||
|
||||
/**
|
||||
* Inside the tag: monitorHost Task host
|
||||
*/
|
||||
String TAG_MONITOR_HOST = "monitorHost";
|
||||
|
||||
/**
|
||||
* Inside the tag: policyId Alarm threshold rule ID
|
||||
*/
|
||||
String TAG_THRESHOLD_ID = "thresholdId";
|
||||
|
||||
/**
|
||||
* Inside the tag: app Type of monitoring
|
||||
*/
|
||||
String TAG_MONITOR_APP = "app";
|
||||
|
||||
/**
|
||||
* Inside the tag: metrics
|
||||
*/
|
||||
String TAG_METRICS = "metrics";
|
||||
|
||||
/**
|
||||
* Inside the tag: metric
|
||||
*/
|
||||
String TAG_METRIC = "metric";
|
||||
|
||||
/**
|
||||
* Inside the tag: code
|
||||
*/
|
||||
String TAG_CODE = "code";
|
||||
|
||||
/**
|
||||
* Tag Type: Auto-generated
|
||||
*/
|
||||
byte TAG_TYPE_AUTO_GENERATE = 0;
|
||||
/**
|
||||
* Tag Type: User-generated
|
||||
*/
|
||||
byte TAG_TYPE_USER_GENERATE = 1;
|
||||
/**
|
||||
* Tag Type: System preset
|
||||
*/
|
||||
byte TAG_TYPE_SYSTEM_PRESET = 2;
|
||||
|
||||
/**
|
||||
* notice_period type Type field, daily type
|
||||
*/
|
||||
int NOTICE_PERIOD_DAILY = 0;
|
||||
|
||||
/**
|
||||
* key is receiver.id, value is noticePeriod cache key prefix
|
||||
*/
|
||||
String RECEIVER_NOTICE_PERIOD_CACHE_PREFIX = "receiver_notice_period:";
|
||||
|
||||
/**
|
||||
* cache key notice_rule
|
||||
*/
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ public class GroupAlert {
|
||||
|
||||
@Schema(title = "Alert Fingerprints", example = "[\"dxsdfdsf\"]")
|
||||
@Convert(converter = JsonStringListAttributeConverter.class)
|
||||
@Column(length = 8192)
|
||||
@Column(length = 2048)
|
||||
private List<String> alertFingerprints;
|
||||
|
||||
@Schema(title = "The creator of this record", example = "tom")
|
||||
|
||||
@@ -28,7 +28,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
@@ -115,7 +114,7 @@ public class Job {
|
||||
/**
|
||||
* Refresh time list for one cycle of the job
|
||||
*/
|
||||
private ConcurrentLinkedDeque<Long> intervals;
|
||||
private LinkedList<Long> intervals;
|
||||
/**
|
||||
* Whether it is a recurring periodic task true is yes, false is no
|
||||
*/
|
||||
@@ -329,7 +328,7 @@ public class Job {
|
||||
* @param metricsIntervals A unique list composed of intervals for all metrics
|
||||
* Generate a list of refresh intervals for metric collection
|
||||
*/
|
||||
public synchronized void generateMetricsIntervals(List<Long> metricsIntervals) {
|
||||
public void generateMetricsIntervals(List<Long> metricsIntervals) {
|
||||
// 1. To find the least common multiple (LCM) of all metric refresh intervals
|
||||
long lcm = lcm(metricsIntervals);
|
||||
List<Long> refreshTimes = new LinkedList<>();
|
||||
@@ -349,16 +348,14 @@ public class Job {
|
||||
for (int i = 1; i < refreshTimes.size(); i++) {
|
||||
intervals.add(refreshTimes.get(i) - refreshTimes.get(i - 1));
|
||||
}
|
||||
setIntervals(new ConcurrentLinkedDeque<>(intervals));
|
||||
setIntervals(intervals);
|
||||
}
|
||||
|
||||
public synchronized long getInterval() {
|
||||
if (!CollectionUtils.isEmpty(this.intervals)) {
|
||||
Long interval = this.intervals.removeFirst();
|
||||
if (interval != null) {
|
||||
this.intervals.addLast(interval);
|
||||
return interval;
|
||||
}
|
||||
public long getInterval() {
|
||||
if (!CollectionUtils.isEmpty(getIntervals())) {
|
||||
long interval = getIntervals().remove();
|
||||
getIntervals().add(interval);
|
||||
return interval;
|
||||
}
|
||||
return getDefaultInterval();
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.DnsProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.FtpProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.HttpProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.HttpSdProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.ModbusProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.PlcProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.RegistryProtocol;
|
||||
@@ -64,6 +63,7 @@ import org.apache.hertzbeat.common.entity.job.protocol.TelnetProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.UdpProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.WebsocketProtocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.entity.sd.ServiceDiscoveryProtocol;
|
||||
|
||||
/**
|
||||
* Details of the monitoring metrics collected
|
||||
@@ -74,7 +74,6 @@ import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@Slf4j
|
||||
@SuppressWarnings("checkstyle:MemberName")
|
||||
public class Metrics {
|
||||
|
||||
/**
|
||||
@@ -261,9 +260,9 @@ public class Metrics {
|
||||
*/
|
||||
private KafkaProtocol kclient;
|
||||
/**
|
||||
* http sd protocol
|
||||
* Collect sd data protocol
|
||||
*/
|
||||
private HttpSdProtocol http_sd;
|
||||
private ServiceDiscoveryProtocol sdProtocol;
|
||||
/**
|
||||
* Monitoring configuration information using the public plc protocol
|
||||
*/
|
||||
|
||||
+1
-2
@@ -21,8 +21,7 @@ package org.apache.hertzbeat.common.entity.job.protocol;
|
||||
* Define common field method for each protocol in {@link org.apache.hertzbeat.common.entity.job.Metrics}
|
||||
*/
|
||||
public interface CommonRequestProtocol {
|
||||
|
||||
void setHost(String host);
|
||||
|
||||
void setPort(String port);
|
||||
}
|
||||
}
|
||||
@@ -77,10 +77,6 @@ public class Monitor {
|
||||
@Size(max = 100)
|
||||
private String app;
|
||||
|
||||
@Schema(title = "Scrape type: static | http_sd", example = "static", accessMode = READ_WRITE)
|
||||
@Size(max = 100)
|
||||
private String scrape;
|
||||
|
||||
@Schema(title = "peer host: ipv4, ipv6, domain name", example = "192.167.25.11", accessMode = READ_WRITE)
|
||||
@Size(max = 100)
|
||||
@HostValid
|
||||
|
||||
+10
-5
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.apache.hertzbeat.common.entity.manager;
|
||||
|
||||
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
@@ -26,6 +27,8 @@ import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Index;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
@@ -57,16 +60,18 @@ public class MonitorBind {
|
||||
@Schema(title = "primary id", example = "23")
|
||||
private Long id;
|
||||
|
||||
@Schema(title = "key string: ip:port")
|
||||
private String keyStr;
|
||||
|
||||
@Schema(title = "connect bind id", example = "87432674384")
|
||||
@Schema(title = "collector name", example = "87432674384")
|
||||
private Long bizId;
|
||||
|
||||
@Schema(title = "monitor ID", example = "87432674336")
|
||||
@Column(name = "monitor_id")
|
||||
private Long monitorId;
|
||||
|
||||
@Schema(title = "Bind type 0: sd", accessMode = READ_WRITE)
|
||||
@Min(0)
|
||||
@Max(3)
|
||||
private byte type;
|
||||
|
||||
@Schema(title = "The creator of this record", example = "tom")
|
||||
@CreatedBy
|
||||
private String creator;
|
||||
@@ -82,4 +87,4 @@ public class MonitorBind {
|
||||
@Schema(title = "Record the latest modification time (timestamp in milliseconds)")
|
||||
@LastModifiedDate
|
||||
private LocalDateTime gmtUpdate;
|
||||
}
|
||||
}
|
||||
+8
-6
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.common.entity.job.protocol;
|
||||
package org.apache.hertzbeat.common.entity.manager;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
@@ -23,13 +23,15 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Dns protocol
|
||||
* sd monitor param
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class HttpSdProtocol implements Protocol {
|
||||
|
||||
private String url;
|
||||
}
|
||||
public class SdMonitorParam {
|
||||
private String detectedHost;
|
||||
private String detectedPort;
|
||||
private Param sdParam;
|
||||
private Long bizId;
|
||||
}
|
||||
+1
-2
@@ -454,9 +454,8 @@ public final class CollectRep {
|
||||
return new MetricsData(new ArrowTable(root));
|
||||
} catch (Exception e1) {
|
||||
log.error(e1.getMessage(), e1);
|
||||
throw e1;
|
||||
} finally {
|
||||
root.close();
|
||||
throw e1;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.common.entity.sd;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* service discovery common field
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ServiceDiscoveryProtocol {
|
||||
private Long id;
|
||||
private List<Long> jobIdList;
|
||||
/**
|
||||
* sd type
|
||||
*/
|
||||
private Type type;
|
||||
/**
|
||||
* Target sd sources by which collector updates SDCache.
|
||||
*/
|
||||
private String sdSource;
|
||||
|
||||
/**
|
||||
* sd Type
|
||||
*/
|
||||
public enum Type {
|
||||
HTTP_SD("httpsd"),
|
||||
;
|
||||
|
||||
private final String protocolName;
|
||||
|
||||
Type(String protocolName) {
|
||||
this.protocolName = protocolName;
|
||||
}
|
||||
|
||||
public static Type getType(String str) {
|
||||
return Arrays.stream(Type.values()).filter(t -> t.toString().equalsIgnoreCase(str)).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public String getProtocolName() {
|
||||
return protocolName;
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-6
@@ -20,7 +20,6 @@ package org.apache.hertzbeat.common.support.valid;
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
import org.apache.hertzbeat.common.util.IpDomainUtil;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Host Param Validator
|
||||
@@ -34,13 +33,10 @@ public class HostParamValidator implements ConstraintValidator<HostValid, String
|
||||
|
||||
@Override
|
||||
public boolean isValid(String value, ConstraintValidatorContext context) {
|
||||
if (!StringUtils.hasText(value)) {
|
||||
return true;
|
||||
}
|
||||
if (value.toLowerCase().contains(HTTP)){
|
||||
if (value != null && value.toLowerCase().contains(HTTP)){
|
||||
value = value.replaceFirst(PATTERN_HTTP, BLANK);
|
||||
}
|
||||
if (value.toLowerCase().contains(HTTPS)){
|
||||
if (value != null && value.toLowerCase().contains(HTTPS)){
|
||||
value = value.replaceFirst(PATTERN_HTTPS, BLANK);
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,6 @@ public final class ArrowUtil {
|
||||
writer.writeBatch();
|
||||
writer.end();
|
||||
writer.close();
|
||||
root.close();
|
||||
}
|
||||
return out.toByteArray();
|
||||
} catch (IOException e) {
|
||||
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.common.util;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Configmap;
|
||||
import org.apache.hertzbeat.common.entity.job.Job;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.manager.MonitorBind;
|
||||
import org.apache.hertzbeat.common.entity.manager.Param;
|
||||
import org.apache.hertzbeat.common.entity.manager.SdMonitorParam;
|
||||
import org.apache.hertzbeat.common.entity.manager.Label;
|
||||
import org.apache.hertzbeat.common.entity.sd.ServiceDiscoveryProtocol;
|
||||
|
||||
/**
|
||||
* Operator for service discovery
|
||||
*/
|
||||
public class SdMonitorOperator {
|
||||
public static Optional<Param> getSdParam(List<Param> params) {
|
||||
return params.stream()
|
||||
.filter(param -> StringUtils.isNoneBlank(param.getField(), param.getParamValue()))
|
||||
.filter(param -> Objects.nonNull(ServiceDiscoveryProtocol.Type.getType(param.getField())))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
public static List<Param> removeSdParam(List<Param> params) {
|
||||
return params.stream()
|
||||
.filter(param -> Objects.isNull(ServiceDiscoveryProtocol.Type.getType(param.getField())))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static List<Param> cloneParamList(List<Param> params) {
|
||||
return params.stream()
|
||||
.map(Param::clone)
|
||||
.peek(param -> param.setId(null))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static MonitorBind buildSdSubMonitorBind(SdMonitorParam sdMonitorParam, long monitorId, Map<String, String> labels) {
|
||||
if (Objects.isNull(sdMonitorParam.getBizId())) {
|
||||
return null;
|
||||
}
|
||||
labels.put(CommonConstants.TAG_AUTO_CREATED, String.valueOf(sdMonitorParam.getBizId()));
|
||||
return MonitorBind.builder()
|
||||
.id(SnowFlakeIdGenerator.generateId())
|
||||
.bizId(sdMonitorParam.getBizId())
|
||||
.monitorId(monitorId)
|
||||
.type(CommonConstants.MONITOR_BIND_TYPE_SD_SUB_MONITOR)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static MonitorBind buildSdMainMonitorBind(SdMonitorParam sdMonitorParam, long monitorId) {
|
||||
if (Objects.isNull(sdMonitorParam.getSdParam())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return MonitorBind.builder()
|
||||
.id(SnowFlakeIdGenerator.generateId())
|
||||
.bizId(sdMonitorParam.getBizId())
|
||||
.monitorId(monitorId)
|
||||
.type(CommonConstants.MONITOR_BIND_TYPE_SD_MAIN_MONITOR)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static Job constructSdJobAndTag(SdMonitorParam sdMonitorParam, Map<String, String> labels, Job appDefine) {
|
||||
if (Objects.isNull(sdMonitorParam.getSdParam())) {
|
||||
return appDefine;
|
||||
}
|
||||
labels.put(CommonConstants.TAG_SD_MAIN_MONITOR, ServiceDiscoveryProtocol.Type.getType(sdMonitorParam.getSdParam().getField()).name());
|
||||
return constructSdJob(appDefine, sdMonitorParam.getSdParam());
|
||||
}
|
||||
|
||||
public static Job constructSdJob(Job appDefine, Param sdParam) {
|
||||
final Job sdJob = appDefine.clone();
|
||||
final ServiceDiscoveryProtocol.Type sdType = ServiceDiscoveryProtocol.Type.getType(sdParam.getField());
|
||||
List<Metrics> metricsList = Lists.newArrayList();
|
||||
Map<String, String> i18n = Maps.newHashMap();
|
||||
i18n.put("zh-CN", "监控目标");
|
||||
i18n.put("en-US", "Monitor Target");
|
||||
List<Metrics.Field> fields = Lists.newArrayList();
|
||||
fields.add(Metrics.Field.builder()
|
||||
.field("host")
|
||||
.type(CommonConstants.TYPE_STRING)
|
||||
.i18n(constructSdFieldI18n("主机", "host"))
|
||||
.build());
|
||||
fields.add(Metrics.Field.builder()
|
||||
.field("port")
|
||||
.type(CommonConstants.TYPE_STRING)
|
||||
.i18n(constructSdFieldI18n("端口", "port"))
|
||||
.build());
|
||||
metricsList.add(Metrics.builder()
|
||||
.name("target")
|
||||
.fields(fields)
|
||||
.i18n(i18n)
|
||||
.protocol(sdType.getProtocolName())
|
||||
.sdProtocol(ServiceDiscoveryProtocol.builder()
|
||||
.sdSource(sdParam.getParamValue())
|
||||
.type(sdType)
|
||||
.build())
|
||||
.build());
|
||||
|
||||
sdJob.setSd(Boolean.TRUE);
|
||||
sdJob.setMetrics(metricsList);
|
||||
sdJob.setConfigmap(Lists.newArrayList(new Configmap(sdParam.getField(), sdParam.getParamValue(), sdParam.getType())));
|
||||
return sdJob;
|
||||
}
|
||||
|
||||
public static List<Label> addMainMonitorTag(List<Label> tagList, Param sdParam) {
|
||||
tagList = tagList.stream()
|
||||
.filter(tag -> !StringUtils.equals(tag.getName(), CommonConstants.TAG_SD_MAIN_MONITOR))
|
||||
.filter(tag -> !StringUtils.equals(tag.getName(), CommonConstants.TAG_AUTO_CREATED))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
tagList.add(Label.builder().name(CommonConstants.TAG_SD_MAIN_MONITOR)
|
||||
.tagValue(ServiceDiscoveryProtocol.Type.getType(sdParam.getField()).name())
|
||||
.type(CommonConstants.TAG_TYPE_AUTO_GENERATE)
|
||||
.build());
|
||||
|
||||
return tagList;
|
||||
}
|
||||
|
||||
private static Map<String, String> constructSdFieldI18n(String zh, String en) {
|
||||
Map<String, String> i18n = Maps.newHashMap();
|
||||
i18n.put("zh-CN", zh);
|
||||
i18n.put("en-US", en);
|
||||
return i18n;
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -53,13 +53,13 @@ class HostParamValidatorTest {
|
||||
@Test
|
||||
public void testIsValid() {
|
||||
boolean result = hostParamValidator.isValid(null, context);
|
||||
assertTrue(result);
|
||||
assertFalse(result);
|
||||
|
||||
result = hostParamValidator.isValid("", context);
|
||||
assertTrue(result);
|
||||
assertFalse(result);
|
||||
|
||||
result = hostParamValidator.isValid(" ", context);
|
||||
assertTrue(result);
|
||||
assertFalse(result);
|
||||
|
||||
result = hostParamValidator.isValid("192.168.1.1", context);
|
||||
assertTrue(result);
|
||||
|
||||
@@ -31,8 +31,6 @@
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<mysql.version>8.0.30</mysql.version>
|
||||
<postgresql.version>42.5.5</postgresql.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -55,46 +53,10 @@
|
||||
<version>${hertzbeat.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Testcontainers Jupiter / JUnit 5 -->
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<artifactId>testcontainers</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- Testcontainers JDBC Support -->
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>jdbc</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- Testcontainers Database Modules -->
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>mysql</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- JDBC Drivers -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>${mysql.version}</version>
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>${postgresql.version}</version>
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
</project>
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.collector.collect.basic.database;
|
||||
|
||||
/**
|
||||
* Database type (image mame) and image tag enumeration class
|
||||
*/
|
||||
public enum DatabaseImagesEnum {
|
||||
MYSQL("mysql", "8.0.36"),
|
||||
POSTGRESQL("postgresql", "15");
|
||||
|
||||
private final String imageName;
|
||||
private final String defaultTag;
|
||||
|
||||
DatabaseImagesEnum(String imageName, String defaultTag) {
|
||||
this.imageName = imageName;
|
||||
this.defaultTag = defaultTag;
|
||||
}
|
||||
|
||||
public String getImageName() {
|
||||
return imageName;
|
||||
}
|
||||
|
||||
public String getDefaultTag() {
|
||||
return defaultTag;
|
||||
}
|
||||
|
||||
public String getFullImageName() {
|
||||
return imageName + ":" + defaultTag;
|
||||
}
|
||||
|
||||
public static DatabaseImagesEnum fromImageName(String imageName) {
|
||||
for (DatabaseImagesEnum value : values()) {
|
||||
if (value.getImageName().equalsIgnoreCase(imageName)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown database image name: " + imageName);
|
||||
}
|
||||
|
||||
public static DatabaseImagesEnum fromFullImageName(String fullImageName) {
|
||||
for (DatabaseImagesEnum value : values()) {
|
||||
if (value.getFullImageName().equalsIgnoreCase(fullImageName)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown full database image name: " + fullImageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DatabaseImagesEnum{"
|
||||
+ "imageName='" + imageName + '\''
|
||||
+ ", defaultTag='" + defaultTag + '\''
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
-99
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.collector.collect.basic.database;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.collect.AbstractCollectE2eTest;
|
||||
import org.apache.hertzbeat.collector.collect.database.JdbcCommonCollect;
|
||||
import org.apache.hertzbeat.collector.util.CollectUtil;
|
||||
import org.apache.hertzbeat.common.entity.job.Configmap;
|
||||
import org.apache.hertzbeat.common.entity.job.Job;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.JdbcProtocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.Protocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
/**
|
||||
* E2e test monitored by Jdbc Common
|
||||
*/
|
||||
@Slf4j
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class JdbcCommonCollectE2eTest extends AbstractCollectE2eTest {
|
||||
private static final DatabaseImagesEnum databaseImage = DatabaseImagesEnum.fromImageName("postgresql");
|
||||
|
||||
private static final String DATABASE_IMAGE_NAME = databaseImage.getImageName();
|
||||
|
||||
private static final String DATABASE_IMAGE_TAG = databaseImage.getDefaultTag();
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
collect = new JdbcCommonCollect();
|
||||
metrics = new Metrics();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CollectRep.MetricsData.Builder collectMetrics(Metrics metricsDef) {
|
||||
JdbcProtocol jdbcProtocol = (JdbcProtocol) buildProtocol(metricsDef);
|
||||
metrics.setJdbc(jdbcProtocol);
|
||||
CollectRep.MetricsData.Builder metricsData = CollectRep.MetricsData.newBuilder();
|
||||
metricsData.setApp(DATABASE_IMAGE_NAME);
|
||||
metrics.setAliasFields(metricsDef.getAliasFields());
|
||||
return collectMetricsData(metrics, metricsDef, metricsData);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Protocol buildProtocol(Metrics metricsDef) {
|
||||
JdbcProtocol jdbcProtocol = metricsDef.getJdbc();
|
||||
jdbcProtocol.setHost(DATABASE_IMAGE_NAME);
|
||||
jdbcProtocol.setPort(DATABASE_IMAGE_TAG);
|
||||
jdbcProtocol.setDatabase("test");
|
||||
jdbcProtocol.setPlatform("testcontainers");
|
||||
return jdbcProtocol;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithJdbcTcUrl() {
|
||||
Job dockerJob = appService.getAppDefine(DATABASE_IMAGE_NAME);
|
||||
List<Map<String, Configmap>> configmapFromPreCollectData = new LinkedList<>();
|
||||
for (Metrics metricsDef : dockerJob.getMetrics()) {
|
||||
metricsDef = CollectUtil.replaceCryPlaceholderToMetrics(metricsDef, configmapFromPreCollectData.size() > 0 ? configmapFromPreCollectData.get(0) : new HashMap<>());
|
||||
String metricName = metricsDef.getName();
|
||||
if ("slow_sql".equals(metricName)) {
|
||||
Metrics finalMetricsDef = metricsDef;
|
||||
assertDoesNotThrow(() -> collectMetrics(finalMetricsDef),
|
||||
String.format("%s failed to collect metrics)", metricName));
|
||||
log.info("{} metrics validation passed", metricName);
|
||||
continue; // skip slow_sql, extra extensions
|
||||
}
|
||||
CollectRep.MetricsData metricsData = validateMetricsCollection(metricsDef, metricName, true);
|
||||
CollectUtil.getConfigmapFromPreCollectData(metricsData);
|
||||
}
|
||||
}
|
||||
}
|
||||
-126
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.collector.collect.basic.redis;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.collect.AbstractCollectE2eTest;
|
||||
import org.apache.hertzbeat.collector.collect.redis.RedisCommonCollectImpl;
|
||||
import org.apache.hertzbeat.common.entity.job.Job;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.Protocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.RedisProtocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.Network;
|
||||
import org.testcontainers.containers.wait.strategy.Wait;
|
||||
import org.testcontainers.lifecycle.Startables;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* End-to-end test class for redis collector
|
||||
*/
|
||||
@Slf4j
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class RedisCommonCollectE2eTest extends AbstractCollectE2eTest {
|
||||
private static final String REDIS_IMAGE = "redis:7.4.2";
|
||||
private static final String HOST = "127.0.0.1";
|
||||
private static final int REDIS_PORT = 6379;
|
||||
private static final String REDIS_PATTERN = "1";
|
||||
private static final List<String> ALLOW_EMPTY_WHITE_LIST = Arrays.asList("server", "errorstats", "commandstats", "keyspace");
|
||||
|
||||
private static GenericContainer<?> redisContainer;
|
||||
|
||||
private Integer mappedPort;
|
||||
|
||||
@AfterAll
|
||||
public static void tearDown() throws InterruptedException {
|
||||
if (redisContainer != null) {
|
||||
redisContainer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
collect = new RedisCommonCollectImpl();
|
||||
|
||||
// Set up and start container
|
||||
setupAndStartContainer();
|
||||
|
||||
mappedPort = redisContainer.getMappedPort(REDIS_PORT);
|
||||
log.info("Container started successfully with mapped port: {}", mappedPort);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedisCollect() throws ExecutionException, InterruptedException, TimeoutException {
|
||||
Assertions.assertTrue(redisContainer.isRunning(), "redis container should be running");
|
||||
|
||||
Job redisJob = appService.getAppDefine("redis");
|
||||
redisJob.getMetrics().forEach(metricsDef -> {
|
||||
if (ALLOW_EMPTY_WHITE_LIST.contains(metricsDef.getName())) {
|
||||
validateMetricsCollection(metricsDef, metricsDef.getName(), true);
|
||||
} else {
|
||||
validateMetricsCollection(metricsDef, metricsDef.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CollectRep.MetricsData.Builder collectMetrics(Metrics metricsDef) {
|
||||
//Build redis protocol configuration
|
||||
RedisProtocol redisProtocol = (RedisProtocol) buildProtocol(metricsDef);
|
||||
metrics.setRedis(redisProtocol);
|
||||
metrics.setName(metricsDef.getName());
|
||||
metrics.setFields(metricsDef.getFields());
|
||||
return collectMetricsData(metrics, metricsDef);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Protocol buildProtocol(Metrics metricsDef) {
|
||||
RedisProtocol redisProtocol = new RedisProtocol();
|
||||
redisProtocol.setHost(HOST);
|
||||
redisProtocol.setPort(mappedPort.toString());
|
||||
redisProtocol.setPattern(REDIS_PATTERN);
|
||||
return redisProtocol;
|
||||
}
|
||||
|
||||
public void setupAndStartContainer() {
|
||||
Network network = Network.builder().build();
|
||||
redisContainer = new GenericContainer<>(DockerImageName.parse(REDIS_IMAGE))
|
||||
.withExposedPorts(REDIS_PORT)
|
||||
.withNetwork(network)
|
||||
.withNetworkAliases("redis")
|
||||
.waitingFor(Wait.forListeningPort())
|
||||
.withStartupTimeout(Duration.ofSeconds(30));
|
||||
|
||||
Startables.deepStart(Stream.of(redisContainer)).join();
|
||||
}
|
||||
}
|
||||
-6
@@ -27,7 +27,6 @@ import org.apache.hertzbeat.common.entity.job.Job;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.Protocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.manager.dao.DefineDao;
|
||||
import org.apache.hertzbeat.manager.service.impl.AppServiceImpl;
|
||||
import org.apache.hertzbeat.manager.service.impl.ObjectStoreConfigServiceImpl;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
@@ -35,7 +34,6 @@ import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -65,13 +63,10 @@ public abstract class AbstractCollectE2eTest {
|
||||
private Timeout timeout;
|
||||
@Mock
|
||||
private Job job;
|
||||
@Mock
|
||||
private DefineDao defineDao;
|
||||
|
||||
public void setUp() throws Exception {
|
||||
// Initialize mocks
|
||||
MockitoAnnotations.openMocks(this);
|
||||
when(defineDao.findAll()).thenReturn(new ArrayList<>());
|
||||
when(timeout.task()).thenReturn(timerJob);
|
||||
when(timerJob.getJob()).thenReturn(job);
|
||||
metricsCollect = new MetricsCollect(mock(Metrics.class), timeout, mock(CollectDataDispatch.class), null, List.of());
|
||||
@@ -83,7 +78,6 @@ public abstract class AbstractCollectE2eTest {
|
||||
|
||||
/**
|
||||
* Validate metrics collection, check if the metrics values are not empty <br/>
|
||||
*
|
||||
* @param metricsDef metrics definition
|
||||
* @param metricName metric name
|
||||
* @return metrics data
|
||||
|
||||
+1
-14
@@ -74,17 +74,4 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers-bom</artifactId>
|
||||
<version>${testcontainers.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
+60
-58
@@ -18,22 +18,25 @@
|
||||
package org.apache.hertzbeat.manager.component.sd;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.LinkedList;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.arrow.RowWrapper;
|
||||
import org.apache.hertzbeat.common.entity.manager.CollectorMonitorBind;
|
||||
import org.apache.hertzbeat.common.entity.manager.Monitor;
|
||||
import org.apache.hertzbeat.common.entity.manager.MonitorBind;
|
||||
import org.apache.hertzbeat.common.entity.manager.Param;
|
||||
import org.apache.hertzbeat.common.entity.manager.SdMonitorParam;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.queue.CommonDataQueue;
|
||||
import org.apache.hertzbeat.common.util.SdMonitorOperator;
|
||||
import org.apache.hertzbeat.manager.dao.CollectorMonitorBindDao;
|
||||
import org.apache.hertzbeat.manager.dao.MonitorBindDao;
|
||||
import org.apache.hertzbeat.manager.dao.MonitorDao;
|
||||
@@ -42,6 +45,7 @@ import org.apache.hertzbeat.manager.scheduler.ManagerWorkerPool;
|
||||
import org.apache.hertzbeat.manager.service.MonitorService;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Service Discovery Worker
|
||||
@@ -49,9 +53,6 @@ import org.springframework.stereotype.Component;
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ServiceDiscoveryWorker implements InitializingBean {
|
||||
|
||||
private static final String FILED_HOST = "host";
|
||||
private static final String FILED_PORT = "port";
|
||||
private final MonitorService monitorService;
|
||||
private final ParamDao paramDao;
|
||||
private final MonitorDao monitorDao;
|
||||
@@ -82,73 +83,74 @@ public class ServiceDiscoveryWorker implements InitializingBean {
|
||||
public void run() {
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
try (final CollectRep.MetricsData metricsData = dataQueue.pollServiceDiscoveryData()) {
|
||||
|
||||
Long monitorId = metricsData.getId();
|
||||
final Monitor mainMonitor = monitorDao.findById(monitorId).orElse(null);
|
||||
if (mainMonitor == null) {
|
||||
log.warn("No monitor found for id {}", monitorId);
|
||||
continue;
|
||||
}
|
||||
final Monitor mainMonitor = monitorDao.findMonitorsByIdIn(Sets.newHashSet(monitorId)).get(0);
|
||||
mainMonitor.getLabels().remove(CommonConstants.TAG_SD_MAIN_MONITOR);
|
||||
// collector
|
||||
final Optional<CollectorMonitorBind> collectorBind = collectorMonitorBindDao.findCollectorMonitorBindByMonitorId(monitorId);
|
||||
final Optional<CollectorMonitorBind> collectorBind = collectorMonitorBindDao.findCollectorMonitorBindByMonitorId(mainMonitor.getId());
|
||||
String collector = collectorBind.map(CollectorMonitorBind::getCollector).orElse(null);
|
||||
// params
|
||||
List<Param> mainMonitorParams = paramDao.findParamsByMonitorId(monitorId);
|
||||
final Map<String, MonitorBind> subMonitorBindMap = monitorBindDao.findMonitorBindsByBizId(monitorId)
|
||||
.stream().collect(Collectors.toMap(MonitorBind::getKeyStr, item -> item));
|
||||
// param
|
||||
List<Param> mainMonitorParamList = paramDao.findParamsByMonitorId(mainMonitor.getId());
|
||||
mainMonitorParamList = SdMonitorOperator.removeSdParam(mainMonitorParamList);
|
||||
|
||||
final Set<Long> subMonitorIdSet = monitorBindDao.findMonitorBindByBizIdAndType(monitorId, CommonConstants.MONITOR_BIND_TYPE_SD_SUB_MONITOR)
|
||||
.stream()
|
||||
.map(MonitorBind::getMonitorId)
|
||||
.collect(Collectors.toSet());
|
||||
final Map<String, List<Monitor>> hostMonitorMap = CollectionUtils.isEmpty(subMonitorIdSet)
|
||||
? Maps.newHashMap()
|
||||
: monitorDao.findMonitorsByIdIn(subMonitorIdSet).stream().collect(Collectors.groupingBy(Monitor::getHost));
|
||||
|
||||
RowWrapper rowWrapper = metricsData.readRow();
|
||||
Map<String, String> fieldsValue = Maps.newHashMapWithExpectedSize(8);
|
||||
|
||||
while (rowWrapper.hasNextRow()) {
|
||||
rowWrapper = rowWrapper.nextRow();
|
||||
fieldsValue.clear();
|
||||
rowWrapper.cellStream().forEach(cell -> {
|
||||
String value = cell.getValue();
|
||||
fieldsValue.put(cell.getField().getName(), value);
|
||||
});
|
||||
final String host = fieldsValue.get(FILED_HOST);
|
||||
final String port = fieldsValue.get(FILED_PORT);
|
||||
final String keyStr = host + ":" + port;
|
||||
if (subMonitorBindMap.containsKey(keyStr)) {
|
||||
subMonitorBindMap.remove(keyStr);
|
||||
continue;
|
||||
|
||||
|
||||
final String host = rowWrapper.nextCell().getValue();
|
||||
final String port = rowWrapper.nextCell().getValue();
|
||||
final List<Monitor> monitorList = hostMonitorMap.get(host);
|
||||
if (CollectionUtils.isEmpty(monitorList)) {
|
||||
monitorService.addAndSaveMonitorJob(mainMonitor.clone(), SdMonitorOperator.cloneParamList(mainMonitorParamList), collector,
|
||||
SdMonitorParam.builder()
|
||||
.detectedHost(host)
|
||||
.detectedPort(port)
|
||||
.bizId(mainMonitor.getId())
|
||||
.build(), null);
|
||||
return;
|
||||
}
|
||||
Monitor newMonitor = mainMonitor.clone();
|
||||
newMonitor.setId(null);
|
||||
newMonitor.setHost(host);
|
||||
newMonitor.setName(newMonitor.getName() + "-" + host + ":" + port);
|
||||
newMonitor.setScrape(CommonConstants.SCRAPE_STATIC);
|
||||
newMonitor.setGmtCreate(LocalDateTime.now());
|
||||
newMonitor.setGmtUpdate(LocalDateTime.now());
|
||||
// replace host port
|
||||
List<Param> newParams = new LinkedList<>();
|
||||
for (Param param : mainMonitorParams) {
|
||||
Param newParam = param.clone();
|
||||
newParam.setId(null);
|
||||
newParam.setGmtUpdate(null);
|
||||
newParam.setGmtCreate(null);
|
||||
if (FILED_HOST.equals(newParam.getField())) {
|
||||
newParam.setParamValue(host);
|
||||
} else if (FILED_PORT.equals(newParam.getField())) {
|
||||
newParam.setParamValue(port);
|
||||
|
||||
for (Monitor monitor : monitorList) {
|
||||
// make sure monitor that has the same host and port is not existed.
|
||||
final Optional<Param> samePortParam = paramDao.findParamsByMonitorId(monitor.getId()).stream()
|
||||
.filter(param -> StringUtils.equals(param.getField(), "port"))
|
||||
.filter(param -> StringUtils.equals(param.getParamValue(), port))
|
||||
.findFirst();
|
||||
if (samePortParam.isEmpty()) {
|
||||
monitorService.addAndSaveMonitorJob(mainMonitor.clone(), SdMonitorOperator.cloneParamList(mainMonitorParamList), collector,
|
||||
SdMonitorParam.builder()
|
||||
.detectedHost(host)
|
||||
.detectedPort(port)
|
||||
.bizId(mainMonitor.getId())
|
||||
.build(), null);
|
||||
} else {
|
||||
monitorService.enableManageMonitors(Sets.newHashSet(monitor.getId()));
|
||||
}
|
||||
newParams.add(newParam);
|
||||
}
|
||||
monitorService.addMonitor(newMonitor, newParams, collector, null);
|
||||
MonitorBind monitorBind = MonitorBind.builder()
|
||||
.bizId(monitorId)
|
||||
.monitorId(newMonitor.getId())
|
||||
.keyStr(keyStr)
|
||||
.build();
|
||||
monitorBindDao.save(monitorBind);
|
||||
|
||||
// make sure hostMonitorMap contains monitors that have not judged yet.
|
||||
hostMonitorMap.remove(host);
|
||||
}
|
||||
|
||||
// hostMonitorMap only contains monitors which are already existed but not in service discovery data
|
||||
// due to monitors that coincide with service discovery data are removed.
|
||||
// Thus, all monitors still in hostMonitorMap need to be cancelled.
|
||||
final Set<Long> needCancelMonitorIdSet = subMonitorBindMap.values().stream()
|
||||
.map(MonitorBind::getMonitorId).collect(Collectors.toSet());
|
||||
final HashSet<Long> needCancelMonitorIdSet = Sets.newHashSet();
|
||||
hostMonitorMap.forEach((key, value) -> needCancelMonitorIdSet.addAll(value.stream()
|
||||
.map(Monitor::getId)
|
||||
.collect(Collectors.toSet())));
|
||||
monitorService.cancelManageMonitors(needCancelMonitorIdSet);
|
||||
for (Long id : needCancelMonitorIdSet) {
|
||||
monitorBindDao.deleteMonitorBindByBizIdAndMonitorId(monitorId, id);
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
log.error(exception.getMessage(), exception);
|
||||
}
|
||||
|
||||
+12
@@ -127,4 +127,16 @@ public class MonitorsController {
|
||||
monitorService.importConfig(file);
|
||||
return ResponseEntity.ok(Message.success("Import success"));
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/copy")
|
||||
@Operation(summary = "copy monitors by ids", description = "copy monitors by ids")
|
||||
public ResponseEntity<Message<Void>> duplicateMonitors(
|
||||
@Parameter(description = "Monitor ID List", example = "6565463543") @RequestParam List<Long> ids
|
||||
) {
|
||||
if (ids != null && !ids.isEmpty()) {
|
||||
monitorService.copyMonitors(ids);
|
||||
}
|
||||
return ResponseEntity.ok(Message.success("copy success"));
|
||||
}
|
||||
}
|
||||
|
||||
+9
-7
@@ -18,20 +18,22 @@
|
||||
package org.apache.hertzbeat.manager.dao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.hertzbeat.common.entity.manager.MonitorBind;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
|
||||
/**
|
||||
* MonitorBind database operation
|
||||
* AuthResources database operation
|
||||
*/
|
||||
public interface MonitorBindDao extends JpaRepository<MonitorBind, Long>, JpaSpecificationExecutor<MonitorBind> {
|
||||
List<MonitorBind> findAllByBizIdInAndType(Set<Long> bizIdList, byte type);
|
||||
|
||||
List<MonitorBind> findMonitorBindsByBizId(Long bizId);
|
||||
List<MonitorBind> findMonitorBindByBizIdAndType(Long bizId, byte type);
|
||||
|
||||
MonitorBind findMonitorBindByBizIdAndMonitorIdAndType(Long bizId, Long monitorId, byte type);
|
||||
|
||||
List<MonitorBind> findAllByType(byte type);
|
||||
|
||||
void deleteByMonitorId(Long monitorId);
|
||||
|
||||
@Modifying
|
||||
void deleteMonitorBindByBizIdAndMonitorId(Long bizId, Long monitorId);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -36,12 +36,12 @@ import org.apache.hertzbeat.common.entity.manager.Param;
|
||||
@Schema(description = "Monitoring information entities")
|
||||
public class MonitorDto {
|
||||
|
||||
@Schema(description = "monitor content", accessMode = READ_WRITE)
|
||||
@Schema(description = "Monitor Content", accessMode = READ_WRITE)
|
||||
@NotNull
|
||||
@Valid
|
||||
private Monitor monitor;
|
||||
|
||||
@Schema(description = "monitor params", accessMode = READ_WRITE)
|
||||
@Schema(description = "Monitor Params", accessMode = READ_WRITE)
|
||||
@NotEmpty
|
||||
@Valid
|
||||
private List<Param> params;
|
||||
|
||||
+17
-8
@@ -19,8 +19,10 @@ package org.apache.hertzbeat.manager.scheduler;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.dto.CollectorInfo;
|
||||
import org.apache.hertzbeat.common.entity.job.Configmap;
|
||||
@@ -28,10 +30,13 @@ import org.apache.hertzbeat.common.entity.job.Job;
|
||||
import org.apache.hertzbeat.common.entity.manager.Collector;
|
||||
import org.apache.hertzbeat.common.entity.manager.CollectorMonitorBind;
|
||||
import org.apache.hertzbeat.common.entity.manager.Monitor;
|
||||
import org.apache.hertzbeat.common.entity.manager.MonitorBind;
|
||||
import org.apache.hertzbeat.common.entity.manager.Param;
|
||||
import org.apache.hertzbeat.common.entity.manager.ParamDefine;
|
||||
import org.apache.hertzbeat.common.util.SdMonitorOperator;
|
||||
import org.apache.hertzbeat.manager.dao.CollectorDao;
|
||||
import org.apache.hertzbeat.manager.dao.CollectorMonitorBindDao;
|
||||
import org.apache.hertzbeat.manager.dao.MonitorBindDao;
|
||||
import org.apache.hertzbeat.manager.dao.MonitorDao;
|
||||
import org.apache.hertzbeat.manager.dao.ParamDao;
|
||||
import org.apache.hertzbeat.manager.service.AppService;
|
||||
@@ -40,7 +45,6 @@ import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* scheduler init
|
||||
@@ -73,6 +77,9 @@ public class SchedulerInit implements CommandLineRunner {
|
||||
|
||||
@Autowired
|
||||
private CollectorMonitorBindDao collectorMonitorBindDao;
|
||||
|
||||
@Autowired
|
||||
private MonitorBindDao monitorBindDao;
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
@@ -91,18 +98,20 @@ public class SchedulerInit implements CommandLineRunner {
|
||||
// init jobs
|
||||
List<Monitor> monitors = monitorDao.findMonitorsByStatusNotInAndJobIdNotNull(List.of(CommonConstants.MONITOR_PAUSED_CODE));
|
||||
List<CollectorMonitorBind> monitorBinds = collectorMonitorBindDao.findAll();
|
||||
final Set<Long> sdMonitorIds = monitorBindDao.findAllByType(CommonConstants.MONITOR_BIND_TYPE_SD_SUB_MONITOR).stream()
|
||||
.map(MonitorBind::getBizId)
|
||||
.collect(Collectors.toSet());
|
||||
Map<Long, String> monitorIdCollectorMap = monitorBinds.stream().collect(
|
||||
Collectors.toMap(CollectorMonitorBind::getMonitorId, CollectorMonitorBind::getCollector));
|
||||
for (Monitor monitor : monitors) {
|
||||
try {
|
||||
// build collect job entity
|
||||
boolean isStatic = CommonConstants.SCRAPE_STATIC.equals(monitor.getScrape()) || !StringUtils.hasText(monitor.getScrape());
|
||||
String app = isStatic ? monitor.getApp() : monitor.getScrape();
|
||||
Job appDefine = appService.getAppDefine(app);
|
||||
if (!isStatic) {
|
||||
appDefine.setSd(true);
|
||||
}
|
||||
Job appDefine = appService.getAppDefine(monitor.getApp());
|
||||
List<Param> params = paramDao.findParamsByMonitorId(monitor.getId());
|
||||
if (sdMonitorIds.contains(monitor.getId())) {
|
||||
appDefine = SdMonitorOperator.constructSdJob(appDefine, SdMonitorOperator.getSdParam(params).get());
|
||||
}
|
||||
|
||||
if (CommonConstants.PROMETHEUS.equals(monitor.getApp())) {
|
||||
appDefine.setApp(CommonConstants.PROMETHEUS_APP_PREFIX + monitor.getName());
|
||||
}
|
||||
@@ -120,7 +129,7 @@ public class SchedulerInit implements CommandLineRunner {
|
||||
.map(param -> new Configmap(param.getField(), param.getParamValue(),
|
||||
param.getType())).collect(Collectors.toList());
|
||||
List<ParamDefine> paramDefaultValue = appDefine.getParams().stream()
|
||||
.filter(item -> StringUtils.hasText(item.getDefaultValue()))
|
||||
.filter(item -> StringUtils.isNotBlank(item.getDefaultValue()))
|
||||
.toList();
|
||||
paramDefaultValue.forEach(defaultVar -> {
|
||||
if (configmaps.stream().noneMatch(item -> item.getKey().equals(defaultVar.getField()))) {
|
||||
|
||||
+14
-3
@@ -22,12 +22,14 @@ import org.apache.hertzbeat.common.entity.grafana.GrafanaDashboard;
|
||||
import org.apache.hertzbeat.common.entity.job.Job;
|
||||
import org.apache.hertzbeat.common.entity.manager.Monitor;
|
||||
import org.apache.hertzbeat.common.entity.manager.Param;
|
||||
import org.apache.hertzbeat.common.entity.manager.SdMonitorParam;
|
||||
import org.apache.hertzbeat.manager.pojo.dto.AppCount;
|
||||
import org.apache.hertzbeat.manager.pojo.dto.MonitorDto;
|
||||
import org.apache.hertzbeat.manager.support.exception.MonitorDetectException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -123,14 +125,14 @@ public interface MonitorService {
|
||||
*
|
||||
* @param ids Monitoring ID List
|
||||
*/
|
||||
void cancelManageMonitors(Set<Long> ids);
|
||||
void cancelManageMonitors(HashSet<Long> ids);
|
||||
|
||||
/**
|
||||
* Start the managed monitoring items in batches according to the monitoring ID list
|
||||
*
|
||||
* @param ids Monitoring ID List
|
||||
*/
|
||||
void enableManageMonitors(Set<Long> ids);
|
||||
void enableManageMonitors(HashSet<Long> ids);
|
||||
|
||||
/**
|
||||
* Query the monitoring category and its corresponding monitoring quantity
|
||||
@@ -181,13 +183,22 @@ public interface MonitorService {
|
||||
*/
|
||||
void importConfig(MultipartFile file) throws Exception;
|
||||
|
||||
/**
|
||||
* Copy monitor in batches based on the id
|
||||
*
|
||||
* @param ids monitor id
|
||||
*/
|
||||
void copyMonitors(List<Long> ids);
|
||||
|
||||
/**
|
||||
* update app collect job by app
|
||||
*
|
||||
* @param job job content
|
||||
*/
|
||||
void updateAppCollectJob(Job job);
|
||||
|
||||
|
||||
void addAndSaveMonitorJob(Monitor monitor, List<Param> params, String collector, SdMonitorParam sdMonitorParam, GrafanaDashboard grafanaDashboard);
|
||||
|
||||
/**
|
||||
* Copy monitor by id
|
||||
*
|
||||
|
||||
+5
-1
@@ -270,6 +270,10 @@ public class AppServiceImpl implements AppService, InitializingBean {
|
||||
public List<Hierarchy> getAllAppHierarchy(String lang) {
|
||||
LinkedList<Hierarchy> hierarchies = new LinkedList<>();
|
||||
for (var job : appDefines.values()) {
|
||||
// TODO temporarily filter out push to solve the front-end problem, and open it after the subsequent design optimization
|
||||
if (DispatchConstants.PROTOCOL_PUSH.equalsIgnoreCase(job.getApp())) {
|
||||
continue;
|
||||
}
|
||||
queryAppHierarchy(lang, hierarchies, job);
|
||||
}
|
||||
return hierarchies;
|
||||
@@ -514,7 +518,7 @@ public class AppServiceImpl implements AppService, InitializingBean {
|
||||
*/
|
||||
private void refreshStore(ObjectStoreDTO<?> objectStoreConfig) {
|
||||
if (objectStoreConfig == null) {
|
||||
appDefineStore = new DatabaseAppDefineStoreImpl();
|
||||
appDefineStore = new LocalFileAppDefineStoreImpl();
|
||||
} else {
|
||||
if (objectStoreConfig.getType() == ObjectStoreDTO.Type.OBS) {
|
||||
appDefineStore = new ObjectStoreAppDefineStoreImpl();
|
||||
|
||||
+327
-128
@@ -23,6 +23,7 @@ import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hertzbeat.alert.dao.AlertDefineBindDao;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
@@ -33,19 +34,23 @@ import org.apache.hertzbeat.common.entity.grafana.GrafanaDashboard;
|
||||
import org.apache.hertzbeat.common.entity.job.Configmap;
|
||||
import org.apache.hertzbeat.common.entity.job.Job;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.CommonRequestProtocol;
|
||||
import org.apache.hertzbeat.common.entity.manager.Collector;
|
||||
import org.apache.hertzbeat.common.entity.manager.CollectorMonitorBind;
|
||||
import org.apache.hertzbeat.common.entity.manager.Monitor;
|
||||
import org.apache.hertzbeat.common.entity.manager.MonitorBind;
|
||||
import org.apache.hertzbeat.common.entity.manager.Param;
|
||||
import org.apache.hertzbeat.common.entity.manager.ParamDefine;
|
||||
import org.apache.hertzbeat.common.entity.manager.SdMonitorParam;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.entity.sd.ServiceDiscoveryProtocol;
|
||||
import org.apache.hertzbeat.common.support.event.MonitorDeletedEvent;
|
||||
import org.apache.hertzbeat.common.util.AesUtil;
|
||||
import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
import org.apache.hertzbeat.common.util.FileUtil;
|
||||
import org.apache.hertzbeat.common.util.IntervalExpressionUtil;
|
||||
import org.apache.hertzbeat.common.util.IpDomainUtil;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.apache.hertzbeat.common.util.SdMonitorOperator;
|
||||
import org.apache.hertzbeat.common.util.SnowFlakeIdGenerator;
|
||||
import org.apache.hertzbeat.grafana.service.DashboardService;
|
||||
import org.apache.hertzbeat.manager.config.ManagerSseManager;
|
||||
@@ -60,6 +65,7 @@ import org.apache.hertzbeat.manager.scheduler.CollectJobScheduling;
|
||||
import org.apache.hertzbeat.manager.service.AppService;
|
||||
import org.apache.hertzbeat.manager.service.ImExportService;
|
||||
import org.apache.hertzbeat.manager.service.MonitorService;
|
||||
import org.apache.hertzbeat.manager.service.LabelService;
|
||||
import org.apache.hertzbeat.manager.support.exception.MonitorDatabaseException;
|
||||
import org.apache.hertzbeat.manager.support.exception.MonitorDetectException;
|
||||
import org.apache.hertzbeat.warehouse.service.WarehouseService;
|
||||
@@ -75,14 +81,15 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -102,11 +109,15 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
private static final Long MONITOR_ID_TMP = 1000000000L;
|
||||
private static final byte ALL_MONITOR_STATUS = 9;
|
||||
|
||||
private static final int TAG_LENGTH = 2;
|
||||
|
||||
private static final String CONTENT_VALUE = MediaType.APPLICATION_OCTET_STREAM_VALUE + SignConstants.SINGLE_MARK + "charset=" + StandardCharsets.UTF_8;
|
||||
private final Map<String, ImExportService> imExportServiceMap = new HashMap<>();
|
||||
@Autowired
|
||||
private AppService appService;
|
||||
@Autowired
|
||||
private LabelService tagService;
|
||||
@Autowired
|
||||
private CollectJobScheduling collectJobScheduling;
|
||||
@Autowired
|
||||
private MonitorDao monitorDao;
|
||||
@@ -136,74 +147,25 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public void detectMonitor(Monitor monitor, List<Param> params, String collector) throws MonitorDetectException {
|
||||
if (CommonConstants.SCRAPE_STATIC.equals(monitor.getScrape()) || !StringUtils.hasText(monitor.getScrape())) {
|
||||
detectMonitorDirectly(monitor, params, collector);
|
||||
final Optional<Param> sdParam = SdMonitorOperator.getSdParam(params);
|
||||
|
||||
if (sdParam.isPresent()) {
|
||||
collectOneTimeSdData(monitor, collector, sdParam.get());
|
||||
} else {
|
||||
detectSdMonitor(monitor, params, collector);
|
||||
detectMonitorDirectly(monitor, params, collector);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void addMonitor(Monitor monitor, List<Param> params, String collector, GrafanaDashboard grafanaDashboard) throws RuntimeException {
|
||||
// Apply for monitor id
|
||||
long monitorId = SnowFlakeIdGenerator.generateId();
|
||||
Map<String, String> labels = monitor.getLabels();
|
||||
if (labels == null) {
|
||||
labels = new HashMap<>(8);
|
||||
monitor.setLabels(labels);
|
||||
final Optional<Param> sdParam = SdMonitorOperator.getSdParam(params);
|
||||
if (sdParam.isPresent()) {
|
||||
addAndSaveMonitorJob(monitor.clone(), params, collector, SdMonitorParam.builder().sdParam(sdParam.get()).build(), grafanaDashboard);
|
||||
return;
|
||||
}
|
||||
// Construct the collection task Job entity
|
||||
boolean isStatic = CommonConstants.SCRAPE_STATIC.equals(monitor.getScrape()) || !StringUtils.hasText(monitor.getScrape());
|
||||
String app = isStatic ? monitor.getApp() : monitor.getScrape();
|
||||
Job appDefine = appService.getAppDefine(app);
|
||||
if (!isStatic) {
|
||||
appDefine.setSd(true);
|
||||
}
|
||||
if (CommonConstants.PROMETHEUS.equals(monitor.getApp())) {
|
||||
appDefine.setApp(CommonConstants.PROMETHEUS_APP_PREFIX + monitor.getName());
|
||||
}
|
||||
appDefine.setMonitorId(monitorId);
|
||||
appDefine.setDefaultInterval(monitor.getIntervals());
|
||||
appDefine.setCyclic(true);
|
||||
appDefine.setTimestamp(System.currentTimeMillis());
|
||||
Map<String, String> metadata = Map.of(CommonConstants.LABEL_INSTANCE_NAME, monitor.getName(),
|
||||
CommonConstants.LABEL_INSTANCE_HOST, monitor.getHost());
|
||||
appDefine.setMetadata(metadata);
|
||||
appDefine.setLabels(monitor.getLabels());
|
||||
appDefine.setAnnotations(monitor.getAnnotations());
|
||||
List<Configmap> configmaps = params.stream().map(param -> {
|
||||
param.setMonitorId(monitorId);
|
||||
return new Configmap(param.getField(), param.getParamValue(), param.getType());
|
||||
}).collect(Collectors.toList());
|
||||
appDefine.setConfigmap(configmaps);
|
||||
long jobId = collector == null ? collectJobScheduling.addAsyncCollectJob(appDefine, null) :
|
||||
collectJobScheduling.addAsyncCollectJob(appDefine, collector);
|
||||
try {
|
||||
detectMonitor(monitor, params, collector);
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
try {
|
||||
if (collector != null) {
|
||||
CollectorMonitorBind collectorMonitorBind = CollectorMonitorBind.builder()
|
||||
.collector(collector)
|
||||
.monitorId(monitorId)
|
||||
.build();
|
||||
collectorMonitorBindDao.save(collectorMonitorBind);
|
||||
}
|
||||
monitor.setId(monitorId);
|
||||
monitor.setJobId(jobId);
|
||||
// create grafana dashboard
|
||||
if (monitor.getApp().equals(CommonConstants.PROMETHEUS) && grafanaDashboard != null && grafanaDashboard.isEnabled()) {
|
||||
dashboardService.createOrUpdateDashboard(grafanaDashboard.getTemplate(), monitorId);
|
||||
}
|
||||
monitorDao.save(monitor);
|
||||
paramDao.saveAll(params);
|
||||
} catch (Exception e) {
|
||||
log.error("Error while adding monitor: {}", e.getMessage(), e);
|
||||
collectJobScheduling.cancelAsyncCollectJob(jobId);
|
||||
throw new MonitorDatabaseException(e.getMessage());
|
||||
}
|
||||
addAndSaveMonitorJob(monitor, params, collector, null, grafanaDashboard);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -271,7 +233,7 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
}
|
||||
}
|
||||
// the dispatch collector must exist if pin
|
||||
if (StringUtils.hasText(monitorDto.getCollector())) {
|
||||
if (StringUtils.isNotBlank(monitorDto.getCollector())) {
|
||||
Optional<Collector> optionalCollector = collectorDao.findCollectorByName(monitorDto.getCollector());
|
||||
if (optionalCollector.isEmpty()) {
|
||||
throw new IllegalArgumentException("The pinned collector does not exist.");
|
||||
@@ -288,7 +250,7 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
if (paramDefine.isRequired() && (param == null || param.getParamValue() == null)) {
|
||||
throw new IllegalArgumentException("Params field " + field + " is required.");
|
||||
}
|
||||
if (param != null && StringUtils.hasText(param.getParamValue())) {
|
||||
if (param != null && param.getParamValue() != null && StringUtils.isNotBlank(param.getParamValue())) {
|
||||
switch (paramDefine.getType()) {
|
||||
case "number":
|
||||
double doubleValue;
|
||||
@@ -414,6 +376,7 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void modifyMonitor(Monitor monitor, List<Param> params, String collector, GrafanaDashboard grafanaDashboard) throws RuntimeException {
|
||||
final Optional<Param> sdParam = SdMonitorOperator.getSdParam(params);
|
||||
long monitorId = monitor.getId();
|
||||
// Check to determine whether the monitor corresponding to the monitor id exists
|
||||
Optional<Monitor> queryOption = monitorDao.findById(monitorId);
|
||||
@@ -430,14 +393,36 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
labels = new HashMap<>(8);
|
||||
monitor.setLabels(labels);
|
||||
}
|
||||
boolean isStatic = CommonConstants.SCRAPE_STATIC.equals(monitor.getScrape()) || !StringUtils.hasText(monitor.getScrape());
|
||||
|
||||
if (preMonitor.getStatus() != CommonConstants.MONITOR_PAUSED_CODE) {
|
||||
// Construct the collection task Job entity
|
||||
String app = isStatic ? monitor.getApp() : monitor.getScrape();
|
||||
Job appDefine = appService.getAppDefine(app);
|
||||
if (!isStatic) {
|
||||
appDefine.setSd(true);
|
||||
Job appDefine = appService.getAppDefine(monitor.getApp());
|
||||
if (sdParam.isPresent()) {
|
||||
// not allow to modify a sub monitor to main monitor
|
||||
labels.entrySet()
|
||||
.stream()
|
||||
.filter(label -> org.apache.commons.lang3.StringUtils.equals(label.getKey(), CommonConstants.TAG_AUTO_CREATED))
|
||||
.findFirst()
|
||||
.ifPresent(label -> {
|
||||
final MonitorBind isSubMonitorBefore = monitorBindDao.findMonitorBindByBizIdAndMonitorIdAndType(Long.valueOf(label.getValue()),
|
||||
monitorId, CommonConstants.MONITOR_BIND_TYPE_SD_SUB_MONITOR);
|
||||
if (Objects.nonNull(isSubMonitorBefore)) {
|
||||
throw new UnsupportedOperationException("Can not change a auto-created monitor to sd! ");
|
||||
}
|
||||
});
|
||||
// create main monitor bind
|
||||
final List<MonitorBind> mainMonitorBind = monitorBindDao.findMonitorBindByBizIdAndType(monitorId, CommonConstants.MONITOR_BIND_TYPE_SD_MAIN_MONITOR);
|
||||
if (CollectionUtils.isEmpty(mainMonitorBind)) {
|
||||
monitorBindDao.save(Objects.requireNonNull(
|
||||
SdMonitorOperator.buildSdMainMonitorBind(
|
||||
SdMonitorParam.builder().sdParam(sdParam.get()).build(), monitorId
|
||||
)));
|
||||
}
|
||||
|
||||
appDefine = SdMonitorOperator.constructSdJob(appDefine, sdParam.get());
|
||||
labels.put(CommonConstants.TAG_SD_MAIN_MONITOR, ServiceDiscoveryProtocol.Type.getType(sdParam.get().getField()).name());
|
||||
}
|
||||
|
||||
if (CommonConstants.PROMETHEUS.equals(monitor.getApp())) {
|
||||
appDefine.setApp(CommonConstants.PROMETHEUS_APP_PREFIX + monitor.getName());
|
||||
}
|
||||
@@ -465,7 +450,8 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
// execute only in non paused status
|
||||
try {
|
||||
detectMonitor(monitor, params, collector);
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
// After the update is successfully released, refresh the database
|
||||
@@ -488,6 +474,12 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
}
|
||||
}
|
||||
monitorDao.save(monitor);
|
||||
|
||||
// delete all sub monitor if sd param is removed
|
||||
final List<MonitorBind> subMonitorBindList = monitorBindDao.findMonitorBindByBizIdAndType(monitorId, CommonConstants.MONITOR_BIND_TYPE_SD_SUB_MONITOR);
|
||||
if (!CollectionUtils.isEmpty(subMonitorBindList) && sdParam.isEmpty()) {
|
||||
deleteMonitors(subMonitorBindList.stream().map(MonitorBind::getMonitorId).collect(Collectors.toSet()));
|
||||
}
|
||||
paramDao.saveAll(params);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
@@ -509,6 +501,12 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
if (CollectionUtils.isEmpty(ids)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<MonitorBind> allSubMonitorBind = monitorBindDao.findAllByBizIdInAndType(ids, CommonConstants.MONITOR_BIND_TYPE_SD_SUB_MONITOR);
|
||||
if (!CollectionUtils.isEmpty(allSubMonitorBind)) {
|
||||
ids.addAll(allSubMonitorBind.stream().map(MonitorBind::getMonitorId).collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
List<Monitor> monitors = monitorDao.findMonitorsByIdIn(ids);
|
||||
if (!monitors.isEmpty()) {
|
||||
monitorDao.deleteAll(monitors);
|
||||
@@ -539,9 +537,7 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
monitorDto.setMetrics(metrics);
|
||||
monitorDto.setGrafanaDashboard(dashboardService.getDashboardByMonitorId(id));
|
||||
} else {
|
||||
boolean isStatic = CommonConstants.SCRAPE_STATIC.equals(monitor.getScrape()) || !StringUtils.hasText(monitor.getScrape());
|
||||
String type = isStatic ? monitor.getApp() : monitor.getScrape();
|
||||
Job job = appService.getAppDefine(type);
|
||||
Job job = appService.getAppDefine(monitor.getApp());
|
||||
List<String> metrics = job.getMetrics().stream()
|
||||
.filter(Metrics::isVisible)
|
||||
.map(Metrics::getName).collect(Collectors.toList());
|
||||
@@ -567,7 +563,7 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
}
|
||||
andList.add(inPredicate);
|
||||
}
|
||||
if (StringUtils.hasText(app)) {
|
||||
if (StringUtils.isNotBlank(app)) {
|
||||
Predicate predicateApp = criteriaBuilder.equal(root.get("app"), app);
|
||||
andList.add(predicateApp);
|
||||
}
|
||||
@@ -579,17 +575,17 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
Predicate andPredicate = criteriaBuilder.and(andList.toArray(andPredicates));
|
||||
|
||||
List<Predicate> orList = new ArrayList<>();
|
||||
if (StringUtils.hasText(search)) {
|
||||
if (StringUtils.isNotBlank(search)) {
|
||||
Predicate predicateHost = criteriaBuilder.like(root.get("host"), "%" + search + "%");
|
||||
Predicate predicateName = criteriaBuilder.like(criteriaBuilder.lower(root.get("name")), "%" + search.toLowerCase() + "%");
|
||||
if (CommonUtil.isNumeric(search)){
|
||||
if (StringUtils.isNumeric(search)){
|
||||
Predicate predicateId = criteriaBuilder.equal(root.get("id"), Long.parseLong(search));
|
||||
orList.add(predicateId);
|
||||
}
|
||||
orList.add(predicateHost);
|
||||
orList.add(predicateName);
|
||||
}
|
||||
if (StringUtils.hasText(labels)) {
|
||||
if (StringUtils.isNotBlank(labels)) {
|
||||
String[] labelAres = labels.split(",");
|
||||
for (String label : labelAres) {
|
||||
String[] labelArr = label.split(":");
|
||||
@@ -624,10 +620,18 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelManageMonitors(Set<Long> ids) {
|
||||
public void cancelManageMonitors(HashSet<Long> ids) {
|
||||
if (CollectionUtils.isEmpty(ids)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Set<Long> subMonitorIds = monitorBindDao.findAllByBizIdInAndType(ids, CommonConstants.MONITOR_BIND_TYPE_SD_SUB_MONITOR).stream()
|
||||
.map(MonitorBind::getMonitorId)
|
||||
.collect(Collectors.toSet());
|
||||
if (!CollectionUtils.isEmpty(subMonitorIds)) {
|
||||
ids.addAll(subMonitorIds);
|
||||
}
|
||||
|
||||
// Update monitoring status Delete corresponding monitoring periodic task
|
||||
// The jobId is not deleted, and the jobId is reused again after the management is started.
|
||||
List<Monitor> managedMonitors = monitorDao.findMonitorsByIdIn(ids)
|
||||
@@ -644,7 +648,7 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableManageMonitors(Set<Long> ids) {
|
||||
public void enableManageMonitors(HashSet<Long> ids) {
|
||||
// Update monitoring status Add corresponding monitoring periodic task
|
||||
List<Monitor> unManagedMonitors = monitorDao.findMonitorsByIdIn(ids)
|
||||
.stream().filter(monitor ->
|
||||
@@ -658,12 +662,20 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
for (Monitor monitor : unManagedMonitors) {
|
||||
// Construct the collection task Job entity
|
||||
List<Param> params = paramDao.findParamsByMonitorId(monitor.getId());
|
||||
boolean isStatic = CommonConstants.SCRAPE_STATIC.equals(monitor.getScrape()) || !StringUtils.hasText(monitor.getScrape());
|
||||
String app = isStatic ? monitor.getApp() : monitor.getScrape();
|
||||
Job appDefine = appService.getAppDefine(app);
|
||||
if (!isStatic) {
|
||||
appDefine.setSd(true);
|
||||
final Optional<Param> sdParam = SdMonitorOperator.getSdParam(params);
|
||||
Job appDefine = appService.getAppDefine(monitor.getApp());
|
||||
if (sdParam.isPresent()) {
|
||||
final List<MonitorBind> mainMonitorBind = monitorBindDao.findMonitorBindByBizIdAndType(monitor.getId(), CommonConstants.MONITOR_BIND_TYPE_SD_MAIN_MONITOR);
|
||||
if (CollectionUtils.isEmpty(mainMonitorBind)) {
|
||||
monitorBindDao.save(Objects.requireNonNull(
|
||||
SdMonitorOperator.buildSdMainMonitorBind(
|
||||
SdMonitorParam.builder().sdParam(sdParam.get()).build(), monitor.getId()
|
||||
)));
|
||||
}
|
||||
|
||||
appDefine = SdMonitorOperator.constructSdJob(appDefine, sdParam.get());
|
||||
}
|
||||
|
||||
if (CommonConstants.PROMETHEUS.equals(monitor.getApp())) {
|
||||
appDefine.setApp(CommonConstants.PROMETHEUS_APP_PREFIX + monitor.getName());
|
||||
}
|
||||
@@ -679,7 +691,7 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
List<Configmap> configmaps = params.stream().map(param ->
|
||||
new Configmap(param.getField(), param.getParamValue(), param.getType())).collect(Collectors.toList());
|
||||
List<ParamDefine> paramDefaultValue = appDefine.getParams().stream()
|
||||
.filter(item -> StringUtils.hasText(item.getDefaultValue()))
|
||||
.filter(item -> StringUtils.isNotBlank(item.getDefaultValue()))
|
||||
.toList();
|
||||
paramDefaultValue.forEach(defaultVar -> {
|
||||
if (configmaps.stream().noneMatch(item -> item.getKey().equals(defaultVar.getField()))) {
|
||||
@@ -740,6 +752,25 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
}).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void copyMonitors(List<Long> ids) {
|
||||
|
||||
ids.stream().parallel().forEach(id -> {
|
||||
// get monitor and Params according id
|
||||
Optional<Monitor> monitorOpt = monitorDao.findById(id);
|
||||
List<Param> params = paramDao.findParamsByMonitorId(id);
|
||||
|
||||
monitorOpt.ifPresentOrElse(monitor -> {
|
||||
// deep copy original monitor to achieve persist in JPA
|
||||
Monitor newMonitor = JsonUtil.fromJson(JsonUtil.toJson(monitor), Monitor.class);
|
||||
if (newMonitor != null) {
|
||||
copyMonitor(newMonitor, params);
|
||||
}
|
||||
}, () -> log.warn("can not find the monitor for id :{}", id));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAppCollectJob(Job job) {
|
||||
List<Monitor> monitors = monitorDao.findMonitorsByAppEquals(job.getApp())
|
||||
@@ -776,7 +807,7 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
List<Configmap> configmaps = params.stream().map(param -> new Configmap(param.getField(),
|
||||
param.getParamValue(), param.getType())).collect(Collectors.toList());
|
||||
List<ParamDefine> paramDefaultValue = appDefine.getParams().stream()
|
||||
.filter(item -> StringUtils.hasText(item.getDefaultValue()))
|
||||
.filter(item -> StringUtils.isNotBlank(item.getDefaultValue()))
|
||||
.toList();
|
||||
paramDefaultValue.forEach(defaultVar -> {
|
||||
if (configmaps.stream().noneMatch(item -> item.getKey().equals(defaultVar.getField()))) {
|
||||
@@ -796,7 +827,80 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addAndSaveMonitorJob(Monitor monitor, List<Param> params, String collector, SdMonitorParam sdMonitorParam, GrafanaDashboard grafanaDashboard) {
|
||||
// Apply for monitor id
|
||||
long monitorId = SnowFlakeIdGenerator.generateId();
|
||||
Map<String, String> labels = monitor.getLabels();
|
||||
if (labels == null) {
|
||||
labels = new HashMap<>(8);
|
||||
monitor.setLabels(labels);
|
||||
}
|
||||
MonitorBind monitorBind = null;
|
||||
// Construct the collection task Job entity
|
||||
Job appDefine = appService.getAppDefine(monitor.getApp());
|
||||
if (Objects.nonNull(sdMonitorParam)) {
|
||||
monitorBind = Objects.isNull(sdMonitorParam.getSdParam())
|
||||
? SdMonitorOperator.buildSdSubMonitorBind(sdMonitorParam, monitorId, labels)
|
||||
: SdMonitorOperator.buildSdMainMonitorBind(sdMonitorParam, monitorId);
|
||||
|
||||
appDefine = SdMonitorOperator.constructSdJobAndTag(sdMonitorParam, labels, appDefine);
|
||||
}
|
||||
if (CommonConstants.PROMETHEUS.equals(monitor.getApp())) {
|
||||
appDefine.setApp(CommonConstants.PROMETHEUS_APP_PREFIX + monitor.getName());
|
||||
}
|
||||
appDefine.setMonitorId(monitorId);
|
||||
appDefine.setDefaultInterval(monitor.getIntervals());
|
||||
appDefine.setCyclic(true);
|
||||
appDefine.setTimestamp(System.currentTimeMillis());
|
||||
Map<String, String> metadata = Map.of(CommonConstants.LABEL_INSTANCE_NAME, monitor.getName(),
|
||||
CommonConstants.LABEL_INSTANCE_HOST, monitor.getHost());
|
||||
appDefine.setMetadata(metadata);
|
||||
appDefine.setLabels(monitor.getLabels());
|
||||
appDefine.setAnnotations(monitor.getAnnotations());
|
||||
resetMetricsCommonField(monitor, appDefine, sdMonitorParam);
|
||||
|
||||
List<Configmap> configmaps = params.stream().map(param -> {
|
||||
param.setMonitorId(monitorId);
|
||||
return new Configmap(param.getField(), param.getParamValue(), param.getType());
|
||||
}).collect(Collectors.toList());
|
||||
appDefine.setConfigmap(configmaps);
|
||||
|
||||
long jobId = collector == null ? collectJobScheduling.addAsyncCollectJob(appDefine, null) :
|
||||
collectJobScheduling.addAsyncCollectJob(appDefine, collector);
|
||||
|
||||
try {
|
||||
detectMonitor(monitor, params, collector);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
try {
|
||||
if (collector != null) {
|
||||
CollectorMonitorBind collectorMonitorBind = CollectorMonitorBind.builder()
|
||||
.collector(collector)
|
||||
.monitorId(monitorId)
|
||||
.build();
|
||||
collectorMonitorBindDao.save(collectorMonitorBind);
|
||||
}
|
||||
monitor.setId(monitorId);
|
||||
monitor.setJobId(jobId);
|
||||
// create grafana dashboard
|
||||
if (monitor.getApp().equals(CommonConstants.PROMETHEUS) && grafanaDashboard != null && grafanaDashboard.isEnabled()) {
|
||||
dashboardService.createOrUpdateDashboard(grafanaDashboard.getTemplate(), monitorId);
|
||||
}
|
||||
monitorDao.save(monitor);
|
||||
if (Objects.nonNull(monitorBind)) {
|
||||
monitorBindDao.save(monitorBind);
|
||||
}
|
||||
paramDao.saveAll(params);
|
||||
} catch (Exception e) {
|
||||
log.error("Error while adding monitor: {}", e.getMessage(), e);
|
||||
collectJobScheduling.cancelAsyncCollectJob(jobId);
|
||||
throw new MonitorDatabaseException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Monitor getMonitor(Long monitorId) {
|
||||
return monitorDao.findById(monitorId).orElse(null);
|
||||
@@ -812,69 +916,73 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
return monitorDao.findMonitorsByAppEquals(app);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void copyMonitor(Long id) {
|
||||
// Get the source monitor information
|
||||
Optional<Monitor> monitorOptional = monitorDao.findById(id);
|
||||
if (monitorOptional.isEmpty()) {
|
||||
throw new IllegalArgumentException("Monitor not found: " + id);
|
||||
private void resetMetricsCommonField(Monitor monitor, Job appDefine, SdMonitorParam sdMonitorParam) {
|
||||
if (Objects.isNull(sdMonitorParam)
|
||||
|| org.apache.commons.lang3.StringUtils.isAnyBlank(sdMonitorParam.getDetectedHost(), sdMonitorParam.getDetectedPort())) {
|
||||
return;
|
||||
}
|
||||
Monitor sourceMonitor = monitorOptional.get();
|
||||
// Get the parameters of source monitor
|
||||
List<Param> sourceParams = paramDao.findParamsByMonitorId(id);
|
||||
// Create new monitor object
|
||||
Monitor newMonitor = new Monitor();
|
||||
// Copy basic properties, exclude ID, jobId and status
|
||||
BeanUtils.copyProperties(sourceMonitor, newMonitor, "id", "jobId", "status");
|
||||
// Set new name
|
||||
newMonitor.setName(sourceMonitor.getName() + "_copy");
|
||||
// Set initial status
|
||||
newMonitor.setStatus(CommonConstants.MONITOR_UP_CODE);
|
||||
// Set create and update time
|
||||
newMonitor.setGmtCreate(LocalDateTime.now());
|
||||
newMonitor.setGmtUpdate(LocalDateTime.now());
|
||||
// Copy parameters
|
||||
List<Param> newParams = new ArrayList<>();
|
||||
if (!sourceParams.isEmpty()) {
|
||||
for (Param sourceParam : sourceParams) {
|
||||
Param newParam = new Param();
|
||||
BeanUtils.copyProperties(sourceParam, newParam, "id");
|
||||
newParam.setMonitorId(newMonitor.getId());
|
||||
newParams.add(newParam);
|
||||
}
|
||||
|
||||
for (Metrics metric : appDefine.getMetrics()) {
|
||||
Arrays.stream(metric.getClass().getDeclaredFields())
|
||||
.filter(field -> metric.getProtocol().equalsIgnoreCase(field.getName()))
|
||||
.findFirst()
|
||||
.ifPresent(field -> {
|
||||
field.setAccessible(true);
|
||||
final Object obj;
|
||||
try {
|
||||
obj = field.get(metric);
|
||||
if (obj instanceof CommonRequestProtocol protocol) {
|
||||
protocol.setHost(sdMonitorParam.getDetectedHost());
|
||||
protocol.setPort(sdMonitorParam.getDetectedPort());
|
||||
}
|
||||
} catch (IllegalAccessException exception) {
|
||||
log.warn("Not such field {}", field.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
addMonitor(newMonitor, newParams, null, null);
|
||||
|
||||
monitor.setHost(sdMonitorParam.getDetectedHost());
|
||||
Map<String, String> labels = monitor.getLabels();
|
||||
if (labels == null) {
|
||||
labels = new HashMap<>(8);
|
||||
monitor.setLabels(labels);
|
||||
}
|
||||
labels.put(CommonConstants.TAG_MONITOR_HOST, sdMonitorParam.getDetectedHost());
|
||||
monitor.setName(monitor.getApp().toUpperCase() + CommonConstants.LOCALE_SEPARATOR + sdMonitorParam.getDetectedHost()
|
||||
+ CommonConstants.LOCALE_SEPARATOR + SnowFlakeIdGenerator.generateId());
|
||||
}
|
||||
|
||||
private void copyMonitor(Monitor monitor, List<Param> params) {
|
||||
monitor.setName(String.format("%s - copy", monitor.getName()));
|
||||
addMonitor(monitor, params, null, null);
|
||||
}
|
||||
|
||||
private void detectSdMonitor(Monitor monitor, List<Param> params, String collector) {
|
||||
private void collectOneTimeSdData(Monitor monitor, String collector, Param sdParam) {
|
||||
Long monitorId = monitor.getId();
|
||||
if (monitorId == null || monitorId == 0) {
|
||||
monitorId = MONITOR_ID_TMP;
|
||||
}
|
||||
if (monitor.getScrape() == null || params == null || params.isEmpty()) {
|
||||
throw new IllegalArgumentException("scrape params is null or empty!");
|
||||
Job appDefine = appService.getAppDefine(monitor.getApp());
|
||||
if (CommonConstants.PROMETHEUS.equals(monitor.getApp())) {
|
||||
appDefine.setApp(CommonConstants.PROMETHEUS_APP_PREFIX + monitor.getName());
|
||||
}
|
||||
Job appDefine = appService.getAppDefine(monitor.getScrape());
|
||||
appDefine.setMonitorId(monitorId);
|
||||
appDefine.setCyclic(false);
|
||||
appDefine.setTimestamp(System.currentTimeMillis());
|
||||
Map<String, String> metadata = Map.of(CommonConstants.LABEL_INSTANCE_NAME, monitor.getName());
|
||||
Map<String, String> metadata = Map.of(CommonConstants.LABEL_INSTANCE_NAME, monitor.getName(),
|
||||
CommonConstants.LABEL_INSTANCE_HOST, monitor.getHost());
|
||||
appDefine.setMetadata(metadata);
|
||||
appDefine.setLabels(monitor.getLabels());
|
||||
appDefine.setAnnotations(monitor.getAnnotations());
|
||||
List<Configmap> configmaps = params.stream().map(param ->
|
||||
new Configmap(param.getField(), param.getParamValue(), param.getType())).collect(Collectors.toList());
|
||||
appDefine.setConfigmap(configmaps);
|
||||
appDefine.setSd(true);
|
||||
final Job sdJob = SdMonitorOperator.constructSdJob(appDefine, sdParam);
|
||||
List<CollectRep.MetricsData> collectRep;
|
||||
if (collector != null) {
|
||||
collectRep = collectJobScheduling.collectSyncJobData(appDefine, collector);
|
||||
collectRep = collectJobScheduling.collectSyncJobData(sdJob, collector);
|
||||
} else {
|
||||
collectRep = collectJobScheduling.collectSyncJobData(appDefine);
|
||||
collectRep = collectJobScheduling.collectSyncJobData(sdJob);
|
||||
}
|
||||
monitor.setStatus(CommonConstants.MONITOR_UP_CODE);
|
||||
|
||||
// If the detection result fails, a detection exception is thrown
|
||||
if (collectRep == null || collectRep.isEmpty()) {
|
||||
monitor.setStatus(CommonConstants.MONITOR_DOWN_CODE);
|
||||
@@ -930,4 +1038,95 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
}
|
||||
collectRep.forEach(CollectRep.MetricsData::close);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void copyMonitor(Long id) {
|
||||
// Get the source monitor information
|
||||
Optional<Monitor> monitorOptional = monitorDao.findById(id);
|
||||
if (monitorOptional.isEmpty()) {
|
||||
throw new IllegalArgumentException("Monitor not found: " + id);
|
||||
}
|
||||
Monitor sourceMonitor = monitorOptional.get();
|
||||
|
||||
// Get the parameters of source monitor
|
||||
List<Param> sourceParams = paramDao.findParamsByMonitorId(id);
|
||||
|
||||
// Create new monitor object
|
||||
Monitor newMonitor = new Monitor();
|
||||
// Copy basic properties, exclude ID, jobId and status
|
||||
BeanUtils.copyProperties(sourceMonitor, newMonitor, "id", "jobId", "status");
|
||||
// Set new name
|
||||
newMonitor.setName(sourceMonitor.getName() + "_copy");
|
||||
// Set initial status
|
||||
newMonitor.setStatus(CommonConstants.MONITOR_UP_CODE);
|
||||
// Set create and update time
|
||||
newMonitor.setGmtCreate(LocalDateTime.now());
|
||||
newMonitor.setGmtUpdate(LocalDateTime.now());
|
||||
// Generate new ID using snowflake algorithm
|
||||
newMonitor.setId(SnowFlakeIdGenerator.generateId());
|
||||
// Save new monitor
|
||||
newMonitor = monitorDao.save(newMonitor);
|
||||
|
||||
// Ensure ID is set
|
||||
if (newMonitor.getId() == null) {
|
||||
throw new RuntimeException("Failed to generate monitor ID");
|
||||
}
|
||||
|
||||
// Copy parameters
|
||||
if (!sourceParams.isEmpty()) {
|
||||
List<Param> newParams = new ArrayList<>();
|
||||
for (Param sourceParam : sourceParams) {
|
||||
Param newParam = new Param();
|
||||
BeanUtils.copyProperties(sourceParam, newParam, "id");
|
||||
newParam.setMonitorId(newMonitor.getId());
|
||||
newParams.add(newParam);
|
||||
}
|
||||
paramDao.saveAll(newParams);
|
||||
}
|
||||
|
||||
try {
|
||||
// Build collect job
|
||||
Job appDefine = appService.getAppDefine(newMonitor.getApp());
|
||||
if (CommonConstants.PROMETHEUS.equals(newMonitor.getApp())) {
|
||||
appDefine.setApp(CommonConstants.PROMETHEUS_APP_PREFIX + newMonitor.getName());
|
||||
}
|
||||
// Ensure using correct monitor ID
|
||||
appDefine.setMonitorId(newMonitor.getId());
|
||||
appDefine.setDefaultInterval(newMonitor.getIntervals());
|
||||
appDefine.setCyclic(true);
|
||||
appDefine.setTimestamp(System.currentTimeMillis());
|
||||
Map<String, String> metadata = Map.of(CommonConstants.LABEL_INSTANCE_NAME, newMonitor.getName(),
|
||||
CommonConstants.LABEL_INSTANCE_HOST, newMonitor.getHost());
|
||||
appDefine.setMetadata(metadata);
|
||||
appDefine.setLabels(newMonitor.getLabels());
|
||||
appDefine.setAnnotations(newMonitor.getAnnotations());
|
||||
List<Configmap> configmaps = sourceParams.stream()
|
||||
.map(param -> new Configmap(param.getField(), param.getParamValue(), param.getType()))
|
||||
.collect(Collectors.toList());
|
||||
appDefine.setConfigmap(configmaps);
|
||||
|
||||
// Get collector configuration from source monitor
|
||||
Optional<CollectorMonitorBind> bindOptional =
|
||||
collectorMonitorBindDao.findCollectorMonitorBindByMonitorId(sourceMonitor.getId());
|
||||
String collector = bindOptional.map(CollectorMonitorBind::getCollector).orElse(null);
|
||||
|
||||
// Dispatch collect job
|
||||
long jobId = collectJobScheduling.addAsyncCollectJob(appDefine, collector);
|
||||
newMonitor.setJobId(jobId);
|
||||
monitorDao.save(newMonitor);
|
||||
|
||||
// Copy collector binding if exists
|
||||
if (collector != null) {
|
||||
CollectorMonitorBind newBind = CollectorMonitorBind.builder()
|
||||
.collector(collector)
|
||||
.monitorId(newMonitor.getId())
|
||||
.build();
|
||||
collectorMonitorBindDao.save(newBind);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Create collect job error: {}", e.getMessage(), e);
|
||||
throw new RuntimeException("Create collect job failed: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,21 +132,10 @@ warehouse:
|
||||
# (please set this configuration reasonably as history records can affect performance when it is large)
|
||||
max-history-record-num: 6000
|
||||
victoria-metrics:
|
||||
# Standalone mode toggle — must be set to false when using cluster mode
|
||||
enabled: false
|
||||
url: http://localhost:8428
|
||||
username: root
|
||||
password: root
|
||||
cluster:
|
||||
enabled: false
|
||||
select:
|
||||
url: http://localhost:8481
|
||||
username: root
|
||||
password: root
|
||||
insert:
|
||||
url: http://localhost:8480
|
||||
username: root
|
||||
password: root
|
||||
td-engine:
|
||||
enabled: false
|
||||
driver-class-name: com.taosdata.jdbc.rs.RestfulDriver
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# The monitoring type category:service-application service monitoring db-database monitoring custom-custom monitoring os-operating system monitoring
|
||||
category: __system__
|
||||
# Monitoring application type name (consistent with file name) eg: linux windows tomcat mysql aws...
|
||||
app: http_sd
|
||||
# The app api i18n name
|
||||
name:
|
||||
zh-CN: Http Service Discovery
|
||||
en-US: Http Service Discovery
|
||||
# Input params define for app api(render web ui by the definition)
|
||||
params:
|
||||
# field-param field key
|
||||
- field: __sd_url__
|
||||
# name-param field display i18n name
|
||||
name:
|
||||
zh-CN: 服务发现地址
|
||||
en-US: Service Discovery Url
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: text
|
||||
# required-true or false
|
||||
required: true
|
||||
|
||||
metrics:
|
||||
- name: target
|
||||
i18n:
|
||||
zh-CN: 监控目标
|
||||
en-US: Monitor Target
|
||||
# metrics scheduling priority(0->127)->(high->low), metrics with the same priority will be scheduled in parallel
|
||||
# priority 0's metrics is availability metrics, it will be scheduled first, only availability metrics collect success will the scheduling continue
|
||||
priority: 0
|
||||
# collect metrics content
|
||||
fields:
|
||||
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
|
||||
- field: host
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: Host
|
||||
en-US: Host
|
||||
- field: port
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: Port
|
||||
en-US: Port
|
||||
# the protocol used for monitoring, eg: sql, ssh, http, telnet, wmi, snmp, sdk
|
||||
protocol: http_sd
|
||||
# the config content when protocol is http_sd
|
||||
http_sd:
|
||||
url: ^_^__sd_url__^_^
|
||||
@@ -14,7 +14,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
# The monitoring type category:service-application service monitoring db-database monitoring custom-custom monitoring os-operating system monitoring
|
||||
category: __system__
|
||||
category: service
|
||||
# Monitoring application type name (consistent with file name) eg: linux windows tomcat mysql aws...
|
||||
app: push
|
||||
# The app api i18n name
|
||||
|
||||
@@ -207,12 +207,12 @@ metrics:
|
||||
# collect metrics content
|
||||
fields:
|
||||
# field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field
|
||||
- field: content_type
|
||||
- field: Content-Type
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: Content Type
|
||||
en-US: Content Type
|
||||
- field: content_length
|
||||
- field: Content-Length
|
||||
type: 1
|
||||
i18n:
|
||||
zh-CN: 响应内容长度
|
||||
|
||||
+11
@@ -145,4 +145,15 @@ class MonitorsControllerTest {
|
||||
.andExpect(jsonPath("$.code").value("0"))
|
||||
.andExpect(jsonPath("$.msg").value("Import success"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void duplicateMonitors() throws Exception {
|
||||
// Mock the behavior of monitorService.copyMonitors
|
||||
doNothing().when(monitorService).copyMonitors(List.of(6565463543L));
|
||||
|
||||
// Perform the POST request and verify the response
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.post("/api/monitors/copy")
|
||||
.param("ids", "6565463543"))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
}
|
||||
|
||||
+6
-14
@@ -17,8 +17,13 @@
|
||||
|
||||
package org.apache.hertzbeat.manager.service;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.when;
|
||||
import java.util.Collections;
|
||||
import org.apache.hertzbeat.common.entity.manager.Monitor;
|
||||
import org.apache.hertzbeat.manager.dao.DefineDao;
|
||||
import org.apache.hertzbeat.manager.dao.MonitorDao;
|
||||
import org.apache.hertzbeat.manager.service.impl.AppServiceImpl;
|
||||
import org.apache.hertzbeat.manager.service.impl.ObjectStoreConfigServiceImpl;
|
||||
@@ -30,15 +35,6 @@ import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Test case for {@link AppService}
|
||||
*/
|
||||
@@ -51,9 +47,6 @@ class AppServiceTest {
|
||||
@Mock
|
||||
private MonitorDao monitorDao;
|
||||
|
||||
@Mock
|
||||
private DefineDao defineDao;
|
||||
|
||||
@Mock
|
||||
private WarehouseService warehouseService;
|
||||
|
||||
@@ -62,7 +55,6 @@ class AppServiceTest {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
when(defineDao.findAll()).thenReturn(new ArrayList<>());
|
||||
appService.afterPropertiesSet();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -744,7 +744,7 @@ class MonitorServiceTest {
|
||||
List<Param> params = Collections.singletonList(new Param());
|
||||
when(monitorDao.findById(1L)).thenReturn(Optional.of(monitor));
|
||||
when(paramDao.findParamsByMonitorId(1L)).thenReturn(params);
|
||||
assertDoesNotThrow(() -> monitorService.copyMonitor(1L));
|
||||
assertDoesNotThrow(() -> monitorService.copyMonitors(List.of(1L)));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+33
-75
@@ -25,7 +25,6 @@ import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.TemporalAmount;
|
||||
import java.util.Arrays;
|
||||
@@ -34,9 +33,6 @@ import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -80,13 +76,11 @@ import static org.apache.hertzbeat.common.constants.ConfigConstants.FunctionModu
|
||||
@Slf4j
|
||||
public class VictoriaMetricsClusterDataStorage extends AbstractHistoryDataStorage {
|
||||
|
||||
private static final String VM_INSERT_BASE_PATH = "/insert/%s/%s";
|
||||
private static final String VM_SELECT_BASE_PATH = "/select/%s/%s";
|
||||
private static final String IMPORT_PATH = "prometheus/api/v1/import";
|
||||
private static final String EXPORT_PATH = "prometheus/api/v1/export";
|
||||
private static final String STATUS_PATH = "prometheus/api/v1/status/tsdb";
|
||||
private static final String IMPORT_PATH = "/api/v1/import";
|
||||
private static final String EXPORT_PATH = "/api/v1/export";
|
||||
private static final String STATUS_PATH = "/api/v1/status/tsdb";
|
||||
private static final String STATUS_SUCCESS = "success";
|
||||
private static final String QUERY_RANGE_PATH = "prometheus/api/v1/query_range";
|
||||
private static final String QUERY_RANGE_PATH = "/api/v1/query_range";
|
||||
private static final String LABEL_KEY_NAME = "__name__";
|
||||
private static final String LABEL_KEY_JOB = "job";
|
||||
private static final String LABEL_KEY_INSTANCE = "instance";
|
||||
@@ -94,7 +88,6 @@ public class VictoriaMetricsClusterDataStorage extends AbstractHistoryDataStorag
|
||||
private static final String MONITOR_METRICS_KEY = "__metrics__";
|
||||
private static final String MONITOR_METRIC_KEY = "__metric__";
|
||||
|
||||
private final VictoriaMetricsClusterProperties vmClusterProps;
|
||||
private final VictoriaMetricsInsertProperties vmInsertProps;
|
||||
private final VictoriaMetricsSelectProperties vmSelectProps;
|
||||
|
||||
@@ -107,7 +100,6 @@ public class VictoriaMetricsClusterDataStorage extends AbstractHistoryDataStorag
|
||||
throw new IllegalArgumentException("please config Warehouse victoriaMetrics cluster props");
|
||||
}
|
||||
this.restTemplate = restTemplate;
|
||||
this.vmClusterProps = vmClusterProps;
|
||||
this.vmInsertProps = vmClusterProps.insert();
|
||||
this.vmSelectProps = vmClusterProps.select();
|
||||
serverAvailable = checkVictoriaMetricsDatasourceAvailable();
|
||||
@@ -116,23 +108,7 @@ public class VictoriaMetricsClusterDataStorage extends AbstractHistoryDataStorag
|
||||
private boolean checkVictoriaMetricsDatasourceAvailable() {
|
||||
// check server status
|
||||
try {
|
||||
String selectNodeStatusUrl = vmClusterProps.select().url() + VM_SELECT_BASE_PATH.formatted(vmClusterProps.accountID(), STATUS_PATH);
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
if (StringUtils.hasText(vmInsertProps.username())
|
||||
&& StringUtils.hasText(vmInsertProps.password())) {
|
||||
String authStr = vmInsertProps.username() + ":" + vmInsertProps.password();
|
||||
String encodedAuth = Base64Util.encode(authStr);
|
||||
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + SignConstants.BLANK + encodedAuth);
|
||||
}
|
||||
HttpEntity<Void> requestEntity = new HttpEntity<>(headers);
|
||||
ResponseEntity<String> responseEntity = restTemplate.exchange(
|
||||
selectNodeStatusUrl,
|
||||
HttpMethod.GET,
|
||||
requestEntity,
|
||||
String.class
|
||||
);
|
||||
|
||||
String result = responseEntity.getBody();
|
||||
String result = restTemplate.getForObject(vmSelectProps.url() + STATUS_PATH, String.class);
|
||||
|
||||
JsonNode jsonNode = JsonUtil.fromJson(result);
|
||||
if (jsonNode != null && STATUS_SUCCESS.equalsIgnoreCase(jsonNode.get(STATUS).asText())) {
|
||||
@@ -249,8 +225,7 @@ public class VictoriaMetricsClusterDataStorage extends AbstractHistoryDataStorag
|
||||
stringBuilder.append(JsonUtil.toJson(content)).append("\n");
|
||||
}
|
||||
HttpEntity<String> httpEntity = new HttpEntity<>(stringBuilder.toString(), headers);
|
||||
String importUrl = vmClusterProps.insert().url() + VM_INSERT_BASE_PATH.formatted(vmClusterProps.accountID(), IMPORT_PATH);
|
||||
ResponseEntity<String> responseEntity = restTemplate.postForEntity(importUrl,
|
||||
ResponseEntity<String> responseEntity = restTemplate.postForEntity(vmInsertProps.url() + IMPORT_PATH,
|
||||
httpEntity, String.class);
|
||||
if (responseEntity.getStatusCode().is2xxSuccessful()) {
|
||||
log.debug("insert metrics data to victoria-metrics success.");
|
||||
@@ -277,11 +252,9 @@ public class VictoriaMetricsClusterDataStorage extends AbstractHistoryDataStorag
|
||||
if (CommonConstants.PROMETHEUS.equals(app)) {
|
||||
labelName = metrics;
|
||||
}
|
||||
String timeSeriesSelector = Stream.of(
|
||||
LABEL_KEY_NAME + "=\"" + labelName + "\"",
|
||||
LABEL_KEY_INSTANCE + "=\"" + monitorId + "\"",
|
||||
CommonConstants.PROMETHEUS.equals(app) ? null : MONITOR_METRIC_KEY + "=\"" + metric + "\""
|
||||
).filter(Objects::nonNull).collect(Collectors.joining(","));
|
||||
String timeSeriesSelector =
|
||||
LABEL_KEY_NAME + "=\"" + labelName + "\"" + "," + LABEL_KEY_INSTANCE + "=\"" + monitorId + "\"" + ","
|
||||
+ (CommonConstants.PROMETHEUS.equals(app) ? "" : "," + MONITOR_METRIC_KEY + "=\"" + metric + "\"");
|
||||
Map<String, List<Value>> instanceValuesMap = new HashMap<>(8);
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
@@ -294,15 +267,11 @@ public class VictoriaMetricsClusterDataStorage extends AbstractHistoryDataStorag
|
||||
+ SignConstants.BLANK + encodedAuth);
|
||||
}
|
||||
HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
|
||||
Instant end = Instant.now();
|
||||
Duration duration = Duration.ofHours(Long.parseLong(history.replace("h", "")));
|
||||
Instant start = end.minus(duration);
|
||||
String exportUrl = vmClusterProps.select().url() + VM_SELECT_BASE_PATH.formatted(vmClusterProps.accountID(), EXPORT_PATH);
|
||||
URI uri = UriComponentsBuilder.fromHttpUrl(exportUrl)
|
||||
.queryParam("match", URLEncoder.encode("{" + timeSeriesSelector + "}", StandardCharsets.UTF_8))
|
||||
.queryParam("start", String.valueOf(start.getEpochSecond()))
|
||||
.queryParam("end", String.valueOf(end.getEpochSecond()))
|
||||
.build(true).toUri();
|
||||
URI uri = UriComponentsBuilder.fromHttpUrl(vmSelectProps.url() + EXPORT_PATH)
|
||||
.queryParam(URLEncoder.encode("match[]", StandardCharsets.UTF_8),
|
||||
URLEncoder.encode("{" + timeSeriesSelector + "}", StandardCharsets.UTF_8))
|
||||
.queryParam("start", URLEncoder.encode("now-" + history, StandardCharsets.UTF_8))
|
||||
.queryParam("end", "now").build(true).toUri();
|
||||
ResponseEntity<String> responseEntity = restTemplate.exchange(uri, HttpMethod.GET, httpEntity,
|
||||
String.class);
|
||||
if (responseEntity.getStatusCode().is2xxSuccessful()) {
|
||||
@@ -378,11 +347,9 @@ public class VictoriaMetricsClusterDataStorage extends AbstractHistoryDataStorag
|
||||
if (CommonConstants.PROMETHEUS.equals(app)) {
|
||||
labelName = metrics;
|
||||
}
|
||||
String timeSeriesSelector = Stream.of(
|
||||
LABEL_KEY_NAME + "=\"" + labelName + "\"",
|
||||
LABEL_KEY_INSTANCE + "=\"" + monitorId + "\"",
|
||||
CommonConstants.PROMETHEUS.equals(app) ? null : MONITOR_METRIC_KEY + "=\"" + metric + "\""
|
||||
).filter(Objects::nonNull).collect(Collectors.joining(","));
|
||||
String timeSeriesSelector =
|
||||
LABEL_KEY_NAME + "=\"" + labelName + "\"" + "," + LABEL_KEY_INSTANCE + "=\"" + monitorId + "\"" + ","
|
||||
+ (CommonConstants.PROMETHEUS.equals(app) ? "" : "," + MONITOR_METRIC_KEY + "=\"" + metric + "\"");
|
||||
Map<String, List<Value>> instanceValuesMap = new HashMap<>(8);
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
@@ -395,13 +362,10 @@ public class VictoriaMetricsClusterDataStorage extends AbstractHistoryDataStorag
|
||||
+ SignConstants.BLANK + encodedAuth);
|
||||
}
|
||||
HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
|
||||
String rangeUrl = VM_SELECT_BASE_PATH.formatted(vmClusterProps.accountID(), QUERY_RANGE_PATH);
|
||||
URI uri = UriComponentsBuilder.fromHttpUrl(rangeUrl)
|
||||
.queryParam("query", URLEncoder.encode("{" + timeSeriesSelector + "}", StandardCharsets.UTF_8))
|
||||
.queryParam("step", "4h")
|
||||
.queryParam("start", startTime)
|
||||
.queryParam("end", endTime)
|
||||
.build(true)
|
||||
URI uri = UriComponentsBuilder.fromHttpUrl(vmSelectProps.url() + QUERY_RANGE_PATH)
|
||||
.queryParam(URLEncoder.encode("query", StandardCharsets.UTF_8),
|
||||
URLEncoder.encode("{" + timeSeriesSelector + "}", StandardCharsets.UTF_8))
|
||||
.queryParam("step", "4h").queryParam("start", startTime).queryParam("end", endTime).build(true)
|
||||
.toUri();
|
||||
ResponseEntity<PromQlQueryContent> responseEntity = restTemplate.exchange(uri, HttpMethod.GET,
|
||||
httpEntity, PromQlQueryContent.class);
|
||||
@@ -436,12 +400,10 @@ public class VictoriaMetricsClusterDataStorage extends AbstractHistoryDataStorag
|
||||
log.error("query metrics data from victoria-metrics failed. {}", responseEntity);
|
||||
}
|
||||
// max
|
||||
uri = UriComponentsBuilder.fromHttpUrl(rangeUrl)
|
||||
.queryParam("query", URLEncoder.encode("max_over_time({" + timeSeriesSelector + "})", StandardCharsets.UTF_8))
|
||||
.queryParam("step", "4h")
|
||||
.queryParam("start", startTime)
|
||||
.queryParam("end", endTime)
|
||||
.build(true)
|
||||
uri = UriComponentsBuilder.fromHttpUrl(vmSelectProps.url() + QUERY_RANGE_PATH)
|
||||
.queryParam(URLEncoder.encode("query", StandardCharsets.UTF_8),
|
||||
URLEncoder.encode("max_over_time({" + timeSeriesSelector + "})", StandardCharsets.UTF_8))
|
||||
.queryParam("step", "4h").queryParam("start", startTime).queryParam("end", endTime).build(true)
|
||||
.toUri();
|
||||
responseEntity = restTemplate.exchange(uri, HttpMethod.GET, httpEntity, PromQlQueryContent.class);
|
||||
if (responseEntity.getStatusCode().is2xxSuccessful()) {
|
||||
@@ -474,12 +436,10 @@ public class VictoriaMetricsClusterDataStorage extends AbstractHistoryDataStorag
|
||||
}
|
||||
}
|
||||
// min
|
||||
uri = UriComponentsBuilder.fromHttpUrl(rangeUrl)
|
||||
.queryParam("query", URLEncoder.encode("min_over_time({" + timeSeriesSelector + "})", StandardCharsets.UTF_8))
|
||||
.queryParam("step", "4h")
|
||||
.queryParam("start", startTime)
|
||||
.queryParam("end", endTime)
|
||||
.build(true)
|
||||
uri = UriComponentsBuilder.fromHttpUrl(vmSelectProps.url() + QUERY_RANGE_PATH)
|
||||
.queryParam(URLEncoder.encode("query", StandardCharsets.UTF_8),
|
||||
URLEncoder.encode("min_over_time({" + timeSeriesSelector + "})", StandardCharsets.UTF_8))
|
||||
.queryParam("step", "4h").queryParam("start", startTime).queryParam("end", endTime).build(true)
|
||||
.toUri();
|
||||
responseEntity = restTemplate.exchange(uri, HttpMethod.GET, httpEntity, PromQlQueryContent.class);
|
||||
if (responseEntity.getStatusCode().is2xxSuccessful()) {
|
||||
@@ -512,12 +472,10 @@ public class VictoriaMetricsClusterDataStorage extends AbstractHistoryDataStorag
|
||||
}
|
||||
}
|
||||
// avg
|
||||
uri = UriComponentsBuilder.fromHttpUrl(rangeUrl)
|
||||
.queryParam("query", URLEncoder.encode("avg_over_time({" + timeSeriesSelector + "})", StandardCharsets.UTF_8))
|
||||
.queryParam("step", "4h")
|
||||
.queryParam("start", startTime)
|
||||
.queryParam("end", endTime)
|
||||
.build(true)
|
||||
uri = UriComponentsBuilder.fromHttpUrl(vmSelectProps.url() + QUERY_RANGE_PATH)
|
||||
.queryParam(URLEncoder.encode("query", StandardCharsets.UTF_8),
|
||||
URLEncoder.encode("avg_over_time({" + timeSeriesSelector + "})", StandardCharsets.UTF_8))
|
||||
.queryParam("step", "4h").queryParam("start", startTime).queryParam("end", endTime).build(true)
|
||||
.toUri();
|
||||
responseEntity = restTemplate.exchange(uri, HttpMethod.GET, httpEntity, PromQlQueryContent.class);
|
||||
if (responseEntity.getStatusCode().is2xxSuccessful()) {
|
||||
|
||||
-3
@@ -21,7 +21,6 @@ import org.apache.hertzbeat.common.constants.ConfigConstants;
|
||||
import org.apache.hertzbeat.common.constants.SignConstants;
|
||||
import org.apache.hertzbeat.warehouse.constants.WarehouseConstants;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
|
||||
/**
|
||||
* Victoriametrics configuration information
|
||||
@@ -33,8 +32,6 @@ import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||
+ SignConstants.DOT
|
||||
+ WarehouseConstants.HistoryName.VM_CLUSTER)
|
||||
public record VictoriaMetricsClusterProperties(
|
||||
@DefaultValue("false") boolean enabled,
|
||||
@DefaultValue("0") String accountID,
|
||||
VictoriaMetricsInsertProperties insert,
|
||||
VictoriaMetricsSelectProperties select
|
||||
) {
|
||||
|
||||
@@ -97,7 +97,7 @@ Due to the Apache Foundation's requirements for license compliance, HertzBeat's
|
||||
|
||||
- MySQL: [https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip)
|
||||
- Oracle (If you want to monitor Oracle, these two drivers are required):
|
||||
- [https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar](https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar)
|
||||
- [https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar)
|
||||
- [https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar)
|
||||
|
||||
Next, run the start-up script as before to experience the latest HertzBeat 1.6.0!
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
# HertzBeat Upgrade Guide (Docker Mode)
|
||||
|
||||
## Docker-based Upgrade
|
||||
|
||||
### 1. Data Backup
|
||||
|
||||
- **Back up the database**: Manually back up MySQL data as needed.
|
||||
|
||||
```bash
|
||||
mysqldump -h<HOST-IP> -P<PORT> -uroot -p"PASSWORD" <DB_NAME> > hertzbeat_backup-`date +%Y-%m-%d`.sql # Single database
|
||||
mysqldump -h<HOST-IP> -P<PORT> -uroot -p"PASSWORD" --all-databases > hertzbeat_backup-`date +%Y-%m-%d`.sql # Full database
|
||||
```
|
||||
|
||||
- **Back up configuration files and data directory**:
|
||||
|
||||
```bash
|
||||
mv application.yml application-bak.yml && mv sureness.yml sureness-bak.yml
|
||||
cp -R data data-`date +%Y-%m-%d`.bak
|
||||
```
|
||||
|
||||
### 2. Stop and Remove the HertzBeat Container
|
||||
|
||||
```bash
|
||||
docker stop hertzbeat && docker rm hertzbeat
|
||||
```
|
||||
|
||||
### 3. Upgrade Database Schema
|
||||
|
||||
Navigate to [HertzBeat GitHub Migration Scripts](https://github.com/apache/hertzbeat/tree/master/hertzbeat-manager/src/main/resources/db/migration), select the appropriate `V160__update_column.sql` file under your database type (e.g., MySQL), and execute it in MySQL.
|
||||
|
||||
### 4. Restart HertzBeat with the New Image
|
||||
|
||||
```bash
|
||||
docker run -d -p 1157:1157 -p 1158:1158 \
|
||||
-v $(pwd)/data:/opt/hertzbeat/data \
|
||||
-v $(pwd)/logs:/opt/hertzbeat/logs \
|
||||
-v $(pwd)/application.yml:/opt/hertzbeat/config/application.yml \
|
||||
-v $(pwd)/sureness.yml:/opt/hertzbeat/config/sureness.yml \
|
||||
--restart=always \
|
||||
--name hertzbeat apache/hertzbeat:v1.7.0
|
||||
```
|
||||
|
||||
### 5. Update Configuration Files
|
||||
|
||||
Modify the backup configurations as needed:
|
||||
|
||||
- **`application.yml`** (Typical modifications):
|
||||
|
||||
```yaml
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
username: root
|
||||
password: root
|
||||
url: jdbc:mysql://localhost:3306/hertzbeat?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
|
||||
hikari:
|
||||
max-lifetime: 120000
|
||||
|
||||
jpa:
|
||||
show-sql: false
|
||||
database-platform: org.eclipse.persistence.platform.database.MySQLPlatform
|
||||
database: mysql
|
||||
properties:
|
||||
eclipselink:
|
||||
logging:
|
||||
level: SEVERE
|
||||
```
|
||||
|
||||
- **`sureness.yml`** (Optional, modify for account/password changes):
|
||||
|
||||
```yaml
|
||||
account:
|
||||
- appId: admin
|
||||
credential: hertzbeat
|
||||
role: [admin]
|
||||
- appId: tom
|
||||
credential: hertzbeat
|
||||
role: [user]
|
||||
- appId: guest
|
||||
credential: hertzbeat
|
||||
role: [guest]
|
||||
- appId: lili
|
||||
credential: 94C6B34E7A199A9F9D4E1F208093B489
|
||||
salt: 123
|
||||
role: [user]
|
||||
```
|
||||
|
||||
### 6. Add Database Drivers
|
||||
|
||||
Due to Apache Foundation’s license compliance requirements, HertzBeat cannot include GPL-licensed dependencies (e.g., MySQL, Oracle). Users must manually download drivers and place them in the `ext-lib` directory, then mount it to `/opt/hertzbeat/ext-lib`:
|
||||
|
||||
- **MySQL Driver**: [Download MySQL Connector/J 8.0.25](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip)
|
||||
- **Oracle Driver** (Required for Oracle monitoring):
|
||||
- [ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar)
|
||||
- [orai18n-21.5.0.0.jar](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar)
|
||||
@@ -1,95 +0,0 @@
|
||||
# HertzBeat Upgrade Guide from v1.6.1 to v1.7.0 (Helm Mode)
|
||||
|
||||
## 1. Prerequisites
|
||||
|
||||
1. Ensure the following tools are installed:
|
||||
- Helm 3.x
|
||||
- kubectl
|
||||
- Git (optional)
|
||||
|
||||
2. Verify current deployment information:
|
||||
|
||||
```bash
|
||||
helm list -n <your-namespace>
|
||||
# If the old chart package is missing, export values.yaml with:
|
||||
helm get values hertzbeat -n <your-namespace> > hertzbeat-1.6.1-values.yaml
|
||||
```
|
||||
|
||||
3. Data Backup:
|
||||
|
||||
> **For custom monitoring templates:**
|
||||
>
|
||||
> - Backup `/opt/hertzbeat/define` from the running pod:
|
||||
>
|
||||
> ```bash
|
||||
> kubectl cp hertzbeat/hertzbeat-978477f84-fr894:/opt/hertzbeat/define ./define
|
||||
> ```
|
||||
>
|
||||
> **For external databases (MySQL/PostgreSQL):**
|
||||
>
|
||||
> - Use `mysqldump`/`pg_dump` or copy PVC directories:
|
||||
>
|
||||
> ```bash
|
||||
> kubectl get pvc -n hertzbeat
|
||||
> ```
|
||||
|
||||
## 2. Upgrade Steps
|
||||
|
||||
### 1. Pull the latest Chart
|
||||
|
||||
```bash
|
||||
helm repo update
|
||||
helm pull hertzbeat/hertzbeat --version 1.7.0 --untar
|
||||
cd hertzbeat
|
||||
```
|
||||
|
||||
Or clone from GitHub:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/hertzbeat/helm-charts.git
|
||||
cd helm-charts/charts/hertzbeat
|
||||
```
|
||||
|
||||
### 2. Update values.yaml
|
||||
|
||||
Compare and merge configurations:
|
||||
|
||||
```bash
|
||||
diff -u ../hertzbeat-1.6.1-values.yaml values.yaml
|
||||
# Use vimdiff to compare and merge changes
|
||||
```
|
||||
|
||||
Key configurations to check:
|
||||
|
||||
- `image.tag`
|
||||
- `resources`
|
||||
- `persistence`
|
||||
- `service.type`
|
||||
- Ingress settings
|
||||
- External database configurations
|
||||
|
||||
### 3. Dry-run Upgrade
|
||||
|
||||
```bash
|
||||
helm upgrade hertzbeat . -n <your-namespace> \
|
||||
--values values.yaml \
|
||||
--dry-run \
|
||||
--debug
|
||||
```
|
||||
|
||||
### 4. Execute Upgrade
|
||||
|
||||
```bash
|
||||
helm upgrade hertzbeat . -n <your-namespace> \
|
||||
--values values.yaml \
|
||||
--atomic \ # Auto-rollback on failure
|
||||
--timeout 10m # Set timeout
|
||||
```
|
||||
|
||||
### 5. Verify Upgrade
|
||||
|
||||
```bash
|
||||
helm status hertzbeat -n <your-namespace>
|
||||
kubectl get pods -n <your-namespace> -l app.kubernetes.io/instance=hertzbeat
|
||||
kubectl logs -n <your-namespace> -l app.kubernetes.io/instance=hertzbeat --tail=100
|
||||
```
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: ai_config
|
||||
id: aiConfig
|
||||
title: AI QuickStart
|
||||
sidebar_label: AI QuickStart
|
||||
sidebar_label: AI QuickStartr
|
||||
keywords: [AI]
|
||||
---
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: alarm_silence
|
||||
title: Alarm Silence
|
||||
sidebar_label: Alarm Silence
|
||||
title: Alert Silence
|
||||
sidebar_label: Alert Silence
|
||||
keywords: [ Open Source Monitoring System, Alert Silence ]
|
||||
---
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
id: alert_integration
|
||||
title: Alert Integration
|
||||
sidebar_label: Alert Integration
|
||||
keywords:
|
||||
[
|
||||
open-source monitoring,
|
||||
alert integration,
|
||||
alert management,
|
||||
multi-source alerts,
|
||||
]
|
||||
---
|
||||
|
||||
> The alert integration module of HertzBeat is designed to achieve unified reception, standardized processing, and intelligent dispatching of alerts from various third-party monitoring and observability platforms. As a centralized "Alert Center," HertzBeat efficiently ingests external alert information and applies flexible alert management strategies.
|
||||
|
||||
### Core Capabilities
|
||||
|
||||
- **Multi-Source Alert Ingestion**: Supports receiving alert messages from major platforms such as Webhook, Prometheus, Alertmanager, SkyWalking, and Tencent Cloud.
|
||||
- **Alert Format Standardization**: Converts alerts from different platforms into a unified internal format within HertzBeat for easier processing.
|
||||
- **Comprehensive Alert Processing Mechanisms**, including:
|
||||
- **Grouping and Convergence**: Manage alerts by grouping them based on labels and deduplicate repeated alerts within a specific time window.
|
||||
- **Inhibition**: Automatically suppresses secondary alerts when specific conditions are met.
|
||||
- **Silencing**: Temporarily disables alert notifications during system maintenance or known issues to avoid unnecessary noise.
|
||||
|
||||
### Supported Alert Sources
|
||||
|
||||
HertzBeat currently supports alert integration from the following third-party monitoring platforms:
|
||||
|
||||
- **Webhook**: A generic integration method supporting customized alert format push.
|
||||
- **Prometheus**: You can configure HertzBeat’s service address directly in the Prometheus Server's Alertmanager configuration, allowing HertzBeat to replace Alertmanager for receiving and handling Prometheus Server alerts.
|
||||
- **Alertmanager**: Supports forwarding alerts from Prometheus AlertManager to the HertzBeat alert platform.
|
||||
- **SkyWalking**: Sends SkyWalking alerts to the HertzBeat alert platform via Webhook.
|
||||
- **Tencent Cloud Monitoring**: Sends Tencent Cloud alerts to the HertzBeat alert platform via Webhook.
|
||||
- **And more**:HertzBeat is actively expanding its integration support. If the integration you need is not yet available, the community is actively contributing and can assist in adding it.
|
||||
|
||||
You can view the detailed integration methods and configuration examples through the "Integration" interface in HertzBeat.
|
||||
|
||||

|
||||
@@ -1,43 +1,55 @@
|
||||
---
|
||||
id: alert_threshold
|
||||
title: Alarm Threshold Configuration
|
||||
sidebar_label: Alarm Threshold
|
||||
title: Threshold Alert Configuration
|
||||
sidebar_label: Threshold Alert Configuration
|
||||
---
|
||||
|
||||
:::tip
|
||||
Alarm Threshold are the core function of `HertzBeat`, users can configure the trigger conditions of the alarm through the threshold rules.
|
||||
Support real-time threshold and scheduled threshold, real-time threshold can directly trigger the alarm when monitoring data is collected, scheduled threshold supports PromQL and other expressions to calculate the trigger alarm within a specified time period.
|
||||
Support visual page configuration or more flexible expression rule configuration, support configuring trigger times, alarm levels, notification templates, associated specified monitoring and so on.
|
||||
:::
|
||||
> Configure alert thresholds for monitoring metrics (warning alert, critical alert, emergency alert). The system triggers alerts based on threshold configuration and collected metric data.
|
||||
|
||||

|
||||
## Operational Steps
|
||||
|
||||
## Real-time Threshold
|
||||
### 1. Setting Labels for Monitoring Services (Optional)
|
||||
|
||||
> Real-time threshold means that the alarm is triggered directly when the monitoring data is collected, which is suitable for scenarios with high real-time requirements.
|
||||
If you need to categorize alerts, you can set labels for the monitored targets. For example: If you have multiple Linux systems to monitor, and each system has different monitoring metrics, such as: Server A has available memory greater than 1G, Server B has available memory greater than 2G, then you can set labels for Server A and Server B respectively, and then configure alerts based on these labels.
|
||||
|
||||
#### Creating Labels
|
||||
|
||||
Navigate to **Label Management -> Add Label**
|
||||
|
||||

|
||||
|
||||
As shown in the image above, add a new label. Here we set the label as: linux:dev (Linux used in development environment).
|
||||
|
||||
#### Configuring Labels
|
||||
|
||||
TODO Update image name
|
||||

|
||||
|
||||
As shown in the image above, click on `Add Label`.
|
||||
|
||||

|
||||
|
||||
Select our label, here demonstrated as selecting the `linux:dev` label.
|
||||
|
||||
### Creating Threshold Rules
|
||||
|
||||
> HertzBeat Page -> Alerting -> Threshold -> New Threshold -> ReadTime Threshold Rule
|
||||
Navigate to **[Threshold Rules] -> [Add Threshold Rule] -> [Confirm Configuration]**
|
||||
|
||||
Configure the threshold, for example: Select the SSL certificate metric object, configure the alarm expression-triggered when the metric `expired` is `true`, that is, `equals(expired,"true")`, set the alarm level notification template information, etc.
|
||||

|
||||
|
||||

|
||||
The above image explains the configuration details:
|
||||
|
||||
Configuration item details:
|
||||
|
||||
- **Rule Name**:Unique name defining this threshold rule
|
||||
- **Metric Object**: Select the monitoring metric object for which we need to configure the threshold. For example: Under website monitoring type -> under the summary metric set -> responseTime metric.
|
||||
- **Threshold Rule**: Configure the alarm trigger rules for specific indicators, support graphical interface and expression rules. For expression environment variables and operators, see the page prompts. For detailed help on threshold expressions, see [Threshold Expression Help](alert_threshold_expr).
|
||||
- **Associated Monitors**:Apply this threshold rule to the specified monitoring object (support direct binding and label association). If not configured, it will be applied to all monitoring objects that meet this threshold type rule.
|
||||
- **Threshold Rule**: Use this expression to calculate whether to trigger the threshold. Expression variables and operators are provided on the page for reference. For example: Set an alert to trigger if response time is greater than 50, the expression would be `responseTime > 50`. For detailed help on threshold expressions, see [Threshold Expression Help](alert_threshold_expr).
|
||||
- **Alert Level**: The alert level triggered by the threshold, from low to high: warning, critical, emergency.
|
||||
- **Trigger Count**: Set how many times the threshold must be triggered before the alert is actually triggered.
|
||||
- **Notification Template**: The template for the notification message sent after the alert is triggered. Template variables are provided on the page. For example: `${app}.${metrics}.${metric} metric value is ${responseTime}, which is greater than 50 triggering the alert`.
|
||||
- **Bind Label**: Select the label we need to apply. If no label is selected, it will apply to all services corresponding to the set metric object.
|
||||
- **Bing Annotation**:Add annotation information to this threshold rule (the annotation content supports environment variables). When an alarm is generated, this annotation information will be rendered and attached to the alarm.
|
||||
- **Apply Globally**: Set whether this threshold applies globally to all such metrics, default is no. After adding a threshold, it needs to be associated with the monitoring object for the threshold to take effect.
|
||||
- **Recovery Notification**: Whether to send a recovery notification after the alert is triggered, default is not to send.
|
||||
- **Enable Alert**: Enable or disable this alert threshold configuration.
|
||||
|
||||
**The threshold alert configuration is complete, and alerts that have been successfully triggered can be viewed in the [Alarm Center].**
|
||||
**If you need to send alert notifications via email, WeChat, DingTalk, or Feishu, you can configure it in [Notification].**
|
||||
**The threshold alert configuration is complete, and alerts that have been successfully triggered can be viewed in the [Alert Center].**
|
||||
**If you need to send alert notifications via email, WeChat, DingTalk, or Feishu, you can configure it in [Alert Notifications].**
|
||||
|
||||
## Scheduled Threshold
|
||||
For other issues, you can provide feedback through the community chat group or issue tracker!
|
||||
|
||||
@@ -9,7 +9,7 @@ keywords: [open source monitoring tool, open source database monitoring tool, mo
|
||||
|
||||
### Attention, Need Add ORACLE jdbc driver jar
|
||||
|
||||
- Download the ORACLE jdbc driver jar package, such as [ojdbc8.jar](https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar) [oracle-i18n](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar)
|
||||
- Download the ORACLE jdbc driver jar package, such as [ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar) [oracle-i18n](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar)
|
||||
- Copy the jar package to the `hertzbeat/ext-lib` directory.
|
||||
- Restart the HertzBeat service.
|
||||
|
||||
|
||||
+27
-47
@@ -7,6 +7,15 @@ slug: /
|
||||
|
||||
> A real-time monitoring system with agentless, performance cluster, prometheus-compatible, custom monitoring and status page building capabilities.
|
||||
|
||||
[](https://discord.gg/Fb6M73htGr)
|
||||
[](https://www.reddit.com/r/hertzbeat/)
|
||||
[](https://x.com/hertzbeat1024)
|
||||
[](https://www.bestpractices.dev/projects/8139)
|
||||
[](https://hub.docker.com/r/apache/hertzbeat)
|
||||
[](https://artifacthub.io/packages/search?repo=hertzbeat)
|
||||
[](https://qm.qq.com/q/FltGGGIX2m)
|
||||
[](https://www.youtube.com/channel/UCri75zfWX0GHqJFPENEbLow)
|
||||
|
||||
**Home: [hertzbeat.apache.org](https://hertzbeat.apache.org)**
|
||||
|
||||
## 🎡 <font color="green">Introduction</font>
|
||||
@@ -27,7 +36,7 @@ slug: /
|
||||
|
||||
---
|
||||
|
||||
### Powerful Monitoring Template
|
||||
### Powerful Monitoring Templates
|
||||
|
||||
> Before we discuss the customizable monitoring capabilities of HertzBeat, which we mentioned at the beginning, let's introduce the different monitoring templates of HertzBeat. And it is because of this monitoring template design that the advanced features come later.
|
||||
|
||||
@@ -82,7 +91,7 @@ Do you believe that users can just write a monitoring template on the UI page, c
|
||||
* And More Your Custom Template.
|
||||
* Notified Support `Discord` `Slack` `Telegram` `Email` `Dingtalk` `WeChat` `FeiShu` `Webhook` `SMS` `ServerChan`.
|
||||
|
||||
### Customization
|
||||
### Powerful Customization
|
||||
|
||||
> From the previous introduction of **Monitoring Templates**, it is clear that `HertzBeat` has powerful customization features.
|
||||
> Each monitor type is considered as a monitor template, no matter it is built-in or user-defined. You can easily add, modify and delete indicators by modifying the monitoring template.
|
||||
@@ -147,7 +156,7 @@ In an isolated network where multiple networks are not connected, we need to dep
|
||||
|
||||
---
|
||||
|
||||
## 🥐 Experience Now
|
||||
## Quickly Start
|
||||
|
||||
Just run a single command in a Docker environment: `docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat apache/hertzbeat`
|
||||
Browser access `http://localhost:1157` default account password `admin/hertzbeat`
|
||||
@@ -162,7 +171,7 @@ Browser access `http://localhost:1157` default account password `admin/hertzbeat
|
||||
|
||||
* The global overview page shows the distribution of current monitoring categories, users can visualize the current monitoring types and quantities and click to jump to the corresponding monitoring types for maintenance and management.
|
||||
* Show the status of currently registered collector clusters, including collector on-line status, monitoring tasks, startup time, IP address, name and so on.
|
||||
* Show the list of recent alarm messages, alarm level distribution etc.
|
||||
* Show the list of recent alarm messages, alarm level distribution and alarm processing rate.
|
||||
|
||||

|
||||
|
||||
@@ -213,7 +222,7 @@ Built-in support for monitoring types include:
|
||||
|
||||

|
||||
|
||||
### New Monitor
|
||||
### Add and Modify Surveillance
|
||||
|
||||
* You can add or modify monitoring instances of a specific monitoring type, configure the IP, port and other parameters of the monitoring on the other end, set the collection period, collection task scheduling method, support detecting availability in advance, etc. The monitoring instances on the page are defined by the corresponding monitoring templates.
|
||||
* The monitoring parameters configured on the page are defined by the monitoring template of the corresponding monitoring type, and users can modify the configuration parameters on the page by modifying the monitoring template.
|
||||
@@ -226,7 +235,7 @@ Built-in support for monitoring types include:
|
||||
* The monitoring data detail page shows the basic parameter information of the current monitoring, and the monitoring indicator data information.
|
||||
* Monitor Real-time Data Report displays the real-time values of all the currently monitored indicators in the form of a list of small cards, and users can configure alarm threshold rules based on the real-time values for reference.
|
||||
* Monitor Historical Data Report displays the historical values of the currently monitored metrics in the form of trend charts, supports querying hourly, daily and monthly historical data, and supports configuring the page refresh time.
|
||||
* ⚠️ Note that the monitoring history charts need to be configured with an external timing database in order to get the full functionality.
|
||||
* ⚠️ Note that the monitoring history charts need to be configured with an external timing database in order to get the full functionality, timing database support: IOTDB, TDengine, InfluxDB, GreptimeDB
|
||||
|
||||

|
||||
|
||||
@@ -239,34 +248,24 @@ Built-in support for monitoring types include:
|
||||
|
||||

|
||||
|
||||
### Alarm Threshold
|
||||
### Threshold Rules
|
||||
|
||||
* Alarm Threshold are the core function of `HertzBeat`, users can configure the trigger conditions of the alarm through the threshold rules.
|
||||
* Support real-time threshold and scheduled threshold, real-time threshold can directly trigger the alarm when monitoring data is collected, scheduled threshold supports PromQL and other expressions to calculate the trigger alarm within a specified time period.
|
||||
* Support visual page configuration or more flexible expression rule configuration, support configuring trigger times, alarm levels, notification templates, associated specified monitoring and so on.
|
||||
* Threshold rules can be configured for monitoring the availability status, and alerts can be issued when the value of a particular metric exceeds the expected range.
|
||||
* There are three levels of alerts: notification alerts, critical alerts, and emergency alerts.
|
||||
* Threshold rules support visual page configuration or expression rule configuration for more flexibility.
|
||||
* It supports configuring the number of triggers, alarm levels, notification templates, associated with a specific monitor and so on.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### Alarm Integration
|
||||
|
||||
* Integration with third-party monitoring systems such as `Prometheus`, `WebHook`, `Skywalking`, `AlertManager`, etc. to receive alarm messages from these systems and perform alarm processing.
|
||||
|
||||

|
||||
|
||||
### Alarm Grouping
|
||||
### Alarm Convergence
|
||||
|
||||
* Group convergence supports merging alarms for specified group labels by grouping. It deduplicates and converges the same repeated alarms in a time period.
|
||||
* When the threshold rule triggers an alarm or an external alarm is reported, it will enter the grouping convergence for alarm grouping and alarm deduplication to avoid alarm storms caused by a large number of alarm messages.
|
||||
* When the alarm is triggered by the threshold rule, it will enter into the alarm convergence, the alarm convergence will be based on the rules of the specific time period of the duplicate alarm message de-emphasis convergence, to avoid a large number of repetitive alarms lead to the receiver alarm numbness.
|
||||
* Alarm convergence rules support duplicate alarm effective time period, label matching and alarm level matching filter.
|
||||
|
||||

|
||||
|
||||
### Alarm Inhibition
|
||||
|
||||
* Alarm suppression is used to configure the suppression relationship between alarms. For example, high-level alarms suppress low-level alarms under the same instance.
|
||||
* When an alarm occurs, it can suppress the occurrence of other alarms. For example, when a server crashes, it can suppress all alarms on that server.
|
||||
|
||||

|
||||
|
||||
### Alarm Silence
|
||||
@@ -275,6 +274,8 @@ Built-in support for monitoring types include:
|
||||
* This application scenario, such as users in the system maintenance, do not need to send known alarms. Users will only receive alarm messages on weekdays, and users need to avoid disturbances at night.
|
||||
* Alarm silence rules support one-time time period or periodic time period, support label matching and alarm level matching.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### Message Notification
|
||||
@@ -291,38 +292,17 @@ Built-in support for monitoring types include:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### Monitoring Template
|
||||
### Monitoring Templates
|
||||
|
||||
* HertzBeat makes `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` and other protocols configurable so that you can customize the metrics you want to collect using these protocols by simply configuring the monitoring template `YML` in your browser. Would you believe that you can instantly adapt a new monitoring type such as `K8s` or `Docker` just by configuring it?
|
||||
* All our built-in monitoring types (mysql, website, jvm, k8s) are also mapped to corresponding monitoring templates, so you can add and modify monitoring templates to customize your monitoring functions.
|
||||
|
||||

|
||||
|
||||
### Collector Cluster
|
||||
|
||||
* Users can configure collector clusters to achieve distributed collection of large-scale monitoring tasks.
|
||||
* The collector cluster supports multi-node deployment, automatic load balancing, automatic failover, etc.
|
||||
* Supports unified management of multiple isolated networks, cloud-edge collaboration.
|
||||
|
||||

|
||||
|
||||
### Status Page
|
||||
|
||||
* Based on HertzBeat, quickly build an external status page for your own product and easily convey the real-time status of your product service to users. For example, the service status page provided by Github <https://www.githubstatus.com>.
|
||||
* Support synchronization between the status of the status page component and the monitoring status, as well as the fault event maintenance management mechanism, etc. Improve your transparency, professionalism, and user trust, and reduce communication costs.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
**More functions are welcome to be explored. Have Fun!**
|
||||
**There's so much more to discover. Have Fun!**
|
||||
|
||||
---
|
||||
|
||||
**Github: <https://github.com/apache/hertzbeat>**
|
||||
|
||||
**Home: <https://hertzbeat.apache.org/>**
|
||||
|
||||
@@ -35,7 +35,7 @@ It is necessary to have Docker environment in your environment. If not installed
|
||||
- `-v $(pwd)/logs:/opt/hertzbeat/logs` : (optional) Mount the log file to the local host to facilitate viewing.
|
||||
- `-v $(pwd)/application.yml:/opt/hertzbeat/config/application.yml` : (optional) Mount the configuration file to the container (please ensure that the file exists locally). [Download](https://github.com/apache/hertzbeat/raw/master/script/application.yml)
|
||||
- `-v $(pwd)/sureness.yml:/opt/hertzbeat/config/sureness.yml` : (optional) Mount the account configuration file to the container (please ensure that the file exists locally). [Download](https://github.com/apache/hertzbeat/raw/master/script/sureness.yml)
|
||||
- `-v $(pwd)/ext-lib:/opt/hertzbeat/ext-lib` : (optional) Mount external third-party JAR package [mysql-jdbc](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip) [oracle-jdbc](https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar) [oracle-i18n](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar)
|
||||
- `-v $(pwd)/ext-lib:/opt/hertzbeat/ext-lib` : (optional) Mount external third-party JAR package [mysql-jdbc](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip) [oracle-jdbc](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar) [oracle-i18n](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar)
|
||||
- `--name hertzbeat` : (optional) Naming container name hertzbeat
|
||||
- `--restart=always` : (optional) Configure the container to restart automatically.
|
||||
- `apache/hertzbeat` : Use the [official application mirror](https://hub.docker.com/r/apache/hertzbeat) to start the container, if the network times out, use `quay.io/tancloud/hertzbeat` instead.
|
||||
|
||||
@@ -1,45 +1,39 @@
|
||||
---
|
||||
id: ssl-cert-practice
|
||||
title: SSL Certificate Monitoring Practice
|
||||
sidebar_label: SSL Certificate Monitoring Practice
|
||||
title: SSL Certificate Monitor Practice
|
||||
sidebar_label: SSL Certificate Monitor Practice
|
||||
---
|
||||
|
||||
:::tip
|
||||
Most websites now support HTTPS by default. The certificate we apply for is usually 3 months or 1 year. It is easy to expire the SSL certificate over time, but we did not find it the first time, or did not update the certificate in time before it expired.
|
||||
:::
|
||||
|
||||
This article introduces how to use the hertzbeat monitoring tool to detect the validity period of our website's SSL certificate, and send us a warning message when the certificate expires or a few days before the certificate expires.
|
||||
|
||||
## What is HertzBeat
|
||||
#### What is HertzBeat
|
||||
|
||||
Apache HertzBeat (incubating) is a real-time monitoring tool with powerful custom monitoring capabilities without Agent. Website monitoring, PING connectivity, port availability, database, operating system, middleware, API monitoring, threshold alarms, alarm notification (email, WeChat, Ding Ding Feishu).
|
||||
|
||||
github: <https://github.com/apache/hertzbeat>
|
||||
|
||||
## Install HertzBeat
|
||||
#### Install HertzBeat
|
||||
|
||||
1. The `docker` environment can be installed with just one command
|
||||
|
||||
`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat apache/hertzbeat`
|
||||
`docker run -d -p 1157:1157 --name hertzbeat apache/hertzbeat`
|
||||
|
||||
2. After the installation is successful, the browser can access `localhost:1157` to start, the default account password is `admin/hertzbeat`
|
||||
|
||||
:::note
|
||||
The production environment recommends a complete deployment method, refer <https://hertzbeat.apache.org/docs/start/docker-compose-deploy>
|
||||
:::
|
||||
|
||||
## Monitoring SSL Certificates
|
||||
#### Monitoring SSL certificates
|
||||
|
||||
1. Click Add SSL Certificate Monitor
|
||||
|
||||
> HertzBeat Page -> Monitors Menu -> New Monitor -> Service Monitor -> Add SSL Certificate
|
||||
> System Page -> Monitor Menu -> SSL Certificate -> Add SSL Certificate
|
||||
|
||||

|
||||
|
||||
2. Configure the monitoring website
|
||||
|
||||
> Here we take the example of monitoring Baidu website, configure monitoring host domain name, name, collection interval, etc.
|
||||
> Click OK Note
|
||||
> Here we take the example of monitoring Baidu website, configure monitoring host domain name, name, collection interval, etc.
|
||||
> Click OK Note ⚠️Before adding, it will test the connectivity of the website by default, and the connection will be successful before adding. Of course, you can also gray out the **Test or not** button.
|
||||
|
||||

|
||||
|
||||
@@ -49,46 +43,51 @@ The production environment recommends a complete deployment method, refer <https
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
4. Set the threshold (triggered when the certificate expires)
|
||||
|
||||
> HertzBeat Page -> Alerting -> Threshold -> New Threshold -> ReadTime Threshold Rule
|
||||
> System Page -> Alarms -> Alarm Thresholds -> New Thresholds
|
||||
|
||||

|
||||
|
||||
> Configure the threshold, select the SSL certificate metric object, configure the alarm expression-triggered when the metric `expired` is `true`, that is, `equals(expired,"true")`, set the alarm level notification template information, etc.
|
||||
|
||||

|
||||
|
||||
> Threshold rule has others function you can try eg: associating thresholds with monitoring, trigger times so on.
|
||||
|
||||
5. Set the threshold (triggered one week before the certificate expires)
|
||||
|
||||
> In the same way, switch coding threshold, add a new configuration threshold and configure an alarm expression - when the metric expires timestamp `end_timestamp`, the `now()` function is the current timestamp, if the configuration triggers an alarm one week in advance: `end_timestamp <= (now() + 604800000)` , where `604800000` is the 7-day total time difference in milliseconds.
|
||||
> Associating thresholds with monitoring, in the threshold list, set which monitoring this threshold applies to.
|
||||
|
||||

|
||||
|
||||
> Finally, you can see the triggered alarm in the alarm center.
|
||||
5. Set the threshold (triggered one week before the certificate expires)
|
||||
|
||||
> In the same way, add a new configuration threshold and configure an alarm expression - when the metric expires timestamp `end_timestamp`, the `now()` function is the current timestamp, if the configuration triggers an alarm one week in advance: `end_timestamp <= (now( ) + 604800000)` , where `604800000` is the 7-day total time difference in milliseconds.
|
||||
|
||||

|
||||
|
||||
> Finally, you can see the triggered alarm in the alarm center.
|
||||
|
||||

|
||||
|
||||
6. Alarm notification (in time notification via Dingding WeChat Feishu, etc.)
|
||||
|
||||
> HertzBeat Page -> Notification -> Notice Receiver -> New Receiver -> Config the Feishu Receiver
|
||||
> Monitoring Tool -> Alarm Notification -> New Receiver
|
||||
|
||||

|
||||

|
||||
|
||||
For token configuration such as Feishu, please refer to the help document
|
||||
For token configuration such as Dingding WeChat Feishu, please refer to the help document
|
||||
|
||||
<https://hertzbeat.apache.org/docs/help/alert_feishu>
|
||||
<https://hertzbeat.apache.org/docs/help/alert_dingtalk>
|
||||
|
||||
> Notification -> Notice Policy -> New Notice Policy -> Enable Notification for the Recipient Just Configured
|
||||
> Alarm Notification -> New Alarm Notification Policy -> Enable Notification for the Recipient Just Configured
|
||||
|
||||

|
||||

|
||||
|
||||
7. OK When the threshold is triggered, we can receive the corresponding alarm message. If there is no notification, you can also view the alarm information in the alarm center.
|
||||
|
||||
----
|
||||
|
||||
## The End
|
||||
#### Finish
|
||||
|
||||
The practice of monitoring SSL certificates is here. Of course, for hertzbeat, this function is just the tip of the iceberg. If you think hertzbeat is a good open source project, please give us a Gitee star on GitHub, thank you very much. Thank you for your support. Refill!
|
||||
|
||||
@@ -6,12 +6,8 @@ sidebar_label: Update to 1.6.0 guide
|
||||
|
||||
## HertzBeat 1.6.0 Upgrade Guide
|
||||
|
||||
:::note
|
||||
This guide is applicable for upgrading from 1.5.0 to 1.6.0 to version 1.6.0.
|
||||
If you are using an older version, it is recommended to reinstall using the export function, or upgrade to 1.5.0 and then follow this guide to 1.6.0.
|
||||
:::
|
||||
|
||||
Follow the [HertzBeat New Version Upgrade](upgrade)
|
||||
**Note: This guide is applicable for upgrading from 1.5.0 to 1.6.0 to version 1.6.0.**
|
||||
**If you are using an older version, it is recommended to reinstall using the export function, or upgrade to 1.5.0 and then follow this guide to 1.6.0.**
|
||||
|
||||
## Binary Installation Package Upgrade
|
||||
|
||||
@@ -107,7 +103,7 @@ Due to the Apache Foundation's requirements for license compliance, HertzBeat's
|
||||
|
||||
- MySQL: [https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip)
|
||||
- Oracle (If you want to monitor Oracle, these two drivers are required):
|
||||
- [https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar](https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar)
|
||||
- [https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar)
|
||||
- [https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar)
|
||||
|
||||
Next, run the start-up script as before to experience the latest HertzBeat 1.6.0!
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
id: 1.7.0-update
|
||||
title: How to update to 1.7.0
|
||||
sidebar_label: Update to 1.7.0 guide
|
||||
---
|
||||
|
||||
## HertzBeat 1.7.0 Upgrade Guide
|
||||
|
||||
:::note
|
||||
This guide is applicable for upgrading from 1.6.x to version 1.7.0.
|
||||
If you are using an older version, it is recommended to reinstall using the export function, or upgrade to 1.6.0 and then follow this guide to 1.7.0.
|
||||
:::
|
||||
|
||||
Follow the [HertzBeat New Version Upgrade](upgrade)
|
||||
|
||||
## Installation Upgrade
|
||||
|
||||
### Upgrade Database
|
||||
|
||||
In 1.7.0, we use the `label` instead of `tag`, in some environment, we need drop or delete the table `hzb_tag_monitor_bind` in database.
|
||||
|
||||
```sql
|
||||
DELETE FROM hzb_tag_monitor_bind;
|
||||
```
|
||||
|
||||
### Upgrade Alarm Threshold
|
||||
|
||||
In 1.7.0, we redesign the new alarm threshold, include the Real-Time Threshold and Scheduled Threshold.
|
||||
We need reconfigure the alarm threshold, alarm group by manual.
|
||||
|
||||
:::tip
|
||||
There are no default built-in threshold rules, such as the previous availability threshold.
|
||||
So if you find that there is no alarm after the monitoring is down, you need to configure the corresponding availability threshold yourself.
|
||||
:::
|
||||
|
||||
## Upgrade via Export and Import
|
||||
|
||||
If you do not want to go through the tedious script upgrade method mentioned above, you can directly export and import the monitoring tasks and threshold information from the old environment.
|
||||
|
||||
- Deploy a new environment with the latest version.
|
||||
- Export the monitoring tasks and threshold information from the old environment on the page
|
||||
@@ -1,245 +0,0 @@
|
||||
---
|
||||
id: springboot-auto-practice
|
||||
title: SpringBoot Web Application Monitoring Practice
|
||||
sidebar_label: SpringBoot Web Application Monitoring Practice
|
||||
---
|
||||
|
||||
:::tip
|
||||
|
||||
In the cloud-native era, monitoring and visualization of SpringBoot applications have become core components of O&M (Operations & Maintenance) systems. Real-time monitoring of application performance is crucial for ensuring system stability.
|
||||
|
||||
:::
|
||||
|
||||
This article introduces an integrated solution using the HertzBeat monitoring system, covering metric collection, visualization, and alarm notifications. It demonstrates the complete operational procedure and monitoring practices.
|
||||
|
||||
## What is HertzBeat
|
||||
|
||||
Apache HertzBeat (incubating) is a real-time monitoring tool with powerful custom monitoring capabilities without Agent. Website monitoring, PING connectivity, port availability, database, operating system, middleware, API monitoring, threshold alarms, alarm notification (email, WeChat, Ding Ding Feishu).
|
||||
|
||||
**github: <https://github.com/apache/hertzbeat>**
|
||||
|
||||
## Install HertzBeat
|
||||
|
||||
> For production environments, it is recommended to deploy HertzBeat using **PostgreSQL** + **VictoriaMetrics**.
|
||||
|
||||
1. Deploy HertzBeat
|
||||
|
||||
> Download the installation package
|
||||
>
|
||||
> Download the `apache-hertzbeat-xxx-incubating-bin.tar.gz` installation package corresponding to your system environment from the [Download Page](https://hertzbeat.apache.org/docs/download). Extract the package to a host directory, e.g., `/opt/hertzbeat`.
|
||||
|
||||
```sh
|
||||
tar zxvf apache-hertzbeat-xxx-incubating-bin.tar.gz
|
||||
```
|
||||
|
||||
2. Quick Install PostgreSQL + VictoriaMetrics Services
|
||||
|
||||
> Install PostgreSQL via Docker
|
||||
|
||||
```sh
|
||||
docker run -d --name postgresql -p 5432:5432 -v "$PWD/postgresql:/var/lib/postgresql/data" -e POSTGRES_USER=root -e POSTGRES_PASSWORD=123456 -e TZ=Asia/Shanghai postgres:15
|
||||
```
|
||||
|
||||
> Install VictoriaMetrics via Docker
|
||||
|
||||
```sh
|
||||
docker run -d -p 8428:8428 -v "$PWD/victoria-metrics-data:/victoria-metrics-data" --name victoria-metrics victoriametrics/victoria-metrics:v1.95.1
|
||||
```
|
||||
|
||||
3. Modify HertzBeat's Configuration File
|
||||
|
||||
> Switch the metadata storage data source
|
||||
>
|
||||
> Modify the `hertzbeat/config/application.yml` configuration file to use the PostgreSQL service.
|
||||
|
||||
:::note
|
||||
For PostgreSQL configuration details, please refer to the documentation: [Meta Store PostgreSQL (Recommended)](https://hertzbeat.apache.org/docs/start/postgresql-change)
|
||||
:::
|
||||
|
||||
> Configure the time-series database for metric storage
|
||||
>
|
||||
> Modify the `hertzbeat/config/application.yml` configuration file to enable the VictoriaMetrics service for storing metrics.
|
||||
|
||||
:::note
|
||||
For VictoriaMetrics configuration details, please refer to the documentation: [Metrics Store VictoriaMetrics (Recommended)](https://hertzbeat.apache.org/docs/start/victoria-metrics-init)
|
||||
:::
|
||||
|
||||
4. Start HertzBeat
|
||||
|
||||
> Use the `startup.sh` script located in the `bin` directory of the extracted installation folder. For Windows environments, use `startup.bat`.
|
||||
>
|
||||
> After successful startup, access `http://localhost:1157` in your browser to begin. The default username and password are `admin/hertzbeat`.
|
||||
|
||||
```sh
|
||||
./startup.sh
|
||||
```
|
||||
|
||||
## SpringBoot Application Configuration
|
||||
|
||||
1. Enable Actuator Configuration
|
||||
|
||||
> Add the following dependencies to project's `pom.xml`:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-registry-prometheus</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
> Configure `application.yml` to expose endpoints:
|
||||
|
||||
```yml
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: '*'
|
||||
enabled-by-default: true
|
||||
metrics:
|
||||
export:
|
||||
prometheus:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
> *Note: If your project also introduces authentication related dependencies, such as springboot security, the interfaces exposed by SpringBoot Actor may be intercepted. In this case, you need to manually release these interfaces. Taking springboot security as an example, you should add the following code to the Security Configuration class:*
|
||||
|
||||
```java
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter{
|
||||
@Override
|
||||
protected void configure(HttpSecurity httpSecurity) throws Exception{
|
||||
httpSecurity
|
||||
// Configure the interfaces to be opened
|
||||
.antMatchers("/actuator/**").permitAll()
|
||||
.antMatchers("/metrics/**").permitAll()
|
||||
.antMatchers("/trace").permitAll()
|
||||
.antMatchers("/heapdump").permitAll()
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. Verify Endpoints
|
||||
|
||||
> After starting SpringBoot application, request the following URL:
|
||||
>
|
||||
> * `http://<your-host>:<port>/actuator`: Verify the enabled endpoints.
|
||||
> * `http://<your-host>:<port>/actuator/prometheus`: Should return metric data in Prometheus format.
|
||||
|
||||
## Monitor SpringBoot Application
|
||||
|
||||
1. Add AUTO Monitor
|
||||
|
||||
> HertzBeat Page -> Monitors -> New Monitor -> AUTO -> Prometheus Task
|
||||
|
||||

|
||||
|
||||
2. Fill in Key Parameters
|
||||
|
||||
> **Target Host**: The server address of the SpringBoot application (without the protocol prefix like `http://` or `https://`).
|
||||
>
|
||||
> **Port**: The application service port (e.g., `8080`).
|
||||
>
|
||||
> **Endpoint Path**: `/actuator/prometheus`
|
||||
>
|
||||
> Tasks can be managed using label classification, such as adding Bind Labels like `env=test`.
|
||||
|
||||

|
||||
|
||||
3. View Detected Metric Data
|
||||
|
||||
> Click on the newly created monitor to view detailed metric data, historical charts, etc.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Grafana Visualization Integration (Optional)
|
||||
|
||||
1. Grafana Dashboard Configuration
|
||||
|
||||
> Enable Grafana's embedding feature and enable anonymous access.
|
||||
|
||||
:::note
|
||||
For complete configuration details, please refer to the documentation: [Grafana Historical Charts](https://hertzbeat.apache.org/docs/help/grafana_dashboard)
|
||||
:::
|
||||
|
||||
2. Embed Grafana Dashboard in HertzBeat Monitor
|
||||
|
||||
> After configuring and enabling Grafana, restart the HertzBeat service. Then, in the newly added AUTO monitor settings, enable Grafana Templates and upload a Grafana dashboard template.
|
||||
>
|
||||
> For example: In Grafana, select `hertzbeat-victoria-metrics` as the data source. Then click on the dashboard:「Share」→「Export」→「Save to file」to download the template and upload this file to the HertzBeat monitor.
|
||||
|
||||

|
||||
|
||||
3. View Grafana Dashboard
|
||||
|
||||
> Go to the AUTO monitor page, click the Grafana icon button, and view the integrated Grafana dashboard.
|
||||
|
||||

|
||||
|
||||
## Alarm and Notification Interlinkage
|
||||
|
||||
1. HertzBeat Alarm Configuration
|
||||
|
||||
> HertzBeat Page -> Alerting -> Threshold -> New -> New Threshold Rule
|
||||
|
||||

|
||||
|
||||
> HertzBeat provides two types of threshold rule settings: **RealTime Calculation** and **Periodically execute**. Here, we use the **Periodically execute** threshold rule as an example.
|
||||
>
|
||||
> * **Rule Name**: Name of the threshold rule.
|
||||
> * **Threshold Rule**: Enter the rule for monitoring the metric (supports `PromQL`).
|
||||
> * **Execution Period**: The time interval for periodically executing the threshold calculation.
|
||||
> * **Alarm Severity**: The alarm level that triggers the threshold, from low to high: Warning,Critical,Emergency.
|
||||
> * **Trigger Times**: Set how many times the threshold is triggered before sending an alarm.
|
||||
> * **Alarm Content**: Enter the content of the monitoring alarm(supports variables).
|
||||
|
||||
2. Set Threshold Rule
|
||||
|
||||
> For example, to monitor the CPU usage of the SpringBoot application, add a threshold rule like: `system_cpu_usage{job="Jolly_Vulture_43vT"} > 0.01`
|
||||
>
|
||||
>You can create many combinations of threshold rules. Users can set up richer alarm rules based on their specific needs.
|
||||
|
||||

|
||||
|
||||
> Finally, you can see the triggered alarm in the alarm center.
|
||||
|
||||

|
||||
|
||||
3. Alarm Notification
|
||||
|
||||
> HertzBeat Page -> Notification -> Notice Receiver -> New Receiver
|
||||
|
||||

|
||||
|
||||
> HertzBeat Page -> Notification -> Notice Policy -> New Notice Policy -> Select Recipient(s) and Enable Notification
|
||||
|
||||

|
||||
|
||||
4. OK! When the threshold is triggered, we will receive the corresponding alarm message. If no notification is configured, you can also view the alarm information in the alarm center.
|
||||
|
||||
## Quick Summary
|
||||
|
||||
This article demonstrates how users can build a complete monitoring system within minutes using simple configurations, highlighting several advantages of HertzBeat:
|
||||
|
||||
* Monitors SpringBoot Actuator endpoints without needing to deploy Exporter or Agent, supporting custom metric collection and alert rules.
|
||||
* Lightweight: Compared to the traditional Prometheus + AlertManager combination, HertzBeat simplifies the deployment and maintenance process.
|
||||
* Seamless Integration with Grafana: HertzBeat pushes collected time-series data to Grafana in real-time for building visualization dashboards.
|
||||
* Integrated Monitoring + Alerting + Notification: Combines full protocol coverage, real-time/periodic threshold detection, and multi-channel notifications (DingTalk/Feishu/Webhook, etc.).
|
||||
|
||||
------
|
||||
|
||||
## Wrapping Up
|
||||
|
||||
That concludes this practical guide on monitoring SpringBoot applications with HertzBeat. Of course, this functionality is just the tip of the iceberg for HertzBeat. If you think hertzbeat is a good open source project, please give it a **Star** on GitHub and Gitee! Your Star motivate our continuous improvement! Feel free to light up the little star ✨
|
||||
|
||||
**Making monitoring simpler, looking forward to building the ecosystem together!** 💝
|
||||
|
||||
**GitHub: <https://github.com/apache/hertzbeat>**
|
||||
|
||||
**Gitee: <https://gitee.com/hertzbeat/hertzbeat>**
|
||||
@@ -60,94 +60,6 @@ After the installation you can check if the Docker version normally output at th
|
||||
|
||||
4. Restart HertzBeat
|
||||
|
||||
### Use VictoriaMetrics in Cluster Mode
|
||||
|
||||
VictoriaMetrics provides a **cluster mode** with separate components for ingestion (`vminsert`), storage (`vmstorage`), and querying (`vmselect`). The following section explains how to deploy VictoriaMetrics in cluster mode and integrate it with HertzBeat.
|
||||
|
||||
#### 1. Deploy VictoriaMetrics Cluster via Docker Compose
|
||||
|
||||
Create a `docker-compose.yml` file with the following content:
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
vmstorage1:
|
||||
image: victoriametrics/vmstorage
|
||||
command:
|
||||
- "-retentionPeriod=1"
|
||||
- "-storageDataPath=/storage"
|
||||
volumes:
|
||||
- vmstorage-data:/storage
|
||||
ports:
|
||||
- "8400:8400" # vminsert connects here
|
||||
- "8401:8401" # vmselect connects here
|
||||
|
||||
vminsert:
|
||||
image: victoriametrics/vminsert
|
||||
command:
|
||||
- "-storageNode=vmstorage1:8400"
|
||||
- "-httpAuth.username=root"
|
||||
- "-httpAuth.password=root"
|
||||
ports:
|
||||
- "8480:8480" # ingestion endpoint
|
||||
|
||||
vmselect:
|
||||
image: victoriametrics/vmselect
|
||||
command:
|
||||
- "-storageNode=vmstorage1:8401"
|
||||
- "-httpAuth.username=root"
|
||||
- "-httpAuth.password=root"
|
||||
ports:
|
||||
- "8481:8481" # query endpoint
|
||||
|
||||
volumes:
|
||||
vmstorage-data:
|
||||
```
|
||||
|
||||
Start the cluster with:
|
||||
|
||||
```shell
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
Check all components are running via:
|
||||
|
||||
```shell
|
||||
docker ps
|
||||
```
|
||||
|
||||
#### 2. Configure HertzBeat for Cluster Mode
|
||||
|
||||
Edit the `hertzbeat/config/application.yml` configuration file as follows:
|
||||
|
||||
```yaml
|
||||
warehouse:
|
||||
store:
|
||||
jpa:
|
||||
enabled: false
|
||||
victoria-metrics:
|
||||
cluster:
|
||||
enabled: true
|
||||
select:
|
||||
url: http://127.0.0.1:8481
|
||||
username: root
|
||||
password: root
|
||||
insert:
|
||||
url: http://127.0.0.1:8480
|
||||
username: root
|
||||
password: root
|
||||
```
|
||||
|
||||
**Note:**
|
||||
|
||||
- Set `enabled: true` under `cluster` to enable cluster mode.
|
||||
- Ensure the URLs match your Docker Compose network or deployment addresses.
|
||||
|
||||
#### 3. Restart HertzBeat
|
||||
|
||||
Once configured, restart HertzBeat to connect to the VictoriaMetrics cluster.
|
||||
|
||||
### FAQ
|
||||
|
||||
1. Do both the time series databases need to be configured? Can they both be used?
|
||||
|
||||
@@ -47,12 +47,16 @@
|
||||
"message": "Server Monitor",
|
||||
"description": "The label for category Server in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.alert": {
|
||||
"message": "Alerting",
|
||||
"description": "The label for category alert in sidebar docs"
|
||||
"sidebar.docs.category.threshold": {
|
||||
"message": "Threshold Alarm Setting",
|
||||
"description": "The label for category threshold in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.reduce": {
|
||||
"message": "Alarm Reduce Setting",
|
||||
"description": "The label for category reduce in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.notice": {
|
||||
"message": "Notification",
|
||||
"message": "Alarm Notice Setting",
|
||||
"description": "The label for category notice in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.mid": {
|
||||
|
||||
+3
-3
@@ -99,7 +99,7 @@
|
||||
|
||||
mysql:[https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.18.zip)
|
||||
oracle(如果你要监控oracle,这两个驱动是必须的):
|
||||
[https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar](https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar)
|
||||
[https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar)
|
||||
[https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar?utm_source=mavenlibs.com)
|
||||
接下来,像之前那样运行启动脚本,即可体验最新的HertzBeat1.6.0!
|
||||
|
||||
@@ -175,7 +175,7 @@ oracle(如果你要监控oracle,这两个驱动是必须的):
|
||||
|
||||
mysql:[https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.18.zip)
|
||||
oracle(如果你要监控oracle,这两个驱动是必须的):
|
||||
[https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar](https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar)
|
||||
[https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar)
|
||||
[https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar?utm_source=mavenlibs.com)
|
||||
接下来,像之前那样 Docker 运行启动 HertzBeat,即可体验最新的HertzBeat1.6.0!
|
||||
|
||||
@@ -259,7 +259,7 @@ oracle(如果你要监控oracle,这两个驱动是必须的):
|
||||
|
||||
mysql:[https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.18.zip)
|
||||
oracle(如果你要监控oracle,这两个驱动是必须的):
|
||||
[https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar](https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar)
|
||||
[https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar)
|
||||
[https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar?utm_source=mavenlibs.com)
|
||||
接下来,像之前那样 Docker 运行启动,即可体验最新的HertzBeat1.6.0!
|
||||
|
||||
|
||||
-103
@@ -1,103 +0,0 @@
|
||||
# HertzBeat 升级指导-(Docker Mode)
|
||||
|
||||
## Docker 方式升级 - Mysql 数据库
|
||||
|
||||
1. 数据备份
|
||||
|
||||
- 备份数据库,将 mysql 数据手动做备份,按需备份
|
||||
|
||||
```bash
|
||||
mysqldump -h<HOST-IP> -P<PORT> -uroot -p"PASSWORD" <库名> hertzbeat_backup-`date +%Y-%m-%d`.sql #单库备份
|
||||
mysqldump -h<HOST-IP> -P<PORT> -uroot -p"PASSWORD" > hertzbeat_backup-`date +%Y-%m-%d`.sql # 整库备份
|
||||
```
|
||||
|
||||
- 备份配置文件及数据目录
|
||||
|
||||
```bash
|
||||
mv application.yml application-bak.yml && mv sureness-bak.yml
|
||||
cp -R data data-`date +%Y-%m-%d`. bak
|
||||
```
|
||||
|
||||
2. 关闭 并移除 HertzBeat 容器
|
||||
|
||||
```shell
|
||||
docker stop hertzbeat && docker rm hertzbeat
|
||||
```
|
||||
|
||||
3. 升级数据库脚本
|
||||
|
||||
打开[https://github.com/apache/hertzbeat/tree/master/hertzbeat-manager/src/main/resources/db/migration](https://github.com/apache/hertzbeat/tree/master/hertzbeat-manager/src/main/resources/db/migration), 选择你使用的数据库的目录下相应的 `V160__update_column.sql`文件在 Mysql 执行升级 sql。
|
||||
|
||||
4. 更换镜像重新启动 HertzBeat 容器
|
||||
|
||||
```bash
|
||||
$ docker run -d -p 1157:1157 -p 1158:1158 \
|
||||
-v $(pwd)/data:/opt/hertzbeat/data \
|
||||
-v $(pwd)/logs:/opt/hertzbeat/logs \
|
||||
-v $(pwd)/application.yml:/opt/hertzbeat/config/application.yml \
|
||||
-v $(pwd)/sureness.yml:/opt/hertzbeat/config/sureness.yml \
|
||||
--restart=always \
|
||||
--name hertzbeat apache/hertzbeat:v1.7.0
|
||||
```
|
||||
|
||||
5. 升级配置文件
|
||||
|
||||
参考备份配置根据自己的需求基础上进行修改。
|
||||
|
||||
- `application.yml`一般需要修改以下部分
|
||||
|
||||
默认为:
|
||||
|
||||
```yaml
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
username: root
|
||||
password: root
|
||||
url: jdbc:mysql://localhost:3306/hertzbeat?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
|
||||
hikari:
|
||||
max-lifetime: 120000
|
||||
|
||||
jpa:
|
||||
show-sql: false
|
||||
database-platform: org.eclipse.persistence.platform.database.MySQLPlatform
|
||||
database: mysql
|
||||
properties:
|
||||
eclipselink:
|
||||
logging:
|
||||
level: SEVERE
|
||||
```
|
||||
|
||||
- `sureness.yml`修改是可选的,一般在你需要修改账号密码时
|
||||
|
||||
```yaml
|
||||
# account info config
|
||||
# eg: admin has role [admin,user], password is hertzbeat
|
||||
# eg: tom has role [user], password is hertzbeat
|
||||
# eg: lili has role [guest], plain password is lili, salt is 123, salted password is 1A676730B0C7F54654B0E09184448289
|
||||
account:
|
||||
- appId: admin
|
||||
credential: hertzbeat
|
||||
role: [admin]
|
||||
- appId: tom
|
||||
credential: hertzbeat
|
||||
role: [user]
|
||||
- appId: guest
|
||||
credential: hertzbeat
|
||||
role: [guest]
|
||||
- appId: lili
|
||||
# credential = MD5(password + salt)
|
||||
# plain password: hertzbeat
|
||||
# attention: digest authentication does not support salted encrypted password accounts
|
||||
credential: 94C6B34E7A199A9F9D4E1F208093B489
|
||||
salt: 123
|
||||
role: [user]
|
||||
```
|
||||
|
||||
6. 添加相应的数据库驱动
|
||||
|
||||
由于 apache 基金会对于 license 合规的要求,HertzBeat 的安装包不能包含 mysql,oracle 等 gpl 许可的依赖,需要用户自行添加,用户可通过以下链接自行下载驱动 jar 放到本地 `ext-lib`目录下,然后启动时将`ext-lib`挂载到容器的 `/opt/hertzbeat/ext-lib`目录。
|
||||
|
||||
mysql:[https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.18.zip)
|
||||
oracle(如果你要监控 oracle,这两个驱动是必须的):
|
||||
[https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar)
|
||||
[https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar?utm_source=mavenlibs.com)
|
||||
-103
@@ -1,103 +0,0 @@
|
||||
# HertzBeat 从 1.6.1 版本升级到 1.7.0 版本指引-(Helm Mode)
|
||||
|
||||
## 1. 前置准备
|
||||
|
||||
1. 确保已安装以下工具:
|
||||
|
||||
- Helm 3.x
|
||||
- kubectl
|
||||
- Git (可选)
|
||||
|
||||
2. 确认当前部署信息:
|
||||
|
||||
```bash
|
||||
helm list -n <your-namespace>
|
||||
# 如果你老版本的chart包不见了,可以使用以下命令导出values.yaml文件
|
||||
helm get values hertzbeat -n <your-namespace> > hertzbeat-1.6.1-values.yaml
|
||||
```
|
||||
|
||||
3. 数据备份
|
||||
|
||||
> 1. 若使用了自定义监控模版
|
||||
>
|
||||
> - 需要备份 `kubectl cp hertzbeat/hertzbeat-978477f84-fr894:/opt/hertzbeat/define ./define` 当前运行 pod里面的 `/opt/hertzbeat/define` 目录到当前主机下,如果做了持久化 请拷贝持久化目录
|
||||
> - `kubectl cp hertzbeat/hertzbeat-978477f84-fr894:/opt/hertzbeat/define ./define`
|
||||
>
|
||||
> 2. 若使用外置关系型数据库 Mysql, PostgreSQL数据
|
||||
>
|
||||
> - 一般使用helm部署部署都做了持久化,可以选择拷贝持久化目录,也可以通过mysqldump、pgdump等工具完成备份
|
||||
>
|
||||
> ```bash
|
||||
> kubectl get pvc -n hertzbeat
|
||||
> NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
|
||||
> hertzbeat-database Bound pvc-c63cf479-0033-423b-8466-eb00aa181657 4Gi RWO standard 68d
|
||||
> hertzbeat-ext-lib Bound pvc-31fee163-1211-424f-8966-e3e805c23ff5 1Gi RWX standard 68d
|
||||
> hertzbeat-tsdb Bound pvc-4f2ef614-0302-4a4c-8dd4-e68b34e9061c 4Gi RWO standard 68d
|
||||
> ```
|
||||
|
||||
## 2. 升级步骤
|
||||
|
||||
### 1. 拉取最新Chart到本地
|
||||
|
||||
```bash
|
||||
helm repo update
|
||||
helm pull hertzbeat/hertzbeat --version 1.7.0 --untar
|
||||
cd hertzbeat
|
||||
```
|
||||
|
||||
或者从GitHub仓库获取(按需修改Chart):
|
||||
|
||||
```bash
|
||||
git clone https://github.com/hertzbeat/helm-charts.git
|
||||
cd helm-charts/charts/hertzbeat
|
||||
```
|
||||
|
||||
### 2. 修改values.yaml
|
||||
|
||||
比较并合并您的自定义配置到新版本values.yaml:
|
||||
|
||||
```bash
|
||||
# 使用diff工具比较新旧values文件
|
||||
diff -u ../hertzbeat-1.6.1-values.yaml values.yaml
|
||||
vimdiff 对着修改,改完继续diff,无输出则正常
|
||||
```
|
||||
|
||||
常见需要关注的配置项:
|
||||
|
||||
- 镜像版本: `image.tag`
|
||||
- 资源限制: `resources`
|
||||
- 持久化配置: `persistence`
|
||||
- 服务类型: `service.type`
|
||||
- Ingress配置
|
||||
- 数据库配置(如果使用外部数据库)
|
||||
|
||||
### 3. 测试升级(干运行)
|
||||
|
||||
```bash
|
||||
helm upgrade hertzbeat . -n <your-namespace> \
|
||||
--values values.yaml \
|
||||
--dry-run \
|
||||
--debug
|
||||
```
|
||||
|
||||
### 4. 执行升级
|
||||
|
||||
```bash
|
||||
helm upgrade hertzbeat . -n <your-namespace> \
|
||||
--values values.yaml \
|
||||
--atomic \ # 升级失败自动回滚
|
||||
--timeout 10m # 设置超时时间
|
||||
```
|
||||
|
||||
### 5. 验证升级
|
||||
|
||||
```bash
|
||||
# 检查发布状态
|
||||
helm status hertzbeat -n <your-namespace>
|
||||
|
||||
# 检查Pod状态
|
||||
kubectl get pods -n <your-namespace> -l app.kubernetes.io/instance=hertzbeat
|
||||
|
||||
# 检查日志
|
||||
kubectl logs -n <your-namespace> -l app.kubernetes.io/instance=hertzbeat --tail=100
|
||||
```
|
||||
-102
@@ -1,102 +0,0 @@
|
||||
# hertzbeat 升级指导-docker模式
|
||||
|
||||
## Docker 方式升级 - Mysql数据库
|
||||
|
||||
1. 数据备份
|
||||
- 备份数据库,将mysql数据库手动做备份,按需备份
|
||||
|
||||
```bash
|
||||
mysqldump -h<HOST-IP> -P<PORT> -uroot -p"PASSWORD" <库名> hertzbeat_backup-`date +%Y-%m-%d`.sql #单库备份
|
||||
mysqldump -h<HOST-IP> -P<PORT> -uroot -p"PASSWORD" > hertzbeat_backup-`date +%Y-%m-%d`.sql # 整库备份
|
||||
```
|
||||
|
||||
- 备份配置文件及数据目录
|
||||
|
||||
```bash
|
||||
mv application.yml application-bak.yml && mv sureness-bak.yml
|
||||
cp -R data data-`date +%Y-%m-%d`. bak
|
||||
```
|
||||
|
||||
2. 关闭 并HertzBeat 容器
|
||||
|
||||
```shell
|
||||
docker stop hertzbeat && docker rm hertzbeat
|
||||
```
|
||||
|
||||
3. 升级数据库脚本
|
||||
|
||||
打开[https://github.com/apache/hertzbeat/tree/master/hertzbeat-manager/src/main/resources/db/migration](https://github.com/apache/hertzbeat/tree/master/hertzbeat-manager/src/main/resources/db/migration), 选择你使用的数据库的目录下相应的 `V160__update_column.sql`文件在 Mysql 执行升级sql。
|
||||
|
||||
4. 更换镜像重新启动HertzBeat 容器
|
||||
|
||||
```bash
|
||||
$ docker run -d -p 1157:1157 -p 1158:1158 \
|
||||
-v $(pwd)/data:/opt/hertzbeat/data \
|
||||
-v $(pwd)/logs:/opt/hertzbeat/logs \
|
||||
-v $(pwd)/application.yml:/opt/hertzbeat/config/application.yml \
|
||||
-v $(pwd)/sureness.yml:/opt/hertzbeat/config/sureness.yml \
|
||||
--restart=always \
|
||||
--name hertzbeat apache/hertzbeat:v1.7.0
|
||||
```
|
||||
|
||||
5. 升级配置文件
|
||||
|
||||
参考备份配置根据自己的需求基础上进行修改。
|
||||
|
||||
- `application.yml`一般需要修改以下部分
|
||||
|
||||
默认为:
|
||||
|
||||
```yaml
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
username: root
|
||||
password: root
|
||||
url: jdbc:mysql://localhost:3306/hertzbeat?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
|
||||
hikari:
|
||||
max-lifetime: 120000
|
||||
|
||||
jpa:
|
||||
show-sql: false
|
||||
database-platform: org.eclipse.persistence.platform.database.MySQLPlatform
|
||||
database: mysql
|
||||
properties:
|
||||
eclipselink:
|
||||
logging:
|
||||
level: SEVERE
|
||||
```
|
||||
|
||||
- `sureness.yml`修改是可选的,一般在你需要修改账号密码时
|
||||
|
||||
```yaml
|
||||
# account info config
|
||||
# eg: admin has role [admin,user], password is hertzbeat
|
||||
# eg: tom has role [user], password is hertzbeat
|
||||
# eg: lili has role [guest], plain password is lili, salt is 123, salted password is 1A676730B0C7F54654B0E09184448289
|
||||
account:
|
||||
- appId: admin
|
||||
credential: hertzbeat
|
||||
role: [admin]
|
||||
- appId: tom
|
||||
credential: hertzbeat
|
||||
role: [user]
|
||||
- appId: guest
|
||||
credential: hertzbeat
|
||||
role: [guest]
|
||||
- appId: lili
|
||||
# credential = MD5(password + salt)
|
||||
# plain password: hertzbeat
|
||||
# attention: digest authentication does not support salted encrypted password accounts
|
||||
credential: 94C6B34E7A199A9F9D4E1F208093B489
|
||||
salt: 123
|
||||
role: [user]
|
||||
```
|
||||
|
||||
6. 添加相应的数据库驱动
|
||||
|
||||
由于apache基金会对于license合规的要求,HertzBeat的安装包不能包含mysql,oracle等gpl许可的依赖,需要用户自行添加,用户可通过以下链接自行下载驱动 jar 放到本地 `ext-lib`目录下,然后启动时将`ext-lib`挂载到容器的 `/opt/hertzbeat/ext-lib`目录。
|
||||
|
||||
mysql:[https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.18.zip)
|
||||
oracle(如果你要监控oracle,这两个驱动是必须的):
|
||||
[https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar)
|
||||
[https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar?utm_source=mavenlibs.com)
|
||||
@@ -51,12 +51,16 @@
|
||||
"message": "中间件监控",
|
||||
"description": "The label for category mid in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.alert": {
|
||||
"message": "阈值告警",
|
||||
"sidebar.docs.category.threshold": {
|
||||
"message": "阈值告警配置",
|
||||
"description": "The label for category threshold in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.reduce": {
|
||||
"message": "告警收敛配置",
|
||||
"description": "The label for category reduce in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.notice": {
|
||||
"message": "消息通知",
|
||||
"message": "告警通知配置",
|
||||
"description": "The label for category notice in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Others": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
id: ai_config
|
||||
id: aiConfig
|
||||
title: AI 快速入门
|
||||
sidebar_label: AI 快速入门
|
||||
keywords: [人工智能 AI]
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: alert_enterprise_wechat_app
|
||||
title: 企业微信应用告警通知
|
||||
sidebar_label: 告警企业微信应用通知
|
||||
sidebar_label: 企业微信应用告警通知
|
||||
keywords: [开源告警系统, 开源监控告警系统, 企业微信应用告警通知]
|
||||
---
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
id: alert_integration
|
||||
title: 告警集成
|
||||
sidebar_label: 告警集成
|
||||
keywords: [开源监控, 告警集成, 告警管理, 多源告警]
|
||||
---
|
||||
|
||||
> HertzBeat 的告警集成模块旨在实现对来自不同第三方监控与可观测性平台的告警进行统一接收、标准化处理与智能分发。作为一个集中式“告警中心”,HertzBeat 能够高效地接入外部系统的告警信息,并执行灵活的告警管理策略。
|
||||
|
||||
### 核心能力
|
||||
|
||||
- **多源告警接入**:支持从 Webhook、Prometheus、Alertmanager、SkyWalking、腾讯云等主流平台接收告警消息
|
||||
- **告警格式标准化**:将来自不同平台的告警数据转换为 HertzBeat 内部统一格式,便于后续处理
|
||||
- **丰富的告警处理机制**,包括:
|
||||
- **分组收敛**:根据标签对告警进行分组管理,对时间段的相同重复告警去重收敛
|
||||
- **抑制**:在满足特定条件时自动屏蔽次要告警
|
||||
- **静默**:在系统维护或已知异常期间临时关闭告警通知,避免干扰
|
||||
|
||||
### 已支持的告警来源
|
||||
|
||||
HertzBeat 当前已支持以下第三方监控平台的告警接入:
|
||||
|
||||
- **Webhook**:通用接入方式,支持自定义告警格式推送。
|
||||
- **Prometheus**:可以在 Prometheus Server 的 Alertmanager 配置中直接配置 HertzBeat 的服务地址,使用 HertzBeat 替换 Alertmanager 直接来接收处理 Prometheus Server 的告警信息。
|
||||
- **Alertmanager**:支持将 Prometheus AlertManager 的告警发送到 HertzBeat 告警平台。
|
||||
- **SkyWalking**:将 SkyWalking 的告警通过 Webhook 方式发送到 HertzBeat 告警平台。
|
||||
- **腾讯云监控**:将腾讯云的告警通过 Webhook 方式发送到 HertzBeat 告警平台。
|
||||
- **更多**:HertzBeat 正在积极扩展其集成支持。如果暂时没有找到你需要的集成,活跃的社区也可以协助你添加。
|
||||
|
||||
你可以通过 HertzBeat 的“集成接入”界面查看具体的接入方式和配置示例。
|
||||
|
||||

|
||||
@@ -1,43 +1,55 @@
|
||||
---
|
||||
id: alert_threshold
|
||||
title: 告警阈值规则配置
|
||||
sidebar_label: 阈值规则
|
||||
title: 阈值告警配置
|
||||
sidebar_label: 阈值告警配置
|
||||
---
|
||||
|
||||
:::tip
|
||||
告警阈值规则是 `HertzBeat` 的核心功能,用户可以通过阈值规则来配置告警的触发条件。
|
||||
阈值规则支持实时阈值和计划阈值,实时阈值可以在监控数据采集时直接触发告警,计划阈值支持 PromQL 等表达式在指定时间段内计算触发告警。
|
||||
支持可视化页面配置或更高灵活性的表达式规则配置,支持配置触发次数,告警级别,通知模版,关联指定监控等。
|
||||
:::
|
||||
> 对监控指标配置告警阈值(警告告警,严重告警,紧急告警),系统根据阈值配置和采集指标数据计算触发告警。
|
||||
|
||||
## 操作步骤
|
||||
|
||||
### 1. 监控服务设置标签(可选)
|
||||
|
||||
如果您需要对告警进行分类,可以对监控的目标设置标签。如:您有多个Linux系统需要监控,并且每个系统监控指标不同,比如:A服务器可用内存大于1G,B服务器可用内存大于2G,那么您可以为A服务器和B服务器分别设置标签,然后针对标签进行告警配置。
|
||||
|
||||
#### 创建标签
|
||||
|
||||
依次点击 **标签管理 -> 新增标签**
|
||||
|
||||

|
||||
|
||||
如上图所示,新增标签,这里我们设置标签为: linux:dev (开发环境使用Linux)
|
||||
|
||||
#### 配置标签
|
||||
|
||||
TODO 图片名称更新
|
||||

|
||||
|
||||
如上图所示,我们点击`新增标签`
|
||||
|
||||

|
||||
|
||||
选择我们的标签,这里演示选择`linux:dev`标签
|
||||
|
||||
### 创建阈值规则
|
||||
|
||||
依次点击 **【阈值规则】->【新增阈值规则】-> 【配置后确定】**
|
||||
|
||||

|
||||
|
||||
## 实时阈值
|
||||
上图配置具体说明:
|
||||
|
||||
> 实时阈值是指在监控数据采集时直接触发告警,适用于对实时性要求较高的场景。
|
||||
|
||||
### 创建实时阈值规则
|
||||
|
||||
> 系统页面 -> 告警 -> 告警阈值 -> 新增阈值 -> 新增实时阈值规则
|
||||
|
||||
配置阈值,例如: 选择SSL证书指标对象,配置告警表达式-当指标`expired`为`true`触发,即`equals(expired,"true")` , 设置告警级别通知模版信息等。
|
||||
|
||||

|
||||
|
||||
配置项说明:
|
||||
|
||||
- **阈值名称**:定义此阈值规则的唯一名称
|
||||
- **指标对象**:选择我们需要配置阈值的监控指标对象 例如:网站监控类型下的 -> 响应时间指标
|
||||
- **阈值规则**:配置具体指标的告警触发规则,支持图形界面和表达式规则,表达式环境变量和操作符见页面提示。阈值表达式详细帮助见 [阈值表达式帮助](alert_threshold_expr)
|
||||
- **关联监控**:应用此阈值规则到指定的监控对象(支持直接绑定和标签关联),若未配置,则应用到所有符合此阈值类型规则的监控对象上。
|
||||
- **指标对象**:选择我们需要配置阈值的监控指标对象 例如:网站监控类型下的 -> summary指标集合下的 -> responseTime响应时间指标
|
||||
- **阈值规则**:根据此表达式来计算判断是否触发阈值,表达式环境变量和操作符见页面提示,例如:设置响应时间大于50触发告警,表达式为 `responseTime > 50`。阈值表达式详细帮助见 [阈值表达式帮助](alert_threshold_expr)
|
||||
- **告警级别**:触发阈值的告警级别,从低到高依次为:警告-warning,严重-critical,紧急-emergency
|
||||
- **触发次数**:设置触发阈值多少次之后才会真正的触发告警
|
||||
- **通知模版**:告警触发后发送的通知信息模版,模版环境变量见页面提示,例如:`${__instancename__} ${__metric__} 指标的值为 ${responseTime},大于 50 触发告警`
|
||||
- **附加标签**:给此阈值规则附加标签,当产生告警时,此标签也会附加到告警上。
|
||||
- **附加声明**:给此阈值规则附加声明信息(声明内容支持环境变量),当产生告警时,此声明信息会被渲染并附加到告警上。
|
||||
- **通知模版**:告警触发后发送的通知信息模版,模版环境变量见页面提示,例如:`${app}.${metrics}.${metric}指标的值为${responseTime},大于50触发告警`
|
||||
- **绑定标签**:选择我们需要应用的标签,如果不选择标签则会应用到设置指标对象对应的所有服务上。
|
||||
- **应用全局**:设置此阈值是否对全局的此类指标都应用有效,默认否。新增阈值后还需将阈值与监控对象关联,这样阈值才会对此监控生效。
|
||||
- **恢复通知**:告警触发后是否发送恢复通知,默认不发送。
|
||||
- **启用告警**:此告警阈值配置开启生效或关闭
|
||||
|
||||
**阈值告警配置完毕,已经被成功触发的告警信息可以在【告警中心】看到。**
|
||||
**若需要将告警信息邮件,微信,钉钉飞书通知给相关人员,可以在【消息通知】配置。**
|
||||
**若需要将告警信息邮件,微信,钉钉飞书通知给相关人员,可以在【告警通知】配置。**
|
||||
|
||||
## 计划阈值
|
||||
其它问题可以通过交流群ISSUE反馈哦!
|
||||
|
||||
@@ -9,7 +9,7 @@ keywords: [开源监控系统, 开源数据库监控, Oracle数据库监控]
|
||||
|
||||
### 注意, 必须添加 ORACLE jdbc 驱动 jar
|
||||
|
||||
- 下载 ORACLE jdbc 驱动 jar 包,例如 [ojdbc8.jar](https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar) [oracle-i18n](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar)
|
||||
- 下载 ORACLE jdbc 驱动 jar 包,例如 [ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar) [oracle-i18n](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar)
|
||||
- 将 jar 包复制到 `hertzbeat/ext-lib` 目录下。
|
||||
- 重启 HertzBeat 服务。
|
||||
|
||||
|
||||
@@ -7,7 +7,14 @@ slug: /
|
||||
|
||||
> 实时监控系统,无需Agent,性能集群,兼容Prometheus,自定义监控和状态页构建能力。
|
||||
|
||||
**官网: [hertzbeat.apache.org](https://hertzbeat.apache.org)**
|
||||
[](https://discord.gg/Fb6M73htGr)
|
||||
[](https://www.reddit.com/r/hertzbeat/)
|
||||
[](https://x.com/hertzbeat1024)
|
||||
[](https://www.bestpractices.dev/projects/8139)
|
||||
[](https://hub.docker.com/r/apache/hertzbeat)
|
||||
[](https://artifacthub.io/packages/search?repo=hertzbeat)
|
||||
[](https://qm.qq.com/q/FltGGGIX2m)
|
||||
[](https://www.youtube.com/channel/UCri75zfWX0GHqJFPENEbLow)
|
||||
|
||||
## 🎡 <font color="green">介绍</font>
|
||||
|
||||
@@ -83,9 +90,9 @@ HertzBeat 使用这些已有的标准协议或规范,将他们抽象规范可
|
||||
- 和更多自定义监控模版。
|
||||
- 通知支持 `Discord` `Slack` `Telegram` `邮件` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱`。
|
||||
|
||||
### 自定义能力
|
||||
### 强大自定义功能
|
||||
|
||||
> 由前面的**监控模版**介绍,大概清楚了 `HertzBeat` 拥有的自定义功能。
|
||||
> 由前面的**监控模版**介绍,大概清楚了 `HertzBeat` 拥有的强大自定义功能。
|
||||
> 我们将每个监控类型都视为一个监控模版,不管是官方内置的还是后期用户自定义新增的。用户都可以方便的通过修改监控模版来新增修改删除监控指标。
|
||||
> 模版里面包含各个协议的使用配置,环境变量,指标转换,指标计算,单位转换,指标采集等一系列功能,帮助用户能采集到自己想要的监控指标。
|
||||
|
||||
@@ -163,7 +170,7 @@ Docker 环境下运行一条命令即可:`docker run -d -p 1157:1157 -p 1158:1
|
||||
|
||||
- 全局概览页面,分类展示了当前监控大类别数量分布,用户可直观查看当前的监控类型与数量并点击跳转至对应监控类型进行维护管理。
|
||||
- 展示当前注册的采集器集群状态,包括采集器的上线状态,监控任务,启动时间,IP地址,名称等。
|
||||
- 下发展示了最近告警信息列表,告警级别分布情况等。
|
||||
- 下发展示了最近告警信息列表,告警级别分布情况,告警处理率情况。
|
||||
|
||||

|
||||
|
||||
@@ -214,7 +221,7 @@ Docker 环境下运行一条命令即可:`docker run -d -p 1157:1157 -p 1158:1
|
||||
|
||||

|
||||
|
||||
### 新增监控
|
||||
### 新增修改监控
|
||||
|
||||
- 新增或修改指定监控类型的监控实例,配置对端监控的IP,端口等参数,设置采集周期,采集任务调度方式,支持提前探测可用性等。
|
||||
- 页面上配置的监控参数由对应监控类型的监控模版所定义,用户可以通过修改监控模版来修改页面配置参数。
|
||||
@@ -227,7 +234,7 @@ Docker 环境下运行一条命令即可:`docker run -d -p 1157:1157 -p 1158:1
|
||||
- 监控的数据详情页面,展示了当前监控的基本参数信息,监控指标数据信息。
|
||||
- 监控实时数据报告,以小卡片列表的形式展示了当前监控的所有指标实时值,用户可根据实时值参考配置告警阈值规则。
|
||||
- 监控历史数据报告,以趋势图表的形式展示了当前监控数值类型的指标的历史值,支持查询小时,天,月的历史数据,支持配置页面刷新时间。
|
||||
- ⚠️注意监控历史图表需配置外置时序数据库才能获取完整功能。
|
||||
- ⚠️注意监控历史图表需配置外置时序数据库才能获取完整功能,时序数据库支持: IOTDB, TDengine, InfluxDB, GreptimeDB
|
||||
|
||||

|
||||
|
||||
@@ -242,32 +249,22 @@ Docker 环境下运行一条命令即可:`docker run -d -p 1157:1157 -p 1158:1
|
||||
|
||||
### 阈值规则
|
||||
|
||||
- 告警阈值规则是 `HertzBeat` 的核心功能,用户可以通过阈值规则来配置告警的触发条件。
|
||||
- 阈值规则支持实时阈值和计划阈值,实时阈值可以在监控数据采集时直接触发告警,计划阈值支持 PromQL 等表达式在指定时间段内计算触发告警。
|
||||
- 阈值规则支持可视化页面配置或更高灵活性的表达式规则配置,支持配置触发次数,告警级别,通知模版,关联指定监控等。
|
||||
- 对于监控的可用性状态设置阈值规则,特定指标的值超过我们预期范围时发出告警,这些都可以在阈值规则这里配置。
|
||||
- 告警级别分为三级:通知告警,严重告警,紧急告警。
|
||||
- 阈值规则支持可视化页面配置或表达式规则配置,灵活性更高。
|
||||
- 支持配置触发次数,告警级别,通知模版,关联指定监控等。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### 告警集成
|
||||
|
||||
- 统一管理不同第三方平台告警,集成接入第三方监控观测系统的告警消息,对其进行分组,收敛,抑制,静默,分发通知等。
|
||||
|
||||

|
||||
|
||||
### 告警分组
|
||||
### 告警收敛
|
||||
|
||||
- 分组收敛支持对指定分组标签的告警进行分组合并,对时间段的相同重复告警去重收敛。
|
||||
- 当阈值规则触发告警或外部告警上报后,会进入到分组收敛进行告警分组,告警去重,以避免大量告警消息导致告警风暴。
|
||||
- 当通过阈值规则判断触发告警后,会进入到告警收敛,告警收敛会根据规则对特定时间段的重复告警消息去重收敛,已避免大量重复性告警导致接收人告警麻木。
|
||||
- 告警收敛规则支持重复告警生效时间段,标签匹配和告警级别匹配过滤。
|
||||
|
||||

|
||||
|
||||
### 告警抑制
|
||||
|
||||
- 告警抑制用于配置告警之间的抑制关系,比如同一实例下高级别告警抑制低级别告警。
|
||||
- 当某个告警发生时,可以抑制其他告警的产生。例如,当服务器宕机时,可以抑制该服务器上的所有告警。
|
||||
|
||||

|
||||
|
||||
### 告警静默
|
||||
@@ -276,6 +273,8 @@ Docker 环境下运行一条命令即可:`docker run -d -p 1157:1157 -p 1158:1
|
||||
- 此应用场景如用户在系统维护中,无需发已知告警。用户在工作日时间才会接收告警消息,用户在晚上需避免打扰等。
|
||||
- 告警静默规则支持一次性时间段或周期性时间段,支持标签匹配和告警级别匹配。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### 消息通知
|
||||
@@ -292,8 +291,6 @@ Docker 环境下运行一条命令即可:`docker run -d -p 1157:1157 -p 1158:1
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### 监控模版
|
||||
|
||||
- HertzBeat 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
@@ -301,29 +298,10 @@ Docker 环境下运行一条命令即可:`docker run -d -p 1157:1157 -p 1158:1
|
||||
|
||||

|
||||
|
||||
### 采集集群
|
||||
|
||||
- 用户可以通过配置采集器集群来实现对大规模监控任务的分布式采集。
|
||||
- 采集器集群支持多节点部署,支持自动负载均衡,自动故障转移等。
|
||||
- 支持多隔离网络的统一管理,云边协同。
|
||||
|
||||

|
||||
|
||||
### 状态页面
|
||||
|
||||
- 基于 HertzBeat 快速构建自己产品的对外状态页,轻松向用户传达您产品服务的实时状态。例如 Github 提供的服务状态页 <https://www.githubstatus.com>。
|
||||
- 支持状态页组件状态和监控状态联动同步,故障事件维护管理机制等。提高您的透明度,专业度和用户信任,降低沟通成本。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
**更多功能欢迎探索呀。Have Fun!**
|
||||
**还有更多强大的功能快去探索呀。Have Fun!**
|
||||
|
||||
---
|
||||
|
||||
**Github: <https://github.com/apache/hertzbeat>**
|
||||
|
||||
**Home: <https://hertzbeat.apache.org/>**
|
||||
|
||||
@@ -35,7 +35,7 @@ sidebar_label: Docker方式安装
|
||||
- `-v $(pwd)/logs:/opt/hertzbeat/logs` : (可选) 挂载日志文件到本地主机方便查看
|
||||
- `-v $(pwd)/application.yml:/opt/hertzbeat/config/application.yml` : (可选) 挂载配置文件到容器中(请确保本地已有此文件)。[下载源](https://github.com/apache/hertzbeat/raw/master/script/application.yml)
|
||||
- `-v $(pwd)/sureness.yml:/opt/hertzbeat/config/sureness.yml` : (可选) 挂载账户配置文件到容器中(请确保本地已有此文件)。[下载源](https://github.com/apache/hertzbeat/raw/master/script/sureness.yml)
|
||||
- `-v $(pwd)/ext-lib:/opt/hertzbeat/ext-lib` : (可选) 挂载外部的第三方 JAR 包 [mysql-jdbc](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip) [oracle-jdbc](https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar) [oracle-i18n](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar)
|
||||
- `-v $(pwd)/ext-lib:/opt/hertzbeat/ext-lib` : (可选) 挂载外部的第三方 JAR 包 [mysql-jdbc](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip) [oracle-jdbc](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar) [oracle-i18n](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar)
|
||||
- `--name hertzbeat` : (可选) 命名容器名称为 hertzbeat
|
||||
- `--restart=always` : (可选) 配置容器自动重启。
|
||||
- `apache/hertzbeat` : 使用[官方应用镜像](https://hub.docker.com/r/apache/hertzbeat)来启动容器, 若网络超时可用`quay.io/tancloud/hertzbeat`代替。
|
||||
|
||||
+31
-29
@@ -1,44 +1,40 @@
|
||||
---
|
||||
id: ssl-cert-practice
|
||||
title: SSL 证书过期监控案例
|
||||
sidebar_label: SSL 证书过期监控案例
|
||||
title: SSL证书过期监控使用案例
|
||||
sidebar_label: SSL证书过期监控使用案例
|
||||
---
|
||||
|
||||
:::tip
|
||||
现在大部分网站都默认支持 HTTPS,我们申请的证书一般是3个月或者1年,很容易随着时间的流逝SSL证书过期了我们却没有第一时间发现,或者在过期之前没有及时更新证书。
|
||||
:::
|
||||
|
||||
这篇文章介绍如果使用 hertzbeat 监控系统来检测我们网站的SSL证书有效期,当证书过期时或证书快过期前几天,给我们发告警消息。
|
||||
|
||||
## HertzBeat 是什么
|
||||
#### HertzBeat 是什么
|
||||
|
||||
Apache HertzBeat (incubating) 一个拥有强大自定义监控能力,无需Agent的实时监控工具。网站监测,PING连通性,端口可用性,数据库,操作系统,中间件,API监控,阈值告警,告警通知(邮件微信钉钉飞书)。
|
||||
|
||||
github: <https://github.com/apache/hertzbeat>
|
||||
|
||||
## 安装 HertzBeat
|
||||
#### 安装 HertzBeat
|
||||
|
||||
1. `docker` 环境仅需一条命令即可安装
|
||||
|
||||
`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat apache/hertzbeat`
|
||||
`docker run -d -p 1157:1157 --name hertzbeat apache/hertzbeat`
|
||||
|
||||
2. 安装成功浏览器访问 `localhost:1157` 即可开始,默认账号密码 `admin/hertzbeat`
|
||||
|
||||
:::note
|
||||
生产环境建议完整部署方式, 参考 <https://hertzbeat.apache.org/docs/start/docker-compose-deploy>
|
||||
:::
|
||||
#### 监控SSL证书
|
||||
|
||||
## 监控 SSL 证书
|
||||
1. 点击新增SSL证书监控
|
||||
|
||||
1. 新增 SSL 证书监控
|
||||
|
||||
> 系统页面 -> 监控菜单 -> 新增监控 -> 服务监控 -> SSL 证书 -> 新增 SSL 证书
|
||||
> 系统页面 -> 监控菜单 -> SSL证书 -> 新增SSL证书
|
||||
|
||||

|
||||
|
||||
2. 配置监控网站
|
||||
|
||||
> 我们这里举例监控百度网站, 配置监控host域名,名称,采集间隔等。
|
||||
>
|
||||
> 点击确定 注意⚠️新增前默认会先去测试网站连接性,连接成功才会新增,当然也可以把**是否测试**按钮置灰。
|
||||
|
||||

|
||||
|
||||
@@ -48,46 +44,52 @@ github: <https://github.com/apache/hertzbeat>
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
4. 设置阈值(证书过期时触发)
|
||||
|
||||
> 系统页面 -> 告警 -> 告警阈值 -> 新增阈值 -> 新增实时阈值规则
|
||||
> 系统页面 -> 告警 -> 告警阈值 -> 新增阈值
|
||||
|
||||

|
||||
|
||||
> 配置阈值,选择SSL证书指标对象,配置告警表达式-当指标`expired`为`true`触发,即`equals(expired,"true")` , 设置告警级别通知模版信息等。
|
||||
|
||||

|
||||
|
||||
> 阈值规则还有其它功能可以配置, 比如阈值关联指定监控, 触发次数, 关联标签等等。
|
||||
|
||||
5. 设置阈值(证书过期前一周触发)
|
||||
|
||||
> 同理如上,选择代码阈值规则,新增配置阈值,配置告警表达式-当指标有效期时间戳 `end_timestamp`,`now()`函数为当前时间戳,若配置提前一周触发告警即:`end_timestamp <= (now() + 604800000)` , 其中 `604800000` 为7天总时间差毫秒值。
|
||||
> 关联阈值与监控, 在阈值列表设置此阈值应用于哪些监控。
|
||||
|
||||

|
||||
|
||||
> 最终可以在告警中心看到已触发的告警。
|
||||
5. 设置阈值(证书过期前一周触发)
|
||||
|
||||
> 同理如上,新增配置阈值,配置告警表达式-当指标有效期时间戳 `end_timestamp`,`now()`函数为当前时间戳,若配置提前一周触发告警即:`end_timestamp <= (now() + 604800000)` , 其中 `604800000` 为7天总时间差毫秒值。
|
||||
|
||||

|
||||
|
||||
> 最终可以在告警中心看到已触发的告警。
|
||||
|
||||

|
||||
|
||||
6. 告警通知(通过钉钉微信飞书等及时通知)
|
||||
|
||||
> 系统页面 -> 消息通知 -> 通知媒介 -> 新增接收对象
|
||||
> 监控系统 -> 告警通知 -> 新增接收人
|
||||
|
||||

|
||||

|
||||
|
||||
钉钉微信飞书等 token 配置可以参考帮助文档
|
||||
钉钉微信飞书等token配置可以参考帮助文档
|
||||
|
||||
<https://hertzbeat.apache.org/docs/help/alert_feishu>
|
||||
<https://hertzbeat.apache.org/docs/help/alert_dingtalk>
|
||||
<https://tancloud.cn/docs/help/alert_dingtalk>
|
||||
|
||||
> 消息通知 -> 通知策略 -> 新增告警通知策略 -> 将刚才配置的接收人启用通知
|
||||
> 告警通知 -> 新增告警通知策略 -> 将刚才配置的接收人启用通知
|
||||
|
||||

|
||||

|
||||
|
||||
7. OK 当阈值触发后我们就可以收到对应告警消息啦,如果没有配通知,也可以在告警中心查看告警信息。
|
||||
|
||||
----
|
||||
|
||||
## 结束搞定
|
||||
#### 完
|
||||
|
||||
监控SSL证书的实践就到这里,当然对hertzbeat来说这个功能只是冰山一角,如果您觉得hertzbeat这个开源项目不错的话欢迎给我们在GitHub Gitee star哦,灰常感谢。感谢老铁们的支持。笔芯!
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: 1.6.0-update
|
||||
title: 如何升级到 1.6.0
|
||||
sidebar_label: 1.6.0 升级指南
|
||||
title: 如何升级到1.6.0
|
||||
sidebar_label: 1.6.0升级指南
|
||||
---
|
||||
|
||||
## HertzBeat 1.6.0 升级指南
|
||||
@@ -105,7 +105,7 @@ sidebar_label: 1.6.0 升级指南
|
||||
|
||||
mysql:[https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.18.zip)
|
||||
oracle(如果你要监控oracle,这两个驱动是必须的):
|
||||
[https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar](https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar)
|
||||
[https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar)
|
||||
[https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar?utm_source=mavenlibs.com)
|
||||
接下来,像之前那样运行启动脚本,即可体验最新的HertzBeat1.6.0!
|
||||
|
||||
@@ -181,7 +181,7 @@ oracle(如果你要监控oracle,这两个驱动是必须的):
|
||||
|
||||
mysql:[https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.18.zip)
|
||||
oracle(如果你要监控oracle,这两个驱动是必须的):
|
||||
[https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar](https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar)
|
||||
[https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar)
|
||||
[https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar?utm_source=mavenlibs.com)
|
||||
接下来,像之前那样 Docker 运行启动 HertzBeat,即可体验最新的HertzBeat1.6.0!
|
||||
|
||||
@@ -265,7 +265,7 @@ oracle(如果你要监控oracle,这两个驱动是必须的):
|
||||
|
||||
mysql:[https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.18.zip)
|
||||
oracle(如果你要监控oracle,这两个驱动是必须的):
|
||||
[https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar](https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar)
|
||||
[https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar)
|
||||
[https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar?utm_source=mavenlibs.com)
|
||||
接下来,像之前那样 Docker 运行启动,即可体验最新的HertzBeat1.6.0!
|
||||
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
id: 1.7.0-update
|
||||
title: 如何升级到 1.7.0
|
||||
sidebar_label: 1.7.0 升级指南
|
||||
---
|
||||
|
||||
## HertzBeat 1.7.0 升级指南
|
||||
|
||||
:::note
|
||||
该指南适用于 1.6.x 向1.6.0 版本升级 1.7.0
|
||||
如果你使用更老的版本,建议使用导出功能重新安装,或先升级到 1.6.0 再按本指南升级到 1.7.0
|
||||
:::
|
||||
|
||||
其它请参考 [版本更新指引](upgrade)
|
||||
|
||||
## 升级注意
|
||||
|
||||
### 变更数据库
|
||||
|
||||
在 1.7.0 中,我们使用 `label` 代替 `tag`,在某些环境中,我们需要删除数据库中的表 `hzb_tag_monitor_bind`。
|
||||
|
||||
```sql
|
||||
DELETE FROM hzb_tag_monitor_bind;
|
||||
```
|
||||
|
||||
### 更新告警阈值
|
||||
|
||||
在 1.7.0 中,我们重新设计了新的告警阈值,包括实时阈值和计划阈值。
|
||||
我们需要手动重新配置告警阈值和告警分组。
|
||||
|
||||
:::tip
|
||||
新版本没有内置的默认阈值规则,比如之前的可用性阈值。
|
||||
如果您发现监控下线后没有告警,您需要自己配置相应的可用性阈值。
|
||||
:::
|
||||
|
||||
## 通过导出导入升级
|
||||
|
||||
若不想如上繁琐的脚本升级方式,可以直接将老环境的监控任务导出导入。
|
||||
|
||||
- 部署一套最新版本的新环境
|
||||
- 在页面上将老环境的监控任务导出再导入
|
||||
-256
@@ -1,256 +0,0 @@
|
||||
---
|
||||
id: springboot-auto-practice
|
||||
title: SpringBoot Web应用监控案例
|
||||
sidebar_label: SpringBoot Web应用监控案例
|
||||
---
|
||||
|
||||
:::tip
|
||||
|
||||
在云原生时代,SpringBoot应用的监控与可视化已然成为运维体系的核心环节,实时监控应用性能是保障系统稳定性的关键。
|
||||
|
||||
:::
|
||||
|
||||
这篇文章介绍使用 HertzBeat 监控系统实现从指标采集、可视化到告警通知的一体化解决方案,并展示完整操作流程与监控实践。
|
||||
|
||||
## HertzBeat 是什么
|
||||
|
||||
Apache HertzBeat (incubating) 一个拥有强大自定义监控能力,无需Agent的实时监控工具。网站监测,PING连通性,端口可用性,数据库,操作系统,中间件,API监控,阈值告警,告警通知(邮件微信钉钉飞书)。
|
||||
|
||||
**github: <https://github.com/apache/hertzbeat>**
|
||||
|
||||
## 安装 HertzBeat
|
||||
|
||||
> 生产环境中推荐使用 **PostgreSQL** + **VictoriaMetrics** 服务的方式部署 HertzBeat。
|
||||
|
||||
1. 部署 HertzBeat
|
||||
|
||||
> 下载安装包
|
||||
>
|
||||
> 从 [下载页面](https://hertzbeat.apache.org/zh-cn/docs/download) 下载您系统环境对应的安装包版本 `apache-hertzbeat-xxx-incubating-bin.tar.gz`。解压安装包到主机,如: `/opt/hertzbeat`
|
||||
|
||||
```sh
|
||||
tar zxvf apache-hertzbeat-xxx-incubating-bin.tar.gz
|
||||
```
|
||||
|
||||
2. 快速安装 PostgreSQL + VictoriaMetrics 服务
|
||||
|
||||
> Docker 安装 PostgreSQL
|
||||
|
||||
```sh
|
||||
docker run -d --name postgresql -p 5432:5432 -v "$PWD/postgresql:/var/lib/postgresql/data" -e POSTGRES_USER=root -e POSTGRES_PASSWORD=123456 -e TZ=Asia/Shanghai postgres:15
|
||||
```
|
||||
|
||||
> Docker 安装 VictoriaMetrics
|
||||
|
||||
```sh
|
||||
docker run -d -p 8428:8428 -v "$PWD/victoria-metrics-data:/victoria-metrics-data" --name victoria-metrics victoriametrics/victoria-metrics:v1.95.1
|
||||
```
|
||||
|
||||
3. 修改 HertzBeat 的配置文件
|
||||
|
||||
> 切换元数据储存数据源
|
||||
>
|
||||
> 修改位于 `hertzbeat/config/application.yml` 的配置文件,替换为 PostgreSQL 服务。
|
||||
|
||||
:::note
|
||||
|
||||
PostgreSQL 配置请查阅文档:[元数据存储PostgreSQL(推荐)](https://hertzbeat.apache.org/zh-cn/docs/start/postgresql-change)
|
||||
:::
|
||||
|
||||
> 配置时序数据库存储指标
|
||||
>
|
||||
> 同理,修改 `hertzbeat/config/application.yml` 的配置文件,开启 VictoriaMetrics 服务用于存储指标。
|
||||
|
||||
:::note
|
||||
|
||||
VictoriaMetrics 配置请查阅文档:[指标数据存储VictoriaMetrics(推荐)](https://hertzbeat.apache.org/zh-cn/docs/start/victoria-metrics-init)
|
||||
:::
|
||||
|
||||
4. 启动 HertzBeat
|
||||
|
||||
> 在上述解压好的安装目录 bin 下的启动脚本 `startup.sh`,windows 环境下为 `startup.bat`。
|
||||
>
|
||||
> 启动成功后,浏览器访问 `localhost:1157` 即可开始,默认账号密码 `admin/hertzbeat`。
|
||||
|
||||
```sh
|
||||
./startup.sh
|
||||
```
|
||||
|
||||
## SpringBoot 应用配置
|
||||
|
||||
1. 开启 Actuator 配置
|
||||
|
||||
> 在项目 `pom.xml` 中添加依赖:
|
||||
>
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-registry-prometheus</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
> 配置 `application.yml` 暴露端点:
|
||||
>
|
||||
|
||||
```yml
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: '*'
|
||||
enabled-by-default: true
|
||||
metrics:
|
||||
export:
|
||||
prometheus:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
> *注意:如果你的项目里还引入了认证相关的依赖,比如 springboot-security ,那么 SpringBoot Actuator 暴露出的接口可能会被拦截,此时需要你手动放开这些接口,以 springboot-security 为例,需要在 SecurityConfig 配置类中加入以下代码:*
|
||||
|
||||
```java
|
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter{
|
||||
@Override
|
||||
protected void configure(HttpSecurity httpSecurity) throws Exception{
|
||||
httpSecurity
|
||||
// 配置要放开的接口
|
||||
.antMatchers("/actuator/**").permitAll()
|
||||
.antMatchers("/metrics/**").permitAll()
|
||||
.antMatchers("/trace").permitAll()
|
||||
.antMatchers("/heapdump").permitAll()
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. 验证端点
|
||||
|
||||
> SpringBoot 程序启动后,请求URL:
|
||||
>
|
||||
> - `http://<your-host>:<port>/actuator`: 验证已启用的端点,
|
||||
> - `http://<your-host>:<port>/actuator/prometheus`: 应返回指标数据。
|
||||
|
||||
## 监控 SpringBoot 应用
|
||||
|
||||
1. 新增 AUTO 监控
|
||||
|
||||
> 系统页面 -> 监控中心 -> 新增监控 -> AUTO -> Prometheus任务
|
||||
>
|
||||
|
||||

|
||||
|
||||
2. 填写关键参数
|
||||
|
||||
> **目标Host**:SpringBoot 应用服务器地址(不带协议头,例如: https://, http:// )
|
||||
>
|
||||
> **端口**:应用服务端口(例如: 8080)
|
||||
>
|
||||
> **端点路径**:`/actuator/prometheus`
|
||||
>
|
||||
> 可以使用标签分类来管理任务,如添加`env=test`等业务相关标签。
|
||||
|
||||

|
||||
|
||||
3. 查看检测指标数据
|
||||
|
||||
> 点击进入新建监控,可以查看指标数据详情及指标历史图表等。
|
||||
>
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Grafana可视化集成 (可选)
|
||||
|
||||
1. Grafana 图表配置
|
||||
|
||||
> 需启用 Grafana 可嵌入功能,并开启匿名访问。
|
||||
|
||||
:::note
|
||||
|
||||
完整配置请参考文档:[Grafana历史图表](https://hertzbeat.apache.org/zh-cn/docs/help/grafana_dashboard)
|
||||
:::
|
||||
|
||||
2. 在 HertzBeat 监控中嵌入 Grafana 仪表盘
|
||||
|
||||
> 配置启用 Grafana 后,重启 HertzBeat 服务,在新增的 AUTO 监控中启用并上传 Grafana 模板。
|
||||
>
|
||||
> 比如:Grafana 数据源选择`hertzbeat-victoria-metrics`,然后在仪表盘点击:「Share」→「Export」→「Save to file」下载模板并上传至 HertzBeat 监控中。
|
||||
|
||||

|
||||
|
||||
3. 查看 Grafana 图表
|
||||
|
||||
> 进入新增 AUTO 监控页面,点击 Grafana 图标按钮,即可查看 Grafana 图表。
|
||||
|
||||

|
||||
|
||||
## 告警与通知联动
|
||||
|
||||
1. HertzBeat 告警配置
|
||||
|
||||
> 系统页面 -> 告警 -> 阈值规则 -> 新增 -> 新增阈值
|
||||
>
|
||||
|
||||

|
||||
|
||||
> HertzBeat 提供了 **实时计算** 和 **计划周期** 两种类型的阈值规则设置,这里我们以 **计划周期** 阈值规则为例。
|
||||
>
|
||||
> - **阈值名称**:阈值规则名称
|
||||
> - **阈值规则**:填写指标监测的规则(支持 `PromQL`)
|
||||
> - **执行周期**:周期性执行阈值计算的时间间隔
|
||||
> - **告警级别**:触发阈值的告警级别,从低到高依次为: 警告-warning,严重-critical,紧急-emergency
|
||||
> - **触发次数**:设置触发阈值多少次之后才会发送告警
|
||||
> - **告警内容**:填写监测告警的内容(支持填写变量)
|
||||
|
||||
2. 设置阈值规则
|
||||
|
||||
> 比如监测 SpringBoot 应用程序的 CPU 占用,添加阈值规则:`system_cpu_usage{job="Jolly_Vulture_43vT"} > 0.01`
|
||||
>
|
||||
> 可以设置的阈值规则组合有很多,用户可以根据自身需要设置更丰富的告警规则。
|
||||
|
||||

|
||||
|
||||
> 最后可以在 告警中心 看到已触发的告警。
|
||||
>
|
||||
|
||||

|
||||
|
||||
3. 告警通知
|
||||
|
||||
> 系统页面 -> 消息通知 -> 通知媒介 -> 新增接收对象
|
||||
>
|
||||
|
||||

|
||||
|
||||
> 系统页面 -> 消息通知 -> 通知策略 -> 新增通知策略 -> 选择接收对象并启用通知
|
||||
>
|
||||
|
||||

|
||||
|
||||
4. OK!当阈值规则触发后我们就可以收到对应告警消息啦,如果没有配置通知,也可以在告警中心查看告警信息。
|
||||
|
||||
## 小总结
|
||||
|
||||
这篇文章通过简洁的监控配置,用户在几分钟之内就可搭建完整的监控体系,这充分体现了 HertzBeat 的几点优势:
|
||||
|
||||
- 无需部署 Exporter 和 Agent 即可监控 SpringBoot Actuator 端点,支持自定义指标采集与告警规则。
|
||||
- 轻量化:相比传统 Prometheus + AlertManager 组合,HertzBeat 简化了部署和维护流程。
|
||||
- 无缝集成 Grafana,HertzBeat 将采集的时序数据实时推送至 Grafana 构建可视化仪表盘。
|
||||
- 监控+告警+通知功能整合联动,全协议覆盖 + 实时/周期阈值检测 + 多通道通知(钉钉/飞书/Webhook等)。
|
||||
|
||||
------
|
||||
|
||||
## 结束搞定
|
||||
|
||||
监控 SpringBoot 应用的实践就到这里,当然对 HertzBeat 来说这个功能只是冰山一角,如果您觉得 HertzBeat 这个开源项目不错的话欢迎在 GitHub、Gitee 点 **Star** 哦,您的 Star 是我们持续优化的动力!欢迎点亮小星星✨
|
||||
|
||||
**让监控更简单,期待与您共建生态!** 💝
|
||||
|
||||
**github: <https://github.com/apache/hertzbeat>**
|
||||
|
||||
**gitee: <https://gitee.com/hertzbeat/hertzbeat>**
|
||||
@@ -62,94 +62,6 @@ Docker 工具自身的下载请参考 [Docker官网文档](https://docs.docker.c
|
||||
|
||||
4. 重启 HertzBeat
|
||||
|
||||
### 使用 VictoriaMetrics 集群模式(Cluster Mode)
|
||||
|
||||
VictoriaMetrics 支持 **集群模式**,将数据写入(`vminsert`)、存储(`vmstorage`)与查询(`vmselect`)分别由独立组件负责。以下是如何部署 VictoriaMetrics 集群并集成至 HertzBeat 的说明。
|
||||
|
||||
#### 1. 使用 Docker Compose 部署 VictoriaMetrics 集群
|
||||
|
||||
创建一个名为 `docker-compose.yml` 的文件,内容如下:
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
vmstorage1:
|
||||
image: victoriametrics/vmstorage
|
||||
command:
|
||||
- "-retentionPeriod=1"
|
||||
- "-storageDataPath=/storage"
|
||||
volumes:
|
||||
- vmstorage-data:/storage
|
||||
ports:
|
||||
- "8400:8400" # 提供给 vminsert 写入连接
|
||||
- "8401:8401" # 提供给 vmselect 查询连接
|
||||
|
||||
vminsert:
|
||||
image: victoriametrics/vminsert
|
||||
command:
|
||||
- "-storageNode=vmstorage1:8400"
|
||||
- "-httpAuth.username=root"
|
||||
- "-httpAuth.password=root"
|
||||
ports:
|
||||
- "8480:8480" # 数据写入端口
|
||||
|
||||
vmselect:
|
||||
image: victoriametrics/vmselect
|
||||
command:
|
||||
- "-storageNode=vmstorage1:8401"
|
||||
- "-httpAuth.username=root"
|
||||
- "-httpAuth.password=root"
|
||||
ports:
|
||||
- "8481:8481" # 查询接口端口
|
||||
|
||||
volumes:
|
||||
vmstorage-data:
|
||||
```
|
||||
|
||||
使用以下命令启动集群:
|
||||
|
||||
```shell
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
使用以下命令确认所有组件是否运行成功:
|
||||
|
||||
```shell
|
||||
docker ps
|
||||
```
|
||||
|
||||
#### 2. 配置 HertzBeat 使用集群模式
|
||||
|
||||
修改 `hertzbeat/config/application.yml` 配置文件,内容如下:
|
||||
|
||||
```yaml
|
||||
warehouse:
|
||||
store:
|
||||
jpa:
|
||||
enabled: false
|
||||
victoria-metrics:
|
||||
cluster:
|
||||
enabled: true
|
||||
select:
|
||||
url: http://127.0.0.1:8481
|
||||
username: root
|
||||
password: root
|
||||
insert:
|
||||
url: http://127.0.0.1:8480
|
||||
username: root
|
||||
password: root
|
||||
```
|
||||
|
||||
**注意事项:**
|
||||
|
||||
- `cluster.enabled` 设置为 `true` 表示启用集群模式;
|
||||
- `select.url` 和 `insert.url` 需与部署时的地址保持一致,确保网络互通。
|
||||
|
||||
#### 3. 重启 HertzBeat
|
||||
|
||||
完成配置后,重启 HertzBeat 以连接至 VictoriaMetrics 集群。
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. 时序数据库是否都需要配置,能不能都用
|
||||
|
||||
+42
-30
@@ -1,43 +1,55 @@
|
||||
---
|
||||
id: alert_threshold
|
||||
title: 告警阈值规则配置
|
||||
sidebar_label: 阈值规则
|
||||
id: alert_threshold
|
||||
title: 阈值告警配置
|
||||
sidebar_label: 阈值告警配置
|
||||
---
|
||||
|
||||
:::tip
|
||||
告警阈值规则是 `HertzBeat` 的核心功能,用户可以通过阈值规则来配置告警的触发条件。
|
||||
阈值规则支持实时阈值和计划阈值,实时阈值可以在监控数据采集时直接触发告警,计划阈值支持 PromQL 等表达式在指定时间段内计算触发告警。
|
||||
支持可视化页面配置或更高灵活性的表达式规则配置,支持配置触发次数,告警级别,通知模版,关联指定监控等。
|
||||
:::
|
||||
> 对监控指标配置告警阈值(警告告警,严重告警,紧急告警),系统根据阈值配置和采集指标数据计算触发告警。
|
||||
|
||||
## 操作步骤
|
||||
|
||||
### 1. 监控服务设置标签(可选)
|
||||
|
||||
如果您需要对告警进行分类,可以对监控的目标设置标签。如:您有多个Linux系统需要监控,并且每个系统监控指标不同,比如:A服务器可用内存大于1G,B服务器可用内存大于2G,那么您可以为A服务器和B服务器分别设置标签,然后针对标签进行告警配置。
|
||||
|
||||
#### 创建标签
|
||||
|
||||
依次点击 **标签管理 -> 新增标签**
|
||||
|
||||

|
||||
|
||||
如上图所示,新增标签,这里我们设置标签为: linux:dev (开发环境使用Linux)
|
||||
|
||||
#### 配置标签
|
||||
|
||||
TODO 图片名称更新
|
||||

|
||||
|
||||
如上图所示,我们点击`新增标签`
|
||||
|
||||

|
||||
|
||||
选择我们的标签,这里演示选择`linux:dev`标签
|
||||
|
||||
### 创建阈值规则
|
||||
|
||||
依次点击 **【阈值规则】->【新增阈值规则】-> 【配置后确定】**
|
||||
|
||||

|
||||
|
||||
## 实时阈值
|
||||
上图配置具体说明:
|
||||
|
||||
> 实时阈值是指在监控数据采集时直接触发告警,适用于对实时性要求较高的场景。
|
||||
|
||||
### 创建实时阈值规则
|
||||
|
||||
> 系统页面 -> 告警 -> 告警阈值 -> 新增阈值 -> 新增实时阈值规则
|
||||
|
||||
配置阈值,例如: 选择SSL证书指标对象,配置告警表达式-当指标`expired`为`true`触发,即`equals(expired,"true")` , 设置告警级别通知模版信息等。
|
||||
|
||||

|
||||
|
||||
配置项说明:
|
||||
|
||||
- **阈值名称**:定义此阈值规则的唯一名称
|
||||
- **指标对象**:选择我们需要配置阈值的监控指标对象 例如:网站监控类型下的 -> 响应时间指标
|
||||
- **阈值规则**:配置具体指标的告警触发规则,支持图形界面和表达式规则,表达式环境变量和操作符见页面提示。阈值表达式详细帮助见 [阈值表达式帮助](alert_threshold_expr)
|
||||
- **关联监控**:应用此阈值规则到指定的监控对象(支持直接绑定和标签关联),若未配置,则应用到所有符合此阈值类型规则的监控对象上。
|
||||
- **指标对象**:选择我们需要配置阈值的监控指标对象 例如:网站监控类型下的 -> summary指标集合下的 -> responseTime响应时间指标
|
||||
- **阈值规则**:根据此表达式来计算判断是否触发阈值,表达式环境变量和操作符见页面提示,例如:设置响应时间大于50触发告警,表达式为 `responseTime > 50`。阈值表达式详细帮助见 [阈值表达式帮助](alert_threshold_expr)
|
||||
- **告警级别**:触发阈值的告警级别,从低到高依次为:警告-warning,严重-critical,紧急-emergency
|
||||
- **触发次数**:设置触发阈值多少次之后才会真正的触发告警
|
||||
- **通知模版**:告警触发后发送的通知信息模版,模版环境变量见页面提示,例如:`${__instancename__} ${__metric__} 指标的值为 ${responseTime},大于 50 触发告警`
|
||||
- **附加标签**:给此阈值规则附加标签,当产生告警时,此标签也会附加到告警上。
|
||||
- **附加声明**:给此阈值规则附加声明信息(声明内容支持环境变量),当产生告警时,此声明信息会被渲染并附加到告警上。
|
||||
- **通知模版**:告警触发后发送的通知信息模版,模版环境变量见页面提示,例如:`${app}.${metrics}.${metric}指标的值为${responseTime},大于50触发告警`
|
||||
- **绑定标签**:选择我们需要应用的标签,如果不选择标签则会应用到设置指标对象对应的所有服务上。
|
||||
- **应用全局**:设置此阈值是否对全局的此类指标都应用有效,默认否。新增阈值后还需将阈值与监控对象关联,这样阈值才会对此监控生效。
|
||||
- **恢复通知**:告警触发后是否发送恢复通知,默认不发送。
|
||||
- **启用告警**:此告警阈值配置开启生效或关闭
|
||||
|
||||
**阈值告警配置完毕,已经被成功触发的告警信息可以在【告警中心】看到。**
|
||||
**若需要将告警信息邮件,微信,钉钉飞书通知给相关人员,可以在【消息通知】配置。**
|
||||
**若需要将告警信息邮件,微信,钉钉飞书通知给相关人员,可以在【告警通知】配置。**
|
||||
|
||||
## 计划阈值
|
||||
其它问题可以通过交流群ISSUE反馈哦!
|
||||
|
||||
@@ -9,7 +9,7 @@ keywords: [开源监控系统, 开源数据库监控, Oracle数据库监控]
|
||||
|
||||
### 注意, 必须添加 ORACLE jdbc 驱动 jar
|
||||
|
||||
- 下载 ORACLE jdbc 驱动 jar 包,例如 [ojdbc8.jar](https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar) [oracle-i18n](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar)
|
||||
- 下载 ORACLE jdbc 驱动 jar 包,例如 [ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar) [oracle-i18n](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar)
|
||||
- 将 jar 包复制到 `hertzbeat/ext-lib` 目录下。
|
||||
- 重启 HertzBeat 服务。
|
||||
|
||||
|
||||
@@ -256,7 +256,7 @@ Docker 环境下运行一条命令即可:`docker run -d -p 1157:1157 -p 1158:1
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
### 告警收敛
|
||||
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ sidebar_label: Docker方式安装
|
||||
- `-v $(pwd)/logs:/opt/hertzbeat/logs` : (可选) 挂载日志文件到本地主机方便查看
|
||||
- `-v $(pwd)/application.yml:/opt/hertzbeat/config/application.yml` : (可选) 挂载配置文件到容器中(请确保本地已有此文件)。[下载源](https://github.com/apache/hertzbeat/raw/master/script/application.yml)
|
||||
- `-v $(pwd)/sureness.yml:/opt/hertzbeat/config/sureness.yml` : (可选) 挂载账户配置文件到容器中(请确保本地已有此文件)。[下载源](https://github.com/apache/hertzbeat/raw/master/script/sureness.yml)
|
||||
- `-v $(pwd)/ext-lib:/opt/hertzbeat/ext-lib` : (可选) 挂载外部的第三方 JAR 包 [mysql-jdbc](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip) [oracle-jdbc](https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar) [oracle-i18n](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar)
|
||||
- `-v $(pwd)/ext-lib:/opt/hertzbeat/ext-lib` : (可选) 挂载外部的第三方 JAR 包 [mysql-jdbc](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip) [oracle-jdbc](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar) [oracle-i18n](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar)
|
||||
- `--name hertzbeat` : (可选) 命名容器名称为 hertzbeat
|
||||
- `--restart=always` : (可选) 配置容器自动重启。
|
||||
- `apache/hertzbeat` : 使用[官方应用镜像](https://hub.docker.com/r/apache/hertzbeat)来启动容器, 若网络超时可用`quay.io/tancloud/hertzbeat`代替。
|
||||
|
||||
+37
-35
@@ -1,93 +1,95 @@
|
||||
---
|
||||
id: ssl-cert-practice
|
||||
title: SSL证书过期监控使用案例
|
||||
id: ssl-cert-practice
|
||||
title: SSL证书过期监控使用案例
|
||||
sidebar_label: SSL证书过期监控使用案例
|
||||
---
|
||||
|
||||
:::tip
|
||||
现在大部分网站都默认支持 HTTPS,我们申请的证书一般是3个月或者1年,很容易随着时间的流逝SSL证书过期了我们却没有第一时间发现,或者在过期之前没有及时更新证书。
|
||||
:::
|
||||
|
||||
这篇文章介绍如果使用 hertzbeat 监控系统来检测我们网站的SSL证书有效期,当证书过期时或证书快过期前几天,给我们发告警消息。
|
||||
|
||||
## HertzBeat 是什么
|
||||
#### HertzBeat 是什么
|
||||
|
||||
Apache HertzBeat (incubating) 一个拥有强大自定义监控能力,无需Agent的实时监控工具。网站监测,PING连通性,端口可用性,数据库,操作系统,中间件,API监控,阈值告警,告警通知(邮件微信钉钉飞书)。
|
||||
|
||||
github: <https://github.com/apache/hertzbeat>
|
||||
|
||||
## 安装 HertzBeat
|
||||
#### 安装 HertzBeat
|
||||
|
||||
1. `docker` 环境仅需一条命令即可安装
|
||||
|
||||
`docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat apache/hertzbeat`
|
||||
`docker run -d -p 1157:1157 --name hertzbeat apache/hertzbeat`
|
||||
|
||||
2. 安装成功浏览器访问 `localhost:1157` 即可开始,默认账号密码 `admin/hertzbeat`
|
||||
|
||||
:::note
|
||||
生产环境建议完整部署方式, 参考 <https://hertzbeat.apache.org/docs/start/docker-compose-deploy>
|
||||
:::
|
||||
#### 监控SSL证书
|
||||
|
||||
## 监控 SSL 证书
|
||||
1. 点击新增SSL证书监控
|
||||
|
||||
1. 新增 SSL 证书监控
|
||||
> 系统页面 -> 监控菜单 -> SSL证书 -> 新增SSL证书
|
||||
|
||||
> 系统页面 -> 监控菜单 -> 新增监控 -> 服务监控 -> SSL 证书 -> 新增 SSL 证书
|
||||
|
||||

|
||||

|
||||
|
||||
2. 配置监控网站
|
||||
|
||||
> 我们这里举例监控百度网站, 配置监控host域名,名称,采集间隔等。
|
||||
> 我们这里举例监控百度网站, 配置监控host域名,名称,采集间隔等。
|
||||
>
|
||||
> 点击确定 注意⚠️新增前默认会先去测试网站连接性,连接成功才会新增,当然也可以把**是否测试**按钮置灰。
|
||||
|
||||

|
||||

|
||||
|
||||
3. 查看检测指标数据
|
||||
|
||||
> 在监控列表可以查看任务状态,进监控详情可以查看指标数据图表等。
|
||||
> 在监控列表可以查看任务状态,进监控详情可以查看指标数据图表等。
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
4. 设置阈值(证书过期时触发)
|
||||
|
||||
> 系统页面 -> 告警 -> 告警阈值 -> 新增阈值 -> 新增实时阈值规则
|
||||
> 配置阈值,选择SSL证书指标对象,配置告警表达式-当指标`expired`为`true`触发,即`equals(expired,"true")` , 设置告警级别通知模版信息等。
|
||||
> 系统页面 -> 告警 -> 告警阈值 -> 新增阈值
|
||||
|
||||

|
||||

|
||||
|
||||
> 阈值规则还有其它功能可以配置, 比如阈值关联指定监控, 触发次数, 关联标签等等。
|
||||
> 配置阈值,选择SSL证书指标对象,配置告警表达式-当指标`expired`为`true`触发,即`equals(expired,"true")` , 设置告警级别通知模版信息等。
|
||||
|
||||

|
||||
|
||||
> 关联阈值与监控, 在阈值列表设置此阈值应用于哪些监控。
|
||||
|
||||

|
||||
|
||||
5. 设置阈值(证书过期前一周触发)
|
||||
|
||||
> 同理如上,选择代码阈值规则,新增配置阈值,配置告警表达式-当指标有效期时间戳 `end_timestamp`,`now()`函数为当前时间戳,若配置提前一周触发告警即:`end_timestamp <= (now() + 604800000)` , 其中 `604800000` 为7天总时间差毫秒值。
|
||||
> 同理如上,新增配置阈值,配置告警表达式-当指标有效期时间戳 `end_timestamp`,`now()`函数为当前时间戳,若配置提前一周触发告警即:`end_timestamp <= (now() + 604800000)` , 其中 `604800000` 为7天总时间差毫秒值。
|
||||
|
||||

|
||||

|
||||
|
||||
> 最终可以在告警中心看到已触发的告警。
|
||||
> 最终可以在告警中心看到已触发的告警。
|
||||
|
||||

|
||||

|
||||
|
||||
6. 告警通知(通过钉钉微信飞书等及时通知)
|
||||
|
||||
> 系统页面 -> 消息通知 -> 通知媒介 -> 新增接收对象
|
||||
> 监控系统 -> 告警通知 -> 新增接收人
|
||||
|
||||

|
||||

|
||||
|
||||
钉钉微信飞书等 token 配置可以参考帮助文档
|
||||
钉钉微信飞书等token配置可以参考帮助文档
|
||||
|
||||
<https://hertzbeat.apache.org/docs/help/alert_feishu>
|
||||
<https://hertzbeat.apache.org/docs/help/alert_dingtalk>
|
||||
<https://tancloud.cn/docs/help/alert_dingtalk>
|
||||
|
||||
> 消息通知 -> 通知策略 -> 新增告警通知策略 -> 将刚才配置的接收人启用通知
|
||||
> 告警通知 -> 新增告警通知策略 -> 将刚才配置的接收人启用通知
|
||||
|
||||

|
||||

|
||||
|
||||
7. OK 当阈值触发后我们就可以收到对应告警消息啦,如果没有配通知,也可以在告警中心查看告警信息。
|
||||
|
||||
----
|
||||
|
||||
## 结束搞定
|
||||
#### 完
|
||||
|
||||
监控SSL证书的实践就到这里,当然对hertzbeat来说这个功能只是冰山一角,如果您觉得hertzbeat这个开源项目不错的话欢迎给我们在GitHub Gitee star哦,灰常感谢。感谢老铁们的支持。笔芯!
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ sidebar_label: 1.6.0升级指南
|
||||
|
||||
mysql:[https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.18.zip)
|
||||
oracle(如果你要监控oracle,这两个驱动是必须的):
|
||||
[https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar](https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar)
|
||||
[https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar)
|
||||
[https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar?utm_source=mavenlibs.com)
|
||||
接下来,像之前那样运行启动脚本,即可体验最新的HertzBeat1.6.0!
|
||||
|
||||
@@ -181,7 +181,7 @@ oracle(如果你要监控oracle,这两个驱动是必须的):
|
||||
|
||||
mysql:[https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.18.zip)
|
||||
oracle(如果你要监控oracle,这两个驱动是必须的):
|
||||
[https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar](https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar)
|
||||
[https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar)
|
||||
[https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar?utm_source=mavenlibs.com)
|
||||
接下来,像之前那样 Docker 运行启动 HertzBeat,即可体验最新的HertzBeat1.6.0!
|
||||
|
||||
@@ -265,7 +265,7 @@ oracle(如果你要监控oracle,这两个驱动是必须的):
|
||||
|
||||
mysql:[https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.25.zip](https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-8.0.18.zip)
|
||||
oracle(如果你要监控oracle,这两个驱动是必须的):
|
||||
[https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar](https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc8/23.4.0.24.05/ojdbc8-23.4.0.24.05.jar)
|
||||
[https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar](https://download.oracle.com/otn-pub/otn_software/jdbc/234/ojdbc8.jar)
|
||||
[https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar](https://repo.mavenlibs.com/maven/com/oracle/database/nls/orai18n/21.5.0.0/orai18n-21.5.0.0.jar?utm_source=mavenlibs.com)
|
||||
接下来,像之前那样 Docker 运行启动,即可体验最新的HertzBeat1.6.0!
|
||||
|
||||
|
||||
+43
-51
@@ -30,7 +30,7 @@
|
||||
{
|
||||
"type": "category",
|
||||
"label": "upgrade",
|
||||
"items": ["start/1.7.0-update", "start/1.6.0-update", "start/upgrade"]
|
||||
"items": ["start/1.6.0-update", "start/upgrade"]
|
||||
},
|
||||
{
|
||||
"type": "category",
|
||||
@@ -51,10 +51,7 @@
|
||||
{
|
||||
"type": "category",
|
||||
"label": "use-case",
|
||||
"items": [
|
||||
"start/usecase/ssl-cert-practice",
|
||||
"start/usecase/springboot-auto-practice"
|
||||
]
|
||||
"items": ["start/ssl-cert-practice"]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -64,45 +61,6 @@
|
||||
"items": [
|
||||
"help/guide",
|
||||
"help/security_model",
|
||||
{
|
||||
"type": "category",
|
||||
"label": "use-case",
|
||||
"items": [
|
||||
"start/usecase/ssl-cert-practice",
|
||||
"start/usecase/springboot-auto-practice"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "category",
|
||||
"label": "alert",
|
||||
"items": [
|
||||
"help/alarm_center",
|
||||
"help/alert_threshold",
|
||||
"help/alert_threshold_expr",
|
||||
"help/alarm_group",
|
||||
"help/alarm_inhibit",
|
||||
"help/alarm_silence",
|
||||
"help/alert_integration"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "category",
|
||||
"label": "notice",
|
||||
"items": [
|
||||
"help/alert_email",
|
||||
"help/alert_sms",
|
||||
"help/alert_webhook",
|
||||
"help/alert_discord",
|
||||
"help/alert_slack",
|
||||
"help/alert_telegram",
|
||||
"help/alert_wework",
|
||||
"help/alert_dingtalk",
|
||||
"help/alert_feishu",
|
||||
"help/alert_console",
|
||||
"help/alert_enterprise_wechat_app",
|
||||
"help/alert_smn"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "category",
|
||||
"label": "service",
|
||||
@@ -188,10 +146,10 @@
|
||||
"type": "category",
|
||||
"label": "server",
|
||||
"items": [
|
||||
"help/ipmi",
|
||||
"help/hikvision_isapi",
|
||||
"help/dahua",
|
||||
"help/uniview"
|
||||
"help/ipmi",
|
||||
"help/hikvision_isapi",
|
||||
"help/dahua",
|
||||
"help/uniview"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -256,20 +214,54 @@
|
||||
},
|
||||
{
|
||||
"type": "category",
|
||||
"label": "network",
|
||||
"items": ["help/huawei_switch"]
|
||||
"label": "aiConfig",
|
||||
"items": ["help/aiConfig"]
|
||||
},
|
||||
{
|
||||
"type": "category",
|
||||
"label": "custom",
|
||||
"items": ["help/kafka_promql", "help/influxdb_promql"]
|
||||
},
|
||||
{
|
||||
"type": "category",
|
||||
"label": "network",
|
||||
"items": ["help/huawei_switch"]
|
||||
},
|
||||
{
|
||||
"type": "category",
|
||||
"label": "threshold",
|
||||
"items": ["help/alert_threshold", "help/alert_threshold_expr"]
|
||||
},
|
||||
{
|
||||
"type": "category",
|
||||
"label": "reduce",
|
||||
"items": ["help/alarm_group","help/alarm_inhibit"]
|
||||
},
|
||||
"help/alarm_silence",
|
||||
{
|
||||
"type": "category",
|
||||
"label": "notice",
|
||||
"items": [
|
||||
"help/alert_email",
|
||||
"help/alert_sms",
|
||||
"help/alert_webhook",
|
||||
"help/alert_discord",
|
||||
"help/alert_slack",
|
||||
"help/alert_telegram",
|
||||
"help/alert_wework",
|
||||
"help/alert_dingtalk",
|
||||
"help/alert_feishu",
|
||||
"help/alert_console",
|
||||
"help/alert_enterprise_wechat_app",
|
||||
"help/alert_smn"
|
||||
]
|
||||
},
|
||||
"help/bulletin",
|
||||
"help/plugin",
|
||||
"help/time_expression",
|
||||
"help/grafana_dashboard",
|
||||
"help/collector",
|
||||
"help/ai_config",
|
||||
"help/alarm_center",
|
||||
"help/issue"
|
||||
]
|
||||
},
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 251 KiB After Width: | Height: | Size: 95 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user