github.com/loafoe/cli@v7.1.0+incompatible/command/v7/logs_command.go (about) 1 package v7 2 3 import ( 4 "os" 5 "os/signal" 6 "time" 7 8 "code.cloudfoundry.org/cli/actor/actionerror" 9 "code.cloudfoundry.org/cli/actor/sharedaction" 10 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" 11 "code.cloudfoundry.org/cli/command" 12 "code.cloudfoundry.org/cli/command/flag" 13 ) 14 15 type LogsCommand struct { 16 BaseCommand 17 18 RequiredArgs flag.AppName `positional-args:"yes"` 19 Recent bool `long:"recent" description:"Dump recent logs instead of tailing"` 20 usage interface{} `usage:"CF_NAME logs APP_NAME"` 21 relatedCommands interface{} `related_commands:"app, apps, ssh"` 22 23 CC_Client *ccv3.Client 24 LogCacheClient sharedaction.LogCacheClient 25 } 26 27 func (cmd *LogsCommand) Setup(config command.Config, ui command.UI) error { 28 err := cmd.BaseCommand.Setup(config, ui) 29 if err != nil { 30 return err 31 } 32 33 cmd.LogCacheClient = command.NewLogCacheClient(cmd.cloudControllerClient.Info.LogCache(), config, ui) 34 return nil 35 } 36 37 func (cmd LogsCommand) Execute(args []string) error { 38 err := cmd.SharedActor.CheckTarget(true, true) 39 if err != nil { 40 return err 41 } 42 43 user, err := cmd.Config.CurrentUser() 44 if err != nil { 45 return err 46 } 47 48 cmd.UI.DisplayTextWithFlavor("Retrieving logs for app {{.AppName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", 49 map[string]interface{}{ 50 "AppName": cmd.RequiredArgs.AppName, 51 "OrgName": cmd.Config.TargetedOrganization().Name, 52 "SpaceName": cmd.Config.TargetedSpace().Name, 53 "Username": user.Name, 54 }) 55 cmd.UI.DisplayNewline() 56 57 if cmd.Recent { 58 return cmd.displayRecentLogs() 59 } 60 61 stop := make(chan struct{}) 62 stoppedRefreshing := make(chan struct{}) 63 stoppedOutputtingRefreshErrors := make(chan struct{}) 64 err = cmd.refreshTokenPeriodically(stop, stoppedRefreshing, stoppedOutputtingRefreshErrors) 65 if err != nil { 66 return err 67 } 68 69 err = cmd.streamLogs() 70 71 close(stop) 72 <-stoppedRefreshing 73 <-stoppedOutputtingRefreshErrors 74 75 return err 76 } 77 78 func (cmd LogsCommand) displayRecentLogs() error { 79 messages, warnings, err := cmd.Actor.GetRecentLogsForApplicationByNameAndSpace( 80 cmd.RequiredArgs.AppName, 81 cmd.Config.TargetedSpace().GUID, 82 cmd.LogCacheClient, 83 ) 84 85 for _, message := range messages { 86 cmd.UI.DisplayLogMessage(message, true) 87 } 88 89 cmd.UI.DisplayWarnings(warnings) 90 return err 91 } 92 93 func (cmd LogsCommand) refreshTokenPeriodically( 94 stop chan struct{}, 95 stoppedRefreshing chan struct{}, 96 stoppedOutputtingRefreshErrors chan struct{}) error { 97 98 tokenRefreshErrors, err := cmd.Actor.ScheduleTokenRefresh(time.After, stop, stoppedRefreshing) 99 if err != nil { 100 return err 101 } 102 103 go func() { 104 defer close(stoppedOutputtingRefreshErrors) 105 106 for { 107 select { 108 case err := <-tokenRefreshErrors: 109 cmd.UI.DisplayError(err) 110 case <-stop: 111 return 112 } 113 } 114 }() 115 116 return nil 117 } 118 119 func (cmd LogsCommand) handleLogErr(logErr error) { 120 switch logErr.(type) { 121 case actionerror.LogCacheTimeoutError: 122 cmd.UI.DisplayWarning("timeout connecting to log server, no log will be shown") 123 default: 124 cmd.UI.DisplayWarning("Failed to retrieve logs from Log Cache: {{.Error}}", map[string]interface{}{ 125 "Error": logErr, 126 }) 127 } 128 } 129 130 func (cmd LogsCommand) streamLogs() error { 131 messages, logErrs, stopStreaming, warnings, err := cmd.Actor.GetStreamingLogsForApplicationByNameAndSpace( 132 cmd.RequiredArgs.AppName, 133 cmd.Config.TargetedSpace().GUID, 134 cmd.LogCacheClient, 135 ) 136 137 cmd.UI.DisplayWarnings(warnings) 138 if err != nil { 139 return err 140 } 141 c := make(chan os.Signal, 1) 142 signal.Notify(c, os.Interrupt) 143 144 defer stopStreaming() 145 var messagesClosed, errLogsClosed bool 146 for { 147 select { 148 case message, ok := <-messages: 149 if !ok { 150 messagesClosed = true 151 break 152 } 153 cmd.UI.DisplayLogMessage(message, true) 154 case logErr, ok := <-logErrs: 155 if !ok { 156 errLogsClosed = true 157 break 158 } 159 cmd.handleLogErr(logErr) 160 case <-c: 161 return nil 162 } 163 164 if messagesClosed && errLogsClosed { 165 break 166 } 167 } 168 169 return nil 170 }