github.com/LukasHeimann/cloudfoundrycli/v8@v8.4.4/command/v7/logs_command.go (about) 1 package v7 2 3 import ( 4 "os" 5 "os/signal" 6 "time" 7 8 "github.com/LukasHeimann/cloudfoundrycli/v8/actor/actionerror" 9 "github.com/LukasHeimann/cloudfoundrycli/v8/actor/sharedaction" 10 "github.com/LukasHeimann/cloudfoundrycli/v8/actor/v7action" 11 "github.com/LukasHeimann/cloudfoundrycli/v8/api/logcache" 12 "github.com/LukasHeimann/cloudfoundrycli/v8/command" 13 "github.com/LukasHeimann/cloudfoundrycli/v8/command/flag" 14 ) 15 16 type LogsCommand struct { 17 BaseCommand 18 19 RequiredArgs flag.AppName `positional-args:"yes"` 20 Recent bool `long:"recent" description:"Dump recent logs instead of tailing"` 21 usage interface{} `usage:"CF_NAME logs APP_NAME"` 22 relatedCommands interface{} `related_commands:"app, apps, ssh"` 23 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, err = logcache.NewClient(config.LogCacheEndpoint(), config, ui, v7action.NewDefaultKubernetesConfigGetter()) 34 return err 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.Actor.GetCurrentUser() 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 if !cmd.Config.IsCFOnK8s() { 62 stop := make(chan struct{}) 63 stoppedRefreshing := make(chan struct{}) 64 stoppedOutputtingRefreshErrors := make(chan struct{}) 65 err = cmd.refreshTokenPeriodically(stop, stoppedRefreshing, stoppedOutputtingRefreshErrors) 66 if err != nil { 67 return err 68 } 69 defer func() { 70 close(stop) 71 <-stoppedRefreshing 72 <-stoppedOutputtingRefreshErrors 73 }() 74 } 75 76 err = cmd.streamLogs() 77 78 return err 79 } 80 81 func (cmd LogsCommand) displayRecentLogs() error { 82 messages, warnings, err := cmd.Actor.GetRecentLogsForApplicationByNameAndSpace( 83 cmd.RequiredArgs.AppName, 84 cmd.Config.TargetedSpace().GUID, 85 cmd.LogCacheClient, 86 ) 87 88 for _, message := range messages { 89 cmd.UI.DisplayLogMessage(message, true) 90 } 91 92 cmd.UI.DisplayWarnings(warnings) 93 return err 94 } 95 96 func (cmd LogsCommand) refreshTokenPeriodically( 97 stop chan struct{}, 98 stoppedRefreshing chan struct{}, 99 stoppedOutputtingRefreshErrors chan struct{}) error { 100 101 tokenRefreshErrors, err := cmd.Actor.ScheduleTokenRefresh(time.After, stop, stoppedRefreshing) 102 if err != nil { 103 return err 104 } 105 106 go func() { 107 defer close(stoppedOutputtingRefreshErrors) 108 109 for { 110 select { 111 case err := <-tokenRefreshErrors: 112 cmd.UI.DisplayError(err) 113 case <-stop: 114 return 115 } 116 } 117 }() 118 119 return nil 120 } 121 122 func (cmd LogsCommand) handleLogErr(logErr error) { 123 switch logErr.(type) { 124 case actionerror.LogCacheTimeoutError: 125 cmd.UI.DisplayWarning("timeout connecting to log server, no log will be shown") 126 default: 127 cmd.UI.DisplayWarning("Failed to retrieve logs from Log Cache: {{.Error}}", map[string]interface{}{ 128 "Error": logErr, 129 }) 130 } 131 } 132 133 func (cmd LogsCommand) streamLogs() error { 134 messages, logErrs, stopStreaming, warnings, err := cmd.Actor.GetStreamingLogsForApplicationByNameAndSpace( 135 cmd.RequiredArgs.AppName, 136 cmd.Config.TargetedSpace().GUID, 137 cmd.LogCacheClient, 138 ) 139 140 cmd.UI.DisplayWarnings(warnings) 141 if err != nil { 142 return err 143 } 144 c := make(chan os.Signal, 1) 145 signal.Notify(c, os.Interrupt) 146 147 defer stopStreaming() 148 var messagesClosed, errLogsClosed bool 149 for { 150 select { 151 case message, ok := <-messages: 152 if !ok { 153 messagesClosed = true 154 break 155 } 156 cmd.UI.DisplayLogMessage(message, true) 157 case logErr, ok := <-logErrs: 158 if !ok { 159 errLogsClosed = true 160 break 161 } 162 cmd.handleLogErr(logErr) 163 case <-c: 164 return nil 165 } 166 167 if messagesClosed && errLogsClosed { 168 break 169 } 170 } 171 172 return nil 173 }