github.com/sleungcy/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  }