mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-16 07:16:38 +00:00
197 lines
5.4 KiB
Go
197 lines
5.4 KiB
Go
//go:build !(linux && 386)
|
|
|
|
package services
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"github.com/netbirdio/netbird/client/proto"
|
|
)
|
|
|
|
// DebugService exposes debug bundle creation and log-level control to the Wails frontend.
|
|
type DebugService struct {
|
|
grpcClient GRPCClientIface
|
|
}
|
|
|
|
// NewDebugService creates a new DebugService.
|
|
func NewDebugService(g GRPCClientIface) *DebugService {
|
|
return &DebugService{grpcClient: g}
|
|
}
|
|
|
|
// DebugBundleParams holds the parameters for creating a debug bundle.
|
|
type DebugBundleParams struct {
|
|
Anonymize bool `json:"anonymize"`
|
|
SystemInfo bool `json:"systemInfo"`
|
|
Upload bool `json:"upload"`
|
|
UploadURL string `json:"uploadUrl"`
|
|
RunDurationMins int `json:"runDurationMins"`
|
|
EnablePersistence bool `json:"enablePersistence"`
|
|
}
|
|
|
|
// DebugBundleResult holds the result of creating a debug bundle.
|
|
type DebugBundleResult struct {
|
|
LocalPath string `json:"localPath"`
|
|
UploadedKey string `json:"uploadedKey"`
|
|
UploadFailureReason string `json:"uploadFailureReason"`
|
|
}
|
|
|
|
// CreateDebugBundle creates a debug bundle via the daemon.
|
|
func (s *DebugService) CreateDebugBundle(params DebugBundleParams) (*DebugBundleResult, error) {
|
|
conn, err := s.grpcClient.GetClient(time.Second)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get client: %w", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
|
defer cancel()
|
|
|
|
if params.RunDurationMins > 0 {
|
|
if err := s.configureForDebug(ctx, conn, params); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
req := &proto.DebugBundleRequest{
|
|
Anonymize: params.Anonymize,
|
|
SystemInfo: params.SystemInfo,
|
|
}
|
|
if params.Upload && params.UploadURL != "" {
|
|
req.UploadURL = params.UploadURL
|
|
}
|
|
|
|
resp, err := conn.DebugBundle(ctx, req)
|
|
if err != nil {
|
|
log.Errorf("DebugBundle rpc failed: %v", err)
|
|
return nil, fmt.Errorf("create debug bundle: %w", err)
|
|
}
|
|
|
|
return &DebugBundleResult{
|
|
LocalPath: resp.GetPath(),
|
|
UploadedKey: resp.GetUploadedKey(),
|
|
UploadFailureReason: resp.GetUploadFailureReason(),
|
|
}, nil
|
|
}
|
|
|
|
func (s *DebugService) configureForDebug(ctx context.Context, conn proto.DaemonServiceClient, params DebugBundleParams) error {
|
|
statusResp, err := conn.Status(ctx, &proto.StatusRequest{})
|
|
if err != nil {
|
|
return fmt.Errorf("get status: %w", err)
|
|
}
|
|
|
|
wasConnected := statusResp.Status == "Connected" || statusResp.Status == "Connecting"
|
|
|
|
logLevelResp, err := conn.GetLogLevel(ctx, &proto.GetLogLevelRequest{})
|
|
if err != nil {
|
|
return fmt.Errorf("get log level: %w", err)
|
|
}
|
|
originalLogLevel := logLevelResp.GetLevel()
|
|
|
|
// Set trace log level
|
|
if _, err := conn.SetLogLevel(ctx, &proto.SetLogLevelRequest{Level: proto.LogLevel_TRACE}); err != nil {
|
|
return fmt.Errorf("set log level: %w", err)
|
|
}
|
|
|
|
// Bring service down then up to capture full connection logs
|
|
if _, err := conn.Down(ctx, &proto.DownRequest{}); err != nil {
|
|
log.Warnf("bring down for debug: %v", err)
|
|
}
|
|
time.Sleep(time.Second)
|
|
|
|
if params.EnablePersistence {
|
|
if _, err := conn.SetSyncResponsePersistence(ctx, &proto.SetSyncResponsePersistenceRequest{Enabled: true}); err != nil {
|
|
log.Warnf("enable sync persistence: %v", err)
|
|
}
|
|
}
|
|
|
|
if _, err := conn.Up(ctx, &proto.UpRequest{}); err != nil {
|
|
return fmt.Errorf("bring service up: %w", err)
|
|
}
|
|
time.Sleep(3 * time.Second)
|
|
|
|
if _, err := conn.StartCPUProfile(ctx, &proto.StartCPUProfileRequest{}); err != nil {
|
|
log.Warnf("start CPU profiling: %v", err)
|
|
}
|
|
|
|
// Wait for the collection duration
|
|
collectionDur := time.Duration(params.RunDurationMins) * time.Minute
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-time.After(collectionDur):
|
|
}
|
|
|
|
if _, err := conn.StopCPUProfile(ctx, &proto.StopCPUProfileRequest{}); err != nil {
|
|
log.Warnf("stop CPU profiling: %v", err)
|
|
}
|
|
|
|
// Restore original state
|
|
if !wasConnected {
|
|
if _, err := conn.Down(ctx, &proto.DownRequest{}); err != nil {
|
|
log.Warnf("restore down state: %v", err)
|
|
}
|
|
}
|
|
|
|
if originalLogLevel < proto.LogLevel_TRACE {
|
|
if _, err := conn.SetLogLevel(ctx, &proto.SetLogLevelRequest{Level: originalLogLevel}); err != nil {
|
|
log.Warnf("restore log level: %v", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetLogLevel returns the current daemon log level.
|
|
func (s *DebugService) GetLogLevel() (string, error) {
|
|
conn, err := s.grpcClient.GetClient(3 * time.Second)
|
|
if err != nil {
|
|
return "", fmt.Errorf("get client: %w", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
resp, err := conn.GetLogLevel(ctx, &proto.GetLogLevelRequest{})
|
|
if err != nil {
|
|
return "", fmt.Errorf("get log level rpc: %w", err)
|
|
}
|
|
|
|
return resp.GetLevel().String(), nil
|
|
}
|
|
|
|
// SetLogLevel sets the daemon log level.
|
|
func (s *DebugService) SetLogLevel(level string) error {
|
|
conn, err := s.grpcClient.GetClient(3 * time.Second)
|
|
if err != nil {
|
|
return fmt.Errorf("get client: %w", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
var protoLevel proto.LogLevel
|
|
switch level {
|
|
case "TRACE":
|
|
protoLevel = proto.LogLevel_TRACE
|
|
case "DEBUG":
|
|
protoLevel = proto.LogLevel_DEBUG
|
|
case "INFO":
|
|
protoLevel = proto.LogLevel_INFO
|
|
case "WARN", "WARNING":
|
|
protoLevel = proto.LogLevel_WARN
|
|
case "ERROR":
|
|
protoLevel = proto.LogLevel_ERROR
|
|
default:
|
|
protoLevel = proto.LogLevel_INFO
|
|
}
|
|
|
|
if _, err := conn.SetLogLevel(ctx, &proto.SetLogLevelRequest{Level: protoLevel}); err != nil {
|
|
return fmt.Errorf("set log level rpc: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|