展示月流量使用情况

This commit is contained in:
naiba 2021-11-06 16:00:08 +08:00
parent 611cabf369
commit 561a3c85fb
8 changed files with 136 additions and 39 deletions

View File

@ -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">&nbsp;<img src="https://img.shields.io/github/v/release/naiba/nezha?color=brightgreen&label=Agent&style=for-the-badge&logo=github">&nbsp;<img src="https://img.shields.io/github/workflow/status/naiba/nezha/Agent%20release?label=Agent%20CI&logo=github&style=for-the-badge">&nbsp;<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">&nbsp;<img src="https://img.shields.io/github/v/release/naiba/nezha?color=brightgreen&label=Agent&style=for-the-badge&logo=github">&nbsp;<img src="https://img.shields.io/github/workflow/status/naiba/nezha/Agent%20release?label=Agent%20CI&logo=github&style=for-the-badge">&nbsp;<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>

View File

@ -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,

View File

@ -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

View File

@ -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
}

View File

@ -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 {

View File

@ -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>

View File

@ -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 {

View File

@ -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