v0.6.1 优化CSS、服务报警、服务状态展示
This commit is contained in:
parent
114f47cb0f
commit
d3c3e55c88
@ -1,7 +1,7 @@
|
||||
<div align="center" style="background-color: white">
|
||||
<img width="500" style="max-width:100%" src="https://raw.githubusercontent.com/naiba/nezha/master/resource/static/brand.png" title="哪吒监控">
|
||||
<br><br>
|
||||
<img src="https://img.shields.io/github/workflow/status/naiba/nezha/Dashboard%20image?label=Dash%20v0.5.1&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.4.10-brightgreen?style=for-the-badge&logo=linux">
|
||||
<img src="https://img.shields.io/github/workflow/status/naiba/nezha/Dashboard%20image?label=Dash%20v0.6.1&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.4.10-brightgreen?style=for-the-badge&logo=linux">
|
||||
<br>
|
||||
<p>:trollface: 哪吒监控 一站式轻监控轻运维系统。支持系统状态、HTTP(SSL 证书变更、即将到期、到期)、TCP、Ping 监控报警,命令批量执行和计划任务。</p>
|
||||
</div>
|
||||
|
@ -54,7 +54,7 @@ func ServeWeb(port uint) {
|
||||
}
|
||||
if a == 0 {
|
||||
// 这是从未在线的情况
|
||||
return 1 / float32(b) * 100
|
||||
return 0.00001 / float32(b) * 100
|
||||
}
|
||||
return float32(a) / float32(b) * 100
|
||||
},
|
||||
@ -67,7 +67,7 @@ func ServeWeb(port uint) {
|
||||
}
|
||||
if a == 0 {
|
||||
// 这是从未在线的情况
|
||||
return 1 / float32(b) * 100
|
||||
return 0.00001 / float32(b) * 100
|
||||
}
|
||||
return float32(a) / float32(b) * 100
|
||||
},
|
||||
|
@ -110,6 +110,6 @@ func loadCrons() {
|
||||
func main() {
|
||||
go controller.ServeWeb(dao.Conf.HTTPPort)
|
||||
go rpc.ServeRPC(dao.Conf.GRPCPort)
|
||||
go rpc.DispatchTask(time.Minute * 3)
|
||||
go rpc.DispatchTask(time.Second * 30)
|
||||
dao.AlertSentinelStart()
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/genkiroid/cert"
|
||||
"github.com/go-ping/ping"
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
@ -20,8 +21,8 @@ import (
|
||||
func main() {
|
||||
// icmp()
|
||||
// tcpping()
|
||||
// httpWithSSLInfo()
|
||||
sysinfo()
|
||||
httpWithSSLInfo()
|
||||
// sysinfo()
|
||||
// cmdExec()
|
||||
}
|
||||
|
||||
@ -72,11 +73,12 @@ func httpWithSSLInfo() {
|
||||
httpClient := &http.Client{Transport: transCfg, CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}}
|
||||
resp, err := httpClient.Get("http://mail.nai.ba")
|
||||
fmt.Println(err, resp.StatusCode)
|
||||
url := "https://ops.naibahq.com"
|
||||
resp, err := httpClient.Get(url)
|
||||
fmt.Println(err, resp)
|
||||
// SSL 证书信息获取
|
||||
// c := cert.NewCert("expired-ecc-dv.ssl.com")
|
||||
// fmt.Println(c.Error)
|
||||
c := cert.NewCert(url[8:])
|
||||
fmt.Println(c.Error)
|
||||
}
|
||||
|
||||
func icmp() {
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
.nb-container {
|
||||
padding-top: 75px;
|
||||
min-height: 100%;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 55px;
|
||||
margin-bottom: -47px;
|
||||
}
|
||||
|
2
resource/template/common/header.html
vendored
2
resource/template/common/header.html
vendored
@ -9,7 +9,7 @@
|
||||
<title>{{.Title}}</title>
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.1/dist/semantic.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/static/semantic-ui-alerts.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/static/main.css?v202102051040">
|
||||
<link rel="stylesheet" type="text/css" href="/static/main.css?v202104192145">
|
||||
<link rel="shortcut icon" type="image/png" href="/static/logo.png?v20210320" />
|
||||
</head>
|
||||
|
||||
|
2
resource/template/common/menu.html
vendored
2
resource/template/common/menu.html
vendored
@ -14,7 +14,7 @@
|
||||
</a>
|
||||
{{else}}
|
||||
<a class='item{{if eq .MatchedPath "/"}} active{{end}}' href="/"><i class="home icon"></i>首页</a>
|
||||
<a class='item{{if eq .MatchedPath "/service"}} active{{end}}' href="/service"><i class="rss icon"></i>服务状态</a>
|
||||
<a class='item{{if eq .MatchedPath "/service"}} active{{end}}' href="/service"><i class="rss icon"></i>服务</a>
|
||||
{{end}}
|
||||
<div class="right menu">
|
||||
<div class="item">
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
pb "github.com/naiba/nezha/proto"
|
||||
)
|
||||
|
||||
var Version = "v0.5.1" // !!记得修改 README 重的 badge 版本!!
|
||||
var Version = "v0.6.1" // !!记得修改 README 重的 badge 版本!!
|
||||
|
||||
const (
|
||||
SnapshotDelay = 3
|
||||
|
@ -16,14 +16,21 @@ var ServiceSentinelShared *ServiceSentinel
|
||||
func NewServiceSentinel() {
|
||||
ServiceSentinelShared = &ServiceSentinel{
|
||||
serviceResponseChannel: make(chan *pb.TaskResult, 200),
|
||||
serviceResponseDataStoreToday: make(map[uint64][]model.MonitorHistory),
|
||||
serviceResponseDataStoreTodaySavedIndex: make(map[uint64]int),
|
||||
serviceCurrentStatusIndex: make(map[uint64]int),
|
||||
serviceCurrentStatusData: make(map[uint64][]model.MonitorHistory),
|
||||
serviceResponseDataStoreTodayLastSave: make(map[uint64]time.Time),
|
||||
latestDate: make(map[uint64]string),
|
||||
lastStatus: make(map[uint64]string),
|
||||
serviceResponseDataStoreCurrentUp: make(map[uint64]uint64),
|
||||
serviceResponseDataStoreCurrentDown: make(map[uint64]uint64),
|
||||
monitors: make(map[uint64]model.Monitor),
|
||||
latestDate: time.Now().Format("02-Jan-06"),
|
||||
serviceResponseDataStoreToday: make(map[uint64][]model.MonitorHistory),
|
||||
}
|
||||
ServiceSentinelShared.OnMonitorUpdate()
|
||||
for k := range ServiceSentinelShared.monitors {
|
||||
ServiceSentinelShared.latestDate[k] = time.Now().Format("02-Jan-06")
|
||||
}
|
||||
go ServiceSentinelShared.worker()
|
||||
}
|
||||
|
||||
@ -32,17 +39,19 @@ func NewServiceSentinel() {
|
||||
需要记录上一次的状态信息
|
||||
*/
|
||||
type ServiceSentinel struct {
|
||||
latestDate string
|
||||
serviceResponseDataStoreTodaySavedIndex int
|
||||
serviceResponseDataStoreTodayLastSave time.Time
|
||||
serviceResponseDataStoreLock sync.RWMutex
|
||||
monitorsLock sync.RWMutex
|
||||
serviceResponseChannel chan *pb.TaskResult
|
||||
serviceResponseDataStoreTodaySavedIndex map[uint64]int
|
||||
serviceCurrentStatusIndex map[uint64]int
|
||||
serviceCurrentStatusData map[uint64][]model.MonitorHistory
|
||||
serviceResponseDataStoreTodayLastSave map[uint64]time.Time
|
||||
latestDate map[uint64]string
|
||||
lastStatus map[uint64]string
|
||||
monitors map[uint64]model.Monitor
|
||||
serviceResponseDataStoreToday map[uint64][]model.MonitorHistory
|
||||
serviceResponseDataStoreCurrentUp map[uint64]uint64
|
||||
serviceResponseDataStoreCurrentDown map[uint64]uint64
|
||||
monitors map[uint64]model.Monitor
|
||||
serviceResponseDataStoreToday map[uint64][]model.MonitorHistory
|
||||
}
|
||||
|
||||
func (ss *ServiceSentinel) Dispatch(r *pb.TaskResult) {
|
||||
@ -67,19 +76,28 @@ func (ss *ServiceSentinel) OnMonitorUpdate() {
|
||||
ss.monitors = make(map[uint64]model.Monitor)
|
||||
for i := 0; i < len(monitors); i++ {
|
||||
ss.monitors[monitors[i].ID] = monitors[i]
|
||||
if len(ss.serviceCurrentStatusData[monitors[i].ID]) == 0 {
|
||||
ss.serviceCurrentStatusData[monitors[i].ID] = make([]model.MonitorHistory, 20)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ss *ServiceSentinel) OnMonitorDelete(id uint64) {
|
||||
ss.serviceResponseDataStoreLock.Lock()
|
||||
defer ss.serviceResponseDataStoreLock.Unlock()
|
||||
delete(ss.serviceResponseDataStoreCurrentDown, id)
|
||||
delete(ss.serviceResponseDataStoreCurrentUp, id)
|
||||
delete(ss.serviceResponseDataStoreToday, id)
|
||||
delete(ss.serviceResponseDataStoreTodaySavedIndex, id)
|
||||
delete(ss.serviceCurrentStatusIndex, id)
|
||||
delete(ss.serviceCurrentStatusData, id)
|
||||
delete(ss.serviceResponseDataStoreTodayLastSave, id)
|
||||
delete(ss.latestDate, id)
|
||||
delete(ss.lastStatus, id)
|
||||
delete(ss.serviceResponseDataStoreCurrentUp, id)
|
||||
delete(ss.serviceResponseDataStoreCurrentDown, id)
|
||||
delete(ss.serviceResponseDataStoreToday, id)
|
||||
ss.monitorsLock.Lock()
|
||||
defer ss.monitorsLock.Unlock()
|
||||
delete(ss.monitors, id)
|
||||
Cache.Delete(model.CacheKeyServicePage)
|
||||
}
|
||||
|
||||
func (ss *ServiceSentinel) LoadStats() map[uint64]*model.ServiceItemResponse {
|
||||
@ -124,11 +142,16 @@ func (ss *ServiceSentinel) LoadStats() map[uint64]*model.ServiceItemResponse {
|
||||
// 最新一天的数据
|
||||
ss.serviceResponseDataStoreLock.RLock()
|
||||
defer ss.serviceResponseDataStoreLock.RUnlock()
|
||||
for k, v := range ss.serviceResponseDataStoreToday {
|
||||
for k := range ss.monitors {
|
||||
if msm[k] == nil {
|
||||
msm[k] = &model.ServiceItemResponse{}
|
||||
msm[k] = &model.ServiceItemResponse{
|
||||
Up: new([30]int),
|
||||
Down: new([30]int),
|
||||
Delay: new([30]float32),
|
||||
}
|
||||
}
|
||||
msm[k].Monitor = ss.monitors[k]
|
||||
v := ss.serviceResponseDataStoreToday[k]
|
||||
for i := 0; i < len(v); i++ {
|
||||
if v[i].Successful {
|
||||
msm[k].Up[29]++
|
||||
@ -163,44 +186,60 @@ func getStateStr(percent uint64) string {
|
||||
|
||||
func (ss *ServiceSentinel) worker() {
|
||||
for r := range ss.serviceResponseChannel {
|
||||
if ss.monitors[r.GetId()].ID == 0 {
|
||||
continue
|
||||
}
|
||||
mh := model.PB2MonitorHistory(r)
|
||||
ss.serviceResponseDataStoreLock.Lock()
|
||||
// 先查看是否到下一天
|
||||
nowDate := time.Now().Format("02-Jan-06")
|
||||
if nowDate != ss.latestDate {
|
||||
ss.latestDate = nowDate
|
||||
dataToSave := ss.serviceResponseDataStoreToday[mh.MonitorID][ss.serviceResponseDataStoreTodaySavedIndex:]
|
||||
if nowDate != ss.latestDate[mh.MonitorID] {
|
||||
ss.latestDate[mh.MonitorID] = nowDate
|
||||
dataToSave := ss.serviceResponseDataStoreToday[mh.MonitorID][ss.serviceResponseDataStoreTodaySavedIndex[mh.MonitorID]:]
|
||||
if err := DB.Create(&dataToSave).Error; err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
ss.serviceResponseDataStoreTodaySavedIndex = 0
|
||||
ss.serviceResponseDataStoreTodaySavedIndex[mh.MonitorID] = 0
|
||||
ss.serviceResponseDataStoreToday[mh.MonitorID] = []model.MonitorHistory{}
|
||||
ss.serviceResponseDataStoreCurrentDown[mh.MonitorID] = 0
|
||||
ss.serviceResponseDataStoreCurrentUp[mh.MonitorID] = 0
|
||||
ss.serviceResponseDataStoreTodayLastSave = time.Now()
|
||||
ss.serviceResponseDataStoreTodayLastSave[mh.MonitorID] = time.Now()
|
||||
}
|
||||
// 储存至当日数据
|
||||
ss.serviceResponseDataStoreToday[mh.MonitorID] = append(ss.serviceResponseDataStoreToday[mh.MonitorID], mh)
|
||||
// 每20分钟入库一次
|
||||
if time.Now().After(ss.serviceResponseDataStoreTodayLastSave.Add(time.Minute * 20)) {
|
||||
ss.serviceResponseDataStoreTodayLastSave = time.Now()
|
||||
dataToSave := ss.serviceResponseDataStoreToday[mh.MonitorID][ss.serviceResponseDataStoreTodaySavedIndex:]
|
||||
if time.Now().After(ss.serviceResponseDataStoreTodayLastSave[mh.MonitorID].Add(time.Minute * 20)) {
|
||||
ss.serviceResponseDataStoreTodayLastSave[mh.MonitorID] = time.Now()
|
||||
dataToSave := ss.serviceResponseDataStoreToday[mh.MonitorID][ss.serviceResponseDataStoreTodaySavedIndex[mh.MonitorID]:]
|
||||
if err := DB.Create(&dataToSave).Error; err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
ss.serviceResponseDataStoreTodaySavedIndex = len(ss.serviceResponseDataStoreToday[mh.MonitorID])
|
||||
ss.serviceResponseDataStoreTodaySavedIndex[mh.MonitorID] = len(ss.serviceResponseDataStoreToday[mh.MonitorID])
|
||||
}
|
||||
// 写入当前数据
|
||||
ss.serviceCurrentStatusData[mh.MonitorID][ss.serviceCurrentStatusIndex[mh.MonitorID]] = mh
|
||||
ss.serviceCurrentStatusIndex[mh.MonitorID]++
|
||||
if ss.serviceCurrentStatusIndex[mh.MonitorID] == 20 {
|
||||
ss.serviceCurrentStatusIndex[mh.MonitorID] = 0
|
||||
}
|
||||
// 更新当前状态
|
||||
ss.serviceResponseDataStoreCurrentUp[mh.MonitorID] = 0
|
||||
ss.serviceResponseDataStoreCurrentDown[mh.MonitorID] = 0
|
||||
for i := len(ss.serviceResponseDataStoreToday[mh.MonitorID]) - 1; i >= 0 && i >= len(ss.serviceResponseDataStoreToday[mh.MonitorID])-20; i-- {
|
||||
if ss.serviceResponseDataStoreToday[mh.MonitorID][i].Successful {
|
||||
for i := 0; i < len(ss.serviceCurrentStatusData[mh.MonitorID]); i++ {
|
||||
if ss.serviceCurrentStatusData[mh.MonitorID][i].MonitorID > 0 {
|
||||
if ss.serviceCurrentStatusData[mh.MonitorID][i].Successful {
|
||||
ss.serviceResponseDataStoreCurrentUp[mh.MonitorID]++
|
||||
} else {
|
||||
ss.serviceResponseDataStoreCurrentDown[mh.MonitorID]++
|
||||
}
|
||||
}
|
||||
stateStr := getStateStr(ss.serviceResponseDataStoreCurrentUp[mh.MonitorID] * 100 / (ss.serviceResponseDataStoreCurrentDown[mh.MonitorID] + ss.serviceResponseDataStoreCurrentUp[mh.MonitorID]))
|
||||
}
|
||||
var upPercent uint64 = 0
|
||||
if ss.serviceResponseDataStoreCurrentDown[mh.MonitorID]+ss.serviceResponseDataStoreCurrentUp[mh.MonitorID] > 0 {
|
||||
upPercent = ss.serviceResponseDataStoreCurrentUp[mh.MonitorID] * 100 / (ss.serviceResponseDataStoreCurrentDown[mh.MonitorID] + ss.serviceResponseDataStoreCurrentUp[mh.MonitorID])
|
||||
}
|
||||
stateStr := getStateStr(upPercent)
|
||||
log.Println(ss.monitors[mh.MonitorID].Target, stateStr)
|
||||
if stateStr == "故障" || stateStr != ss.lastStatus[mh.MonitorID] {
|
||||
ss.monitorsLock.RLock()
|
||||
isSendNotification := (ss.lastStatus[mh.MonitorID] != "" || stateStr == "故障") && ss.monitors[mh.MonitorID].Notify
|
||||
|
Loading…
Reference in New Issue
Block a user