@@ -2,8 +2,10 @@ package commands
22
33import (
44 "fmt"
5+ "io"
56 "log"
67 "os"
8+ "path/filepath"
79 "strings"
810
911 "github.com/MakeNowJust/heredoc"
@@ -103,12 +105,21 @@ func NewAstCLI(
103105 rootCmd .PersistentFlags ().Bool (params .ApikeyOverrideFlag , false , "" )
104106
105107 _ = rootCmd .PersistentFlags ().MarkHidden (params .ApikeyOverrideFlag )
108+ rootCmd .PersistentFlags ().String (params .LogFileFlag , "" , params .LogFileUsage )
109+ rootCmd .PersistentFlags ().String (params .LogFileConsoleFlag , "" , params .LogFileConsoleUsage )
106110
107111 // This monitors and traps situations where "extra/garbage" commands
108112 // are passed to Cobra.
109113 rootCmd .PersistentPreRunE = func (cmd * cobra.Command , args []string ) error {
114+ err := customLogConfiguration (rootCmd )
115+ if err != nil {
116+ return err
117+ }
110118 PrintConfiguration ()
111- err := configuration .LoadConfiguration ()
119+ err = configuration .LoadConfiguration ()
120+ if err != nil {
121+ return err
122+ }
112123 // Need to check the __complete command to allow correct behavior of the autocomplete
113124 if len (args ) > 0 && cmd .Name () != params .Help && cmd .Name () != "__complete" {
114125 _ = cmd .Help ()
@@ -137,7 +148,8 @@ func NewAstCLI(
137148 _ = viper .BindPFlag (params .RetryFlag , rootCmd .PersistentFlags ().Lookup (params .RetryFlag ))
138149 _ = viper .BindPFlag (params .RetryDelayFlag , rootCmd .PersistentFlags ().Lookup (params .RetryDelayFlag ))
139150 _ = viper .BindPFlag (params .ApikeyOverrideFlag , rootCmd .PersistentFlags ().Lookup (params .ApikeyOverrideFlag ))
140-
151+ _ = viper .BindPFlag (params .LogFileFlag , rootCmd .PersistentFlags ().Lookup (params .LogFileFlag ))
152+ _ = viper .BindPFlag (params .LogFileConsoleFlag , rootCmd .PersistentFlags ().Lookup (params .LogFileConsoleFlag ))
141153 // Set help func
142154 rootCmd .SetHelpFunc (
143155 func (command * cobra.Command , args []string ) {
@@ -336,3 +348,61 @@ func printByScanInfoFormat(cmd *cobra.Command, view interface{}) error {
336348 f , _ := cmd .Flags ().GetString (params .ScanInfoFormatFlag )
337349 return printer .Print (cmd .OutOrStdout (), view , f )
338350}
351+
352+ func customLogConfiguration (cmd * cobra.Command ) error {
353+ if cmd .PersistentFlags ().Changed (params .LogFileFlag ) {
354+ if err := setLogOutputFromFlag (params .LogFileFlag , viper .GetString (params .LogFileFlag )); err != nil {
355+ return err
356+ }
357+ }
358+ if cmd .PersistentFlags ().Changed (params .LogFileConsoleFlag ) {
359+ if err := setLogOutputFromFlag (params .LogFileConsoleFlag , viper .GetString (params .LogFileConsoleFlag )); err != nil {
360+ return err
361+ }
362+ }
363+ return nil
364+ }
365+
366+ func setLogOutputFromFlag (flag , dirPath string ) error {
367+ if strings .TrimSpace (dirPath ) == "" {
368+ return errors .New ("flag needs an argument: --" + flag )
369+ }
370+
371+ // Confirm it’s a directory
372+ info , err := os .Stat (dirPath )
373+ if err != nil {
374+ if os .IsNotExist (err ) {
375+ return fmt .Errorf ("the specified directory path does not exist. Please check the path: %s" , dirPath )
376+ }
377+ return fmt .Errorf ("an error occurred while accessing the directory path. Please check the path: %s" , dirPath )
378+ }
379+ if ! info .IsDir () {
380+ return fmt .Errorf ("expected a directory path but got a file: %s" , dirPath )
381+ }
382+
383+ // Create full path for the log file
384+ logFilePath := filepath .Join (dirPath , "ast-cli.log" )
385+
386+ const defaultFilePermissions = 0666
387+ // open the log file with write and append permissions
388+ // If file doesn't exist, it will be created. If permission is denied for directory path, return an error.
389+ file , err := os .OpenFile (logFilePath , os .O_CREATE | os .O_WRONLY | os .O_APPEND , defaultFilePermissions )
390+ if err != nil {
391+ if os .IsPermission (err ) {
392+ return fmt .Errorf ("permission denied: cannot write to directory %s" , dirPath )
393+ }
394+ return fmt .Errorf ("unable to open log file %s: %v" , logFilePath , err )
395+ }
396+
397+ // Configure the logger to write to the log file and optionally to stdout.
398+ // If the flag indicates stdout logging is enabled, log output is duplicated to both file and console.
399+ // Otherwise, logs are written only to the file.
400+ var multiWriter io.Writer
401+ if flag == params .LogFileConsoleFlag {
402+ multiWriter = io .MultiWriter (file , os .Stdout )
403+ } else {
404+ multiWriter = io .MultiWriter (file )
405+ }
406+ log .SetOutput (multiWriter )
407+ return nil
408+ }
0 commit comments