mirror of
https://github.com/nezhahq/nezha.git
synced 2025-02-02 01:28:13 -05:00
服务延迟报警 [no ci]
This commit is contained in:
parent
eb07b9468b
commit
c60ebd1bae
@ -393,6 +393,9 @@ type monitorForm struct {
|
|||||||
NotificationTag string
|
NotificationTag string
|
||||||
SkipServersRaw string
|
SkipServersRaw string
|
||||||
Duration uint64
|
Duration uint64
|
||||||
|
MinLatency float32
|
||||||
|
MaxLatency float32
|
||||||
|
LatencyNotify string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ma *memberAPI) addOrEditMonitor(c *gin.Context) {
|
func (ma *memberAPI) addOrEditMonitor(c *gin.Context) {
|
||||||
@ -409,6 +412,9 @@ func (ma *memberAPI) addOrEditMonitor(c *gin.Context) {
|
|||||||
m.Notify = mf.Notify == "on"
|
m.Notify = mf.Notify == "on"
|
||||||
m.NotificationTag = mf.NotificationTag
|
m.NotificationTag = mf.NotificationTag
|
||||||
m.Duration = mf.Duration
|
m.Duration = mf.Duration
|
||||||
|
m.LatencyNotify = mf.LatencyNotify == "on"
|
||||||
|
m.MinLatency = mf.MinLatency
|
||||||
|
m.MaxLatency = mf.MaxLatency
|
||||||
err = m.InitSkipServers()
|
err = m.InitSkipServers()
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -48,6 +48,10 @@ type Monitor struct {
|
|||||||
NotificationTag string // 当前服务监控所属的通知组
|
NotificationTag string // 当前服务监控所属的通知组
|
||||||
Cover uint8
|
Cover uint8
|
||||||
|
|
||||||
|
MinLatency float32
|
||||||
|
MaxLatency float32
|
||||||
|
LatencyNotify bool
|
||||||
|
|
||||||
SkipServers map[uint64]bool `gorm:"-" json:"-"`
|
SkipServers map[uint64]bool `gorm:"-" json:"-"`
|
||||||
CronJobID cron.EntryID `gorm:"-" json:"-"`
|
CronJobID cron.EntryID `gorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
15
resource/l10n/zh-CN.toml
vendored
15
resource/l10n/zh-CN.toml
vendored
@ -130,6 +130,21 @@ other = "秒"
|
|||||||
[EnableFailureNotification]
|
[EnableFailureNotification]
|
||||||
other = "启用故障通知"
|
other = "启用故障通知"
|
||||||
|
|
||||||
|
[FailureNotification]
|
||||||
|
other = "故障通知"
|
||||||
|
|
||||||
|
[MaxLatency]
|
||||||
|
other = "最大延迟(ms)"
|
||||||
|
|
||||||
|
[MinLatency]
|
||||||
|
other = "最小延迟(ms)"
|
||||||
|
|
||||||
|
[EnableLatencyNotification]
|
||||||
|
other = "启用延迟通知"
|
||||||
|
|
||||||
|
[LatencyNotification]
|
||||||
|
other = "延迟通知"
|
||||||
|
|
||||||
[IntroductionOfMonitor]
|
[IntroductionOfMonitor]
|
||||||
other = """
|
other = """
|
||||||
类型为 <b>HTTP-GET</b> 时输入URL(带 http/https, HTTPS协议的会顺带监控SSL证书);<br>
|
类型为 <b>HTTP-GET</b> 时输入URL(带 http/https, HTTPS协议的会顺带监控SSL证书);<br>
|
||||||
|
@ -77,6 +77,8 @@ function showFormModal(modelSelector, formID, URL, getData) {
|
|||||||
item.name === "Duration"
|
item.name === "Duration"
|
||||||
) {
|
) {
|
||||||
obj[item.name] = parseInt(item.value);
|
obj[item.name] = parseInt(item.value);
|
||||||
|
} else if (item.name.endsWith("Latency")) {
|
||||||
|
obj[item.name] = parseFloat(item.value);
|
||||||
} else {
|
} else {
|
||||||
obj[item.name] = item.value;
|
obj[item.name] = item.value;
|
||||||
}
|
}
|
||||||
@ -94,9 +96,9 @@ function showFormModal(modelSelector, formID, URL, getData) {
|
|||||||
if (item.name.endsWith("TasksRaw")) {
|
if (item.name.endsWith("TasksRaw")) {
|
||||||
if (item.value.length > 2) {
|
if (item.value.length > 2) {
|
||||||
obj[item.name] = JSON.stringify(
|
obj[item.name] = JSON.stringify(
|
||||||
[...item.value.matchAll(/\d+/gm)].map((k) =>
|
[...item.value.matchAll(/\d+/gm)].map((k) =>
|
||||||
parseInt(k[0])
|
parseInt(k[0])
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,29 +165,29 @@ function addOrEditAlertRule(rule) {
|
|||||||
const node2 = modal.find("i.dropdown.icon.2");
|
const node2 = modal.find("i.dropdown.icon.2");
|
||||||
for (let i = 0; i < failTriggerTasksList.length; i++) {
|
for (let i = 0; i < failTriggerTasksList.length; i++) {
|
||||||
node1.after(
|
node1.after(
|
||||||
'<a class="ui label transition visible" data-value="' +
|
'<a class="ui label transition visible" data-value="' +
|
||||||
failTriggerTasksList[i] +
|
failTriggerTasksList[i] +
|
||||||
'" style="display: inline-block !important;">ID:' +
|
'" style="display: inline-block !important;">ID:' +
|
||||||
failTriggerTasksList[i] +
|
failTriggerTasksList[i] +
|
||||||
'<i class="delete icon"></i></a>'
|
'<i class="delete icon"></i></a>'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (let i = 0; i < recoverTriggerTasksList.length; i++) {
|
for (let i = 0; i < recoverTriggerTasksList.length; i++) {
|
||||||
node2.after(
|
node2.after(
|
||||||
'<a class="ui label transition visible" data-value="' +
|
'<a class="ui label transition visible" data-value="' +
|
||||||
recoverTriggerTasksList[i] +
|
recoverTriggerTasksList[i] +
|
||||||
'" style="display: inline-block !important;">ID:' +
|
'" style="display: inline-block !important;">ID:' +
|
||||||
recoverTriggerTasksList[i] +
|
recoverTriggerTasksList[i] +
|
||||||
'<i class="delete icon"></i></a>'
|
'<i class="delete icon"></i></a>'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
modal
|
modal
|
||||||
.find("input[name=FailTriggerTasksRaw]")
|
.find("input[name=FailTriggerTasksRaw]")
|
||||||
.val(rule ? "[]," + failTriggerTasks.substr(1, failTriggerTasks.length - 2) : "[]");
|
.val(rule ? "[]," + failTriggerTasks.substr(1, failTriggerTasks.length - 2) : "[]");
|
||||||
modal
|
modal
|
||||||
.find("input[name=RecoverTriggerTasksRaw]")
|
.find("input[name=RecoverTriggerTasksRaw]")
|
||||||
.val(rule ? "[]," + recoverTriggerTasks.substr(1, recoverTriggerTasks.length - 2) : "[]");
|
.val(rule ? "[]," + recoverTriggerTasks.substr(1, recoverTriggerTasks.length - 2) : "[]");
|
||||||
|
|
||||||
showFormModal(".rule.modal", "#ruleForm", "/api/alert-rule");
|
showFormModal(".rule.modal", "#ruleForm", "/api/alert-rule");
|
||||||
}
|
}
|
||||||
@ -257,10 +259,10 @@ function issueNewApiToken(apiToken) {
|
|||||||
const modal = $(".api.modal");
|
const modal = $(".api.modal");
|
||||||
modal.children(".header").text((apiToken ? LANG.Edit : LANG.Add) + ' ' + "API Token");
|
modal.children(".header").text((apiToken ? LANG.Edit : LANG.Add) + ' ' + "API Token");
|
||||||
modal
|
modal
|
||||||
.find(".nezha-primary-btn.button")
|
.find(".nezha-primary-btn.button")
|
||||||
.html(
|
.html(
|
||||||
apiToken ? LANG.Edit + '<i class="edit icon"></i>' : LANG.Add + '<i class="add icon"></i>'
|
apiToken ? LANG.Edit + '<i class="edit icon"></i>' : LANG.Add + '<i class="add icon"></i>'
|
||||||
);
|
);
|
||||||
modal.find("textarea[name=Note]").val(apiToken ? apiToken.Note : null);
|
modal.find("textarea[name=Note]").val(apiToken ? apiToken.Note : null);
|
||||||
showFormModal(".api.modal", "#apiForm", "/api/token");
|
showFormModal(".api.modal", "#apiForm", "/api/token");
|
||||||
}
|
}
|
||||||
@ -313,6 +315,13 @@ function addOrEditMonitor(monitor) {
|
|||||||
} else {
|
} else {
|
||||||
modal.find(".ui.nb-notify.checkbox").checkbox("set unchecked");
|
modal.find(".ui.nb-notify.checkbox").checkbox("set unchecked");
|
||||||
}
|
}
|
||||||
|
modal.find("input[name=MaxLatency]").val(monitor ? monitor.MaxLatency : null);
|
||||||
|
modal.find("input[name=MinLatency]").val(monitor ? monitor.MinLatency : null);
|
||||||
|
if (monitor && monitor.LatencyNotify) {
|
||||||
|
modal.find(".ui.nb-lt-notify.checkbox").checkbox("set checked");
|
||||||
|
} else {
|
||||||
|
modal.find(".ui.nb-lt-notify.checkbox").checkbox("set unchecked");
|
||||||
|
}
|
||||||
modal.find("a.ui.label.visible").each((i, el) => {
|
modal.find("a.ui.label.visible").each((i, el) => {
|
||||||
el.remove();
|
el.remove();
|
||||||
});
|
});
|
||||||
|
2
resource/template/common/footer.html
vendored
2
resource/template/common/footer.html
vendored
@ -10,7 +10,7 @@
|
|||||||
<script src="https://cdn.staticfile.org/semantic-ui/2.4.1/semantic.min.js"></script>
|
<script src="https://cdn.staticfile.org/semantic-ui/2.4.1/semantic.min.js"></script>
|
||||||
<script src="/static/semantic-ui-alerts.min.js"></script>
|
<script src="/static/semantic-ui-alerts.min.js"></script>
|
||||||
<script src="https://cdn.staticfile.org/vue/2.6.14/vue.min.js"></script>
|
<script src="https://cdn.staticfile.org/vue/2.6.14/vue.min.js"></script>
|
||||||
<script src="/static/main.js?v20220915"></script>
|
<script src="/static/main.js?v20220917"></script>
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
updateLang({{.LANG }});
|
updateLang({{.LANG }});
|
||||||
|
14
resource/template/component/monitor.html
vendored
14
resource/template/component/monitor.html
vendored
@ -54,6 +54,20 @@
|
|||||||
<label>{{tr "EnableFailureNotification"}}</label>
|
<label>{{tr "EnableFailureNotification"}}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>{{tr "MaxLatency"}}</label>
|
||||||
|
<input type="number" name="MaxLatency" placeholder="100.88" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>{{tr "MinLatency"}}</label>
|
||||||
|
<input type="number" name="MinLatency" placeholder="100.88" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui nb-lt-notify checkbox">
|
||||||
|
<input name="LatencyNotify" type="checkbox" tabindex="0" class="hidden" />
|
||||||
|
<label>{{tr "EnableLatencyNotification"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div class="ui warning message">
|
<div class="ui warning message">
|
||||||
<p>
|
<p>
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
<th>{{tr "Type"}}</th>
|
<th>{{tr "Type"}}</th>
|
||||||
<th>{{tr "Duration"}}</th>
|
<th>{{tr "Duration"}}</th>
|
||||||
<th>{{tr "NotificationMethodGroup"}}</th>
|
<th>{{tr "NotificationMethodGroup"}}</th>
|
||||||
<th>{{tr "Notification"}}</th>
|
<th>{{tr "FailureNotification"}}</th>
|
||||||
|
<th>{{tr "LatencyNotification"}}</th>
|
||||||
<th>{{tr "Administration"}}</th>
|
<th>{{tr "Administration"}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -39,6 +40,7 @@
|
|||||||
<td>{{$monitor.Duration}} {{tr "Seconds"}}</td>
|
<td>{{$monitor.Duration}} {{tr "Seconds"}}</td>
|
||||||
<td>{{$monitor.NotificationTag}}</td>
|
<td>{{$monitor.NotificationTag}}</td>
|
||||||
<td>{{$monitor.Notify}}</td>
|
<td>{{$monitor.Notify}}</td>
|
||||||
|
<td>{{$monitor.LatencyNotify}}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="ui mini icon buttons">
|
<div class="ui mini icon buttons">
|
||||||
<button class="ui button" onclick="addOrEditMonitor({{$monitor}})">
|
<button class="ui button" onclick="addOrEditMonitor({{$monitor}})">
|
||||||
|
@ -82,10 +82,10 @@ func NewServiceSentinel(serviceSentinelDispatchBus chan<- model.Monitor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
使用缓存 channel,处理上报的 Service 请求结果,然后判断是否需要报警
|
使用缓存 channel,处理上报的 Service 请求结果,然后判断是否需要报警
|
||||||
需要记录上一次的状态信息
|
需要记录上一次的状态信息
|
||||||
|
|
||||||
加锁顺序:serviceResponseDataStoreLock > monthlyStatusLock > monitorsLock
|
加锁顺序:serviceResponseDataStoreLock > monthlyStatusLock > monitorsLock
|
||||||
*/
|
*/
|
||||||
type ServiceSentinel struct {
|
type ServiceSentinel struct {
|
||||||
// 服务监控任务上报通道
|
// 服务监控任务上报通道
|
||||||
@ -364,6 +364,20 @@ func (ss *ServiceSentinel) worker() {
|
|||||||
log.Println("NEZHA>> 服务监控数据持久化失败:", err)
|
log.Println("NEZHA>> 服务监控数据持久化失败:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 延迟报警
|
||||||
|
if mh.Delay > 0 {
|
||||||
|
ss.monitorsLock.RLock()
|
||||||
|
if ss.monitors[mh.MonitorID].LatencyNotify {
|
||||||
|
if mh.Delay > ss.monitors[mh.MonitorID].MaxLatency {
|
||||||
|
go SendNotification(ss.monitors[mh.MonitorID].NotificationTag, fmt.Sprintf("[Latency] %s %2f > %2f", ss.monitors[mh.MonitorID].Name, mh.Delay, ss.monitors[mh.MonitorID].MaxLatency), true)
|
||||||
|
}
|
||||||
|
if mh.Delay < ss.monitors[mh.MonitorID].MinLatency {
|
||||||
|
go SendNotification(ss.monitors[mh.MonitorID].NotificationTag, fmt.Sprintf("[Latency] %s %2f < %2f", ss.monitors[mh.MonitorID].Name, mh.Delay, ss.monitors[mh.MonitorID].MinLatency), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ss.monitorsLock.RUnlock()
|
||||||
|
}
|
||||||
|
// 故障报警
|
||||||
if stateCode == StatusDown || stateCode != ss.lastStatus[mh.MonitorID] {
|
if stateCode == StatusDown || stateCode != ss.lastStatus[mh.MonitorID] {
|
||||||
ss.monitorsLock.RLock()
|
ss.monitorsLock.RLock()
|
||||||
isNeedSendNotification := (ss.lastStatus[mh.MonitorID] != 0 || stateCode == StatusDown) && ss.monitors[mh.MonitorID].Notify
|
isNeedSendNotification := (ss.lastStatus[mh.MonitorID] != 0 || stateCode == StatusDown) && ss.monitors[mh.MonitorID].Notify
|
||||||
|
Loading…
Reference in New Issue
Block a user