init
This commit is contained in:
parent
02db2c5c68
commit
7be4b7fb19
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
logs
|
20
go.mod
Normal file
20
go.mod
Normal file
@ -0,0 +1,20 @@
|
||||
module git.belvedersky.ru/common/logger
|
||||
|
||||
go 1.18
|
||||
|
||||
require github.com/valyala/fasthttp v1.37.0
|
||||
|
||||
require (
|
||||
github.com/mattn/go-colorable v0.1.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/fatih/color v1.13.0
|
||||
github.com/gofiber/fiber/v2 v2.34.0
|
||||
github.com/klauspost/compress v1.15.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
)
|
36
go.sum
Normal file
36
go.sum
Normal file
@ -0,0 +1,36 @@
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/gofiber/fiber/v2 v2.34.0 h1:96BJMw6uaxQhJsHY54SFGOtGgp9pgombK5Hbi4JSEQA=
|
||||
github.com/gofiber/fiber/v2 v2.34.0/go.mod h1:ozRQfS+D7EL1+hMH+gutku0kfx1wLX4hAxDCtDzpj4U=
|
||||
github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U=
|
||||
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.37.0 h1:7WHCyI7EAkQMVmrfBhWTCOaeROb1aCBiTopx63LkMbE=
|
||||
github.com/valyala/fasthttp v1.37.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
|
||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
79
logger.go
Normal file
79
logger.go
Normal file
@ -0,0 +1,79 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"git.belvedersky.ru/common/logger/service"
|
||||
"git.belvedersky.ru/common/logger/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
ansi = "s,\x1b\\[[0-9;]*[a-zA-Z],,g"
|
||||
)
|
||||
|
||||
var (
|
||||
re = regexp.MustCompile(ansi)
|
||||
)
|
||||
|
||||
type (
|
||||
Logger struct {
|
||||
cfg service.Config
|
||||
file os.File
|
||||
}
|
||||
)
|
||||
|
||||
// Сервис логирования
|
||||
func New(cfg service.Config, durationUpdate *time.Duration) *Logger {
|
||||
logFile := utils.GetLogFile(cfg.Directory, cfg.LogFileName)
|
||||
l := &Logger{
|
||||
cfg: cfg,
|
||||
file: *logFile,
|
||||
}
|
||||
if durationUpdate == nil {
|
||||
t := time.Hour * 24
|
||||
durationUpdate = &t
|
||||
}
|
||||
// Смена и очистка файла лога раз в сутки
|
||||
go l.FileUpdate(false, durationUpdate)
|
||||
return l
|
||||
}
|
||||
|
||||
// Смена и очистка лог файла через указанное время
|
||||
func (lg *Logger) FileUpdate(now bool, durationUpdate *time.Duration) (err error) {
|
||||
for {
|
||||
if !now {
|
||||
time.Sleep(*durationUpdate)
|
||||
}
|
||||
b, err := ioutil.ReadAll(&lg.file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clean := re.ReplaceAllString(string(b), "")
|
||||
if err = ioutil.WriteFile(lg.file.Name(), []byte(clean), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
lg.file = *utils.GetLogFile(lg.cfg.Directory, lg.cfg.LogFileName)
|
||||
if now {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Создание нового сервиса логирования
|
||||
func (lg *Logger) Create(name string) *service.LoggerService {
|
||||
// Лог файл
|
||||
l := utils.SetupLog(name)
|
||||
// Дебаг в консоль
|
||||
if lg.cfg.Development {
|
||||
mw := io.MultiWriter(os.Stdout, &lg.file)
|
||||
l.SetOutput(mw)
|
||||
} else {
|
||||
l.SetOutput(&lg.file)
|
||||
}
|
||||
return &service.LoggerService{Log: l, ServiceName: lg.cfg.System, Development: lg.cfg.Development, WebhookUrl: lg.cfg.Webhook}
|
||||
}
|
58
logger_test.go
Normal file
58
logger_test.go
Normal file
@ -0,0 +1,58 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"git.belvedersky.ru/common/logger/service"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
var (
|
||||
cfg = service.Config{
|
||||
System: "test",
|
||||
Development: true,
|
||||
Directory: "test_logs",
|
||||
LogFileName: "test",
|
||||
PanicFileName: "test_panic",
|
||||
Webhook: "https://devtest.galamart.ru/bus/pub?topic=error&channel=error",
|
||||
}
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
|
||||
s := New(cfg, nil)
|
||||
if s.cfg != cfg {
|
||||
t.Errorf("NewLoggerService() = %v, want %v", s.cfg, &cfg)
|
||||
}
|
||||
if s.cfg != cfg {
|
||||
t.Errorf("Log file is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
|
||||
s := New(cfg, nil)
|
||||
if s.cfg != cfg {
|
||||
t.Errorf("NewLoggerService() = %v, want %v", s.cfg, &cfg)
|
||||
}
|
||||
testLogger := s.Create("test")
|
||||
if testLogger.Log == nil {
|
||||
t.Errorf("Log is nil")
|
||||
}
|
||||
testLogger.Print(service.LogStruct{Message: "test", Color: color.FgBlue})
|
||||
}
|
||||
|
||||
func TestFileUpdate(t *testing.T) {
|
||||
s := New(cfg, nil)
|
||||
if s.cfg != cfg {
|
||||
t.Errorf("NewLoggerService() = %v, want %v", s.cfg, &cfg)
|
||||
}
|
||||
if err := s.FileUpdate(true, nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := os.RemoveAll("test_logs"); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
130
service/log.go
Normal file
130
service/log.go
Normal file
@ -0,0 +1,130 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"git.belvedersky.ru/common/logger/utils"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
ServiceError = TypeError{Code: 2, Description: "Сервисная ошибка"}
|
||||
)
|
||||
|
||||
// Трейс ошибки для рекавера
|
||||
func (s *LoggerService) TraceHandler(c *fiber.Ctx, e interface{}) {
|
||||
buf := make([]byte, 1024)
|
||||
buf = buf[:runtime.Stack(buf, false)]
|
||||
err := fmt.Sprintf("panic: %v\n%s\n", e, buf)
|
||||
os.Stderr.WriteString(err)
|
||||
s.Error(LogErrorStruct{
|
||||
Error: ServiceError,
|
||||
Message: err,
|
||||
})
|
||||
c.JSON(err)
|
||||
}
|
||||
|
||||
func (s *LoggerService) Errorf(f string, v ...interface{}) {
|
||||
s.Log.Printf("ERROR: "+f, v...)
|
||||
|
||||
}
|
||||
|
||||
func (s *LoggerService) Warningf(f string, v ...interface{}) {
|
||||
s.Log.Printf("WARNING: "+f, v...)
|
||||
|
||||
}
|
||||
|
||||
func (s *LoggerService) Infof(f string, v ...interface{}) {
|
||||
s.Log.Printf("INFO: "+f, v...)
|
||||
|
||||
}
|
||||
|
||||
func (s *LoggerService) Debugf(f string, v ...interface{}) {
|
||||
s.Log.Printf("DEBUG: "+f, v...)
|
||||
|
||||
}
|
||||
|
||||
// Лог дебага
|
||||
func (s *LoggerService) Debug(ls LogStruct) {
|
||||
|
||||
// Если цвет не указан
|
||||
if ls.Color == 0 {
|
||||
ls.Color = color.FgWhite
|
||||
}
|
||||
|
||||
// Если указан дебаг, то пишем сообщение
|
||||
if s.Development {
|
||||
c := color.New(ls.Color)
|
||||
s.Log.Println(c.Sprint(ls.Message))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LoggerService) Separator() {
|
||||
s.Log.Println("------------------------------------------------------------------")
|
||||
}
|
||||
|
||||
func (s *LoggerService) RouteSeparator(route string) {
|
||||
s.Log.Println("----------------------" + route + "-------------------------------")
|
||||
}
|
||||
|
||||
// Простой вывод текста
|
||||
func (s *LoggerService) Print(ls LogStruct) {
|
||||
// Если цвет не указан
|
||||
if ls.Color == 0 {
|
||||
ls.Color = color.FgWhite
|
||||
}
|
||||
|
||||
// Пишем в консоль
|
||||
c := color.New(ls.Color)
|
||||
s.Log.Println(c.Sprint(ls.Message))
|
||||
|
||||
}
|
||||
|
||||
// Ошибка
|
||||
func (s *LoggerService) Error(message LogErrorStruct) {
|
||||
|
||||
// Меняем цвет на красный
|
||||
c := color.New(color.FgRed)
|
||||
|
||||
// Пишем в консоль
|
||||
s.Log.Println(c.Sprint(message.Message))
|
||||
|
||||
// Добавляем название сервиса
|
||||
message.System = s.ServiceName
|
||||
|
||||
// Маршим в байтики
|
||||
b, err := json.Marshal(message)
|
||||
if err != nil {
|
||||
s.Log.Println(err)
|
||||
}
|
||||
|
||||
// Отправляем в шину
|
||||
if err := utils.SendMessage(s.WebhookUrl, b); err != nil {
|
||||
|
||||
// Не получилось отправить в шину
|
||||
s.Log.Println("Не смогли отправить в nsq:", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Простое оповещение в телеграм без вывода в консоль
|
||||
func (s *LoggerService) Notify(message string) {
|
||||
|
||||
m := &LogErrorStruct{
|
||||
System: s.ServiceName,
|
||||
Message: message,
|
||||
Error: TypeError{Code: -111, Description: "Оповещение"},
|
||||
}
|
||||
// Маршим в байтики
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
s.Log.Println(err)
|
||||
}
|
||||
// Отправляем в шину
|
||||
go utils.SendMessage(s.WebhookUrl, b)
|
||||
}
|
1
service/log_test.go
Normal file
1
service/log_test.go
Normal file
@ -0,0 +1 @@
|
||||
package service
|
47
service/model.go
Normal file
47
service/model.go
Normal file
@ -0,0 +1,47 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
type (
|
||||
Config struct {
|
||||
System string // Название сервиса
|
||||
Development bool // Режим разработки
|
||||
Directory string // Папка в которую необходимо сохранять логи
|
||||
LogFileName string // Файл логов
|
||||
PanicFileName string // Файл ошибок с паникой
|
||||
Webhook string // Url для отправки вебхука
|
||||
}
|
||||
|
||||
LoggerService struct {
|
||||
Log *log.Logger
|
||||
ServiceName string
|
||||
WebhookUrl string
|
||||
Development bool
|
||||
}
|
||||
|
||||
LogStruct struct {
|
||||
Message string
|
||||
Color color.Attribute
|
||||
}
|
||||
|
||||
LogErrorStruct struct {
|
||||
System string `json:"system"`
|
||||
Message string `json:"message"`
|
||||
Error TypeError `json:"error"`
|
||||
}
|
||||
|
||||
TypeError struct {
|
||||
Code int `json:"code"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
)
|
||||
|
||||
// var (
|
||||
// MarshalError = TypeError{Code: 1, Description: "Ошибка маршала"}
|
||||
// ServiceError = TypeError{Code: 2, Description: "Сервисная ошибка"}
|
||||
// CacheError = TypeError{Code: 3, Description: "Ошибка badger"}
|
||||
// )
|
47
utils/utils.go
Normal file
47
utils/utils.go
Normal file
@ -0,0 +1,47 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// Получения файла лога
|
||||
func GetLogFile(logDir, fileName string) *os.File {
|
||||
if _, err := os.Stat(logDir); os.IsNotExist(err) {
|
||||
err := os.Mkdir(logDir, 0777)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
logFile, err := os.OpenFile(fmt.Sprintf("./%s/%s-%s.log", logDir, fileName, time.Now().Format("02-01-2006")), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
log.Fatalf("Ошибка открытия файла логов %v", err)
|
||||
}
|
||||
return logFile
|
||||
}
|
||||
|
||||
// Отправка сообщения
|
||||
func SendMessage(uri string, message []byte) error {
|
||||
req := fasthttp.AcquireRequest()
|
||||
res := fasthttp.AcquireResponse()
|
||||
defer fasthttp.ReleaseRequest(req)
|
||||
defer fasthttp.ReleaseResponse(res)
|
||||
req.SetRequestURI(uri)
|
||||
req.Header.SetMethod("POST")
|
||||
req.Header.SetContentType("application/json")
|
||||
req.SetBody(message)
|
||||
return fasthttp.Do(req, res)
|
||||
}
|
||||
|
||||
// Инициализация лога
|
||||
func SetupLog(name string) *log.Logger {
|
||||
s := []color.Attribute{color.FgBlue, color.FgHiMagenta, color.FgHiGreen, color.FgYellow}
|
||||
prefix := color.New(s[rand.Intn(len(s))])
|
||||
return log.New(nil, prefix.Sprintf(" [%s] ", name), log.LstdFlags)
|
||||
}
|
52
utils/utils_test.go
Normal file
52
utils/utils_test.go
Normal file
@ -0,0 +1,52 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type (
|
||||
LogTestStruct struct {
|
||||
System string `json:"system"`
|
||||
Message string `json:"message"`
|
||||
Error TypeError `json:"error"`
|
||||
}
|
||||
TypeError struct {
|
||||
Code int `json:"code"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
)
|
||||
|
||||
func TestGetLogFile(t *testing.T) {
|
||||
s := GetLogFile("test_logs", "test")
|
||||
if s == nil {
|
||||
t.Error("log file is nil")
|
||||
}
|
||||
if err := os.RemoveAll("test_logs"); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendMessage(t *testing.T) {
|
||||
m := &LogTestStruct{
|
||||
System: "go test",
|
||||
Message: "CI TEST",
|
||||
Error: TypeError{Code: -112, Description: "Тестирование"},
|
||||
}
|
||||
// Маршим в байтики
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := SendMessage("https://devtest.galamart.ru/bus/pub?topic=error&channel=error", b); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupLog(t *testing.T) {
|
||||
l := SetupLog("test")
|
||||
if l.Prefix() != " [test] " {
|
||||
t.Error(l.Prefix())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user