浏览代码

Merge branch 'logger' of git.dmitriygnatenko.ru:dima/go-common

Dima 5 月之前
父节点
当前提交
f3e76ee52c
共有 10 个文件被更改,包括 261 次插入41 次删除
  1. 13 0
      db/db.go
  2. 1 1
      go.mod
  3. 0 0
      go.sum
  4. 35 1
      logger/email_writer.go
  5. 21 2
      logger/logger.go
  6. 48 6
      logger/logger_config.go
  7. 0 31
      main.go
  8. 37 0
      smtp/auth.go
  9. 52 0
      smtp/smtp.go
  10. 54 0
      smtp/smtp_config.go

+ 13 - 0
db/db.go

@@ -3,6 +3,7 @@ package db
 import (
 	"context"
 	"database/sql"
+	"errors"
 	"fmt"
 	"strconv"
 )
@@ -14,6 +15,18 @@ type DB struct {
 }
 
 func NewDB(c Config) (*DB, error) {
+	if len(c.username) == 0 {
+		return nil, errors.New("empty username")
+	}
+
+	if len(c.password) == 0 {
+		return nil, errors.New("empty password")
+	}
+
+	if len(c.dbname) == 0 {
+		return nil, errors.New("empty database name")
+	}
+
 	if len(c.driver) == 0 {
 		c.driver = defaultDriver
 	}

+ 1 - 1
go.mod

@@ -1,3 +1,3 @@
 module git.dmitriygnatenko.ru/dima/go-common
 
-go 1.22
+go 1.21.4

+ 0 - 0
go.sum


+ 35 - 1
logger/email_writer.go

@@ -1,8 +1,42 @@
 package logger
 
+import (
+	"bytes"
+	"encoding/json"
+	"errors"
+
+	"git.dmitriygnatenko.ru/dima/go-common/smtp"
+)
+
 type EmailWriter struct {
+	recipient string
+	subject   string
+	smtp      *smtp.SMTP
+}
+
+func NewEmailWriter(smtp *smtp.SMTP, recipient string, subject string) (*EmailWriter, error) {
+	if len(recipient) == 0 {
+		return nil, errors.New("empty recipient")
+	}
+
+	if smtp == nil {
+		return nil, errors.New("empty smtp client")
+	}
+
+	return &EmailWriter{
+		recipient: recipient,
+		subject:   subject,
+		smtp:      smtp,
+	}, nil
 }
 
-func (w EmailWriter) Write(p []byte) (n int, err error) {
+func (w EmailWriter) Write(p []byte) (int, error) {
+	var out bytes.Buffer
+
+	if err := json.Indent(&out, p, "", "    "); err != nil {
+		return 0, err
+	}
+
+	err := w.smtp.Send(w.recipient, w.subject, out.String(), false)
 	return 0, err
 }

+ 21 - 2
logger/logger.go

@@ -6,6 +6,8 @@ import (
 	"log/slog"
 	"os"
 	"sync"
+
+	"git.dmitriygnatenko.ru/dima/go-common/smtp"
 )
 
 type CtxAttrKey struct{}
@@ -39,7 +41,7 @@ func Init(c Config) error {
 		}
 
 		if c.fileLogEnabled {
-			logger.logFile, err = os.OpenFile(c.fileLogFilepath, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0644)
+			logger.logFile, err = os.OpenFile(c.filepath, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0644)
 			if err != nil {
 				return
 			}
@@ -51,8 +53,25 @@ func Init(c Config) error {
 		}
 
 		if c.emailLogEnabled {
+			smtpClient, smtpErr := smtp.NewSMTP(
+				smtp.NewConfig(
+					smtp.WithHost(c.smtpHost),
+					smtp.WithUsername(c.smtpUsername),
+					smtp.WithPassword(c.smtpPassword),
+					smtp.WithPort(c.smtpPort),
+				),
+			)
+
+			if smtpErr != nil {
+				err = smtpErr
+				return
+			}
 
-			ew := EmailWriter{}
+			ew, ewErr := NewEmailWriter(smtpClient, c.emailRecipient, c.emailSubject)
+			if ewErr != nil {
+				err = ewErr
+				return
+			}
 
 			logger.emailLogger = slog.New(slog.NewJSONHandler(ew, &slog.HandlerOptions{
 				AddSource: c.emailLogAddSource,

+ 48 - 6
logger/logger_config.go

@@ -12,12 +12,18 @@ type Config struct {
 	fileLogEnabled   bool
 	fileLogLevel     slog.Level // INFO by default
 	fileLogAddSource bool
-	fileLogFilepath  string
+	filepath         string
 
 	// email config
 	emailLogEnabled   bool
 	emailLogLevel     slog.Level // INFO by default
 	emailLogAddSource bool
+	smtpHost          string
+	smtpPort          uint16
+	smtpUsername      string
+	smtpPassword      string
+	emailRecipient    string
+	emailSubject      string
 }
 
 type ConfigOption func(*Config)
@@ -37,7 +43,7 @@ func NewConfig(opts ...ConfigOption) Config {
 	return *c
 }
 
-// stdout log
+// stdout
 
 func WithStdoutLogEnabled(enabled bool) ConfigOption {
 	return func(s *Config) {
@@ -56,7 +62,7 @@ func WithStdoutLogAddSource(add bool) ConfigOption {
 	}
 }
 
-// file log
+// file
 
 func WithFileLogEnabled(enabled bool) ConfigOption {
 	return func(s *Config) {
@@ -76,13 +82,13 @@ func WithFileLogAddSource(add bool) ConfigOption {
 	}
 }
 
-func WithFileLogFilepath(path string) ConfigOption {
+func WithFilepath(path string) ConfigOption {
 	return func(s *Config) {
-		s.fileLogFilepath = path
+		s.filepath = path
 	}
 }
 
-// email log
+// email
 
 func WithEmailLogEnabled(enabled bool) ConfigOption {
 	return func(s *Config) {
@@ -101,3 +107,39 @@ func WithEmailLogAddSource(add bool) ConfigOption {
 		s.emailLogAddSource = add
 	}
 }
+
+func WithEmailRecipient(email string) ConfigOption {
+	return func(s *Config) {
+		s.emailRecipient = email
+	}
+}
+
+func WithEmailSubject(subject string) ConfigOption {
+	return func(s *Config) {
+		s.emailSubject = subject
+	}
+}
+
+func WithSMTPHost(host string) ConfigOption {
+	return func(s *Config) {
+		s.smtpHost = host
+	}
+}
+
+func WithSMTPPort(port uint16) ConfigOption {
+	return func(s *Config) {
+		s.smtpPort = port
+	}
+}
+
+func WithSMTPUsername(user string) ConfigOption {
+	return func(s *Config) {
+		s.smtpUsername = user
+	}
+}
+
+func WithSMTPPassword(password string) ConfigOption {
+	return func(s *Config) {
+		s.smtpPassword = password
+	}
+}

+ 0 - 31
main.go

@@ -1,31 +0,0 @@
-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")
-
-}

+ 37 - 0
smtp/auth.go

@@ -0,0 +1,37 @@
+package smtp
+
+import (
+	"fmt"
+	"net/smtp"
+	"strings"
+)
+
+type auth struct {
+	username string
+	password string
+}
+
+func (a auth) Start(_ *smtp.ServerInfo) (string, []byte, error) {
+	return "LOGIN", nil, nil
+}
+
+func (a auth) Next(req []byte, more bool) ([]byte, error) {
+	command := strings.ToLower(strings.TrimSuffix(strings.TrimSpace(string(req)), ":"))
+
+	if more {
+		if command == "username" {
+			return []byte(fmt.Sprintf("%s", a.username)), nil
+		}
+		if command == "password" {
+			return []byte(fmt.Sprintf("%s", a.password)), nil
+		}
+
+		return nil, fmt.Errorf("unexpected server challenge: %s", command)
+	}
+
+	return nil, nil
+}
+
+func getAuth(username, password string) smtp.Auth {
+	return &auth{username, password}
+}

+ 52 - 0
smtp/smtp.go

@@ -0,0 +1,52 @@
+package smtp
+
+import (
+	"errors"
+	"fmt"
+	"net/smtp"
+)
+
+type SMTP struct {
+	config Config
+}
+
+func NewSMTP(c Config) (*SMTP, error) {
+	if len(c.username) == 0 {
+		return nil, errors.New("empty username")
+	}
+
+	if len(c.password) == 0 {
+		return nil, errors.New("empty password")
+	}
+
+	if len(c.host) == 0 {
+		c.host = defaultHost
+	}
+
+	if c.port == 0 {
+		c.port = defaultPort
+	}
+
+	return &SMTP{config: c}, nil
+}
+
+func (s SMTP) Send(recipient string, subject string, content string, html bool) error {
+	contentType := "text/plain"
+	if html {
+		contentType = "text/html"
+	}
+
+	msg := []byte("To: " + recipient + "\r\n" +
+		"From: " + s.config.username + "\r\n" +
+		"Subject: " + subject + "\r\n" +
+		"Content-Type: " + contentType + "; charset=\"UTF-8\"" + "\n\r\n" +
+		content + "\r\n")
+
+	return smtp.SendMail(
+		fmt.Sprintf("%s:%d", s.config.host, s.config.port),
+		getAuth(s.config.username, s.config.password),
+		s.config.username,
+		[]string{recipient},
+		msg,
+	)
+}

+ 54 - 0
smtp/smtp_config.go

@@ -0,0 +1,54 @@
+package smtp
+
+const (
+	defaultHost = "localhost"
+	defaultPort = 587
+)
+
+type Config struct {
+	host     string
+	port     uint16
+	username string
+	password string
+}
+
+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
+}
+
+func WithUsername(username string) ConfigOption {
+	return func(s *Config) {
+		s.username = username
+	}
+}
+
+func WithPassword(password string) ConfigOption {
+	return func(s *Config) {
+		s.password = password
+	}
+}
+
+func WithHost(host string) ConfigOption {
+	return func(s *Config) {
+		s.host = host
+	}
+}
+
+func WithPort(port uint16) ConfigOption {
+	return func(s *Config) {
+		s.port = port
+	}
+}