Files
ctrld/cmd/cli/commands_log.go
Cuong Manh Le a2f8313668 refactor: pass rootCmd as parameter to Init*Cmd functions
- Update all Init*Cmd function signatures to accept rootCmd parameter:
  * InitServiceCmd(rootCmd *cobra.Command)
  * InitClientsCmd(rootCmd *cobra.Command)
  * InitLogCmd(rootCmd *cobra.Command)
  * InitUpgradeCmd(rootCmd *cobra.Command)
  * InitRunCmd(rootCmd *cobra.Command)
  * InitInterfacesCmd(rootCmd *cobra.Command)

- Update function calls in cli.go to pass rootCmd parameter
- Update InitInterfacesCmd call in commands_service.go

Benefits:
- Eliminates global state dependency on rootCmd variable
- Makes dependencies explicit in function signatures
- Improves testability by allowing different root commands
- Better encapsulation and modularity
2025-10-09 17:49:21 +07:00

171 lines
4.1 KiB
Go

package cli
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"path/filepath"
"github.com/docker/go-units"
"github.com/kardianos/service"
"github.com/spf13/cobra"
)
// LogCommand handles log-related operations
type LogCommand struct {
controlClient *controlClient
}
// NewLogCommand creates a new log command handler
func NewLogCommand() (*LogCommand, error) {
dir, err := socketDir()
if err != nil {
return nil, fmt.Errorf("failed to find ctrld home dir: %w", err)
}
cc := newControlClient(filepath.Join(dir, ctrldControlUnixSock))
return &LogCommand{
controlClient: cc,
}, nil
}
// warnRuntimeLoggingNotEnabled logs a warning about runtime logging not being enabled
func (lc *LogCommand) warnRuntimeLoggingNotEnabled() {
mainLog.Load().Warn().Msg("runtime debug logging is not enabled")
mainLog.Load().Warn().Msg(`ctrld may be running without "--cd" flag or logging is already enabled`)
}
// SendLogs sends runtime debug logs to ControlD
func (lc *LogCommand) SendLogs(cmd *cobra.Command, args []string) error {
sc := NewServiceCommand()
s, _, err := sc.initializeServiceManager()
if err != nil {
return err
}
status, err := s.Status()
if errors.Is(err, service.ErrNotInstalled) {
mainLog.Load().Warn().Msg("service not installed")
return nil
}
if status == service.StatusStopped {
mainLog.Load().Warn().Msg("service is not running")
return nil
}
resp, err := lc.controlClient.post(sendLogsPath, nil)
if err != nil {
return fmt.Errorf("failed to send logs: %w", err)
}
defer resp.Body.Close()
switch resp.StatusCode {
case http.StatusServiceUnavailable:
mainLog.Load().Warn().Msg("runtime logs could only be sent once per minute")
return nil
case http.StatusMovedPermanently:
lc.warnRuntimeLoggingNotEnabled()
return nil
}
var logs logSentResponse
if err := json.NewDecoder(resp.Body).Decode(&logs); err != nil {
return fmt.Errorf("failed to decode sent logs result: %w", err)
}
if logs.Error != "" {
return fmt.Errorf("failed to send logs: %s", logs.Error)
}
mainLog.Load().Notice().Msgf("Sent %s of runtime logs", units.BytesSize(float64(logs.Size)))
return nil
}
// ViewLogs views current runtime debug logs
func (lc *LogCommand) ViewLogs(cmd *cobra.Command, args []string) error {
sc := NewServiceCommand()
s, _, err := sc.initializeServiceManager()
if err != nil {
return err
}
status, err := s.Status()
if errors.Is(err, service.ErrNotInstalled) {
mainLog.Load().Warn().Msg("service not installed")
return nil
}
if status == service.StatusStopped {
mainLog.Load().Warn().Msg("service is not running")
return nil
}
resp, err := lc.controlClient.post(viewLogsPath, nil)
if err != nil {
return fmt.Errorf("failed to get logs: %w", err)
}
defer resp.Body.Close()
switch resp.StatusCode {
case http.StatusMovedPermanently:
lc.warnRuntimeLoggingNotEnabled()
return nil
}
var logs logViewResponse
if err := json.NewDecoder(resp.Body).Decode(&logs); err != nil {
return fmt.Errorf("failed to decode view logs result: %w", err)
}
if logs.Data == "" {
mainLog.Load().Notice().Msg("No runtime logs available")
return nil
}
fmt.Print(logs.Data)
return nil
}
// InitLogCmd creates the log command with proper logic
func InitLogCmd(rootCmd *cobra.Command) *cobra.Command {
lc, err := NewLogCommand()
if err != nil {
panic(fmt.Sprintf("failed to create log command: %v", err))
}
logSendCmd := &cobra.Command{
Use: "send",
Short: "Send runtime debug logs to ControlD",
Args: cobra.NoArgs,
PreRun: func(cmd *cobra.Command, args []string) {
checkHasElevatedPrivilege()
},
RunE: lc.SendLogs,
}
logViewCmd := &cobra.Command{
Use: "view",
Short: "View current runtime debug logs",
Args: cobra.NoArgs,
PreRun: func(cmd *cobra.Command, args []string) {
checkHasElevatedPrivilege()
},
RunE: lc.ViewLogs,
}
logCmd := &cobra.Command{
Use: "log",
Short: "Manage runtime debug logs",
Args: cobra.OnlyValidArgs,
ValidArgs: []string{
logSendCmd.Use,
logViewCmd.Use,
},
}
logCmd.AddCommand(logSendCmd)
logCmd.AddCommand(logViewCmd)
rootCmd.AddCommand(logCmd)
return logCmd
}