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  }