location.go•3.71 kB
package logger
import (
"fmt"
"os"
"path/filepath"
"runtime"
"sync"
"github.com/sirupsen/logrus"
)
const logDirName = "gateway"
var (
cachedLogDir string
cachedLogDirOnce sync.Once
)
func DefaultLogDir() string {
cachedLogDirOnce.Do(func() {
cachedLogDir = evaluateDefaultLogDir()
})
return cachedLogDir
}
func evaluateDefaultLogDir() string {
if envDir := os.Getenv("CENTRALMIND_LOG_DIR"); envDir != "" {
if ensureDirectoryExists(envDir) {
logrus.Debugf("Using log directory from environment: %s", envDir)
return envDir
}
logrus.Debugf("Environment-specified log directory %s is not accessible, falling back to defaults", envDir)
}
var defaultDir string
switch runtime.GOOS {
case "windows":
// On Windows, try ProgramData first; if not available, fall back to the executable directory.
if pd := os.Getenv("ProgramData"); pd != "" {
defaultDir = filepath.Join(pd, logDirName)
} else if exe, err := os.Executable(); err == nil {
defaultDir = filepath.Join(filepath.Dir(exe), logDirName)
} else {
logrus.WithError(err).Debug("Unable to get executable path, using current directory as fallback")
defaultDir = filepath.Join(".", logDirName)
}
case "darwin": // macOS
// On macOS, default to a hidden directory inside the executable's folder.
if exe, err := os.Executable(); err == nil {
defaultDir = filepath.Join(filepath.Dir(exe), fmt.Sprintf(".%s", logDirName))
} else {
logrus.WithError(err).Debug("Unable to get executable path, using current directory as fallback")
defaultDir = filepath.Join(".", logDirName)
}
default: // Linux and other Unix-like systems
defaultDir = filepath.Join("/var/log", logDirName)
// If creating /var/log/centralmind fails, fall back to a hidden directory in the home folder.
if !ensureDirectoryExists(defaultDir) {
if homeDir, err := os.UserHomeDir(); err == nil {
defaultDir = filepath.Join(homeDir, fmt.Sprintf(".%s", logDirName))
} else {
logrus.WithError(err).Debug("Unable to determine user home directory, using current directory as fallback")
defaultDir = filepath.Join(".", logDirName)
}
}
}
// Ensure the chosen directory exists.
if !ensureDirectoryExists(defaultDir) {
logrus.Debugf("Failed to create log directory %s, using current directory as last resort", defaultDir)
defaultDir = "."
}
logrus.Debugf("Using log directory: %s", defaultDir)
return defaultDir
}
// ensureDirectoryExists checks if a directory exists, creates it if necessary,
// and verifies it is writable. It returns true if the directory exists or
// was created successfully AND is writable.
func ensureDirectoryExists(dir string) bool {
// Check if the directory already exists.
if fi, err := os.Stat(dir); err == nil && fi.IsDir() {
// Directory exists, check if it's writable
return isDirectoryWritable(dir)
}
// Try to create the directory.
if err := os.MkdirAll(dir, 0755); err != nil {
logrus.WithError(err).WithField("directory", dir).Debug("Failed to create log directory")
return false
}
logrus.WithField("directory", dir).Debug("Created log directory")
// Verify the newly created directory is writable
return isDirectoryWritable(dir)
}
// isDirectoryWritable tests if a directory is writable by creating a temporary file.
func isDirectoryWritable(dir string) bool {
// Try to create a test file
f, err := os.CreateTemp(dir, ".write_test_*")
if err != nil {
logrus.WithError(err).WithField("directory", dir).Debug("Directory is not writable")
return false
}
// Close and remove the test file
filename := f.Name()
_ = f.Close()
_ = os.Remove(filename)
logrus.Debugf("Verified directory is writable: %s", dir)
return true
}