Реализация логирования запросов

This commit is contained in:
Ymnuk 2023-11-17 15:42:51 +03:00
parent b6a8452503
commit 485701de5c
8 changed files with 230 additions and 6 deletions

View File

@ -99,6 +99,10 @@ func generateBackendTmpl() {
FileIn: "tmpl/backend/db/model/user-role.tmpl",
FileOut: filepath.Join(AppConfig.OutdirBackend, "db", "model", "user-role.go"),
},
{
FileIn: "tmpl/backend/db/model/log.tmpl",
FileOut: filepath.Join(AppConfig.OutdirBackend, "db", "model", "log.go"),
},
// routes
{
@ -109,7 +113,7 @@ func generateBackendTmpl() {
FileIn: "tmpl/backend/route/static/static.tmpl",
FileOut: filepath.Join(AppConfig.OutdirBackend, "route", "static", "static.html"),
},
// middlewares
{
FileIn: "tmpl/backend/middleware/middleware.tmpl",
FileOut: filepath.Join(AppConfig.OutdirBackend, "middleware", "middleware.go"),
@ -123,6 +127,11 @@ func generateBackendTmpl() {
}
}
// middlewares
if err := templ.PrepareTmplFile("tmpl/backend/middleware/middleware.tmpl", Project, filepath.Join(AppConfig.OutdirBackend, "middleware", "middleware.go")); err != nil {
panic(err)
}
generateBackendRest()
}
@ -170,6 +179,10 @@ func generateModelBase() {
log.Fatal(err)
}
if err := templ.PrepareTmplFile("tmpl/backend/log/log.tmpl", Project, filepath.Join(AppConfig.OutdirBackend, "log", "log.go")); err != nil {
log.Fatal(err)
}
// middleware
if err := templ.PrepareTmplFile("tmpl/backend/middleware/middleware.tmpl", Project, filepath.Join(AppConfig.OutdirBackend, "middleware", "middleware.go")); err != nil {
log.Fatal(err)

View File

@ -94,6 +94,7 @@ func DBConnect() {
model.User{},
model.Role{},
model.UserRole{},
model.Log{},
{{ range $index, $table := .DB.Tables }}model.{{fieldNamePrepare $table.Name }}{},
{{ end }}
)

View File

@ -0,0 +1,31 @@
package model
import (
"time"
)
// Role Роли пользователей
type Log struct {
BaseInt
ReqID *string `gorm:"column:req_id" json:"req_id,omitempty"`
ReqHeaders *string `gorm:"column:req_headers" json:"req_headers,omitempty"`
ReqParams *string `gorm:"column:req_params" json:"req_params,omitempty"`
QueryParams *string `gorm:"column:query_params" json:"query_params,omitempty"`
ByteIn *int64 `gorm:"column:byte_in" json:"byte_in,omitempty"`
ByteOut *int64 `gorm:"column:byte_out" json:"byte_out,omitempty"`
Host *string `gorm:"column:host" json:"host,omitempty"`
Method *string `gorm:"column:method" json:"method,omitempty"`
Proto *string `gorm:"column:proto" json:"proto,omitempty"`
RemoteAddr *string `gorm:"column:remote_addr" json:"remote_addr,omitempty"`
Uri *string `gorm:"column:uri" json:"uri,omitempty"`
UserAgent *string `gorm:"column:user_agent" json:"user_agent,omitempty"`
Referer *string `gorm:"column:referer" json:"referer,omitempty"`
Status *int `gorm:"column:status" json:"status,omitempty"`
RespHeaders *string `gorm:"column:resp_headers" json:"resp_headers,omitempty"`
DT *time.Time `gorm:"column:dt" json:"dt,omitempty"`
RealIP *string `gorm:"column:real_ip" json:"real_ip,omitempty"`
}
func (log Log) TableName() string {
return "logs"
}

View File

@ -5,8 +5,8 @@ import uuid "github.com/satori/go.uuid"
// UserRole Связь пользователя и роли
type UserRole struct {
Base
UserID uuid.UUID `gorm:"column:id_user,omitempty"`
UserID uuid.UUID `gorm:"column:id_user" json:"idUser,omitempty"`
User User
RoleID uuid.UUID `gorm:"column:id_role,omitempty"`
RoleID uuid.UUID `gorm:"column:id_role" json:"idRole,omitempty"`
Role Role
}

View File

@ -23,6 +23,8 @@ var AppConfig struct {
CacheTTL int `arg:"--cache-ttl,env:CACHE_TTL" default:"604800" help:"Cache time to live, after this itemcan be remove from cache"`
LogEndpoint string `arg:"--log-endpoint" help:"Address endpoint for send logs. Logs have format JSON in array. If not set, logs write in current DB"`
LdapURL string `arg:"--ldap-url,env:LDAP_URL" help:"Ldap url for server"`
LdapBind string `arg:"--ldap-bind,env:LDAP_BIND" help:"Ldap bind for credential"`
LdapPassword string `arg:"--ldap-pwd,env:LDAP_PWD" help:"Ldap password for credential"`

View File

@ -0,0 +1,174 @@
package log
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"{{ .Name }}/db"
"{{ .Name }}/db/model"
"github.com/labstack/echo/v4"
)
type LogService struct {
logChan chan model.Log
endpoint string
httpClient *http.Client
}
func NewLogService(endpoint string) (logService *LogService, err error) {
logService = &LogService{
endpoint: strings.Trim(endpoint, " "),
}
if logService.endpoint != "" {
logService.httpClient = &http.Client{
Timeout: time.Second * 30,
}
}
go logService.worker()
return
}
func (ls *LogService) Shutdown() {
close(ls.logChan)
}
func (ls *LogService) sendLogs(logs []model.Log) (err error) {
if len(logs) == 0 {
return
}
if ls.httpClient != nil {
// Отправка по HTTP
var buff []byte
if buff, err = json.Marshal(logs); err != nil {
return
}
var req *http.Request
req, err = http.NewRequest("POST", ls.endpoint, bytes.NewReader(buff))
var resp *http.Response
if resp, err = ls.httpClient.Do(req); err != nil {
return
}
if resp == nil {
return fmt.Errorf("Response is %s", "empty")
}
if resp.StatusCode != 200 {
return fmt.Errorf("Status code not OK: %d", resp.StatusCode)
}
return
}
// Сохранение в текущей БД
tx := db.BeginTransation()
defer func() {
db.EndTransaction(tx, err)
}()
if res := tx.Create(logs); res.RowsAffected == 0 {
return res.Error
}
return
}
func (ls *LogService) worker() {
ls.logChan = make(chan model.Log, 100)
var logs []model.Log
for {
select {
case log, ok := <-ls.logChan:
if !ok {
ls.sendLogs(logs)
break
}
logs = append(logs, log)
if len(logs) >= 100 {
ls.sendLogs(logs)
logs = nil
}
case <-time.After(time.Second * 10):
ls.sendLogs(logs)
logs = nil
}
}
}
func (ls *LogService) AddLog(c echo.Context) {
// var err error
header := make(map[string][]string)
var log model.Log
for key, value := range c.Request().Header {
if key != "Authorization" &&
strings.ToLower(key) != "user-agent" &&
strings.ToLower(key) != "useragent" &&
strings.ToLower(key) != "x-real-ip" &&
strings.ToLower(key) != "xrealip" {
header[key] = value
}
}
/*var buff []byte
buff, err = json.Marshal(header)
log.ReqHeaders = string(buff)*/
if buff, err := json.Marshal(header); err == nil {
log.ReqHeaders = &[]string{string(buff)}[0]
}
var params = make(map[string]string)
for _, key := range c.ParamNames() {
params[key] = c.Param(key)
}
/*var buff []byte
var err error*/
if buff, err := json.Marshal(params); err == nil {
log.ReqParams = &[]string{string(buff)}[0]
}
if buff, err := json.Marshal(c.QueryParams()); err == nil {
log.QueryParams = &[]string{string(buff)}[0]
}
if c.Request().ContentLength>0 {
log.ByteIn = &[]int64{c.Request().ContentLength}[0]
}
if c.Request().Host!="" {
log.Host = &[]string{c.Request().Host}[0]
}
if c.Request().Method != "" {
log.Method = &[]string{c.Request().Method}[0]
}
if c.Request().Proto != "" {
log.Proto = &[]string{c.Request().Proto}[0]
}
if c.Request().RemoteAddr != "" {
log.RemoteAddr = &[]string{c.Request().RemoteAddr}[0]
}
if c.Request().RequestURI != "" {
log.Uri = &[]string{c.Request().RequestURI}[0]
}
if c.Request().UserAgent() != "" {
log.UserAgent = &[]string{c.Request().UserAgent()}[0]
}
if c.Request().Referer() != "" {
log.Referer = &[]string{c.Request().Referer()}[0]
}
if c.Response().Size >0 {
log.ByteOut = &[]int64{c.Response().Size}[0]
}
log.Status = &[]int{c.Response().Status}[0]
if buff,err:=json.Marshal(c.Response().Writer.Header());err==nil{
log.RespHeaders = &[]string{string(buff)}[0]
}
log.DT = &[]time.Time{time.Now()}[0]
log.ReqID = &[]string{c.Response().Header().Get(echo.HeaderXRequestID)}[0]
log.RealIP = &[]string{c.Response().Header().Get(echo.HeaderXRealIP)}[0]
if *log.RealIP == "" {
log.RealIP = nil
}
ls.logChan <- log
//c.Response().
}

View File

@ -58,11 +58,9 @@ func main() {
go func() {
e = echo.New()
e.IPExtractor = echo.ExtractIPFromXFFHeader()
middleware.Init(e)
route.Init(e)
e.Logger.Fatal(e.Start(fmt.Sprintf(":%s", lib.AppConfig.Port)))
}()

View File

@ -3,6 +3,9 @@ package middleware
import (
"github.com/labstack/echo/v4"
echomiddleware "github.com/labstack/echo/v4/middleware"
"{{ .Name }}/log"
"{{ .Name }}/lib"
)
func Init(e *echo.Echo) {
@ -23,11 +26,13 @@ func Init(e *echo.Echo) {
func logging() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
logging, _ := log.NewLogService(lib.AppConfig.LogEndpoint)
return func(c echo.Context) error {
if err := next(c); err != nil {
c.Error(err)
}
// TODO
logging.AddLog(c)
return nil
}
}