Browse Source

Add logger

Dmitriy Gnatenko 6 months ago
parent
commit
491041bc01
6 changed files with 324 additions and 0 deletions
  1. 9 0
      db/db_config.go
  2. 8 0
      logger/email_writer.go
  3. 145 0
      logger/logger.go
  4. 103 0
      logger/logger_config.go
  5. 28 0
      logger/logger_ctx.go
  6. 31 0
      main.go

+ 9 - 0
db/db_config.go

@@ -36,6 +36,15 @@ func (s *ConfigOptions) Add(option ConfigOption) {
 	*s = append(*s, option)
 }
 
+func NewConfig(opts ...ConfigOption) Config {
+	c := &Config{}
+	for _, opt := range opts {
+		opt(c)
+	}
+
+	return *c
+}
+
 func WithDriver(driver string) ConfigOption {
 	return func(s *Config) {
 		s.driver = driver

+ 8 - 0
logger/email_writer.go

@@ -0,0 +1,8 @@
+package logger
+
+type EmailWriter struct {
+}
+
+func (w EmailWriter) Write(p []byte) (n int, err error) {
+	return 0, err
+}

+ 145 - 0
logger/logger.go

@@ -0,0 +1,145 @@
+package logger
+
+import (
+	"context"
+	"fmt"
+	"log/slog"
+	"os"
+	"sync"
+)
+
+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
+}

+ 103 - 0
logger/logger_config.go

@@ -0,0 +1,103 @@
+package logger
+
+import "log/slog"
+
+type Config struct {
+	// stdout config
+	stdoutLogEnabled   bool
+	stdoutLogLevel     slog.Level // INFO by default
+	stdoutLogAddSource bool
+
+	// file config
+	fileLogEnabled   bool
+	fileLogLevel     slog.Level // INFO by default
+	fileLogAddSource bool
+	fileLogFilepath  string
+
+	// email config
+	emailLogEnabled   bool
+	emailLogLevel     slog.Level // INFO by default
+	emailLogAddSource bool
+}
+
+type ConfigOption func(*Config)
+
+type ConfigOptions []ConfigOption
+
+func (s *ConfigOptions) Add(option ConfigOption) {
+	*s = append(*s, option)
+}
+
+func NewConfig(opts ...ConfigOption) Config {
+	c := &Config{}
+	for _, opt := range opts {
+		opt(c)
+	}
+
+	return *c
+}
+
+// stdout log
+
+func WithStdoutLogEnabled(enabled bool) ConfigOption {
+	return func(s *Config) {
+		s.stdoutLogEnabled = enabled
+	}
+}
+
+func WithStdoutLogLevel(level slog.Level) ConfigOption {
+	return func(s *Config) {
+		s.stdoutLogLevel = level
+	}
+}
+func WithStdoutLogAddSource(add bool) ConfigOption {
+	return func(s *Config) {
+		s.stdoutLogAddSource = add
+	}
+}
+
+// file log
+
+func WithFileLogEnabled(enabled bool) ConfigOption {
+	return func(s *Config) {
+		s.fileLogEnabled = enabled
+	}
+}
+
+func WithFileLogLevel(level slog.Level) ConfigOption {
+	return func(s *Config) {
+		s.fileLogLevel = level
+	}
+}
+
+func WithFileLogAddSource(add bool) ConfigOption {
+	return func(s *Config) {
+		s.fileLogAddSource = add
+	}
+}
+
+func WithFileLogFilepath(path string) ConfigOption {
+	return func(s *Config) {
+		s.fileLogFilepath = path
+	}
+}
+
+// email log
+
+func WithEmailLogEnabled(enabled bool) ConfigOption {
+	return func(s *Config) {
+		s.emailLogEnabled = enabled
+	}
+}
+
+func WithEmailLogLevel(level slog.Level) ConfigOption {
+	return func(s *Config) {
+		s.emailLogLevel = level
+	}
+}
+
+func WithEmailLogAddSource(add bool) ConfigOption {
+	return func(s *Config) {
+		s.emailLogAddSource = add
+	}
+}

+ 28 - 0
logger/logger_ctx.go

@@ -0,0 +1,28 @@
+package logger
+
+import "context"
+
+func With(ctx context.Context, key string, value any) context.Context {
+	ctxAttrMu.Lock()
+	defer ctxAttrMu.Unlock()
+
+	if ctx.Value(CtxAttrKey{}) == nil {
+		ctx = context.WithValue(ctx, CtxAttrKey{}, make([]any, 0))
+	}
+
+	kv, ok := ctx.Value(CtxAttrKey{}).([]any)
+	if !ok {
+		return ctx
+	}
+
+	return context.WithValue(ctx, CtxAttrKey{}, append(kv, key, value))
+}
+
+func AttrFromCtx(ctx context.Context) []any {
+	ctxAttrMu.RLock()
+	defer ctxAttrMu.RUnlock()
+
+	kv, _ := ctx.Value(CtxAttrKey{}).([]any)
+
+	return kv
+}

+ 31 - 0
main.go

@@ -0,0 +1,31 @@
+package main
+
+import (
+	"context"
+	"log/slog"
+
+	"git.dmitriygnatenko.ru/dima/go-common/logger"
+)
+
+func main() {
+
+	err := logger.Init(logger.NewConfig(
+		logger.WithStdoutLogAddSource(true),
+		logger.WithStdoutLogEnabled(true),
+		logger.WithFileLogLevel(slog.LevelError),
+		logger.WithEmailLogEnabled(true),
+		logger.WithFileLogEnabled(true),
+		logger.WithFileLogFilepath("test_log.txt"),
+	))
+
+	_ = err
+
+	ctx := context.Background()
+
+	ctx = logger.With(ctx, "test111", 74658743)
+
+	logger.InfoKV(ctx, "dfgdgdfgdssg", "dsfsd", "val333", "dfgdf", 11)
+
+	// logger.Error(ctx, "dsfdsf dsf dsfs")
+
+}