Logging file work

This commit is contained in:
sirir 2025-03-28 20:13:37 +01:00
parent 506070edcc
commit dcf431c807
2 changed files with 106 additions and 57 deletions

View File

@ -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:
retention:
keep_latest: 10
@ -31,28 +35,4 @@ services:
type: "kopia"
provider: "local"
destination:
path: "/local/backup/path"
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"
path: "/tmp"

View File

@ -14,7 +14,7 @@ import (
var (
// Global logger instance
logger *Logger
once sync.Once
mu sync.Mutex // Mutex to protect logger initialization and replacement
)
// LogLevel represents the severity of a log message
@ -28,59 +28,104 @@ const (
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
type Logger struct {
logLevel LogLevel
output io.Writer
stdLog *log.Logger
errLog *log.Logger
file *os.File // Store file reference for closing
mu sync.Mutex
}
// Singleton
func InitLogger(level LogLevel, logFile string) {
once.Do(func() {
var output io.Writer = os.Stdout
// InitLogger initializes or reinitializes the global logger instance
func InitLogger(level LogLevel, logFilePath string) {
mu.Lock()
defer mu.Unlock()
if logFile != "" {
logDir := filepath.Dir(logFile)
if err := os.MkdirAll(logDir, 0755); err != nil {
fmt.Fprintf(os.Stderr, "Failed to create log directory: %v\n", err)
os.Exit(1)
}
// Close the existing logger if there is one
if logger != nil {
logger.Close()
}
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to open log file: %v\n", err)
os.Exit(1)
}
fmt.Printf("Initializing logger with level %s and log file: %s\n", level.String(), logFilePath)
output = io.MultiWriter(os.Stdout, file)
var output io.Writer = os.Stdout
var fileHandle *os.File
// Set up log file if specified
if logFilePath != "" {
logDir := filepath.Dir(logFilePath)
if err := os.MkdirAll(logDir, 0755); err != nil {
fmt.Fprintf(os.Stderr, "Failed to create log directory %s: %v\n", logDir, err)
os.Exit(1)
}
logger = &Logger{
logLevel: level,
output: output,
stdLog: log.New(output, "", log.LstdFlags),
errLog: log.New(output, "ERROR: ", log.LstdFlags),
file, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to open log file %s: %v\n", logFilePath, err)
os.Exit(1)
}
})
fmt.Printf("Successfully opened log file: %s\n", logFilePath)
fileHandle = file
output = io.MultiWriter(os.Stdout, file)
}
logger = &Logger{
logLevel: level,
output: output,
stdLog: log.New(output, "", 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 {
mu.Lock()
defer mu.Unlock()
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, "")
mu.Lock()
}
return logger
}
// SetLogLevel changes the current log level
func (l *Logger) SetLogLevel(level LogLevel) {
l.mu.Lock()
defer l.mu.Unlock()
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{}) {
if level < l.logLevel {
return
@ -89,20 +134,22 @@ func (l *Logger) log(level LogLevel, format string, v ...interface{}) {
l.mu.Lock()
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)
if !ok {
file = "unknown"
line = 0
}
// Format message with source location
message := fmt.Sprintf("%s:%d: %s", filepath.Base(file), line, fmt.Sprintf(format, v...))
// Format message with source location and level
prefix := fmt.Sprintf("[%s] %s:%d: ", level.String(), filepath.Base(file), line)
message := fmt.Sprintf(format, v...)
fullMessage := prefix + message
if level >= ERROR {
l.errLog.Println(message)
l.errLog.Println(fullMessage)
} 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
func (l *Logger) Fatal(format string, v ...interface{}) {
l.log(FATAL, format, v...)
l.Close() // Ensure logs are flushed before exit
os.Exit(1)
}
@ -139,9 +187,30 @@ func (l *Logger) ErrorWithStack(err error, format string, v ...interface{}) {
}
message := fmt.Sprintf(format, v...)
buf := make([]byte, 1024)
buf := make([]byte, 4096) // Larger buffer for more stack trace info
n := runtime.Stack(buf, false)
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
}
}