github.com/snowflakedb/gosnowflake@v1.9.0/easy_logging.go (about)

     1  package gosnowflake
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"path"
     9  	"runtime"
    10  	"strings"
    11  	"sync"
    12  )
    13  
    14  type initTrials struct {
    15  	everTriedToInitialize bool
    16  	clientConfigFileInput string
    17  	configureCounter      int
    18  	mu                    sync.Mutex
    19  }
    20  
    21  var easyLoggingInitTrials = initTrials{
    22  	everTriedToInitialize: false,
    23  	clientConfigFileInput: "",
    24  	configureCounter:      0,
    25  	mu:                    sync.Mutex{},
    26  }
    27  
    28  func (i *initTrials) setInitTrial(clientConfigFileInput string) {
    29  	i.everTriedToInitialize = true
    30  	i.clientConfigFileInput = clientConfigFileInput
    31  }
    32  
    33  func (i *initTrials) increaseReconfigureCounter() {
    34  	i.configureCounter++
    35  }
    36  
    37  func (i *initTrials) reset() {
    38  	i.mu.Lock()
    39  	defer i.mu.Unlock()
    40  
    41  	i.everTriedToInitialize = false
    42  	i.clientConfigFileInput = ""
    43  	i.configureCounter = 0
    44  }
    45  
    46  func initEasyLogging(clientConfigFileInput string) error {
    47  	easyLoggingInitTrials.mu.Lock()
    48  	defer easyLoggingInitTrials.mu.Unlock()
    49  
    50  	if !allowedToInitialize(clientConfigFileInput) {
    51  		logger.Info("Skipping Easy Logging initialization as it is not allowed to initialize")
    52  		return nil
    53  	}
    54  	logger.Infof("Trying to initialize Easy Logging")
    55  	config, configPath, err := getClientConfig(clientConfigFileInput)
    56  	if err != nil {
    57  		logger.Errorf("Failed to initialize Easy Logging, err: %s", err)
    58  		return easyLoggingInitError(err)
    59  	}
    60  	if config == nil {
    61  		logger.Info("Easy Logging is disabled as no config has been found")
    62  		easyLoggingInitTrials.setInitTrial(clientConfigFileInput)
    63  		return nil
    64  	}
    65  	var logLevel string
    66  	logLevel, err = getLogLevel(config.Common.LogLevel)
    67  	if err != nil {
    68  		logger.Errorf("Failed to initialize Easy Logging, err: %s", err)
    69  		return easyLoggingInitError(err)
    70  	}
    71  	var logPath string
    72  	logPath, err = getLogPath(config.Common.LogPath)
    73  	if err != nil {
    74  		logger.Errorf("Failed to initialize Easy Logging, err: %s", err)
    75  		return easyLoggingInitError(err)
    76  	}
    77  	logger.Infof("Initializing Easy Logging with logPath=%s and logLevel=%s from file: %s", logPath, logLevel, configPath)
    78  	err = reconfigureEasyLogging(logLevel, logPath)
    79  	if err != nil {
    80  		logger.Errorf("Failed to initialize Easy Logging, err: %s", err)
    81  	}
    82  	easyLoggingInitTrials.setInitTrial(clientConfigFileInput)
    83  	easyLoggingInitTrials.increaseReconfigureCounter()
    84  	return err
    85  }
    86  
    87  func easyLoggingInitError(err error) error {
    88  	return &SnowflakeError{
    89  		Number:      ErrCodeClientConfigFailed,
    90  		Message:     errMsgClientConfigFailed,
    91  		MessageArgs: []interface{}{err.Error()},
    92  	}
    93  }
    94  
    95  func reconfigureEasyLogging(logLevel string, logPath string) error {
    96  	newLogger := CreateDefaultLogger()
    97  	err := newLogger.SetLogLevel(logLevel)
    98  	if err != nil {
    99  		return err
   100  	}
   101  	var output io.Writer
   102  	var file *os.File
   103  	output, file, err = createLogWriter(logPath)
   104  	if err != nil {
   105  		return err
   106  	}
   107  	newLogger.SetOutput(output)
   108  	err = newLogger.CloseFileOnLoggerReplace(file)
   109  	if err != nil {
   110  		logger.Errorf("%s", err)
   111  	}
   112  	logger.Replace(&newLogger)
   113  	return nil
   114  }
   115  
   116  func createLogWriter(logPath string) (io.Writer, *os.File, error) {
   117  	if strings.EqualFold(logPath, "STDOUT") {
   118  		return os.Stdout, nil, nil
   119  	}
   120  	logFileName := path.Join(logPath, "snowflake.log")
   121  	file, err := os.OpenFile(logFileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0640)
   122  	if err != nil {
   123  		return nil, nil, err
   124  	}
   125  	return file, file, nil
   126  }
   127  
   128  func allowedToInitialize(clientConfigFileInput string) bool {
   129  	triedToInitializeWithoutConfigFile := easyLoggingInitTrials.everTriedToInitialize && easyLoggingInitTrials.clientConfigFileInput == ""
   130  	isAllowedToInitialize := !easyLoggingInitTrials.everTriedToInitialize || (triedToInitializeWithoutConfigFile && clientConfigFileInput != "")
   131  	if !isAllowedToInitialize && easyLoggingInitTrials.clientConfigFileInput != clientConfigFileInput {
   132  		logger.Warnf("Easy logging will not be configured for CLIENT_CONFIG_FILE=%s because it was previously configured for a different client config", clientConfigFileInput)
   133  	}
   134  	return isAllowedToInitialize
   135  }
   136  
   137  func getLogLevel(logLevel string) (string, error) {
   138  	if logLevel == "" {
   139  		logger.Warn("LogLevel in client config not found. Using default value: OFF")
   140  		return levelOff, nil
   141  	}
   142  	return toLogLevel(logLevel)
   143  }
   144  
   145  func getLogPath(logPath string) (string, error) {
   146  	logPathOrDefault := logPath
   147  	if logPath == "" {
   148  		homeDir, err := os.UserHomeDir()
   149  		if err != nil {
   150  			return "", fmt.Errorf("user home directory is not accessible, err: %w", err)
   151  		}
   152  		logPathOrDefault = homeDir
   153  		logger.Warnf("LogPath in client config not found. Using user home directory as a default value: %s", logPathOrDefault)
   154  	}
   155  	pathWithGoSubdir := path.Join(logPathOrDefault, "go")
   156  	exists, err := dirExists(pathWithGoSubdir)
   157  	if err != nil {
   158  		return "", err
   159  	}
   160  	if !exists {
   161  		err = os.MkdirAll(pathWithGoSubdir, 0700)
   162  		if err != nil {
   163  			return "", err
   164  		}
   165  	}
   166  	logDirPermValid, perm, err := isDirAccessCorrect(pathWithGoSubdir)
   167  	if err != nil {
   168  		return "", err
   169  	}
   170  	if !logDirPermValid {
   171  		logger.Warnf("Log directory: %s could potentially be accessed by others. Directory chmod: 0%o", pathWithGoSubdir, *perm)
   172  	}
   173  	return pathWithGoSubdir, nil
   174  }
   175  
   176  func isDirAccessCorrect(dirPath string) (bool, *os.FileMode, error) {
   177  	if runtime.GOOS == "windows" {
   178  		return true, nil, nil
   179  	}
   180  	dirStat, err := os.Stat(dirPath)
   181  	if err != nil {
   182  		return false, nil, err
   183  	}
   184  	perm := dirStat.Mode().Perm()
   185  	if perm != 0700 {
   186  		return false, &perm, nil
   187  	}
   188  	return true, &perm, nil
   189  }
   190  
   191  func dirExists(dirPath string) (bool, error) {
   192  	stat, err := os.Stat(dirPath)
   193  	if err == nil {
   194  		return stat.IsDir(), nil
   195  	}
   196  	if errors.Is(err, os.ErrNotExist) {
   197  		return false, nil
   198  	}
   199  	return false, err
   200  }