204 lines
4.4 KiB
Go
204 lines
4.4 KiB
Go
package logger
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"runtime"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// Level represents a log level
|
|
type Level int
|
|
|
|
const (
|
|
// DEBUG level
|
|
DEBUG Level = iota
|
|
// INFO level
|
|
INFO
|
|
// WARN level
|
|
WARN
|
|
// ERROR level
|
|
ERROR
|
|
// FATAL level
|
|
FATAL
|
|
)
|
|
|
|
// String returns the string representation of the log level
|
|
func (l Level) 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 represents a logger
|
|
type Logger struct {
|
|
level Level
|
|
output io.Writer
|
|
}
|
|
|
|
// contextKey is a type for context keys
|
|
type contextKey string
|
|
|
|
// requestIDKey is the key for request ID in context
|
|
const requestIDKey = contextKey("request_id")
|
|
|
|
// New creates a new logger
|
|
func New(level Level, output io.Writer) *Logger {
|
|
if output == nil {
|
|
output = os.Stdout
|
|
}
|
|
return &Logger{
|
|
level: level,
|
|
output: output,
|
|
}
|
|
}
|
|
|
|
// WithLevel creates a new logger with the specified level
|
|
func (l *Logger) WithLevel(level Level) *Logger {
|
|
return &Logger{
|
|
level: level,
|
|
output: l.output,
|
|
}
|
|
}
|
|
|
|
// WithOutput creates a new logger with the specified output
|
|
func (l *Logger) WithOutput(output io.Writer) *Logger {
|
|
return &Logger{
|
|
level: l.level,
|
|
output: output,
|
|
}
|
|
}
|
|
|
|
// log logs a message with the specified level
|
|
func (l *Logger) log(ctx context.Context, level Level, format string, args ...interface{}) {
|
|
if level < l.level {
|
|
return
|
|
}
|
|
|
|
// Get request ID from context
|
|
requestID := getRequestID(ctx)
|
|
|
|
// Get caller information
|
|
_, file, line, ok := runtime.Caller(2)
|
|
if !ok {
|
|
file = "unknown"
|
|
line = 0
|
|
}
|
|
// Extract just the filename
|
|
if idx := strings.LastIndex(file, "/"); idx >= 0 {
|
|
file = file[idx+1:]
|
|
}
|
|
|
|
// Format message
|
|
message := fmt.Sprintf(format, args...)
|
|
|
|
// Format log entry
|
|
timestamp := time.Now().Format(time.RFC3339)
|
|
logEntry := fmt.Sprintf("%s [%s] %s:%d [%s] %s\n",
|
|
timestamp, level.String(), file, line, requestID, message)
|
|
|
|
// Write log entry
|
|
_, _ = l.output.Write([]byte(logEntry))
|
|
|
|
// Exit if fatal
|
|
if level == FATAL {
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// Debug logs a debug message
|
|
func (l *Logger) Debug(ctx context.Context, format string, args ...interface{}) {
|
|
l.log(ctx, DEBUG, format, args...)
|
|
}
|
|
|
|
// Info logs an info message
|
|
func (l *Logger) Info(ctx context.Context, format string, args ...interface{}) {
|
|
l.log(ctx, INFO, format, args...)
|
|
}
|
|
|
|
// Warn logs a warning message
|
|
func (l *Logger) Warn(ctx context.Context, format string, args ...interface{}) {
|
|
l.log(ctx, WARN, format, args...)
|
|
}
|
|
|
|
// Error logs an error message
|
|
func (l *Logger) Error(ctx context.Context, format string, args ...interface{}) {
|
|
l.log(ctx, ERROR, format, args...)
|
|
}
|
|
|
|
// Fatal logs a fatal message and exits
|
|
func (l *Logger) Fatal(ctx context.Context, format string, args ...interface{}) {
|
|
l.log(ctx, FATAL, format, args...)
|
|
}
|
|
|
|
// WithRequestID adds a request ID to the context
|
|
func WithRequestID(ctx context.Context) context.Context {
|
|
requestID := uuid.New().String()
|
|
return context.WithValue(ctx, requestIDKey, requestID)
|
|
}
|
|
|
|
// GetRequestID gets the request ID from the context
|
|
func GetRequestID(ctx context.Context) string {
|
|
return getRequestID(ctx)
|
|
}
|
|
|
|
// getRequestID gets the request ID from the context
|
|
func getRequestID(ctx context.Context) string {
|
|
if ctx == nil {
|
|
return "-"
|
|
}
|
|
requestID, ok := ctx.Value(requestIDKey).(string)
|
|
if !ok {
|
|
return "-"
|
|
}
|
|
return requestID
|
|
}
|
|
|
|
// Default logger
|
|
var defaultLogger = New(INFO, os.Stdout)
|
|
|
|
// SetDefaultLogger sets the default logger
|
|
func SetDefaultLogger(logger *Logger) {
|
|
defaultLogger = logger
|
|
}
|
|
|
|
// Debug logs a debug message using the default logger
|
|
func Debug(ctx context.Context, format string, args ...interface{}) {
|
|
defaultLogger.Debug(ctx, format, args...)
|
|
}
|
|
|
|
// Info logs an info message using the default logger
|
|
func Info(ctx context.Context, format string, args ...interface{}) {
|
|
defaultLogger.Info(ctx, format, args...)
|
|
}
|
|
|
|
// Warn logs a warning message using the default logger
|
|
func Warn(ctx context.Context, format string, args ...interface{}) {
|
|
defaultLogger.Warn(ctx, format, args...)
|
|
}
|
|
|
|
// Error logs an error message using the default logger
|
|
func Error(ctx context.Context, format string, args ...interface{}) {
|
|
defaultLogger.Error(ctx, format, args...)
|
|
}
|
|
|
|
// Fatal logs a fatal message and exits using the default logger
|
|
func Fatal(ctx context.Context, format string, args ...interface{}) {
|
|
defaultLogger.Fatal(ctx, format, args...)
|
|
}
|