github.com/hazelops/ize@v1.1.12-0.20230915191306-97d7c0e48f11/internal/commands/logs.go (about) 1 package commands 2 3 import ( 4 "fmt" 5 "os" 6 "strings" 7 "time" 8 9 "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" 10 11 "github.com/aws/aws-sdk-go/aws" 12 "github.com/aws/aws-sdk-go/service/cloudwatchlogs" 13 "github.com/aws/aws-sdk-go/service/ecs" 14 "github.com/hazelops/ize/internal/config" 15 "github.com/sirupsen/logrus" 16 "github.com/spf13/cobra" 17 ) 18 19 type LogsOptions struct { 20 Config *config.Project 21 AppName string 22 EcsCluster string 23 Task string 24 } 25 26 func NewLogsFlags(project *config.Project) *LogsOptions { 27 return &LogsOptions{ 28 Config: project, 29 } 30 } 31 32 func NewCmdLogs(project *config.Project) *cobra.Command { 33 o := NewLogsFlags(project) 34 35 cmd := &cobra.Command{ 36 Use: "logs [app-name]", 37 Short: "Stream logs of container in the ECS", 38 Args: cobra.MinimumNArgs(1), 39 ValidArgsFunction: config.GetApps, 40 RunE: func(cmd *cobra.Command, args []string) error { 41 cmd.SilenceUsage = true 42 err := o.Complete(cmd) 43 if err != nil { 44 return err 45 } 46 47 err = o.Validate() 48 if err != nil { 49 return err 50 } 51 52 err = o.Run() 53 if err != nil { 54 return err 55 } 56 57 return nil 58 }, 59 } 60 61 cmd.Flags().StringVar(&o.EcsCluster, "ecs-cluster", "", "set ECS cluster name") 62 cmd.Flags().StringVar(&o.Task, "task", "", "set ECS task id") 63 64 return cmd 65 } 66 67 func (o *LogsOptions) Complete(cmd *cobra.Command) error { 68 if o.EcsCluster == "" { 69 o.EcsCluster = fmt.Sprintf("%s-%s", o.Config.Env, o.Config.Namespace) 70 } 71 72 o.AppName = cmd.Flags().Args()[0] 73 74 return nil 75 } 76 77 func (o *LogsOptions) Validate() error { 78 if len(o.AppName) == 0 { 79 return fmt.Errorf("can't validate: app name must be specified\n") 80 } 81 return nil 82 } 83 84 func (o *LogsOptions) Run() error { 85 logGroup := fmt.Sprintf("%s-%s", o.Config.Env, o.AppName) 86 87 taskID := o.Task 88 if len(taskID) == 0 { 89 lto, err := o.Config.AWSClient.ECSClient.ListTasks(&ecs.ListTasksInput{ 90 Cluster: &o.EcsCluster, 91 DesiredStatus: aws.String("RUNNING"), 92 ServiceName: &logGroup, 93 MaxResults: aws.Int64(1), 94 }) 95 96 logrus.Infof("log group: %s, cluster name: %s", logGroup, o.EcsCluster) 97 98 if err != nil { 99 return fmt.Errorf("can't run logs: %w", err) 100 } 101 102 taskID = *lto.TaskArns[0] 103 taskID = taskID[strings.LastIndex(taskID, "/")+1:] 104 } 105 106 var token *string 107 logStreamName := fmt.Sprintf("main/%s/%s", o.AppName, taskID) 108 109 GetLogs(o.Config.AWSClient.CloudWatchLogsClient, logGroup, logStreamName, token) 110 111 return nil 112 } 113 114 func GetLogs(clw cloudwatchlogsiface.CloudWatchLogsAPI, logGroup string, logStreamName string, token *string) { 115 for { 116 logEvents, err := clw.GetLogEvents(&cloudwatchlogs.GetLogEventsInput{ 117 LogGroupName: &logGroup, 118 LogStreamName: &logStreamName, 119 NextToken: token, 120 }) 121 if err != nil { 122 _, _ = fmt.Fprintf(os.Stderr, "%s: %v\n", logStreamName, err) 123 continue 124 } 125 126 for _, e := range logEvents.Events { 127 _, m := formatMessage(e) 128 fmt.Println(m) 129 } 130 131 token = logEvents.NextForwardToken 132 133 time.Sleep(time.Second * 5) 134 } 135 } 136 137 func formatMessage(e *cloudwatchlogs.OutputLogEvent) (t time.Time, m string) { 138 m = *e.Message 139 140 if len(m) > 16 { 141 if _, err := time.Parse("Jan 2 15:04:05 ", m[:16]); err == nil { 142 m = m[16:] 143 } 144 } 145 146 t = time.Unix(0, *e.Timestamp*1000000) 147 m = t.Format("2006-01-02 15:04:05 ") + m 148 return 149 }