mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-18 08:16:39 +00:00
Add health status to debug
This commit is contained in:
@@ -68,8 +68,14 @@ var debugPingCmd = &cobra.Command{
|
||||
SilenceUsage: true,
|
||||
}
|
||||
|
||||
var debugLogCmd = &cobra.Command{
|
||||
Use: "log",
|
||||
Short: "Manage client logging",
|
||||
Long: "Commands to manage logging settings for a client connected through the proxy.",
|
||||
}
|
||||
|
||||
var debugLogLevelCmd = &cobra.Command{
|
||||
Use: "loglevel <account-id> <level>",
|
||||
Use: "level <account-id> <level>",
|
||||
Short: "Set client log level",
|
||||
Long: "Set the log level for a client (trace, debug, info, warn, error).",
|
||||
Args: cobra.ExactArgs(2),
|
||||
@@ -109,7 +115,8 @@ func init() {
|
||||
debugCmd.AddCommand(debugStatusCmd)
|
||||
debugCmd.AddCommand(debugSyncCmd)
|
||||
debugCmd.AddCommand(debugPingCmd)
|
||||
debugCmd.AddCommand(debugLogLevelCmd)
|
||||
debugLogCmd.AddCommand(debugLogLevelCmd)
|
||||
debugCmd.AddCommand(debugLogCmd)
|
||||
debugCmd.AddCommand(debugStartCmd)
|
||||
debugCmd.AddCommand(debugStopCmd)
|
||||
|
||||
|
||||
@@ -53,6 +53,48 @@ func (c *Client) Health(ctx context.Context) error {
|
||||
func (c *Client) printHealth(data map[string]any) {
|
||||
_, _ = fmt.Fprintf(c.out, "Status: %v\n", data["status"])
|
||||
_, _ = fmt.Fprintf(c.out, "Uptime: %v\n", data["uptime"])
|
||||
_, _ = fmt.Fprintf(c.out, "Management Connected: %s\n", boolIcon(data["management_connected"]))
|
||||
_, _ = fmt.Fprintf(c.out, "All Clients Healthy: %s\n", boolIcon(data["all_clients_healthy"]))
|
||||
|
||||
clients, ok := data["clients"].(map[string]any)
|
||||
if !ok || len(clients) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(c.out, "\n%-38s %-9s %-7s %-8s %s\n", "ACCOUNT ID", "HEALTHY", "MGMT", "SIGNAL", "RELAYS")
|
||||
_, _ = fmt.Fprintln(c.out, strings.Repeat("-", 80))
|
||||
|
||||
for accountID, v := range clients {
|
||||
ch, ok := v.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
healthy := boolIcon(ch["healthy"])
|
||||
mgmt := boolIcon(ch["management_connected"])
|
||||
signal := boolIcon(ch["signal_connected"])
|
||||
|
||||
relaysConn, _ := ch["relays_connected"].(float64)
|
||||
relaysTotal, _ := ch["relays_total"].(float64)
|
||||
relays := fmt.Sprintf("%d/%d", int(relaysConn), int(relaysTotal))
|
||||
|
||||
_, _ = fmt.Fprintf(c.out, "%-38s %-9s %-7s %-8s %s", accountID, healthy, mgmt, signal, relays)
|
||||
if errMsg, ok := ch["error"].(string); ok && errMsg != "" {
|
||||
_, _ = fmt.Fprintf(c.out, " (%s)", errMsg)
|
||||
}
|
||||
_, _ = fmt.Fprintln(c.out)
|
||||
}
|
||||
}
|
||||
|
||||
func boolIcon(v any) string {
|
||||
b, ok := v.(bool)
|
||||
if !ok {
|
||||
return "?"
|
||||
}
|
||||
if b {
|
||||
return "yes"
|
||||
}
|
||||
return "no"
|
||||
}
|
||||
|
||||
// ListClients fetches the list of all clients.
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
|
||||
nbembed "github.com/netbirdio/netbird/client/embed"
|
||||
nbstatus "github.com/netbirdio/netbird/client/status"
|
||||
"github.com/netbirdio/netbird/proxy/internal/health"
|
||||
"github.com/netbirdio/netbird/proxy/internal/roundtrip"
|
||||
"github.com/netbirdio/netbird/proxy/internal/types"
|
||||
"github.com/netbirdio/netbird/version"
|
||||
@@ -52,9 +53,17 @@ type clientProvider interface {
|
||||
ListClientsForDebug() map[types.AccountID]roundtrip.ClientDebugInfo
|
||||
}
|
||||
|
||||
// healthChecker provides health probe state.
|
||||
type healthChecker interface {
|
||||
ReadinessProbe() bool
|
||||
StartupProbe(ctx context.Context) bool
|
||||
CheckClientsConnected(ctx context.Context) (bool, map[types.AccountID]health.ClientHealth)
|
||||
}
|
||||
|
||||
// Handler provides HTTP debug endpoints.
|
||||
type Handler struct {
|
||||
provider clientProvider
|
||||
health healthChecker
|
||||
logger *log.Logger
|
||||
startTime time.Time
|
||||
templates *template.Template
|
||||
@@ -62,12 +71,13 @@ type Handler struct {
|
||||
}
|
||||
|
||||
// NewHandler creates a new debug handler.
|
||||
func NewHandler(provider clientProvider, logger *log.Logger) *Handler {
|
||||
func NewHandler(provider clientProvider, healthChecker healthChecker, logger *log.Logger) *Handler {
|
||||
if logger == nil {
|
||||
logger = log.StandardLogger()
|
||||
}
|
||||
h := &Handler{
|
||||
provider: provider,
|
||||
health: healthChecker,
|
||||
logger: logger,
|
||||
startTime: time.Now(),
|
||||
}
|
||||
@@ -547,20 +557,41 @@ func (h *Handler) handleClientStop(w http.ResponseWriter, r *http.Request, accou
|
||||
}
|
||||
|
||||
type healthData struct {
|
||||
Uptime string
|
||||
Uptime string
|
||||
Status string
|
||||
ManagementReady bool
|
||||
AllClientsHealthy bool
|
||||
Clients map[types.AccountID]health.ClientHealth
|
||||
}
|
||||
|
||||
func (h *Handler) handleHealth(w http.ResponseWriter, _ *http.Request, wantJSON bool) {
|
||||
func (h *Handler) handleHealth(w http.ResponseWriter, r *http.Request, wantJSON bool) {
|
||||
uptime := time.Since(h.startTime).Round(10 * time.Millisecond).String()
|
||||
|
||||
ready := h.health.ReadinessProbe()
|
||||
allHealthy, clientHealth := h.health.CheckClientsConnected(r.Context())
|
||||
|
||||
status := "ok"
|
||||
if !ready || !allHealthy {
|
||||
status = "degraded"
|
||||
}
|
||||
|
||||
if wantJSON {
|
||||
h.writeJSON(w, map[string]interface{}{
|
||||
"status": "ok",
|
||||
"uptime": time.Since(h.startTime).Round(10 * time.Millisecond).String(),
|
||||
"status": status,
|
||||
"uptime": uptime,
|
||||
"management_connected": ready,
|
||||
"all_clients_healthy": allHealthy,
|
||||
"clients": clientHealth,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
data := healthData{
|
||||
Uptime: time.Since(h.startTime).Round(time.Second).String(),
|
||||
Uptime: time.Since(h.startTime).Round(time.Second).String(),
|
||||
Status: status,
|
||||
ManagementReady: ready,
|
||||
AllClientsHealthy: allHealthy,
|
||||
Clients: clientHealth,
|
||||
}
|
||||
|
||||
h.renderTemplate(w, "health", data)
|
||||
|
||||
@@ -6,8 +6,33 @@
|
||||
<style>{{template "style"}}</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>OK</h1>
|
||||
<h1>{{.Status}}</h1>
|
||||
<p>Uptime: {{.Uptime}}</p>
|
||||
<p>Management Connected: {{.ManagementReady}}</p>
|
||||
<p>All Clients Healthy: {{.AllClientsHealthy}}</p>
|
||||
{{if .Clients}}
|
||||
<h2>Clients</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Account ID</th>
|
||||
<th>Healthy</th>
|
||||
<th>Management</th>
|
||||
<th>Signal</th>
|
||||
<th>Relays</th>
|
||||
<th>Error</th>
|
||||
</tr>
|
||||
{{range $id, $c := .Clients}}
|
||||
<tr>
|
||||
<td>{{$id}}</td>
|
||||
<td>{{$c.Healthy}}</td>
|
||||
<td>{{$c.ManagementConnected}}</td>
|
||||
<td>{{$c.SignalConnected}}</td>
|
||||
<td>{{$c.RelaysConnected}}/{{$c.RelaysTotal}}</td>
|
||||
<td>{{$c.Error}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
{{end}}
|
||||
<p><a href="/debug">← Back</a></p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -230,9 +230,11 @@ func (s *Server) ListenAndServe(ctx context.Context, addr string) (err error) {
|
||||
// Configure Access logs to management server.
|
||||
accessLog := accesslog.NewLogger(s.mgmtClient, s.Logger, s.TrustedProxies)
|
||||
|
||||
s.healthChecker = health.NewChecker(s.Logger, s.netbird)
|
||||
|
||||
if s.DebugEndpointEnabled {
|
||||
debugAddr := debugEndpointAddr(s.DebugEndpointAddress)
|
||||
debugHandler := debug.NewHandler(s.netbird, s.Logger)
|
||||
debugHandler := debug.NewHandler(s.netbird, s.healthChecker, s.Logger)
|
||||
s.debug = &http.Server{
|
||||
Addr: debugAddr,
|
||||
Handler: debugHandler,
|
||||
@@ -255,7 +257,6 @@ func (s *Server) ListenAndServe(ctx context.Context, addr string) (err error) {
|
||||
if healthAddr == "" {
|
||||
healthAddr = "localhost:8080"
|
||||
}
|
||||
s.healthChecker = health.NewChecker(s.Logger, s.netbird)
|
||||
s.healthServer = health.NewServer(healthAddr, s.healthChecker, s.Logger)
|
||||
healthListener, err := net.Listen("tcp", healthAddr)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user