log watcher is done
This commit is contained in:
@@ -139,5 +139,4 @@ func SetLoginLog(adminInfo UserInfo, status bool, reason string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
logRedis.LogRedisClient.LPush(context.Background(), LogLoginQueueKey, string(logJson)).Result()
|
logRedis.LogRedisClient.LPush(context.Background(), LogLoginQueueKey, string(logJson)).Result()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,3 +203,12 @@ func TimeToUnix(timeStr string, layout string) (int64, error) {
|
|||||||
// 转 11 位时间戳(秒)
|
// 转 11 位时间戳(秒)
|
||||||
return t.Unix(), nil
|
return t.Unix(), nil
|
||||||
}
|
}
|
||||||
|
func ParseCustomTime(s string) (CustomTime, error) {
|
||||||
|
ct := CustomTime{}
|
||||||
|
// UnmarshalJSON 需要带双引号的json字符串,所以包装成json字符串格式
|
||||||
|
err := ct.UnmarshalJSON([]byte(`"` + s + `"`))
|
||||||
|
if err != nil {
|
||||||
|
return CustomTime{}, err
|
||||||
|
}
|
||||||
|
return ct, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ func (l *AdminStatusLogic) AdminStatus(in *admin.StatusRequest) (*admin.Response
|
|||||||
}
|
}
|
||||||
if row > utils.NumberZero {
|
if row > utils.NumberZero {
|
||||||
action.NewContent = info
|
action.NewContent = info
|
||||||
|
action.Reason = in.Reason
|
||||||
action.Type = utils.LogActionTypeStatus
|
action.Type = utils.LogActionTypeStatus
|
||||||
action.ModuleName = utils.LogActionModuleAdmin
|
action.ModuleName = utils.LogActionModuleAdmin
|
||||||
utils.SetActionLog(adminInfo, action)
|
utils.SetActionLog(adminInfo, action)
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ func (l *AuthorityStatusLogic) AuthorityStatus(in *admin.StatusRequest) (*admin.
|
|||||||
|
|
||||||
if row > utils.NumberZero {
|
if row > utils.NumberZero {
|
||||||
action.NewContent = info
|
action.NewContent = info
|
||||||
|
action.Reason = in.Reason
|
||||||
action.Type = utils.LogActionTypeStatus
|
action.Type = utils.LogActionTypeStatus
|
||||||
action.ModuleName = utils.LogActionModuleAuthority
|
action.ModuleName = utils.LogActionModuleAuthority
|
||||||
utils.SetActionLog(adminInfo, action)
|
utils.SetActionLog(adminInfo, action)
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ func (l *RoleStatusLogic) RoleStatus(in *admin.StatusRequest) (*admin.Response,
|
|||||||
|
|
||||||
if row > utils.NumberZero {
|
if row > utils.NumberZero {
|
||||||
action.NewContent = info
|
action.NewContent = info
|
||||||
|
action.Reason = in.Reason
|
||||||
action.Type = utils.LogActionTypeStatus
|
action.Type = utils.LogActionTypeStatus
|
||||||
action.ModuleName = utils.LogActionModuleRole
|
action.ModuleName = utils.LogActionModuleRole
|
||||||
utils.SetActionLog(adminInfo, action)
|
utils.SetActionLog(adminInfo, action)
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ func (l *ServiceStatusLogic) ServiceStatus(in *admin.StatusRequest) (*admin.Resp
|
|||||||
|
|
||||||
if row > utils.NumberZero {
|
if row > utils.NumberZero {
|
||||||
action.NewContent = info
|
action.NewContent = info
|
||||||
|
action.Reason = in.Reason
|
||||||
action.Type = utils.LogActionTypeStatus
|
action.Type = utils.LogActionTypeStatus
|
||||||
action.ModuleName = utils.LogActionModuleService
|
action.ModuleName = utils.LogActionModuleService
|
||||||
utils.SetActionLog(adminInfo, action)
|
utils.SetActionLog(adminInfo, action)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import "lone-services/pkg/utils"
|
|||||||
type ActionLog struct {
|
type ActionLog struct {
|
||||||
Id int `gorm:"column:id;type:int(11);primary_key;AUTO_INCREMENT;comment:ID" json:"id"`
|
Id int `gorm:"column:id;type:int(11);primary_key;AUTO_INCREMENT;comment:ID" json:"id"`
|
||||||
Content string `gorm:"column:content;type:text;comment:内容;NOT NULL" json:"content"`
|
Content string `gorm:"column:content;type:text;comment:内容;NOT NULL" json:"content"`
|
||||||
AdminId int `gorm:"column:admin_id;type:int(11);comment:操作人;NOT NULL" json:"admin_id"`
|
AdminId int64 `gorm:"column:admin_id;type:int(11);comment:操作人;NOT NULL" json:"admin_id"`
|
||||||
AdminName string `gorm:"column:admin_name;type:varchar(255);comment:真实姓名" json:"admin_name"`
|
AdminName string `gorm:"column:admin_name;type:varchar(255);comment:真实姓名" json:"admin_name"`
|
||||||
Type uint8 `gorm:"column:type;type:smallint(3);comment:类型,以常量配置文件为准,目前,1为添加,2为编辑,3为删除,4为强退,5为下载;NOT NULL" json:"type"`
|
Type uint8 `gorm:"column:type;type:smallint(3);comment:类型,以常量配置文件为准,目前,1为添加,2为编辑,3为删除,4为强退,5为下载;NOT NULL" json:"type"`
|
||||||
ModuleName string `gorm:"column:module_name;type:varchar(255);comment:操作模块名,如管理员;NOT NULL" json:"module_name"`
|
ModuleName string `gorm:"column:module_name;type:varchar(255);comment:操作模块名,如管理员;NOT NULL" json:"module_name"`
|
||||||
@@ -14,7 +14,6 @@ type ActionLog struct {
|
|||||||
BrowserInfo string `gorm:"column:browser_info;type:varchar(255);comment:浏览器详细信息" json:"browser_info"`
|
BrowserInfo string `gorm:"column:browser_info;type:varchar(255);comment:浏览器详细信息" json:"browser_info"`
|
||||||
BrowserName string `gorm:"column:browser_name;type:varchar(50);comment:浏览器名称" json:"browser_name"`
|
BrowserName string `gorm:"column:browser_name;type:varchar(50);comment:浏览器名称" json:"browser_name"`
|
||||||
BrowserVersion string `gorm:"column:browser_version;type:varchar(50);comment:版本" json:"browser_version"`
|
BrowserVersion string `gorm:"column:browser_version;type:varchar(50);comment:版本" json:"browser_version"`
|
||||||
ServiceId int `gorm:"column:service_id;type:int(11);default:0;comment:项目ID" json:"service_id"`
|
|
||||||
Reason string `gorm:"column:reason;type:varchar(255);comment:原因" json:"reason"`
|
Reason string `gorm:"column:reason;type:varchar(255);comment:原因" json:"reason"`
|
||||||
CreateTime utils.CustomTime `gorm:"column:create_time;type:datetime;default:NULL;comment:添加时间;NOT NULL" json:"create_time"`
|
CreateTime utils.CustomTime `gorm:"column:create_time;type:datetime;default:NULL;comment:添加时间;NOT NULL" json:"create_time"`
|
||||||
}
|
}
|
||||||
@@ -25,15 +24,6 @@ type ActionItems struct {
|
|||||||
Type []ActionType `json:"type"`
|
Type []ActionType `json:"type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ActionAdd struct {
|
|
||||||
NewContent interface{} `json:"new_content"`
|
|
||||||
OldContent interface{} `json:"old_content"`
|
|
||||||
ServiceId int `json:"service_id"`
|
|
||||||
Reason string `json:"reason"`
|
|
||||||
Type uint8 `json:"type"`
|
|
||||||
ModuleName string `json:"module_name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ActionType struct {
|
type ActionType struct {
|
||||||
Id int `json:"id"`
|
Id int `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package dao
|
||||||
|
|
||||||
|
import "lone-services/pkg/utils"
|
||||||
|
|
||||||
|
type LoginLog struct {
|
||||||
|
Id int64 `gorm:"column:id;type:int(11);primary_key;AUTO_INCREMENT;comment:ID" json:"id"`
|
||||||
|
AdminId int64 `gorm:"column:admin_id;type:int(11);default:0;comment:登录人ID;NOT NULL" json:"admin_id"`
|
||||||
|
AdminName string `gorm:"column:admin_name;type:varchar(255);comment:登录人姓名" json:"admin_name"`
|
||||||
|
Ip string `gorm:"column:ip;type:varchar(50);comment:IP;NOT NULL" json:"ip"`
|
||||||
|
BrowserInfo string `gorm:"column:browser_info;type:varchar(255);comment:浏览器详细信息" json:"browser_info"`
|
||||||
|
BrowserName string `gorm:"column:browser_name;type:varchar(50);comment:浏览器名称" json:"browser_name"`
|
||||||
|
BrowserVersion string `gorm:"column:browser_version;type:varchar(50);comment:浏览器版本" json:"browser_version"`
|
||||||
|
Status uint8 `gorm:"column:status;type:tinyint(1);default:1;comment:状态,1为成功,2为失败" json:"status"`
|
||||||
|
CreateTime utils.CustomTime `gorm:"column:create_time;type:datetime;comment:添加时间;NOT NULL" json:"create_time"`
|
||||||
|
Reason string `gorm:"column:reason;type:varchar(50);comment:失败原因" json:"reason"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoginLogItems struct {
|
||||||
|
Items []LoginLog `json:"items"`
|
||||||
|
Count int64 `json:"count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Count struct {
|
||||||
|
Id int `gorm:"column:id;type:int(11);primary_key;AUTO_INCREMENT;comment:ID" json:"id"`
|
||||||
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "lone-services/pkg/modelbase"
|
import (
|
||||||
|
"lone-services/pkg/modelbase"
|
||||||
|
"lone-services/services/lonelog/internal/dao"
|
||||||
|
)
|
||||||
|
|
||||||
type ActionModel struct {
|
type ActionModel struct {
|
||||||
modelbase.Base
|
modelbase.Base
|
||||||
@@ -15,6 +18,6 @@ func (m ActionModel) Init() ActionModel {
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
//func (m ActionModel) Create(data *dao.Admin) error {
|
func (m ActionModel) Create(data *dao.ActionLog) error {
|
||||||
// return m.Base.Create(data)
|
return m.Base.Create(data)
|
||||||
//}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"lone-services/pkg/modelbase"
|
||||||
|
"lone-services/services/lonelog/internal/dao"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LoginModel struct {
|
||||||
|
modelbase.Base
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m LoginModel) TableName() string {
|
||||||
|
return modelbase.Prefix() + "admin_login"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m LoginModel) Init() LoginModel {
|
||||||
|
m.Table = m.TableName()
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m LoginModel) Create(data *dao.LoginLog) error {
|
||||||
|
return m.Base.Create(data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"lone-services/pkg/logRedis"
|
||||||
|
"lone-services/pkg/utils"
|
||||||
|
"lone-services/services/lonelog/internal/dao"
|
||||||
|
"lone-services/services/lonelog/internal/model"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LogWatcher 队列监听器,内部直接使用 logredis.LogRedisClient 包全局变量,不再传入redis参数
|
||||||
|
type LogWatcher struct {
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
wg sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalLogWatcher 全局单例,main初始化之后其它包直接 service.GlobalLogWatcher 访问
|
||||||
|
var GlobalLogWatcher *LogWatcher
|
||||||
|
|
||||||
|
// NewLogWatcher 无入参,直接使用包全局 logredis.LogRedisClient
|
||||||
|
func NewLogWatcher() *LogWatcher {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
return &LogWatcher{
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartWatch 启动两个消费协程
|
||||||
|
func (w *LogWatcher) StartWatch() {
|
||||||
|
w.wg.Add(2)
|
||||||
|
go w.loopActionLog()
|
||||||
|
go w.loopLoginLog()
|
||||||
|
logx.Infof("LogWatcher start, watch %s , %s", utils.LogActionQueueKey, utils.LogLoginQueueKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// loopActionLog 消费action日志队列,带recover自动重启
|
||||||
|
func (w *LogWatcher) loopActionLog() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
logx.Errorf("loopActionLog panic recovered, err=%v", r)
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
w.wg.Add(1)
|
||||||
|
go w.loopActionLog()
|
||||||
|
}
|
||||||
|
w.wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-w.ctx.Done():
|
||||||
|
logx.Info("loopActionLog received exit signal, quit")
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := logRedis.LogRedisClient.BRPop(w.ctx, 5*time.Second, utils.LogActionQueueKey).Result()
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() == "redis: nil" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
logx.Errorf("BRPop %s redis err: %v", utils.LogActionQueueKey, err)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
payload := res[1]
|
||||||
|
|
||||||
|
var action utils.ActionLog
|
||||||
|
if err := json.Unmarshal([]byte(payload), &action); err != nil {
|
||||||
|
logx.Errorf("unmarshal actionlog failed payload=%s err=%v", payload, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ctxBg := context.Background()
|
||||||
|
logTime, _ := utils.ParseCustomTime(action.CreateTime)
|
||||||
|
edit := dao.ActionLog{
|
||||||
|
Content: action.Content,
|
||||||
|
AdminId: action.AdminId,
|
||||||
|
AdminName: action.AdminName,
|
||||||
|
Type: uint8(action.Type),
|
||||||
|
ModuleName: action.ModuleName,
|
||||||
|
Ip: action.Ip,
|
||||||
|
BrowserInfo: action.BrowserInfo,
|
||||||
|
BrowserName: action.BrowserName,
|
||||||
|
BrowserVersion: action.BrowserVersion,
|
||||||
|
Reason: action.Reason,
|
||||||
|
CreateTime: logTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = model.ActionModel{}.Init().Create(&edit)
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("insert actionlog mysql err=%v payload=%s", err, payload)
|
||||||
|
_ = logRedis.LogRedisClient.LPush(ctxBg, utils.LogActionQueueKey, payload).Err()
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// loopLoginLog 消费登录日志队列,带recover自动重启
|
||||||
|
func (w *LogWatcher) loopLoginLog() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
logx.Errorf("loopLoginLog panic recovered, err=%v", r)
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
w.wg.Add(1)
|
||||||
|
go w.loopLoginLog()
|
||||||
|
}
|
||||||
|
w.wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-w.ctx.Done():
|
||||||
|
logx.Info("loopLoginLog received exit signal, quit")
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := logRedis.LogRedisClient.BRPop(w.ctx, 5*time.Second, utils.LogLoginQueueKey).Result()
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() == "redis: nil" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
logx.Errorf("BRPop %s redis err: %v", utils.LogLoginQueueKey, err)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
payload := res[1]
|
||||||
|
|
||||||
|
var login utils.LoginLog
|
||||||
|
if err := json.Unmarshal([]byte(payload), &login); err != nil {
|
||||||
|
logx.Errorf("unmarshal loginlog failed payload=%s err=%v", payload, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ctxBg := context.Background()
|
||||||
|
|
||||||
|
logTime, _ := utils.ParseCustomTime(login.CreateTime)
|
||||||
|
edit := dao.LoginLog{
|
||||||
|
AdminId: login.AdminId,
|
||||||
|
AdminName: login.Name,
|
||||||
|
Ip: login.Ip,
|
||||||
|
BrowserInfo: login.BrowserInfo,
|
||||||
|
BrowserName: login.BrowserName,
|
||||||
|
BrowserVersion: login.BrowserVersion,
|
||||||
|
Reason: login.Reason,
|
||||||
|
CreateTime: logTime,
|
||||||
|
Status: uint8(login.Status),
|
||||||
|
}
|
||||||
|
err = model.LoginModel{}.Init().Create(&edit)
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("insert loginlog mysql err=%v payload=%s", err, payload)
|
||||||
|
_ = logRedis.LogRedisClient.LPush(ctxBg, utils.LogLoginQueueKey, payload).Err()
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopWatch 优雅停止消费协程
|
||||||
|
func (w *LogWatcher) StopWatch() {
|
||||||
|
w.cancel()
|
||||||
|
w.wg.Wait()
|
||||||
|
logx.Info("LogWatcher stopped complete")
|
||||||
|
}
|
||||||
@@ -3,14 +3,15 @@ package main
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"lone-services/pkg/discovery"
|
"lone-services/pkg/discovery"
|
||||||
|
"lone-services/pkg/logRedis"
|
||||||
"lone-services/pkg/modelbase"
|
"lone-services/pkg/modelbase"
|
||||||
"lone-services/pkg/mysql"
|
"lone-services/pkg/mysql"
|
||||||
"lone-services/pkg/redis"
|
|
||||||
"lone-services/pkg/utils"
|
"lone-services/pkg/utils"
|
||||||
"lone-services/pkg/validate"
|
"lone-services/pkg/validate"
|
||||||
lonelog "lone-services/rpc/lonelog/pb"
|
lonelog "lone-services/rpc/lonelog/pb"
|
||||||
"lone-services/services/lonelog/internal/config"
|
"lone-services/services/lonelog/internal/config"
|
||||||
"lone-services/services/lonelog/internal/server"
|
"lone-services/services/lonelog/internal/server"
|
||||||
|
service2 "lone-services/services/lonelog/internal/service"
|
||||||
"lone-services/services/lonelog/internal/svc"
|
"lone-services/services/lonelog/internal/svc"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
@@ -108,13 +109,13 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := redis.Init(redis.Config{
|
if err := logRedis.Init(logRedis.LogRedisConfig{
|
||||||
Host: utils.GetConfigString("redis.host"),
|
Host: utils.GetConfigString("log-redis.host"),
|
||||||
Port: utils.GetConfigInt("redis.port"),
|
Port: utils.GetConfigInt("log-redis.port"),
|
||||||
Password: utils.GetConfigString("redis.password"),
|
Password: utils.GetConfigString("log-redis.password"),
|
||||||
DB: utils.GetConfigInt("redis.db"),
|
DB: utils.GetConfigInt("log-redis.db"),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
logx.Errorf("redis init: %v", err)
|
logx.Errorf("log redis init: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,6 +139,10 @@ func main() {
|
|||||||
|
|
||||||
s.AddUnaryInterceptors(validate.UnaryServerInterceptor(validate.MustNew()))
|
s.AddUnaryInterceptors(validate.UnaryServerInterceptor(validate.MustNew()))
|
||||||
|
|
||||||
|
watcher := service2.NewLogWatcher()
|
||||||
|
watcher.StartWatch()
|
||||||
|
defer watcher.StopWatch()
|
||||||
|
|
||||||
logx.Infof("Starting rpc server at %s...", listenOn)
|
logx.Infof("Starting rpc server at %s...", listenOn)
|
||||||
s.Start()
|
s.Start()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user