mirror of
https://github.com/nezhahq/nezha.git
synced 2025-02-02 01:28:13 -05:00
✨ 展示月流量使用情况
This commit is contained in:
parent
611cabf369
commit
561a3c85fb
@ -4,7 +4,7 @@
|
||||
<br>
|
||||
<small><i>LOGO designed by <a href="https://xio.ng" target="_blank">熊大</a> .</i></small>
|
||||
<br><br>
|
||||
<img src="https://img.shields.io/github/workflow/status/naiba/nezha/Dashboard%20image?label=Dash%20v0.11.2&logo=github&style=for-the-badge"> <img src="https://img.shields.io/github/v/release/naiba/nezha?color=brightgreen&label=Agent&style=for-the-badge&logo=github"> <img src="https://img.shields.io/github/workflow/status/naiba/nezha/Agent%20release?label=Agent%20CI&logo=github&style=for-the-badge"> <img src="https://img.shields.io/badge/Installer-v0.7.1-brightgreen?style=for-the-badge&logo=linux">
|
||||
<img src="https://img.shields.io/github/workflow/status/naiba/nezha/Dashboard%20image?label=Dash%20v0.11.3&logo=github&style=for-the-badge"> <img src="https://img.shields.io/github/v/release/naiba/nezha?color=brightgreen&label=Agent&style=for-the-badge&logo=github"> <img src="https://img.shields.io/github/workflow/status/naiba/nezha/Agent%20release?label=Agent%20CI&logo=github&style=for-the-badge"> <img src="https://img.shields.io/badge/Installer-v0.7.1-brightgreen?style=for-the-badge&logo=linux">
|
||||
<br>
|
||||
<br>
|
||||
<p>:trollface: <b>哪吒监控</b> 一站式轻监控轻运维系统。支持系统状态、HTTP(SSL 证书变更、即将到期、到期)、TCP、Ping 监控报警,计划任务和在线终端。</p>
|
||||
|
@ -98,17 +98,19 @@ func (p *commonPage) checkViewPassword(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (p *commonPage) service(c *gin.Context) {
|
||||
dao.AlertsLock.RLock()
|
||||
defer dao.AlertsLock.RUnlock()
|
||||
c.HTML(http.StatusOK, "theme-"+dao.Conf.Site.Theme+"/service", mygin.CommonEnvironment(c, gin.H{
|
||||
"Title": "服务状态",
|
||||
"Services": dao.ServiceSentinelShared.LoadStats(),
|
||||
"CustomCode": dao.Conf.Site.CustomCode,
|
||||
"Title": "服务状态",
|
||||
"Services": dao.ServiceSentinelShared.LoadStats(),
|
||||
"CycleTransferStats": dao.AlertsCycleTransferStatsStore,
|
||||
"CustomCode": dao.Conf.Site.CustomCode,
|
||||
}))
|
||||
}
|
||||
|
||||
func (cp *commonPage) home(c *gin.Context) {
|
||||
dao.SortedServerLock.RLock()
|
||||
defer dao.SortedServerLock.RUnlock()
|
||||
|
||||
c.HTML(http.StatusOK, "theme-"+dao.Conf.Site.Theme+"/home", mygin.CommonEnvironment(c, gin.H{
|
||||
"Servers": dao.SortedServerList,
|
||||
"CustomCode": dao.Conf.Site.CustomCode,
|
||||
|
@ -61,11 +61,24 @@ func (ma *memberAPI) delete(c *gin.Context) {
|
||||
case "server":
|
||||
err = dao.DB.Unscoped().Delete(&model.Server{}, "id = ?", id).Error
|
||||
if err == nil {
|
||||
// 删除服务器
|
||||
dao.ServerLock.Lock()
|
||||
delete(dao.SecretToID, dao.ServerList[id].Secret)
|
||||
delete(dao.ServerList, id)
|
||||
dao.ServerLock.Unlock()
|
||||
dao.ReSortServer()
|
||||
// 删除循环流量状态中的此服务器相关的记录
|
||||
dao.AlertsLock.Lock()
|
||||
for i := 0; i < len(dao.Alerts); i++ {
|
||||
if dao.AlertsCycleTransferStatsStore[dao.Alerts[i].ID] != nil {
|
||||
delete(dao.AlertsCycleTransferStatsStore[dao.Alerts[i].ID].ServerName, id)
|
||||
delete(dao.AlertsCycleTransferStatsStore[dao.Alerts[i].ID].Transfer, id)
|
||||
delete(dao.AlertsCycleTransferStatsStore[dao.Alerts[i].ID].NextUpdate, id)
|
||||
}
|
||||
}
|
||||
dao.AlertsLock.Unlock()
|
||||
// 删除服务器相关循环流量记录
|
||||
dao.DB.Unscoped().Delete(&model.Transfer{}, "server_id = ?", id)
|
||||
}
|
||||
case "notification":
|
||||
err = dao.DB.Unscoped().Delete(&model.Notification{}, "id = ?", id).Error
|
||||
|
@ -2,10 +2,20 @@ package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type CycleTransferStats struct {
|
||||
Name string
|
||||
From time.Time
|
||||
To time.Time
|
||||
ServerName map[uint64]string
|
||||
Transfer map[uint64]uint64
|
||||
NextUpdate map[uint64]time.Time
|
||||
}
|
||||
|
||||
type AlertRule struct {
|
||||
Common
|
||||
Name string
|
||||
@ -27,10 +37,14 @@ func (r *AlertRule) AfterFind(tx *gorm.DB) error {
|
||||
return json.Unmarshal([]byte(r.RulesRaw), &r.Rules)
|
||||
}
|
||||
|
||||
func (r *AlertRule) Snapshot(server *Server, db *gorm.DB) []interface{} {
|
||||
func (r *AlertRule) Enabled() bool {
|
||||
return r.Enable != nil && *r.Enable
|
||||
}
|
||||
|
||||
func (r *AlertRule) Snapshot(cycleTransferStats *CycleTransferStats, server *Server, db *gorm.DB) []interface{} {
|
||||
var point []interface{}
|
||||
for i := 0; i < len(r.Rules); i++ {
|
||||
point = append(point, r.Rules[i].Snapshot(server, db))
|
||||
point = append(point, r.Rules[i].Snapshot(cycleTransferStats, server, db))
|
||||
}
|
||||
return point
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ func percentage(used, total uint64) float64 {
|
||||
}
|
||||
|
||||
// Snapshot 未通过规则返回 struct{}{}, 通过返回 nil
|
||||
func (u *Rule) Snapshot(server *Server, db *gorm.DB) interface{} {
|
||||
func (u *Rule) Snapshot(cycleTransferStats *CycleTransferStats, server *Server, db *gorm.DB) interface{} {
|
||||
// 监控全部但是排除了此服务器
|
||||
if u.Cover == RuleCoverAll && u.Ignore[server.ID] {
|
||||
return nil
|
||||
@ -139,6 +139,11 @@ func (u *Rule) Snapshot(server *Server, db *gorm.DB) interface{} {
|
||||
} else {
|
||||
u.LastCycleStatus[server.ID] = nil
|
||||
}
|
||||
if cycleTransferStats.ServerName[server.ID] != server.Name {
|
||||
cycleTransferStats.ServerName[server.ID] = server.Name
|
||||
}
|
||||
cycleTransferStats.Transfer[server.ID] = uint64(src)
|
||||
cycleTransferStats.NextUpdate[server.ID] = u.NextTransferAt[server.ID]
|
||||
}
|
||||
|
||||
if u.Type == "offline" && float64(time.Now().Unix())-src > 6 {
|
||||
|
39
resource/template/theme-default/service.html
vendored
39
resource/template/theme-default/service.html
vendored
@ -7,7 +7,6 @@
|
||||
<div class="nb-container">
|
||||
<div class="ui container">
|
||||
<div class="service-status">
|
||||
|
||||
<table class="ui celled table">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -30,12 +29,48 @@
|
||||
</div> {{end}}
|
||||
</td>
|
||||
<td class="ui center aligned delay-today">
|
||||
<i class="delay-today {{className (divU64 $service.CurrentUp (addU64 $service.CurrentUp $service.CurrentDown))}}"></i>
|
||||
<i
|
||||
class="delay-today {{className (divU64 $service.CurrentUp (addU64 $service.CurrentUp $service.CurrentDown))}}"></i>
|
||||
{{statusName (divU64 $service.CurrentUp (addU64 $service.CurrentUp $service.CurrentDown))}}
|
||||
</td>
|
||||
</tr> {{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{if .CycleTransferStats}}
|
||||
|
||||
<h2 style="text-align: center;">循环流量统计</h2>
|
||||
|
||||
<table class="ui celled table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="ui center aligned">ID</th>
|
||||
<th class="ui center aligned">规则</th>
|
||||
<th class="ui center aligned">服务器</th>
|
||||
<th class="ui center aligned">自</th>
|
||||
<th class="ui center aligned">至</th>
|
||||
<th class="ui center aligned">下次刷新</th>
|
||||
<th class="ui center aligned">流量</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range $id, $stats := .CycleTransferStats}}
|
||||
{{range $innerId, $transfer := $stats.Transfer}}
|
||||
<tr>
|
||||
<td class="ui center aligned">{{$id}}</td>
|
||||
<td class="ui center aligned">{{$stats.Name}}</td>
|
||||
<td class="ui center aligned">{{index $stats.ServerName $innerId}}</td>
|
||||
<td class="ui center aligned">{{$stats.From|tf}}</td>
|
||||
<td class="ui center aligned">{{$stats.To|tf}}</td>
|
||||
<td class="ui center aligned">{{(index $stats.NextUpdate $innerId)|tf}}</td>
|
||||
<td class="ui center aligned">{{$transfer|bf}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -16,29 +16,54 @@ const (
|
||||
_RuleCheckPass
|
||||
)
|
||||
|
||||
// 报警规则
|
||||
var alertsLock sync.RWMutex
|
||||
var alerts []*model.AlertRule
|
||||
var alertsStore map[uint64]map[uint64][][]interface{}
|
||||
var alertsPrevState map[uint64]map[uint64]uint
|
||||
|
||||
type NotificationHistory struct {
|
||||
Duration time.Duration
|
||||
Until time.Time
|
||||
}
|
||||
|
||||
// 报警规则
|
||||
var AlertsLock sync.RWMutex
|
||||
var Alerts []*model.AlertRule
|
||||
var alertsStore map[uint64]map[uint64][][]interface{}
|
||||
var alertsPrevState map[uint64]map[uint64]uint
|
||||
var AlertsCycleTransferStatsStore map[uint64]*model.CycleTransferStats
|
||||
|
||||
func addCycleTransferStatsInfo(alert *model.AlertRule) {
|
||||
if !alert.Enabled() {
|
||||
return
|
||||
}
|
||||
for j := 0; j < len(alert.Rules); j++ {
|
||||
if !alert.Rules[j].IsTransferDurationRule() {
|
||||
continue
|
||||
}
|
||||
if AlertsCycleTransferStatsStore[alert.ID] == nil {
|
||||
from := alert.Rules[j].GetTransferDurationStart()
|
||||
AlertsCycleTransferStatsStore[alert.ID] = &model.CycleTransferStats{
|
||||
Name: alert.Name,
|
||||
From: from,
|
||||
To: from.Add(time.Hour * time.Duration(alert.Rules[j].CycleInterval)),
|
||||
ServerName: make(map[uint64]string),
|
||||
Transfer: make(map[uint64]uint64),
|
||||
NextUpdate: make(map[uint64]time.Time),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func AlertSentinelStart() {
|
||||
alertsStore = make(map[uint64]map[uint64][][]interface{})
|
||||
alertsPrevState = make(map[uint64]map[uint64]uint)
|
||||
alertsLock.Lock()
|
||||
if err := DB.Find(&alerts).Error; err != nil {
|
||||
AlertsCycleTransferStatsStore = make(map[uint64]*model.CycleTransferStats)
|
||||
AlertsLock.Lock()
|
||||
if err := DB.Find(&Alerts).Error; err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for i := 0; i < len(alerts); i++ {
|
||||
alertsStore[alerts[i].ID] = make(map[uint64][][]interface{})
|
||||
alertsPrevState[alerts[i].ID] = make(map[uint64]uint)
|
||||
for i := 0; i < len(Alerts); i++ {
|
||||
alertsStore[Alerts[i].ID] = make(map[uint64][][]interface{})
|
||||
alertsPrevState[Alerts[i].ID] = make(map[uint64]uint)
|
||||
addCycleTransferStatsInfo(Alerts[i])
|
||||
}
|
||||
alertsLock.Unlock()
|
||||
AlertsLock.Unlock()
|
||||
|
||||
time.Sleep(time.Second * 10)
|
||||
var lastPrint time.Time
|
||||
@ -59,52 +84,55 @@ func AlertSentinelStart() {
|
||||
}
|
||||
|
||||
func OnRefreshOrAddAlert(alert model.AlertRule) {
|
||||
alertsLock.Lock()
|
||||
defer alertsLock.Unlock()
|
||||
AlertsLock.Lock()
|
||||
defer AlertsLock.Unlock()
|
||||
delete(alertsStore, alert.ID)
|
||||
delete(alertsPrevState, alert.ID)
|
||||
var isEdit bool
|
||||
for i := 0; i < len(alerts); i++ {
|
||||
if alerts[i].ID == alert.ID {
|
||||
alerts[i] = &alert
|
||||
for i := 0; i < len(Alerts); i++ {
|
||||
if Alerts[i].ID == alert.ID {
|
||||
Alerts[i] = &alert
|
||||
isEdit = true
|
||||
}
|
||||
}
|
||||
if !isEdit {
|
||||
alerts = append(alerts, &alert)
|
||||
Alerts = append(Alerts, &alert)
|
||||
}
|
||||
alertsStore[alert.ID] = make(map[uint64][][]interface{})
|
||||
alertsPrevState[alert.ID] = make(map[uint64]uint)
|
||||
delete(AlertsCycleTransferStatsStore, alert.ID)
|
||||
addCycleTransferStatsInfo(&alert)
|
||||
}
|
||||
|
||||
func OnDeleteAlert(id uint64) {
|
||||
alertsLock.Lock()
|
||||
defer alertsLock.Unlock()
|
||||
AlertsLock.Lock()
|
||||
defer AlertsLock.Unlock()
|
||||
delete(alertsStore, id)
|
||||
delete(alertsPrevState, id)
|
||||
for i := 0; i < len(alerts); i++ {
|
||||
if alerts[i].ID == id {
|
||||
alerts = append(alerts[:i], alerts[i+1:]...)
|
||||
for i := 0; i < len(Alerts); i++ {
|
||||
if Alerts[i].ID == id {
|
||||
Alerts = append(Alerts[:i], Alerts[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
delete(AlertsCycleTransferStatsStore, id)
|
||||
}
|
||||
|
||||
func checkStatus() {
|
||||
alertsLock.RLock()
|
||||
defer alertsLock.RUnlock()
|
||||
AlertsLock.RLock()
|
||||
defer AlertsLock.RUnlock()
|
||||
ServerLock.RLock()
|
||||
defer ServerLock.RUnlock()
|
||||
|
||||
for _, alert := range alerts {
|
||||
for _, alert := range Alerts {
|
||||
// 跳过未启用
|
||||
if alert.Enable == nil || !*alert.Enable {
|
||||
if !alert.Enabled() {
|
||||
continue
|
||||
}
|
||||
for _, server := range ServerList {
|
||||
// 监测点
|
||||
alertsStore[alert.ID][server.ID] = append(alertsStore[alert.
|
||||
ID][server.ID], alert.Snapshot(server, DB))
|
||||
ID][server.ID], alert.Snapshot(AlertsCycleTransferStatsStore[alert.ID], server, DB))
|
||||
// 发送通知,分为触发报警和恢复通知
|
||||
max, passed := alert.Check(alertsStore[alert.ID][server.ID])
|
||||
if !passed {
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
pb "github.com/naiba/nezha/proto"
|
||||
)
|
||||
|
||||
var Version = "v0.11.2" // !!记得修改 README 中的 badge 版本!!
|
||||
var Version = "v0.11.3" // !!记得修改 README 中的 badge 版本!!
|
||||
|
||||
var (
|
||||
Conf *model.Config
|
||||
|
Loading…
Reference in New Issue
Block a user