137 lines
3.3 KiB
Go
137 lines
3.3 KiB
Go
package logger
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// FileLogger contains configuration options specific to logging into a file.
|
|
// It is enabled if the FilePath != ""
|
|
type FileLogger struct {
|
|
|
|
// Minimum log level for logging into a file
|
|
Level Level
|
|
|
|
// Absolute or relative path to log files to
|
|
Path string
|
|
|
|
// With this option the path of the log file will be appended with the current date
|
|
// so that an own log file for each day is used. The format of the date is 'YYYYMMDD'
|
|
AppendDate bool
|
|
|
|
// Internal dependency used to synchronize the access to the log file
|
|
fileSync *sync.RWMutex
|
|
// Additional file sync that is used during writing to the log file
|
|
fileSyncWrite *sync.RWMutex
|
|
|
|
logger *log.Logger
|
|
file *os.File
|
|
|
|
// Upper logger struct
|
|
rootLogger *Logger
|
|
}
|
|
|
|
// CloseFile closes the file that is currently used for logging messages to
|
|
// a file
|
|
func (l *FileLogger) CloseFile() {
|
|
if l.file != nil {
|
|
l.fileSync.Lock()
|
|
l.file.Close()
|
|
l.file = nil
|
|
l.logger = nil
|
|
l.fileSync.Unlock()
|
|
}
|
|
}
|
|
|
|
// openFile tries to open the file that is configured inside the loggers fild
|
|
// "LogFilePath" and initializes the mutex
|
|
func (l *FileLogger) openFile() {
|
|
// Initialize new mutex
|
|
if l.fileSync == nil {
|
|
l.fileSync = &sync.RWMutex{}
|
|
l.fileSyncWrite = &sync.RWMutex{}
|
|
}
|
|
|
|
l.fileSync.Lock()
|
|
|
|
path := l.getFilePath()
|
|
file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
|
if err == nil {
|
|
l.logger = log.New(file, "", 0)
|
|
l.file = file
|
|
} else {
|
|
l.rootLogger.Log(LevelError, fmt.Sprintf("Cannot access the log file '%s'\n%s", path, err.Error()))
|
|
}
|
|
|
|
l.fileSync.Unlock()
|
|
}
|
|
|
|
// writeToFile writes the given message to the opened log file
|
|
func (l *FileLogger) writeToFile(message string, level Level) {
|
|
l.fileSync.RLock()
|
|
l.fileSyncWrite.RLock()
|
|
|
|
// When append date is enabled we need to check if file path is still accurate
|
|
if l.AppendDate {
|
|
currentPath := l.getFilePath()
|
|
|
|
if l.file.Name() != currentPath {
|
|
// The file path is not up-to-date anymore → update the log file
|
|
l.fileSync.RUnlock()
|
|
l.fileSyncWrite.RUnlock()
|
|
|
|
l.fileSyncWrite.Lock()
|
|
// The syncWriter is now locked. So check again the file name against the current path because the file could already be changed
|
|
// in the time framew between locking and checking
|
|
if l.file.Name() != currentPath {
|
|
l.CloseFile()
|
|
l.openFile()
|
|
}
|
|
l.fileSyncWrite.Unlock()
|
|
|
|
// Lock previous locks again
|
|
l.fileSync.RLock()
|
|
l.fileSyncWrite.RLock()
|
|
}
|
|
}
|
|
|
|
l.logger.Println(message)
|
|
l.file.Sync()
|
|
|
|
l.fileSync.RUnlock()
|
|
l.fileSyncWrite.RUnlock()
|
|
|
|
// Close the file because for fatal log level the program is going to be exited
|
|
if level == LevelFatal {
|
|
l.CloseFile()
|
|
}
|
|
}
|
|
|
|
// getFilePath returns the path to use for the log file
|
|
func (l *FileLogger) getFilePath() string {
|
|
path := strings.ReplaceAll(l.Path, "\\", "/")
|
|
|
|
// Append the current date to the log path when enabled
|
|
if l.AppendDate {
|
|
lastSlash := strings.LastIndex(path, "/")
|
|
if lastSlash != -1 && (lastSlash+1) < len(path) {
|
|
path = path + "." + getFileDate()
|
|
} else if lastSlash == -1 {
|
|
path = path + "." + getFileDate()
|
|
} else {
|
|
path += getFileDate()
|
|
}
|
|
}
|
|
|
|
return path
|
|
}
|
|
|
|
// getFileDate returns the current date formatted as the log files path name
|
|
func getFileDate() string {
|
|
return time.Now().Format("2006-01-02")
|
|
}
|