Skip to content

Commit 9a41a1e

Browse files
committed
Add verbose logging
1 parent 83ac06b commit 9a41a1e

File tree

2 files changed

+115
-1
lines changed

2 files changed

+115
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ OPTIONS:
224224
-container-dir -cd, the directory path in the container that the heap dump/JFR/... file will be saved to
225225
-dry-run -n, just output to command line what would be executed
226226
-keep -k, keep the heap dump in the container; by default the heap dump/JFR/... will be deleted from the container's filesystem after been downloaded
227+
-verbose -v, enable verbose output for the plugin
227228
</pre>
228229

229230
The heap dumps and profiles will be copied to a local file if `-local-dir` is specified as a full folder path. Without providing `-local-dir` the heap dump will only be created in the container and not transferred.

cf_cli_java_plugin.go

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,20 +81,52 @@ const (
8181
// user facing errors). The CLI will exit 0 if the plugin exits 0 and will exit
8282
// 1 should the plugin exit nonzero.
8383
func (c *JavaPlugin) Run(cliConnection plugin.CliConnection, args []string) {
84+
// Check if verbose flag is in args for early logging
85+
verbose := false
86+
for _, arg := range args {
87+
if arg == "-v" || arg == "--verbose" {
88+
verbose = true
89+
break
90+
}
91+
}
92+
93+
if verbose {
94+
fmt.Printf("[VERBOSE] Run called with args: %v\n", args)
95+
}
96+
8497
_, err := c.DoRun(&commandExecutorImpl{cliConnection: cliConnection}, &uuidGeneratorImpl{}, utils.CfJavaPluginUtilImpl{}, args)
8598
if err != nil {
99+
if verbose {
100+
fmt.Printf("[VERBOSE] Error occurred: %v\n", err)
101+
}
86102
if err.Error() != "unexpected EOF" {
87103
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
88104
}
89105
os.Exit(1)
90106
}
107+
if verbose {
108+
fmt.Printf("[VERBOSE] Run completed successfully\n")
109+
}
91110
}
92111

93112
// DoRun is an internal method that we use to wrap the cmd package with CommandExecutor for test purposes
94113
func (c *JavaPlugin) DoRun(commandExecutor cmd.CommandExecutor, uuidGenerator uuid.UUIDGenerator, util utils.CfJavaPluginUtil, args []string) (string, error) {
95114
traceLogger := trace.NewLogger(os.Stdout, true, os.Getenv("CF_TRACE"), "")
96115
ui := terminal.NewUI(os.Stdin, os.Stdout, terminal.NewTeePrinter(os.Stdout), traceLogger)
97116

117+
// Check if verbose flag is in args for early logging
118+
verbose := false
119+
for _, arg := range args {
120+
if arg == "-v" || arg == "--verbose" {
121+
verbose = true
122+
break
123+
}
124+
}
125+
126+
if verbose {
127+
fmt.Printf("[VERBOSE] DoRun called with args: %v\n", args)
128+
}
129+
98130
output, err := c.execute(commandExecutor, uuidGenerator, util, args)
99131
if err != nil {
100132
if err.Error() == "unexpected EOF" {
@@ -395,7 +427,7 @@ func (c *JavaPlugin) execute(commandExecutor cmd.CommandExecutor, uuidGenerator
395427
}
396428

397429
if os.Getenv("CF_TRACE") == "true" {
398-
return "", errors.New("The environment variable CF_TRACE is set to true. This prevents download of the dump from succeeding")
430+
return "", errors.New("the environment variable CF_TRACE is set to true. This prevents download of the dump from succeeding")
399431
}
400432

401433
commandFlags := flags.New()
@@ -404,6 +436,7 @@ func (c *JavaPlugin) execute(commandExecutor cmd.CommandExecutor, uuidGenerator
404436
commandFlags.NewBoolFlag("keep", "k", "whether to `keep` the heap-dump/JFR/... files on the container of the application instance after having downloaded it locally")
405437
commandFlags.NewBoolFlag("no-download", "nd", "do not download the heap-dump/JFR/... file to the local machine")
406438
commandFlags.NewBoolFlag("dry-run", "n", "triggers the `dry-run` mode to show only the cf-ssh command that would have been executed")
439+
commandFlags.NewBoolFlag("verbose", "v", "enable verbose output for the plugin")
407440
commandFlags.NewStringFlag("container-dir", "cd", "specify the folder path where the dump/JFR/... file should be stored in the container")
408441
commandFlags.NewStringFlag("local-dir", "ld", "specify the folder where the dump/JFR/... file will be downloaded to, dump file wil not be copied to local if this parameter was not set")
409442
commandFlags.NewStringFlag("args", "a", "Miscellaneous arguments to pass to the command in the container, be aware to end it with a space if it is a simple option")
@@ -420,16 +453,35 @@ func (c *JavaPlugin) execute(commandExecutor cmd.CommandExecutor, uuidGenerator
420453
miscArgs = commandFlags.String("args")
421454
}
422455

456+
verbose := commandFlags.IsSet("verbose")
457+
458+
// Helper function for verbose logging
459+
logVerbose := func(message string) {
460+
if verbose {
461+
fmt.Printf("[VERBOSE] %s\n", message)
462+
}
463+
}
464+
465+
logVerbose("Starting command execution")
466+
logVerbose(fmt.Sprintf("Command arguments: %v", args))
467+
423468
applicationInstance := commandFlags.Int("app-instance-index")
424469
noDownload := commandFlags.IsSet("no-download")
425470
keepAfterDownload := commandFlags.IsSet("keep") || noDownload
426471

472+
logVerbose(fmt.Sprintf("Application instance: %d", applicationInstance))
473+
logVerbose(fmt.Sprintf("No download: %t", noDownload))
474+
logVerbose(fmt.Sprintf("Keep after download: %t", keepAfterDownload))
475+
427476
remoteDir := commandFlags.String("container-dir")
428477
localDir := commandFlags.String("local-dir")
429478
if localDir == "" {
430479
localDir = "."
431480
}
432481

482+
logVerbose(fmt.Sprintf("Remote directory: %s", remoteDir))
483+
logVerbose(fmt.Sprintf("Local directory: %s", localDir))
484+
433485
arguments := commandFlags.Args()
434486
argumentLen := len(arguments)
435487

@@ -438,6 +490,7 @@ func (c *JavaPlugin) execute(commandExecutor cmd.CommandExecutor, uuidGenerator
438490
}
439491

440492
commandName := arguments[0]
493+
logVerbose(fmt.Sprintf("Command name: %s", commandName))
441494

442495
index := -1
443496
for i, command := range commands {
@@ -456,9 +509,12 @@ func (c *JavaPlugin) execute(commandExecutor cmd.CommandExecutor, uuidGenerator
456509
}
457510

458511
command := commands[index]
512+
logVerbose(fmt.Sprintf("Found command: %s - %s", command.Name, command.Description))
459513
if !command.GenerateFiles && !command.GenerateArbitraryFiles {
514+
logVerbose("Command does not generate files, checking for invalid file flags")
460515
for _, flag := range fileFlags {
461516
if commandFlags.IsSet(flag) {
517+
logVerbose(fmt.Sprintf("Invalid flag %q detected for command %s", flag, command.Name))
462518
return "", &InvalidUsageError{message: fmt.Sprintf("The flag %q is not supported for %s", flag, command.Name)}
463519
}
464520
}
@@ -467,11 +523,16 @@ func (c *JavaPlugin) execute(commandExecutor cmd.CommandExecutor, uuidGenerator
467523
trimmedMiscArgs := strings.TrimLeft(miscArgs, " ")
468524
if len(trimmedMiscArgs) > 6 && trimmedMiscArgs[:6] == "start " {
469525
noDownload = true
526+
logVerbose("asprof start command detected, setting noDownload to true")
470527
} else {
471528
noDownload = trimmedMiscArgs == "start"
529+
if noDownload {
530+
logVerbose("asprof start command detected, setting noDownload to true")
531+
}
472532
}
473533
}
474534
if !command.HasMiscArgs() && commandFlags.IsSet("args") {
535+
logVerbose(fmt.Sprintf("Command %s does not support --args flag", command.Name))
475536
return "", &InvalidUsageError{message: fmt.Sprintf("The flag %q is not supported for %s", "args", command.Name)}
476537
}
477538
if argumentLen == 1 {
@@ -481,28 +542,39 @@ func (c *JavaPlugin) execute(commandExecutor cmd.CommandExecutor, uuidGenerator
481542
}
482543

483544
applicationName := arguments[1]
545+
logVerbose(fmt.Sprintf("Application name: %s", applicationName))
484546

485547
cfSSHArguments := []string{"ssh", applicationName}
486548
if applicationInstance > 0 {
487549
cfSSHArguments = append(cfSSHArguments, "--app-instance-index", strconv.Itoa(applicationInstance))
488550
}
489551

552+
logVerbose(fmt.Sprintf("CF SSH arguments: %v", cfSSHArguments))
553+
490554
supported, err := util.CheckRequiredTools(applicationName)
491555

492556
if err != nil || !supported {
493557
return "required tools checking failed", err
494558
}
495559

560+
logVerbose("Required tools check passed")
561+
496562
var remoteCommandTokens = []string{JavaDetectionCommand}
497563

564+
logVerbose("Building remote command tokens")
565+
logVerbose(fmt.Sprintf("Java detection command: %s", JavaDetectionCommand))
566+
498567
for _, requiredTool := range command.RequiredTools {
568+
logVerbose(fmt.Sprintf("Setting up required tool: %s", requiredTool))
499569
uppercase := strings.ToUpper(requiredTool)
500570
var toolCommand = fmt.Sprintf("%s_COMMAND=$(realpath $(find -executable -name %s | head -1 | tr -d [:space:])); if [ -z \"${%s_COMMAND}\" ]; then echo \"%s not found\"; exit 1; fi", uppercase, requiredTool, uppercase, requiredTool)
501571
if requiredTool == "jcmd" {
502572
// add code that first checks whether asprof is present and if so use `asprof jcmd` instead of `jcmd`
503573
remoteCommandTokens = append(remoteCommandTokens, toolCommand, "ASPROF_COMMAND=$(realpath $(find -executable -name asprof | head -1 | tr -d [:space:])); if [ -n \"${ASPROF_COMMAND}\" ]; then JCMD_COMMAND=\"${ASPROF_COMMAND} jcmd\"; fi")
574+
logVerbose("Added jcmd with asprof fallback")
504575
} else {
505576
remoteCommandTokens = append(remoteCommandTokens, toolCommand)
577+
logVerbose(fmt.Sprintf("Added tool command for %s", requiredTool))
506578
}
507579
}
508580
fileName := ""
@@ -514,21 +586,26 @@ func (c *JavaPlugin) execute(commandExecutor cmd.CommandExecutor, uuidGenerator
514586
}
515587

516588
if command.GenerateFiles || command.NeedsFileName || command.GenerateArbitraryFiles {
589+
logVerbose("Command requires file generation")
517590
fspath, err = util.GetAvailablePath(applicationName, remoteDir)
518591
if err != nil {
519592
return "", err
520593
}
594+
logVerbose(fmt.Sprintf("Available path: %s", fspath))
521595
if command.GenerateArbitraryFiles {
522596
fspath = fspath + "/" + command.GenerateArbitraryFilesFolderName
597+
logVerbose(fmt.Sprintf("Updated path for arbitrary files: %s", fspath))
523598
}
524599

525600
fileName = fspath + "/" + applicationName + "-" + command.FileNamePart + "-" + uuidGenerator.Generate() + command.FileExtension
601+
logVerbose(fmt.Sprintf("Generated filename: %s", fileName))
526602
replacements["$$FILE_NAME"] = fileName
527603
replacements["$$FSPATH"] = fspath
528604
if command.GenerateArbitraryFiles {
529605
// prepend 'mkdir -p $$FSPATH' to the command to create the directory if it does not exist
530606
remoteCommandTokens = append([]string{"mkdir -p " + fspath}, remoteCommandTokens...)
531607
remoteCommandTokens = append(remoteCommandTokens, "cd "+fspath)
608+
logVerbose(fmt.Sprintf("Added directory creation and navigation commands for: %s", fspath))
532609
}
533610
}
534611

@@ -538,83 +615,118 @@ func (c *JavaPlugin) execute(commandExecutor cmd.CommandExecutor, uuidGenerator
538615
}
539616
remoteCommandTokens = append(remoteCommandTokens, commandText)
540617

618+
logVerbose(fmt.Sprintf("Command text after replacements: %s", commandText))
619+
logVerbose(fmt.Sprintf("Full remote command tokens: %v", remoteCommandTokens))
620+
541621
cfSSHArguments = append(cfSSHArguments, "--command")
542622
remoteCommand := strings.Join(remoteCommandTokens, "; ")
543623

624+
logVerbose(fmt.Sprintf("Final remote command: %s", remoteCommand))
625+
544626
if commandFlags.IsSet("dry-run") {
627+
logVerbose("Dry-run mode enabled, returning command without execution")
545628
// When printing out the entire command line for separate execution, we wrap the remote command in single quotes
546629
// to prevent the shell processing it from running it in local
547630
cfSSHArguments = append(cfSSHArguments, "'"+remoteCommand+"'")
548631
return "cf " + strings.Join(cfSSHArguments, " "), nil
549632
}
550633

551634
fullCommand := append(cfSSHArguments, remoteCommand)
635+
logVerbose(fmt.Sprintf("Executing command: %v", fullCommand))
552636

553637
output, err := commandExecutor.Execute(fullCommand)
638+
logVerbose("Command execution completed")
554639

555640
if command.GenerateFiles && !noDownload {
641+
logVerbose("Processing file generation and download")
556642

557643
finalFile := ""
558644
var err error
559645
switch command.FileExtension {
560646
case ".hprof":
647+
logVerbose("Finding heap dump file")
561648
finalFile, err = util.FindHeapDumpFile(cfSSHArguments, fileName, fspath)
562649
case ".jfr":
650+
logVerbose("Finding JFR file")
563651
finalFile, err = util.FindJFRFile(cfSSHArguments, fileName, fspath)
564652
default:
565653
return "", &InvalidUsageError{message: fmt.Sprintf("Unsupported file extension %q", command.FileExtension)}
566654
}
567655
if err == nil && finalFile != "" {
568656
fileName = finalFile
657+
logVerbose(fmt.Sprintf("Found file: %s", finalFile))
569658
fmt.Println("Successfully created " + command.FileLabel + " in application container at: " + fileName)
570659
} else {
660+
logVerbose(fmt.Sprintf("Failed to find file, error: %v", err))
571661
fmt.Println("Failed to find " + command.FileLabel + " in application container")
572662
return "", err
573663
}
574664

575665
localFileFullPath := localDir + "/" + applicationName + "-" + command.FileNamePart + "-" + uuidGenerator.Generate() + command.FileExtension
666+
logVerbose(fmt.Sprintf("Downloading file to: %s", localFileFullPath))
576667
err = util.CopyOverCat(cfSSHArguments, fileName, localFileFullPath)
577668
if err == nil {
669+
logVerbose("File download completed successfully")
578670
fmt.Println(toSentenceCase(command.FileLabel) + " file saved to: " + localFileFullPath)
579671
} else {
672+
logVerbose(fmt.Sprintf("File download failed: %v", err))
580673
return "", err
581674
}
582675

583676
if !keepAfterDownload {
677+
logVerbose("Deleting remote file")
584678
err = util.DeleteRemoteFile(cfSSHArguments, fileName)
585679
if err != nil {
680+
logVerbose(fmt.Sprintf("Failed to delete remote file: %v", err))
586681
return "", err
587682
}
683+
logVerbose("Remote file deleted successfully")
588684
fmt.Println(toSentenceCase(command.FileLabel) + " file deleted in application container")
685+
} else {
686+
logVerbose("Keeping remote file as requested")
589687
}
590688
}
591689
if command.GenerateArbitraryFiles && !noDownload {
690+
logVerbose("Processing arbitrary files download")
592691
// download all files in the generic folder
593692
files, err := util.ListFiles(cfSSHArguments, fspath)
594693
if err != nil {
694+
logVerbose(fmt.Sprintf("Failed to list files: %v", err))
595695
return "", err
596696
}
697+
logVerbose(fmt.Sprintf("Found %d files to download", len(files)))
597698
if len(files) != 0 {
598699
for _, file := range files {
700+
logVerbose(fmt.Sprintf("Downloading file: %s", file))
599701
localFileFullPath := localDir + "/" + file
600702
err = util.CopyOverCat(cfSSHArguments, fspath+"/"+file, localFileFullPath)
601703
if err == nil {
704+
logVerbose(fmt.Sprintf("File %s downloaded successfully", file))
602705
fmt.Printf("File %s saved to: %s\n", file, localFileFullPath)
603706
} else {
707+
logVerbose(fmt.Sprintf("Failed to download file %s: %v", file, err))
604708
return "", err
605709
}
606710
}
607711

608712
if !keepAfterDownload {
713+
logVerbose("Deleting remote file folder")
609714
err = util.DeleteRemoteFile(cfSSHArguments, fspath)
610715
if err != nil {
716+
logVerbose(fmt.Sprintf("Failed to delete remote folder: %v", err))
611717
return "", err
612718
}
719+
logVerbose("Remote folder deleted successfully")
613720
fmt.Println("File folder deleted in application container")
721+
} else {
722+
logVerbose("Keeping remote files as requested")
614723
}
724+
} else {
725+
logVerbose("No files found to download")
615726
}
616727
}
617728
// We keep this around to make the compiler happy, but commandExecutor.Execute will cause an os.Exit
729+
logVerbose("Command execution completed successfully")
618730
return strings.Join(output, "\n"), err
619731
}
620732

@@ -678,6 +790,7 @@ func (c *JavaPlugin) GetMetadata() plugin.PluginMetadata {
678790
"container-dir": "-cd, the directory path in the container that the heap dump/JFR/... file will be saved to",
679791
"local-dir": "-ld, the local directory path that the dump/JFR/... file will be saved to, defaults to the current directory",
680792
"args": "-a, Miscellaneous arguments to pass to the command (if supported) in the container, be aware to end it with a space if it is a simple option",
793+
"verbose": "-v, enable verbose output for the plugin",
681794
},
682795
},
683796
},

0 commit comments

Comments
 (0)