upload logging.go
This commit is contained in:
286
logging.go
Executable file
286
logging.go
Executable file
@@ -0,0 +1,286 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
const (
|
||||
COLOR_RESET = "\033[0m"
|
||||
COLOR_RED = "\033[91m"
|
||||
COLOR_YELLOW = "\033[93m"
|
||||
COLOR_GREEN = "\033[92m"
|
||||
COLOR_BLUE = "\033[94m"
|
||||
COLOR_CYAN = "\033[96m"
|
||||
COLOR_WHITE = "\033[97m"
|
||||
COLOR_GRAY = "\033[90m"
|
||||
COLOR_GREY = COLOR_GRAY
|
||||
)
|
||||
|
||||
type LogLevel int
|
||||
|
||||
const (
|
||||
Debug LogLevel = iota
|
||||
Info
|
||||
Warn
|
||||
Error
|
||||
Critical
|
||||
)
|
||||
|
||||
func (l LogLevel) String() string {
|
||||
switch l {
|
||||
case Debug:
|
||||
return "DEBUG"
|
||||
case Info:
|
||||
return "INFO"
|
||||
case Warn:
|
||||
return "WARN"
|
||||
case Error:
|
||||
return "ERROR"
|
||||
case Critical:
|
||||
return "CRITICAL"
|
||||
default:
|
||||
return "UNKNOWN"
|
||||
}
|
||||
}
|
||||
|
||||
var LevelColorMap = map[LogLevel]string{
|
||||
Debug: COLOR_GRAY,
|
||||
Info: COLOR_WHITE,
|
||||
Warn: COLOR_YELLOW,
|
||||
Error: COLOR_RED,
|
||||
Critical: COLOR_RED,
|
||||
}
|
||||
|
||||
type Formatter interface {
|
||||
Format(level LogLevel, timestamp time.Time, message string) string
|
||||
}
|
||||
|
||||
type ConsoleFormatter struct {
|
||||
TimestampFormatter string
|
||||
UseColor bool
|
||||
LevelColors map[LogLevel]string
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func NewConsoleFormatter(timestampFormat string, useColor bool, levelColors map[LogLevel]string) *ConsoleFormatter {
|
||||
if timestampFormat == "" {
|
||||
timestampFormat = "[02/01/06 15:04:05]"
|
||||
}
|
||||
if levelColors == nil {
|
||||
levelColors = LevelColorMap
|
||||
}
|
||||
|
||||
return &ConsoleFormatter{
|
||||
TimestampFormatter: timestampFormat,
|
||||
UseColor: useColor,
|
||||
LevelColors: levelColors,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *ConsoleFormatter) Format(level LogLevel, timestamp time.Time, message string) string {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
timestampStr := timestamp.Format(f.TimestampFormatter)
|
||||
levelStr := level.String()
|
||||
color, ok := f.LevelColors[level]
|
||||
if !ok {
|
||||
color = COLOR_WHITE
|
||||
}
|
||||
|
||||
if f.UseColor && isTTY() {
|
||||
return fmt.Sprintf("%s %s[%s]%s %s", timestampStr, color, levelStr, COLOR_RESET, message)
|
||||
}
|
||||
return fmt.Sprintf("%s [%s] %s", timestampStr, levelStr, message)
|
||||
}
|
||||
|
||||
type FileFormatter struct {
|
||||
TimestampFormat string
|
||||
LoggerName string
|
||||
}
|
||||
|
||||
func NewFileFormatter(timestampFormat, loggerName string) *FileFormatter {
|
||||
if timestampFormat == "" {
|
||||
timestampFormat = "2006-01-02 15:04:05"
|
||||
}
|
||||
return &FileFormatter{
|
||||
TimestampFormat: timestampFormat,
|
||||
LoggerName: loggerName,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FileFormatter) Format(level LogLevel, timestamp time.Time, message string) string {
|
||||
return fmt.Sprintf("[ %s | %s | %s ] %s", timestamp.Format(f.TimestampFormat), f.LoggerName, level.String(), message)
|
||||
}
|
||||
|
||||
type Logger struct {
|
||||
name string
|
||||
level LogLevel
|
||||
consoleColor bool
|
||||
logDir string
|
||||
logFilenamePattern string
|
||||
formatter Formatter
|
||||
consoleWriter io.Writer
|
||||
fileWriter io.WriteCloser
|
||||
fileLogger *log.Logger
|
||||
fileFormatter *FileFormatter
|
||||
mu sync.Mutex
|
||||
initialized bool
|
||||
}
|
||||
|
||||
var (
|
||||
globalLogger *Logger
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
func NewLogger(
|
||||
loggerName string,
|
||||
logLevel LogLevel,
|
||||
consoleColor bool,
|
||||
logDir string,
|
||||
logFilenamePattern string,
|
||||
) *Logger {
|
||||
if loggerName == "" {
|
||||
loggerName = "main"
|
||||
}
|
||||
if logDir == "" {
|
||||
logDir = "logs"
|
||||
}
|
||||
if logFilenamePattern == "" {
|
||||
logFilenamePattern = "2006-01-02"
|
||||
}
|
||||
|
||||
return &Logger{
|
||||
name: loggerName,
|
||||
level: logLevel,
|
||||
consoleColor: consoleColor,
|
||||
logDir: logDir,
|
||||
logFilenamePattern: logFilenamePattern,
|
||||
consoleWriter: os.Stdout,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) init() {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
if l.initialized {
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(l.logDir, 0755); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error creating log directory %s: %v\n", l.logDir, err)
|
||||
} else {
|
||||
logFilePath := l.genUniqueLogFilename()
|
||||
file, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error opening log file %s: %v\n", logFilePath, err)
|
||||
} else {
|
||||
l.fileWriter = file
|
||||
if l.fileWriter != nil {
|
||||
l.fileFormatter = NewFileFormatter("", l.name)
|
||||
}
|
||||
l.fileLogger = log.New(l.fileWriter, "", 0)
|
||||
fmt.Printf("Log file will be saved to %s\n", logFilePath)
|
||||
}
|
||||
}
|
||||
|
||||
l.formatter = NewConsoleFormatter("", l.consoleColor, nil)
|
||||
|
||||
l.initialized = true
|
||||
}
|
||||
|
||||
func (l *Logger) genUniqueLogFilename() string {
|
||||
now := time.Now()
|
||||
baseFilename := now.Format(l.logFilenamePattern)
|
||||
logFilePath := filepath.Join(l.logDir, fmt.Sprintf("%s.log", baseFilename))
|
||||
|
||||
if _, err := os.Stat(logFilePath); os.IsNotExist(err) {
|
||||
return logFilePath
|
||||
}
|
||||
|
||||
counter := 1
|
||||
for {
|
||||
suffixedFilePath := filepath.Join(l.logDir, fmt.Sprintf("%s_%d.log", baseFilename, counter))
|
||||
if _, err := os.Stat(suffixedFilePath); os.IsNotExist(err) {
|
||||
return suffixedFilePath
|
||||
}
|
||||
counter += 1
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Log(level LogLevel, message string) {
|
||||
if !l.initialized {
|
||||
l.init()
|
||||
}
|
||||
|
||||
if level < l.level {
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
if l.formatter != nil {
|
||||
formattedMsg := l.formatter.Format(level, now, message)
|
||||
fmt.Fprintln(l.consoleWriter, formattedMsg)
|
||||
} else {
|
||||
fmt.Fprintf(l.consoleWriter, "%s [%s] %s\n", now.Format("[15:04:05]"), level.String(), message)
|
||||
}
|
||||
|
||||
if l.fileLogger != nil && l.fileFormatter != nil {
|
||||
formattedFileMsg := l.fileFormatter.Format(level, now, message)
|
||||
l.fileLogger.Println(formattedFileMsg)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Debug(message string, v ...any) {
|
||||
l.Log(Debug, fmt.Sprintf(message, v...))
|
||||
}
|
||||
|
||||
func (l *Logger) Warn(message string, v ...any) {
|
||||
l.Log(Warn, fmt.Sprintf(message, v...))
|
||||
}
|
||||
|
||||
func (l *Logger) Info(message string, v ...any) {
|
||||
l.Log(Info, fmt.Sprintf(message, v...))
|
||||
}
|
||||
|
||||
func (l *Logger) Error(message string, v ...any) {
|
||||
l.Log(Error, fmt.Sprintf(message, v...))
|
||||
}
|
||||
|
||||
func (l *Logger) Critical(message string, v ...any) {
|
||||
l.Log(Critical, fmt.Sprintf(message, v...))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (l *Logger) Close() error {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.fileWriter != nil {
|
||||
err := l.fileWriter.Close()
|
||||
l.fileWriter = nil
|
||||
l.fileLogger = nil
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetLogger() *Logger {
|
||||
once.Do(func() {
|
||||
globalLogger = NewLogger("shizuku", Debug, true, "logs", "2006-01-02")
|
||||
})
|
||||
return globalLogger
|
||||
}
|
||||
|
||||
func isTTY() bool {
|
||||
return os.Getenv("TERM") != "" && term.IsTerminal(int(os.Stdout.Fd()))
|
||||
}
|
||||
Reference in New Issue
Block a user