|
@@ -1,9 +1,145 @@
|
|
|
package logger
|
|
|
|
|
|
import (
|
|
|
+ "context"
|
|
|
+ "fmt"
|
|
|
"log/slog"
|
|
|
+ "os"
|
|
|
+ "sync"
|
|
|
)
|
|
|
|
|
|
-func NewLogger(c Config) *slog.Logger {
|
|
|
- return slog.New(NewHandler(c))
|
|
|
+type CtxAttrKey struct{}
|
|
|
+
|
|
|
+var (
|
|
|
+ once sync.Once
|
|
|
+ ctxAttrMu sync.RWMutex
|
|
|
+ logger *Logger
|
|
|
+)
|
|
|
+
|
|
|
+type Logger struct {
|
|
|
+ config Config
|
|
|
+ logFile *os.File
|
|
|
+
|
|
|
+ stdoutLogger *slog.Logger
|
|
|
+ fileLogger *slog.Logger
|
|
|
+ emailLogger *slog.Logger
|
|
|
+}
|
|
|
+
|
|
|
+func Init(c Config) error {
|
|
|
+ var err error
|
|
|
+
|
|
|
+ once.Do(func() {
|
|
|
+ logger = &Logger{config: c}
|
|
|
+
|
|
|
+ if c.stdoutLogEnabled {
|
|
|
+ logger.stdoutLogger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
|
|
+ AddSource: c.stdoutLogAddSource,
|
|
|
+ Level: c.stdoutLogLevel,
|
|
|
+ }))
|
|
|
+ }
|
|
|
+
|
|
|
+ if c.fileLogEnabled {
|
|
|
+ logger.logFile, err = os.OpenFile(c.fileLogFilepath, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0644)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ logger.fileLogger = slog.New(slog.NewJSONHandler(logger.logFile, &slog.HandlerOptions{
|
|
|
+ AddSource: c.fileLogAddSource,
|
|
|
+ Level: c.fileLogLevel,
|
|
|
+ }))
|
|
|
+ }
|
|
|
+
|
|
|
+ if c.emailLogEnabled {
|
|
|
+
|
|
|
+ ew := EmailWriter{}
|
|
|
+
|
|
|
+ logger.emailLogger = slog.New(slog.NewJSONHandler(ew, &slog.HandlerOptions{
|
|
|
+ AddSource: c.emailLogAddSource,
|
|
|
+ Level: c.emailLogLevel,
|
|
|
+ }))
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ return err
|
|
|
+}
|
|
|
+
|
|
|
+func Default() *Logger {
|
|
|
+ if logger == nil {
|
|
|
+ panic("logger not initialised")
|
|
|
+ }
|
|
|
+
|
|
|
+ return logger
|
|
|
+}
|
|
|
+
|
|
|
+func ErrorKV(ctx context.Context, msg string, args ...any) {
|
|
|
+ log(ctx, slog.LevelError, msg, args...)
|
|
|
+}
|
|
|
+
|
|
|
+func WarnKV(ctx context.Context, msg string, args ...any) {
|
|
|
+ log(ctx, slog.LevelWarn, msg, args...)
|
|
|
+}
|
|
|
+
|
|
|
+func InfoKV(ctx context.Context, msg string, args ...any) {
|
|
|
+ log(ctx, slog.LevelInfo, msg, args...)
|
|
|
+}
|
|
|
+
|
|
|
+func DebugKV(ctx context.Context, msg string, args ...any) {
|
|
|
+ log(ctx, slog.LevelDebug, msg, args...)
|
|
|
+}
|
|
|
+
|
|
|
+func Errorf(ctx context.Context, format string, args ...any) {
|
|
|
+ log(ctx, slog.LevelError, fmt.Sprintf(format, args...))
|
|
|
+}
|
|
|
+
|
|
|
+func Warnf(ctx context.Context, format string, args ...any) {
|
|
|
+ log(ctx, slog.LevelWarn, fmt.Sprintf(format, args...))
|
|
|
+}
|
|
|
+
|
|
|
+func Infof(ctx context.Context, format string, args ...any) {
|
|
|
+ log(ctx, slog.LevelInfo, fmt.Sprintf(format, args...))
|
|
|
+}
|
|
|
+
|
|
|
+func Debugf(ctx context.Context, format string, args ...any) {
|
|
|
+ log(ctx, slog.LevelDebug, fmt.Sprintf(format, args...))
|
|
|
+}
|
|
|
+
|
|
|
+func Error(ctx context.Context, msg string) {
|
|
|
+ log(ctx, slog.LevelError, msg)
|
|
|
+}
|
|
|
+
|
|
|
+func Warn(ctx context.Context, msg string) {
|
|
|
+ log(ctx, slog.LevelWarn, msg)
|
|
|
+}
|
|
|
+
|
|
|
+func Info(ctx context.Context, msg string) {
|
|
|
+ log(ctx, slog.LevelInfo, msg)
|
|
|
+}
|
|
|
+
|
|
|
+func Debug(ctx context.Context, msg string) {
|
|
|
+ log(ctx, slog.LevelDebug, msg)
|
|
|
+}
|
|
|
+
|
|
|
+func log(ctx context.Context, level slog.Level, msg string, args ...any) {
|
|
|
+ if Default().stdoutLogger != nil {
|
|
|
+ Default().stdoutLogger.Log(ctx, level, msg, append(args, AttrFromCtx(ctx)...)...)
|
|
|
+ }
|
|
|
+
|
|
|
+ if Default().fileLogger != nil {
|
|
|
+ Default().fileLogger.Log(ctx, level, msg, append(args, AttrFromCtx(ctx)...)...)
|
|
|
+ }
|
|
|
+
|
|
|
+ if Default().emailLogger != nil {
|
|
|
+ Default().emailLogger.Log(ctx, level, msg, append(args, AttrFromCtx(ctx)...)...)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func Close() error {
|
|
|
+ if Default().logFile != nil {
|
|
|
+ if err := Default().logFile.Close(); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
}
|