app_logs.go 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. package v2
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "os"
  7. "os/signal"
  8. "syscall"
  9. "time"
  10. "github.com/fatih/color"
  11. api "github.com/porter-dev/porter/api/client"
  12. "github.com/porter-dev/porter/cli/cmd/config"
  13. )
  14. // AppLogsInput is the input for the AppLogs function
  15. type AppLogsInput struct {
  16. // CLIConfig is the CLI configuration
  17. CLIConfig config.CLIConfig
  18. // Client is the Porter API client
  19. Client api.Client
  20. // DeploymentTargetName is the name of deployment target where the app is deployed
  21. DeploymentTargetName string
  22. // AppName is the name of the app to get logs for
  23. AppName string
  24. // ServiceName is an optional service name filter
  25. ServiceName string
  26. }
  27. // ServiceName_AllServices is a special value for ServiceName that indicates all services should be included
  28. const ServiceName_AllServices = "all"
  29. // AppLogs gets logs for an app
  30. func AppLogs(ctx context.Context, inp AppLogsInput) error {
  31. ctx, cancel := context.WithCancel(ctx)
  32. defer cancel()
  33. termChan := make(chan os.Signal, 1)
  34. signal.Notify(termChan, syscall.SIGINT, syscall.SIGTERM)
  35. color.New(color.FgGreen).Printf("Streaming logs for app %s...\n\n", inp.AppName) // nolint:errcheck,gosec
  36. conn, err := inp.Client.AppLogsStream(ctx, api.AppLogsInput{
  37. ProjectID: inp.CLIConfig.Project,
  38. ClusterID: inp.CLIConfig.Cluster,
  39. AppName: inp.AppName,
  40. DeploymentTargetName: inp.DeploymentTargetName,
  41. ServiceName: inp.ServiceName,
  42. })
  43. if err != nil {
  44. return fmt.Errorf("error connecting to app logs stream: %w", err)
  45. }
  46. defer conn.Close() // nolint:errcheck
  47. go func() {
  48. select {
  49. case <-termChan:
  50. color.New(color.FgYellow).Println("Shutdown signal received, canceling processes") // nolint:errcheck,gosec
  51. // ReadMessage will block until the next message is received, so we need to set a read deadline
  52. conn.SetReadDeadline(time.Now()) // nolint:errcheck,gosec
  53. cancel()
  54. case <-ctx.Done():
  55. }
  56. }()
  57. for {
  58. select {
  59. case <-ctx.Done():
  60. return ctx.Err()
  61. default:
  62. _, message, _ := conn.ReadMessage()
  63. if err != nil {
  64. return err
  65. }
  66. if len(message) == 0 {
  67. return nil
  68. }
  69. var line struct {
  70. Line string `json:"line"`
  71. }
  72. err = json.Unmarshal(message, &line)
  73. if err != nil {
  74. return err
  75. }
  76. message = append([]byte(line.Line), '\n')
  77. if _, err = os.Stdout.Write(message); err != nil {
  78. return nil
  79. }
  80. }
  81. }
  82. }