@@ -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.
8383func (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
94113func (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