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