Logging file work
This commit is contained in:
parent
506070edcc
commit
dcf431c807
30
config.yml
30
config.yml
@ -1,3 +1,7 @@
|
|||||||
|
logging:
|
||||||
|
log_level: "debug" # Can be debug, info, warn, error, or fatal
|
||||||
|
log_file: "/var/log/backea/app.log" # Path to log file (optional)
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
retention:
|
retention:
|
||||||
keep_latest: 10
|
keep_latest: 10
|
||||||
@ -31,28 +35,4 @@ services:
|
|||||||
type: "kopia"
|
type: "kopia"
|
||||||
provider: "local"
|
provider: "local"
|
||||||
destination:
|
destination:
|
||||||
path: "/local/backup/path"
|
path: "/tmp"
|
||||||
2:
|
|
||||||
backup_strategy:
|
|
||||||
type: "kopia"
|
|
||||||
provider: "b2"
|
|
||||||
# Credentials are in .env
|
|
||||||
3:
|
|
||||||
backup_strategy:
|
|
||||||
<<: *sftp_strategy # This uses the common sftp strategy defined above
|
|
||||||
|
|
||||||
# Example of another service
|
|
||||||
another_service:
|
|
||||||
source:
|
|
||||||
host: "local"
|
|
||||||
path: "/path/to/another/service"
|
|
||||||
hooks:
|
|
||||||
before_hook: "systemctl stop service-name"
|
|
||||||
after_hook: "systemctl start service-name"
|
|
||||||
backup_configs:
|
|
||||||
1:
|
|
||||||
backup_strategy:
|
|
||||||
type: "kopia"
|
|
||||||
provider: "local"
|
|
||||||
destination:
|
|
||||||
path: "/local/backup/path"
|
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
// Global logger instance
|
// Global logger instance
|
||||||
logger *Logger
|
logger *Logger
|
||||||
once sync.Once
|
mu sync.Mutex // Mutex to protect logger initialization and replacement
|
||||||
)
|
)
|
||||||
|
|
||||||
// LogLevel represents the severity of a log message
|
// LogLevel represents the severity of a log message
|
||||||
@ -28,33 +28,65 @@ const (
|
|||||||
FATAL
|
FATAL
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// String returns the string representation of the log level
|
||||||
|
func (l LogLevel) String() string {
|
||||||
|
switch l {
|
||||||
|
case DEBUG:
|
||||||
|
return "DEBUG"
|
||||||
|
case INFO:
|
||||||
|
return "INFO"
|
||||||
|
case WARN:
|
||||||
|
return "WARN"
|
||||||
|
case ERROR:
|
||||||
|
return "ERROR"
|
||||||
|
case FATAL:
|
||||||
|
return "FATAL"
|
||||||
|
default:
|
||||||
|
return "UNKNOWN"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Logger provides logging functionality
|
// Logger provides logging functionality
|
||||||
type Logger struct {
|
type Logger struct {
|
||||||
logLevel LogLevel
|
logLevel LogLevel
|
||||||
output io.Writer
|
output io.Writer
|
||||||
stdLog *log.Logger
|
stdLog *log.Logger
|
||||||
errLog *log.Logger
|
errLog *log.Logger
|
||||||
|
file *os.File // Store file reference for closing
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Singleton
|
// InitLogger initializes or reinitializes the global logger instance
|
||||||
func InitLogger(level LogLevel, logFile string) {
|
func InitLogger(level LogLevel, logFilePath string) {
|
||||||
once.Do(func() {
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
// Close the existing logger if there is one
|
||||||
|
if logger != nil {
|
||||||
|
logger.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Initializing logger with level %s and log file: %s\n", level.String(), logFilePath)
|
||||||
|
|
||||||
var output io.Writer = os.Stdout
|
var output io.Writer = os.Stdout
|
||||||
|
var fileHandle *os.File
|
||||||
|
|
||||||
if logFile != "" {
|
// Set up log file if specified
|
||||||
logDir := filepath.Dir(logFile)
|
if logFilePath != "" {
|
||||||
|
logDir := filepath.Dir(logFilePath)
|
||||||
if err := os.MkdirAll(logDir, 0755); err != nil {
|
if err := os.MkdirAll(logDir, 0755); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Failed to create log directory: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Failed to create log directory %s: %v\n", logDir, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
file, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Failed to open log file: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Failed to open log file %s: %v\n", logFilePath, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Successfully opened log file: %s\n", logFilePath)
|
||||||
|
fileHandle = file
|
||||||
output = io.MultiWriter(os.Stdout, file)
|
output = io.MultiWriter(os.Stdout, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,24 +95,37 @@ func InitLogger(level LogLevel, logFile string) {
|
|||||||
output: output,
|
output: output,
|
||||||
stdLog: log.New(output, "", log.LstdFlags),
|
stdLog: log.New(output, "", log.LstdFlags),
|
||||||
errLog: log.New(output, "ERROR: ", log.LstdFlags),
|
errLog: log.New(output, "ERROR: ", log.LstdFlags),
|
||||||
|
file: fileHandle,
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
// Log initialization message through the logger itself
|
||||||
|
logger.Info("Logger initialized with level %s and log file %s", level.String(), logFilePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Singleton
|
// GetLogger returns the global logger instance, initializing it if necessary
|
||||||
func GetLogger() *Logger {
|
func GetLogger() *Logger {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
|
// Initialize with default settings if not already initialized
|
||||||
|
// We need to unlock before calling InitLogger since it will lock mu
|
||||||
|
mu.Unlock()
|
||||||
InitLogger(INFO, "")
|
InitLogger(INFO, "")
|
||||||
|
mu.Lock()
|
||||||
}
|
}
|
||||||
return logger
|
return logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLogLevel changes the current log level
|
||||||
func (l *Logger) SetLogLevel(level LogLevel) {
|
func (l *Logger) SetLogLevel(level LogLevel) {
|
||||||
l.mu.Lock()
|
l.mu.Lock()
|
||||||
defer l.mu.Unlock()
|
defer l.mu.Unlock()
|
||||||
l.logLevel = level
|
l.logLevel = level
|
||||||
|
l.Info("Log level set to %s", level.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log is the internal logging function
|
||||||
func (l *Logger) log(level LogLevel, format string, v ...interface{}) {
|
func (l *Logger) log(level LogLevel, format string, v ...interface{}) {
|
||||||
if level < l.logLevel {
|
if level < l.logLevel {
|
||||||
return
|
return
|
||||||
@ -89,20 +134,22 @@ func (l *Logger) log(level LogLevel, format string, v ...interface{}) {
|
|||||||
l.mu.Lock()
|
l.mu.Lock()
|
||||||
defer l.mu.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
// Get caller information
|
// Get caller information (skip 2 levels to get the actual calling function)
|
||||||
_, file, line, ok := runtime.Caller(2)
|
_, file, line, ok := runtime.Caller(2)
|
||||||
if !ok {
|
if !ok {
|
||||||
file = "unknown"
|
file = "unknown"
|
||||||
line = 0
|
line = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format message with source location
|
// Format message with source location and level
|
||||||
message := fmt.Sprintf("%s:%d: %s", filepath.Base(file), line, fmt.Sprintf(format, v...))
|
prefix := fmt.Sprintf("[%s] %s:%d: ", level.String(), filepath.Base(file), line)
|
||||||
|
message := fmt.Sprintf(format, v...)
|
||||||
|
fullMessage := prefix + message
|
||||||
|
|
||||||
if level >= ERROR {
|
if level >= ERROR {
|
||||||
l.errLog.Println(message)
|
l.errLog.Println(fullMessage)
|
||||||
} else {
|
} else {
|
||||||
l.stdLog.Println(message)
|
l.stdLog.Println(fullMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,6 +176,7 @@ func (l *Logger) Error(format string, v ...interface{}) {
|
|||||||
// Fatal logs a fatal message and exits the program
|
// Fatal logs a fatal message and exits the program
|
||||||
func (l *Logger) Fatal(format string, v ...interface{}) {
|
func (l *Logger) Fatal(format string, v ...interface{}) {
|
||||||
l.log(FATAL, format, v...)
|
l.log(FATAL, format, v...)
|
||||||
|
l.Close() // Ensure logs are flushed before exit
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,9 +187,30 @@ func (l *Logger) ErrorWithStack(err error, format string, v ...interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message := fmt.Sprintf(format, v...)
|
message := fmt.Sprintf(format, v...)
|
||||||
|
buf := make([]byte, 4096) // Larger buffer for more stack trace info
|
||||||
buf := make([]byte, 1024)
|
|
||||||
n := runtime.Stack(buf, false)
|
n := runtime.Stack(buf, false)
|
||||||
|
|
||||||
l.Error("%s: %v\n%s", message, err, buf[:n])
|
l.Error("%s: %v\n%s", message, err, buf[:n])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close flushes and closes the log file if one is being used
|
||||||
|
func (l *Logger) Close() {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
|
if l.file != nil {
|
||||||
|
l.file.Sync() // Flush any buffered data
|
||||||
|
l.file.Close() // Close the file
|
||||||
|
l.file = nil // Clear the reference
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown properly closes the logger
|
||||||
|
func Shutdown() {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
if logger != nil {
|
||||||
|
logger.Close()
|
||||||
|
logger = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user