mirror of
https://github.com/yusing/godoxy.git
synced 2026-03-29 05:11:51 +02:00
- Removed `display_name`, `is_docker`, and `is_excluded` fields from the `RouteAggregate` struct and corresponding Swagger documentation. - Updated references in the README and code to reflect the removal of these fields, ensuring consistency across the codebase.
154 lines
4.1 KiB
Go
154 lines
4.1 KiB
Go
package uptime
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/url"
|
|
"slices"
|
|
"time"
|
|
|
|
"github.com/lithammer/fuzzysearch/fuzzy"
|
|
"github.com/yusing/godoxy/internal/metrics/period"
|
|
metricsutils "github.com/yusing/godoxy/internal/metrics/utils"
|
|
"github.com/yusing/godoxy/internal/route/routes"
|
|
"github.com/yusing/godoxy/internal/types"
|
|
)
|
|
|
|
type (
|
|
StatusByAlias struct {
|
|
Map map[string]routes.HealthInfoWithoutDetail `json:"statuses"`
|
|
Timestamp int64 `json:"timestamp"`
|
|
} // @name RouteStatusesByAlias
|
|
Status struct {
|
|
Status types.HealthStatus `json:"status" swaggertype:"string" enums:"healthy,unhealthy,unknown,napping,starting"`
|
|
Latency int32 `json:"latency"`
|
|
Timestamp int64 `json:"timestamp"`
|
|
} // @name RouteStatus
|
|
RouteStatuses map[string][]Status // @name RouteStatuses
|
|
RouteAggregate struct {
|
|
Alias string `json:"alias"`
|
|
Uptime float32 `json:"uptime"`
|
|
Downtime float32 `json:"downtime"`
|
|
Idle float32 `json:"idle"`
|
|
AvgLatency float32 `json:"avg_latency"`
|
|
CurrentStatus types.HealthStatus `json:"current_status" swaggertype:"string" enums:"healthy,unhealthy,unknown,napping,starting"`
|
|
Statuses []Status `json:"statuses"`
|
|
} // @name RouteUptimeAggregate
|
|
Aggregated []RouteAggregate
|
|
)
|
|
|
|
var Poller = period.NewPoller("uptime", getStatuses, aggregateStatuses)
|
|
|
|
func getStatuses(ctx context.Context, _ StatusByAlias) (StatusByAlias, error) {
|
|
return StatusByAlias{
|
|
Map: routes.GetHealthInfoWithoutDetail(),
|
|
Timestamp: time.Now().Unix(),
|
|
}, nil
|
|
}
|
|
|
|
func (s *Status) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(map[string]any{
|
|
"status": s.Status.String(),
|
|
"latency": s.Latency,
|
|
"timestamp": s.Timestamp,
|
|
})
|
|
}
|
|
|
|
func aggregateStatuses(entries []StatusByAlias, query url.Values) (int, Aggregated) {
|
|
limit := metricsutils.QueryInt(query, "limit", 0)
|
|
offset := metricsutils.QueryInt(query, "offset", 0)
|
|
keyword := query.Get("keyword")
|
|
|
|
statuses := make(RouteStatuses)
|
|
for _, entry := range entries {
|
|
for alias, status := range entry.Map {
|
|
statuses[alias] = append(statuses[alias], Status{
|
|
Status: status.Status,
|
|
Latency: int32(status.Latency.Milliseconds()),
|
|
Timestamp: entry.Timestamp,
|
|
})
|
|
}
|
|
}
|
|
if keyword != "" {
|
|
for alias := range statuses {
|
|
if !fuzzy.MatchFold(keyword, alias) {
|
|
delete(statuses, alias)
|
|
}
|
|
}
|
|
}
|
|
return len(statuses), statuses.aggregate(limit, offset)
|
|
}
|
|
|
|
func (rs RouteStatuses) calculateInfo(statuses []Status) (up float32, down float32, idle float32, _ float32) {
|
|
if len(statuses) == 0 {
|
|
return 0, 0, 0, 0
|
|
}
|
|
total := float32(0)
|
|
latency := float32(0)
|
|
for _, status := range statuses {
|
|
// ignoring unknown; treating napping and starting as downtime
|
|
if status.Status == types.StatusUnknown {
|
|
continue
|
|
}
|
|
switch {
|
|
case status.Status == types.StatusHealthy:
|
|
up++
|
|
case status.Status.Idling():
|
|
idle++
|
|
default:
|
|
down++
|
|
}
|
|
total++
|
|
latency += float32(status.Latency)
|
|
}
|
|
if total == 0 {
|
|
return 0, 0, 0, 0
|
|
}
|
|
return up / total, down / total, idle / total, latency / total
|
|
}
|
|
|
|
func (rs RouteStatuses) aggregate(limit int, offset int) Aggregated {
|
|
n := len(rs)
|
|
beg, end, ok := metricsutils.CalculateBeginEnd(n, limit, offset)
|
|
if !ok {
|
|
return Aggregated{}
|
|
}
|
|
i := 0
|
|
sortedAliases := make([]string, n)
|
|
for alias := range rs {
|
|
sortedAliases[i] = alias
|
|
i++
|
|
}
|
|
slices.Sort(sortedAliases)
|
|
sortedAliases = sortedAliases[beg:end]
|
|
result := make(Aggregated, len(sortedAliases))
|
|
for i, alias := range sortedAliases {
|
|
statuses := rs[alias]
|
|
up, down, idle, latency := rs.calculateInfo(statuses)
|
|
|
|
status := types.StatusUnknown
|
|
r, ok := routes.GetIncludeExcluded(alias)
|
|
if ok {
|
|
mon := r.HealthMonitor()
|
|
if mon != nil {
|
|
status = mon.Status()
|
|
}
|
|
}
|
|
|
|
result[i] = RouteAggregate{
|
|
Alias: alias,
|
|
Uptime: up,
|
|
Downtime: down,
|
|
Idle: idle,
|
|
AvgLatency: latency,
|
|
CurrentStatus: status,
|
|
Statuses: statuses,
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (result Aggregated) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal([]RouteAggregate(result))
|
|
}
|