mirror of
https://github.com/TeaOSLab/EdgeAPI.git
synced 2025-11-05 01:20:25 +08:00
自动对访问日志进行分表
This commit is contained in:
@@ -29,8 +29,7 @@ type httpAccessLogDefinition struct {
|
||||
}
|
||||
|
||||
// HTTP服务访问
|
||||
var httpAccessLogDAOMapping = map[int64]*HTTPAccessLogDAOWrapper{} // dbNodeId => DAO
|
||||
var httpAccessLogTableMapping = map[string]*httpAccessLogDefinition{} // tableName_crc(dsn) => true
|
||||
var httpAccessLogDAOMapping = map[int64]*HTTPAccessLogDAOWrapper{} // dbNodeId => DAO
|
||||
|
||||
// DNS服务访问
|
||||
var nsAccessLogDAOMapping = map[int64]*NSAccessLogDAOWrapper{} // dbNodeId => DAO
|
||||
@@ -86,36 +85,6 @@ func randomNSAccessLogDAO() (dao *NSAccessLogDAOWrapper) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检查表格是否存在
|
||||
func findHTTPAccessLogTableName(db *dbs.DB, day string) (tableName string, hasRemoteAddr bool, hasDomain bool, ok bool, err error) {
|
||||
if !regexp.MustCompile(`^\d{8}$`).MatchString(day) {
|
||||
err = errors.New("invalid day '" + day + "', should be YYYYMMDD")
|
||||
return
|
||||
}
|
||||
|
||||
config, err := db.Config()
|
||||
if err != nil {
|
||||
return "", false, false, false, err
|
||||
}
|
||||
|
||||
tableName = "edgeHTTPAccessLogs_" + day
|
||||
cacheKey := tableName + "_" + fmt.Sprintf("%d", crc32.ChecksumIEEE([]byte(config.Dsn)))
|
||||
|
||||
accessLogLocker.RLock()
|
||||
def, ok := httpAccessLogTableMapping[cacheKey]
|
||||
accessLogLocker.RUnlock()
|
||||
if ok {
|
||||
return tableName, def.HasRemoteAddr, def.HasDomain, true, nil
|
||||
}
|
||||
|
||||
def, err = findHTTPAccessLogTable(db, day, false)
|
||||
if err != nil {
|
||||
return tableName, false, false, false, err
|
||||
}
|
||||
|
||||
return tableName, def.HasRemoteAddr, def.HasDomain, def.Exists, nil
|
||||
}
|
||||
|
||||
func findNSAccessLogTableName(db *dbs.DB, day string) (tableName string, ok bool, err error) {
|
||||
if !regexp.MustCompile(`^\d{8}$`).MatchString(day) {
|
||||
err = errors.New("invalid day '" + day + "', should be YYYYMMDD")
|
||||
@@ -145,75 +114,6 @@ func findNSAccessLogTableName(db *dbs.DB, day string) (tableName string, ok bool
|
||||
return tableName, utils.ContainsStringInsensitive(tableNames, tableName), nil
|
||||
}
|
||||
|
||||
// 根据日期获取表名
|
||||
func findHTTPAccessLogTable(db *dbs.DB, day string, force bool) (*httpAccessLogDefinition, error) {
|
||||
config, err := db.Config()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tableName := "edgeHTTPAccessLogs_" + day
|
||||
cacheKey := tableName + "_" + fmt.Sprintf("%d", crc32.ChecksumIEEE([]byte(config.Dsn)))
|
||||
|
||||
if !force {
|
||||
accessLogLocker.RLock()
|
||||
definition, ok := httpAccessLogTableMapping[cacheKey]
|
||||
accessLogLocker.RUnlock()
|
||||
if ok {
|
||||
return definition, nil
|
||||
}
|
||||
}
|
||||
|
||||
tableNames, err := db.TableNames()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if utils.ContainsStringInsensitive(tableNames, tableName) {
|
||||
table, err := db.FindTable(tableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accessLogLocker.Lock()
|
||||
var definition = &httpAccessLogDefinition{
|
||||
Name: tableName,
|
||||
HasRemoteAddr: table.FindFieldWithName("remoteAddr") != nil,
|
||||
HasDomain: table.FindFieldWithName("domain") != nil,
|
||||
Exists: true,
|
||||
}
|
||||
httpAccessLogTableMapping[cacheKey] = definition
|
||||
accessLogLocker.Unlock()
|
||||
return definition, nil
|
||||
}
|
||||
|
||||
if !force {
|
||||
return &httpAccessLogDefinition{
|
||||
Name: tableName,
|
||||
HasRemoteAddr: true,
|
||||
HasDomain: true,
|
||||
Exists: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 创建表格
|
||||
_, err = db.Exec("CREATE TABLE `" + tableName + "` (\n `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',\n `serverId` int(11) unsigned DEFAULT '0' COMMENT '服务ID',\n `nodeId` int(11) unsigned DEFAULT '0' COMMENT '节点ID',\n `status` int(3) unsigned DEFAULT '0' COMMENT '状态码',\n `createdAt` bigint(11) unsigned DEFAULT '0' COMMENT '创建时间',\n `content` json DEFAULT NULL COMMENT '日志内容',\n `requestId` varchar(128) DEFAULT NULL COMMENT '请求ID',\n `firewallPolicyId` int(11) unsigned DEFAULT '0' COMMENT 'WAF策略ID',\n `firewallRuleGroupId` int(11) unsigned DEFAULT '0' COMMENT 'WAF分组ID',\n `firewallRuleSetId` int(11) unsigned DEFAULT '0' COMMENT 'WAF集ID',\n `firewallRuleId` int(11) unsigned DEFAULT '0' COMMENT 'WAF规则ID',\n `remoteAddr` varchar(64) DEFAULT NULL COMMENT 'IP地址',\n `domain` varchar(128) DEFAULT NULL COMMENT '域名',\n `requestBody` mediumblob COMMENT '请求内容',\n `responseBody` mediumblob COMMENT '响应内容',\n PRIMARY KEY (`id`),\n KEY `serverId` (`serverId`),\n KEY `nodeId` (`nodeId`),\n KEY `serverId_status` (`serverId`,`status`),\n KEY `requestId` (`requestId`),\n KEY `firewallPolicyId` (`firewallPolicyId`),\n KEY `firewallRuleGroupId` (`firewallRuleGroupId`),\n KEY `firewallRuleSetId` (`firewallRuleSetId`),\n KEY `firewallRuleId` (`firewallRuleId`),\n KEY `remoteAddr` (`remoteAddr`),\n KEY `domain` (`domain`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='访问日志';")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accessLogLocker.Lock()
|
||||
var definition = &httpAccessLogDefinition{
|
||||
Name: tableName,
|
||||
HasRemoteAddr: true,
|
||||
Exists: true,
|
||||
}
|
||||
httpAccessLogTableMapping[cacheKey] = definition
|
||||
accessLogLocker.Unlock()
|
||||
|
||||
return definition, nil
|
||||
}
|
||||
|
||||
func findNSAccessLogTable(db *dbs.DB, day string, force bool) (string, error) {
|
||||
config, err := db.Config()
|
||||
if err != nil {
|
||||
@@ -351,22 +251,17 @@ func (this *DBNodeInitializer) loop() error {
|
||||
// 检查表是否存在
|
||||
// httpAccessLog
|
||||
{
|
||||
tableDef, err := findHTTPAccessLogTable(db, timeutil.Format("Ymd"), true)
|
||||
tableDef, err := SharedHTTPAccessLogManager.FindTable(db, timeutil.Format("Ymd"), true)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "1050") { // 非表格已存在错误
|
||||
remotelogs.Error("DB_NODE", "create first table in database node failed: "+err.Error())
|
||||
remotelogs.Error("DB_NODE", "create first table in database node failed: "+err.Error())
|
||||
|
||||
// 创建节点日志
|
||||
createLogErr := SharedNodeLogDAO.CreateLog(nil, nodeconfigs.NodeRoleDatabase, nodeId, 0, 0, "error", "ACCESS_LOG", "can not create access log table: "+err.Error(), time.Now().Unix(), "", nil)
|
||||
if createLogErr != nil {
|
||||
remotelogs.Error("NODE_LOG", createLogErr.Error())
|
||||
}
|
||||
|
||||
continue
|
||||
} else {
|
||||
err = nil
|
||||
continue
|
||||
// 创建节点日志
|
||||
createLogErr := SharedNodeLogDAO.CreateLog(nil, nodeconfigs.NodeRoleDatabase, nodeId, 0, 0, "error", "ACCESS_LOG", "can not create access log table: "+err.Error(), time.Now().Unix(), "", nil)
|
||||
if createLogErr != nil {
|
||||
remotelogs.Error("NODE_LOG", createLogErr.Error())
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
daoObject := dbs.DAOObject{
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestDBNodeInitializer_loop(t *testing.T) {
|
||||
@@ -14,32 +12,3 @@ func TestDBNodeInitializer_loop(t *testing.T) {
|
||||
}
|
||||
t.Log(len(accessLogDBMapping), len(httpAccessLogDAOMapping))
|
||||
}
|
||||
|
||||
func TestFindAccessLogTable(t *testing.T) {
|
||||
before := time.Now()
|
||||
db := SharedHTTPAccessLogDAO.Instance
|
||||
tableName, err := findHTTPAccessLogTable(db, "20201010", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(tableName)
|
||||
t.Log(time.Since(before).Seconds()*1000, "ms")
|
||||
|
||||
before = time.Now()
|
||||
tableName, err = findHTTPAccessLogTable(db, "20201010", false)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(tableName)
|
||||
t.Log(time.Since(before).Seconds()*1000, "ms")
|
||||
}
|
||||
|
||||
func BenchmarkFindAccessLogTable(b *testing.B) {
|
||||
db := SharedHTTPAccessLogDAO.Instance
|
||||
|
||||
runtime.GOMAXPROCS(1)
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = findHTTPAccessLogTable(db, "20201010", false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,13 +34,25 @@ type HTTPAccessLogDAO dbs.DAO
|
||||
var SharedHTTPAccessLogDAO *HTTPAccessLogDAO
|
||||
|
||||
// 队列
|
||||
var oldAccessLogQueue = make(chan *pb.HTTPAccessLog)
|
||||
var accessLogQueue = make(chan *pb.HTTPAccessLog, 10_000)
|
||||
var accessLogQueueMaxLength = 100_000
|
||||
var accessLogQueuePercent = 100 // 0-100
|
||||
var accessLogCountPerSecond = 10_000 // 0 表示不限制
|
||||
var accessLogConfigJSON = []byte{}
|
||||
var accessLogQueueChanged = make(chan zero.Zero, 1)
|
||||
var (
|
||||
oldAccessLogQueue = make(chan *pb.HTTPAccessLog)
|
||||
accessLogQueue = make(chan *pb.HTTPAccessLog, 10_000)
|
||||
accessLogQueueMaxLength = 100_000
|
||||
accessLogQueuePercent = 100 // 0-100
|
||||
accessLogCountPerSecond = 10_000 // 0 表示不限制
|
||||
accessLogConfigJSON = []byte{}
|
||||
accessLogQueueChanged = make(chan zero.Zero, 1)
|
||||
|
||||
accessLogEnableAutoPartial = true // 是否启用自动分表
|
||||
accessLogPartialRows int64 = 500_000 // 自动分表的单表最大值
|
||||
)
|
||||
|
||||
type accessLogTableQuery struct {
|
||||
daoWrapper *HTTPAccessLogDAOWrapper
|
||||
name string
|
||||
hasRemoteAddrField bool
|
||||
hasDomainField bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
@@ -120,7 +132,7 @@ func (this *HTTPAccessLogDAO) CreateHTTPAccessLogs(tx *dbs.Tx, accessLogs []*pb.
|
||||
|
||||
// DumpAccessLogsFromQueue 从队列导入访问日志
|
||||
func (this *HTTPAccessLogDAO) DumpAccessLogsFromQueue(tx *dbs.Tx, size int) error {
|
||||
dao := randomHTTPAccessLogDAO()
|
||||
var dao = randomHTTPAccessLogDAO()
|
||||
if dao == nil {
|
||||
dao = &HTTPAccessLogDAOWrapper{
|
||||
DAO: SharedHTTPAccessLogDAO,
|
||||
@@ -168,8 +180,8 @@ Loop:
|
||||
|
||||
// CreateHTTPAccessLog 写入单条访问日志
|
||||
func (this *HTTPAccessLogDAO) CreateHTTPAccessLog(tx *dbs.Tx, dao *HTTPAccessLogDAO, accessLog *pb.HTTPAccessLog) error {
|
||||
day := timeutil.Format("Ymd", time.Unix(accessLog.Timestamp, 0))
|
||||
tableDef, err := findHTTPAccessLogTable(dao.Instance, day, false)
|
||||
var day = timeutil.Format("Ymd", time.Unix(accessLog.Timestamp, 0))
|
||||
tableDef, err := SharedHTTPAccessLogManager.FindTable(dao.Instance, day, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -203,27 +215,17 @@ func (this *HTTPAccessLogDAO) CreateHTTPAccessLog(tx *dbs.Tx, dao *HTTPAccessLog
|
||||
}
|
||||
fields["content"] = content
|
||||
|
||||
_, err = dao.Query(tx).
|
||||
var lastId int64
|
||||
lastId, err = dao.Query(tx).
|
||||
Table(tableDef.Name).
|
||||
Sets(fields).
|
||||
Insert()
|
||||
if err != nil {
|
||||
// 是否为 Error 1146: Table 'xxx.xxx' doesn't exist 如果是,则创建表之后重试
|
||||
if strings.Contains(err.Error(), "1146") {
|
||||
tableDef, err = findHTTPAccessLogTable(dao.Instance, day, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = dao.Query(tx).
|
||||
Table(tableDef.Name).
|
||||
Sets(fields).
|
||||
Insert()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
remotelogs.Error("HTTP_ACCESS_LOG", err.Error())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if lastId%accessLogPartialRows == 0 {
|
||||
SharedHTTPAccessLogManager.ResetTable(dao.Instance, day)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -296,42 +298,56 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, s
|
||||
}}
|
||||
}
|
||||
|
||||
locker := sync.Mutex{}
|
||||
// 查询某个集群下的节点
|
||||
var nodeIds = []int64{}
|
||||
if clusterId > 0 {
|
||||
nodeIds, err = SharedNodeDAO.FindAllEnabledNodeIdsWithClusterId(tx, clusterId)
|
||||
if err != nil {
|
||||
remotelogs.Error("DBNODE", err.Error())
|
||||
return
|
||||
}
|
||||
sort.Slice(nodeIds, func(i, j int) bool {
|
||||
return nodeIds[i] < nodeIds[j]
|
||||
})
|
||||
}
|
||||
|
||||
count := len(daoList)
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(count)
|
||||
// 准备查询
|
||||
var tableQueries = []*accessLogTableQuery{}
|
||||
for _, daoWrapper := range daoList {
|
||||
go func(daoWrapper *HTTPAccessLogDAOWrapper) {
|
||||
var instance = daoWrapper.DAO.Instance
|
||||
tableDefs, err := SharedHTTPAccessLogManager.FindTables(instance, day)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
for _, def := range tableDefs {
|
||||
tableQueries = append(tableQueries, &accessLogTableQuery{
|
||||
daoWrapper: daoWrapper,
|
||||
name: def.Name,
|
||||
hasRemoteAddrField: def.HasRemoteAddr,
|
||||
hasDomainField: def.HasDomain,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var locker = sync.Mutex{}
|
||||
|
||||
var statusPrefixReg = regexp.MustCompile(`status:\s*(\d{3})`)
|
||||
|
||||
var count = len(tableQueries)
|
||||
var wg = &sync.WaitGroup{}
|
||||
wg.Add(count)
|
||||
for _, tableQuery := range tableQueries {
|
||||
go func(tableQuery *accessLogTableQuery) {
|
||||
defer wg.Done()
|
||||
|
||||
dao := daoWrapper.DAO
|
||||
|
||||
tableName, hasRemoteAddrField, hasDomainField, exists, err := findHTTPAccessLogTableName(dao.Instance, day)
|
||||
if err != nil {
|
||||
logs.Println("[DB_NODE]" + err.Error())
|
||||
return
|
||||
}
|
||||
if !exists {
|
||||
// 表格不存在则跳过
|
||||
return
|
||||
}
|
||||
|
||||
query := dao.Query(tx)
|
||||
var dao = tableQuery.daoWrapper.DAO
|
||||
var query = dao.Query(tx)
|
||||
|
||||
// 条件
|
||||
if nodeId > 0 {
|
||||
query.Attr("nodeId", nodeId)
|
||||
} else if clusterId > 0 {
|
||||
nodeIds, err := SharedNodeDAO.FindAllEnabledNodeIdsWithClusterId(tx, clusterId)
|
||||
if err != nil {
|
||||
remotelogs.Error("DBNODE", err.Error())
|
||||
return
|
||||
}
|
||||
if len(nodeIds) > 0 {
|
||||
sort.Slice(nodeIds, func(i, j int) bool {
|
||||
return nodeIds[i] < nodeIds[j]
|
||||
})
|
||||
var nodeIdStrings = []string{}
|
||||
for _, subNodeId := range nodeIds {
|
||||
nodeIdStrings = append(nodeIdStrings, types.String(subNodeId))
|
||||
@@ -370,7 +386,7 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, s
|
||||
// keyword
|
||||
if len(ip) > 0 {
|
||||
// TODO 支持IP范围
|
||||
if hasRemoteAddrField {
|
||||
if tableQuery.hasRemoteAddrField {
|
||||
// IP格式
|
||||
if strings.Contains(ip, ",") || strings.Contains(ip, "-") {
|
||||
rangeConfig, err := shared.ParseIPRange(ip)
|
||||
@@ -389,7 +405,7 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, s
|
||||
}
|
||||
}
|
||||
if len(domain) > 0 {
|
||||
if hasDomainField {
|
||||
if tableQuery.hasDomainField {
|
||||
if strings.Contains(domain, "*") {
|
||||
domain = strings.ReplaceAll(domain, "*", "%")
|
||||
domain = regexp.MustCompile(`[^a-zA-Z0-9-.%]`).ReplaceAllString(domain, "")
|
||||
@@ -404,11 +420,12 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, s
|
||||
Param("host1", domain)
|
||||
}
|
||||
}
|
||||
|
||||
if len(keyword) > 0 {
|
||||
// remoteAddr
|
||||
if hasRemoteAddrField && net.ParseIP(keyword) != nil {
|
||||
if tableQuery.hasRemoteAddrField && net.ParseIP(keyword) != nil {
|
||||
query.Attr("remoteAddr", keyword)
|
||||
} else if hasRemoteAddrField && regexp.MustCompile(`^ip:.+`).MatchString(keyword) {
|
||||
} else if tableQuery.hasRemoteAddrField && regexp.MustCompile(`^ip:.+`).MatchString(keyword) {
|
||||
keyword = keyword[3:]
|
||||
pieces := strings.SplitN(keyword, ",", 2)
|
||||
if len(pieces) == 1 || len(pieces[1]) == 0 {
|
||||
@@ -416,12 +433,15 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, s
|
||||
} else {
|
||||
query.Between("INET_ATON(remoteAddr)", utils.IP2Long(pieces[0]), utils.IP2Long(pieces[1]))
|
||||
}
|
||||
} else if statusPrefixReg.MatchString(keyword) {
|
||||
var matches = statusPrefixReg.FindStringSubmatch(keyword)
|
||||
query.Attr("status", matches[1])
|
||||
} else {
|
||||
if regexp.MustCompile(`^ip:.+`).MatchString(keyword) {
|
||||
keyword = keyword[3:]
|
||||
}
|
||||
|
||||
useOriginKeyword := false
|
||||
var useOriginKeyword = false
|
||||
|
||||
where := "JSON_EXTRACT(content, '$.remoteAddr') LIKE :keyword OR JSON_EXTRACT(content, '$.requestURI') LIKE :keyword OR JSON_EXTRACT(content, '$.host') LIKE :keyword OR JSON_EXTRACT(content, '$.userAgent') LIKE :keyword"
|
||||
|
||||
@@ -447,13 +467,13 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, s
|
||||
|
||||
// 响应状态码
|
||||
if regexp.MustCompile(`^\d{3}$`).MatchString(keyword) {
|
||||
where += " OR JSON_EXTRACT(content, '$.status')=:intKeyword"
|
||||
where += " OR status=:intKeyword"
|
||||
query.Param("intKeyword", types.Int(keyword))
|
||||
}
|
||||
|
||||
if regexp.MustCompile(`^\d{3}-\d{3}$`).MatchString(keyword) {
|
||||
pieces := strings.Split(keyword, "-")
|
||||
where += " OR JSON_EXTRACT(content, '$.status') BETWEEN :intKeyword1 AND :intKeyword2"
|
||||
where += " OR status BETWEEN :intKeyword1 AND :intKeyword2"
|
||||
query.Param("intKeyword1", types.Int(pieces[0]))
|
||||
query.Param("intKeyword2", types.Int(pieces[1]))
|
||||
}
|
||||
@@ -490,20 +510,21 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, s
|
||||
|
||||
// 开始查询
|
||||
ones, err := query.
|
||||
Table(tableName).
|
||||
Table(tableQuery.name).
|
||||
Limit(size).
|
||||
FindAll()
|
||||
if err != nil {
|
||||
logs.Println("[DB_NODE]" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
locker.Lock()
|
||||
for _, one := range ones {
|
||||
accessLog := one.(*HTTPAccessLog)
|
||||
result = append(result, accessLog)
|
||||
}
|
||||
locker.Unlock()
|
||||
}(daoWrapper)
|
||||
}(tableQuery)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
@@ -524,7 +545,7 @@ func (this *HTTPAccessLogDAO) listAccessLogs(tx *dbs.Tx, lastRequestId string, s
|
||||
result = result[:size]
|
||||
}
|
||||
|
||||
requestId := result[len(result)-1].RequestId
|
||||
var requestId = result[len(result)-1].RequestId
|
||||
if reverse {
|
||||
lists.Reverse(result)
|
||||
}
|
||||
@@ -556,28 +577,36 @@ func (this *HTTPAccessLogDAO) FindAccessLogWithRequestId(tx *dbs.Tx, requestId s
|
||||
}}
|
||||
}
|
||||
|
||||
count := len(daoList)
|
||||
wg := &sync.WaitGroup{}
|
||||
// 准备查询
|
||||
var day = timeutil.FormatTime("Ymd", types.Int64(requestId[:10]))
|
||||
var tableQueries = []*accessLogTableQuery{}
|
||||
for _, daoWrapper := range daoList {
|
||||
var instance = daoWrapper.DAO.Instance
|
||||
tableDefs, err := SharedHTTPAccessLogManager.FindTables(instance, day)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, def := range tableDefs {
|
||||
tableQueries = append(tableQueries, &accessLogTableQuery{
|
||||
daoWrapper: daoWrapper,
|
||||
name: def.Name,
|
||||
hasRemoteAddrField: def.HasRemoteAddr,
|
||||
hasDomainField: def.HasDomain,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var count = len(tableQueries)
|
||||
var wg = &sync.WaitGroup{}
|
||||
wg.Add(count)
|
||||
var result *HTTPAccessLog = nil
|
||||
day := timeutil.FormatTime("Ymd", types.Int64(requestId[:10]))
|
||||
for _, daoWrapper := range daoList {
|
||||
go func(daoWrapper *HTTPAccessLogDAOWrapper) {
|
||||
for _, tableQuery := range tableQueries {
|
||||
go func(tableQuery *accessLogTableQuery) {
|
||||
defer wg.Done()
|
||||
|
||||
dao := daoWrapper.DAO
|
||||
|
||||
tableName, _, _, exists, err := findHTTPAccessLogTableName(dao.Instance, day)
|
||||
if err != nil {
|
||||
logs.Println("[DB_NODE]" + err.Error())
|
||||
return
|
||||
}
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
|
||||
var dao = tableQuery.daoWrapper.DAO
|
||||
one, err := dao.Query(tx).
|
||||
Table(tableName).
|
||||
Table(tableQuery.name).
|
||||
Attr("requestId", requestId).
|
||||
Find()
|
||||
if err != nil {
|
||||
@@ -587,7 +616,7 @@ func (this *HTTPAccessLogDAO) FindAccessLogWithRequestId(tx *dbs.Tx, requestId s
|
||||
if one != nil {
|
||||
result = one.(*HTTPAccessLog)
|
||||
}
|
||||
}(daoWrapper)
|
||||
}(tableQuery)
|
||||
}
|
||||
wg.Wait()
|
||||
return result, nil
|
||||
|
||||
297
internal/db/models/http_access_log_manager.go
Normal file
297
internal/db/models/http_access_log_manager.go
Normal file
@@ -0,0 +1,297 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// 访问日志的两个表格形式
|
||||
var accessLogTableMainReg = regexp.MustCompile(`_(\d{8})$`)
|
||||
var accessLogTablePartialReg = regexp.MustCompile(`_(\d{8})_(\d{4})$`)
|
||||
|
||||
var SharedHTTPAccessLogManager = NewHTTPAccessLogManager()
|
||||
|
||||
type HTTPAccessLogManager struct {
|
||||
currentTableMapping map[string]*httpAccessLogDefinition // dsn => def
|
||||
|
||||
locker sync.Mutex
|
||||
}
|
||||
|
||||
func NewHTTPAccessLogManager() *HTTPAccessLogManager {
|
||||
return &HTTPAccessLogManager{
|
||||
currentTableMapping: map[string]*httpAccessLogDefinition{},
|
||||
}
|
||||
}
|
||||
|
||||
// FindTableNames 读取数据库中某日所有日志表名称
|
||||
func (this *HTTPAccessLogManager) FindTableNames(db *dbs.DB, day string) ([]string, error) {
|
||||
var results = []string{}
|
||||
|
||||
// 需要防止用户设置了表名自动小写
|
||||
for _, prefix := range []string{"edgeHTTPAccessLogs_" + day + "%", "edgehttpaccesslogs_" + day + "%"} {
|
||||
ones, columnNames, err := db.FindOnes(`SHOW TABLES LIKE '` + prefix + `'`)
|
||||
if err != nil {
|
||||
return nil, errors.New("query table names error: " + err.Error())
|
||||
}
|
||||
|
||||
var columnName = columnNames[0]
|
||||
|
||||
for _, one := range ones {
|
||||
var tableName = one[columnName].(string)
|
||||
|
||||
if lists.ContainsString(results, tableName) {
|
||||
continue
|
||||
}
|
||||
|
||||
if accessLogTableMainReg.MatchString(tableName) || accessLogTablePartialReg.MatchString(tableName) {
|
||||
results = append(results, tableName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 排序
|
||||
sort.Strings(results)
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// FindTables 读取数据库中某日所有日志表
|
||||
func (this *HTTPAccessLogManager) FindTables(db *dbs.DB, day string) ([]*httpAccessLogDefinition, error) {
|
||||
var results = []*httpAccessLogDefinition{}
|
||||
var tableNames = []string{}
|
||||
|
||||
// 需要防止用户设置了表名自动小写
|
||||
for _, prefix := range []string{"edgeHTTPAccessLogs_" + day + "%", "edgehttpaccesslogs_" + day + "%"} {
|
||||
ones, columnNames, err := db.FindOnes(`SHOW TABLES LIKE '` + prefix + `'`)
|
||||
if err != nil {
|
||||
return nil, errors.New("query table names error: " + err.Error())
|
||||
}
|
||||
|
||||
var columnName = columnNames[0]
|
||||
|
||||
for _, one := range ones {
|
||||
var tableName = one[columnName].(string)
|
||||
|
||||
if lists.ContainsString(tableNames, tableName) {
|
||||
continue
|
||||
}
|
||||
|
||||
if accessLogTableMainReg.MatchString(tableName) {
|
||||
tableNames = append(tableNames, tableName)
|
||||
|
||||
hasRemoteAddrField, hasDomainField, err := this.checkTableFields(db, tableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
results = append(results, &httpAccessLogDefinition{
|
||||
Name: tableName,
|
||||
HasRemoteAddr: hasRemoteAddrField,
|
||||
HasDomain: hasDomainField,
|
||||
Exists: true,
|
||||
})
|
||||
} else if accessLogTablePartialReg.MatchString(tableName) {
|
||||
tableNames = append(tableNames, tableName)
|
||||
|
||||
results = append(results, &httpAccessLogDefinition{
|
||||
Name: tableName,
|
||||
HasRemoteAddr: true,
|
||||
HasDomain: true,
|
||||
Exists: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 排序
|
||||
sort.Slice(results, func(i, j int) bool {
|
||||
return results[i].Name < results[j].Name
|
||||
})
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// FindTable 根据日期获取表名
|
||||
// 表名组成
|
||||
// - PREFIX_DAY
|
||||
// - PREFIX_DAY_0001
|
||||
func (this *HTTPAccessLogManager) FindTable(db *dbs.DB, day string, force bool) (*httpAccessLogDefinition, error) {
|
||||
this.locker.Lock()
|
||||
defer this.locker.Unlock()
|
||||
|
||||
config, err := db.Config()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cacheKey = config.Dsn
|
||||
def, ok := this.currentTableMapping[cacheKey]
|
||||
if ok {
|
||||
return def, nil
|
||||
}
|
||||
|
||||
def, err = this.findTableWithoutCache(db, day, force)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
this.currentTableMapping[cacheKey] = def
|
||||
return def, nil
|
||||
}
|
||||
|
||||
// CreateTable 创建访问日志表格
|
||||
func (this *HTTPAccessLogManager) CreateTable(db *dbs.DB, tableName string) error {
|
||||
_, err := db.Exec("CREATE TABLE `" + tableName + "` (\n `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',\n `serverId` int(11) unsigned DEFAULT '0' COMMENT '服务ID',\n `nodeId` int(11) unsigned DEFAULT '0' COMMENT '节点ID',\n `status` int(3) unsigned DEFAULT '0' COMMENT '状态码',\n `createdAt` bigint(11) unsigned DEFAULT '0' COMMENT '创建时间',\n `content` json DEFAULT NULL COMMENT '日志内容',\n `requestId` varchar(128) DEFAULT NULL COMMENT '请求ID',\n `firewallPolicyId` int(11) unsigned DEFAULT '0' COMMENT 'WAF策略ID',\n `firewallRuleGroupId` int(11) unsigned DEFAULT '0' COMMENT 'WAF分组ID',\n `firewallRuleSetId` int(11) unsigned DEFAULT '0' COMMENT 'WAF集ID',\n `firewallRuleId` int(11) unsigned DEFAULT '0' COMMENT 'WAF规则ID',\n `remoteAddr` varchar(64) DEFAULT NULL COMMENT 'IP地址',\n `domain` varchar(128) DEFAULT NULL COMMENT '域名',\n `requestBody` mediumblob COMMENT '请求内容',\n `responseBody` mediumblob COMMENT '响应内容',\n PRIMARY KEY (`id`),\n KEY `serverId` (`serverId`),\n KEY `nodeId` (`nodeId`),\n KEY `serverId_status` (`serverId`,`status`),\n KEY `requestId` (`requestId`),\n KEY `firewallPolicyId` (`firewallPolicyId`),\n KEY `firewallRuleGroupId` (`firewallRuleGroupId`),\n KEY `firewallRuleSetId` (`firewallRuleSetId`),\n KEY `firewallRuleId` (`firewallRuleId`),\n KEY `remoteAddr` (`remoteAddr`),\n KEY `domain` (`domain`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='访问日志';")
|
||||
if err != nil {
|
||||
// 快速判断错误方法
|
||||
mysqlErr, ok := err.(*mysql.MySQLError)
|
||||
if ok && mysqlErr.Number == 1050 { // Error 1050: Table 'xxx' already exists
|
||||
return nil
|
||||
}
|
||||
|
||||
// 防止二次包装过程中错误丢失的保底错误判断方法
|
||||
if strings.Contains(err.Error(), "Error 1050") {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResetTable 清除某个数据库表名缓存
|
||||
func (this *HTTPAccessLogManager) ResetTable(db *dbs.DB, day string) {
|
||||
this.locker.Lock()
|
||||
defer this.locker.Unlock()
|
||||
|
||||
config, err := db.Config()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
delete(this.currentTableMapping, config.Dsn)
|
||||
}
|
||||
|
||||
// 查找某个表格
|
||||
func (this *HTTPAccessLogManager) findTableWithoutCache(db *dbs.DB, day string, force bool) (*httpAccessLogDefinition, error) {
|
||||
tableNames, err := this.FindTableNames(db, day)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var prefix = "edgeHTTPAccessLogs_" + day
|
||||
|
||||
if len(tableNames) == 0 {
|
||||
if force {
|
||||
err := this.CreateTable(db, prefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &httpAccessLogDefinition{
|
||||
Name: prefix,
|
||||
HasRemoteAddr: true,
|
||||
HasDomain: true,
|
||||
Exists: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &httpAccessLogDefinition{
|
||||
Name: prefix,
|
||||
HasRemoteAddr: true,
|
||||
HasDomain: true,
|
||||
Exists: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var lastTableName = tableNames[len(tableNames)-1]
|
||||
if !force || !accessLogEnableAutoPartial {
|
||||
hasRemoteAddrField, hasDomainField, err := this.checkTableFields(db, lastTableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &httpAccessLogDefinition{
|
||||
Name: lastTableName,
|
||||
HasRemoteAddr: hasRemoteAddrField,
|
||||
HasDomain: hasDomainField,
|
||||
Exists: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 检查是否生成下个分表
|
||||
lastId, err := db.FindCol(0, "SELECT id FROM "+lastTableName+" ORDER BY id DESC LIMIT 1")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if lastId != nil {
|
||||
var lastInt64Id = types.Int64(lastId)
|
||||
if lastInt64Id >= accessLogPartialRows {
|
||||
// create next partial table
|
||||
var nextTableName = ""
|
||||
if accessLogTableMainReg.MatchString(lastTableName) {
|
||||
nextTableName = prefix + "_0001"
|
||||
} else if accessLogTablePartialReg.MatchString(lastTableName) {
|
||||
var matches = accessLogTablePartialReg.FindStringSubmatch(lastTableName)
|
||||
if len(matches) < 3 {
|
||||
return nil, errors.New("fatal error: invalid 'accessLogTablePartialReg'")
|
||||
}
|
||||
var lastPartial = matches[2]
|
||||
nextTableName = prefix + "_" + fmt.Sprintf("%04d", types.Int(lastPartial)+1)
|
||||
} else {
|
||||
nextTableName = prefix + "_0001"
|
||||
}
|
||||
|
||||
err = this.CreateTable(db, nextTableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &httpAccessLogDefinition{
|
||||
Name: nextTableName,
|
||||
HasRemoteAddr: true,
|
||||
HasDomain: true,
|
||||
Exists: true,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 检查字段
|
||||
hasRemoteAddrField, hasDomainField, err := this.checkTableFields(db, lastTableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &httpAccessLogDefinition{
|
||||
Name: lastTableName,
|
||||
HasRemoteAddr: hasRemoteAddrField,
|
||||
HasDomain: hasDomainField,
|
||||
Exists: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TODO 考虑缓存检查结果
|
||||
func (this *HTTPAccessLogManager) checkTableFields(db *dbs.DB, tableName string) (hasRemoteAddrField bool, hasDomainField bool, err error) {
|
||||
fields, _, err := db.FindOnes("SHOW FIELDS FROM " + tableName)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
for _, field := range fields {
|
||||
var fieldName = field.GetString("Field")
|
||||
if strings.ToLower(fieldName) == strings.ToLower("remoteAddr") {
|
||||
hasRemoteAddrField = true
|
||||
}
|
||||
if strings.ToLower(fieldName) == "domain" {
|
||||
hasDomainField = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
148
internal/db/models/http_access_log_manager_test.go
Normal file
148
internal/db/models/http_access_log_manager_test.go
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package models_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNewHTTPAccessLogManager(t *testing.T) {
|
||||
var config = &dbs.DBConfig{
|
||||
Driver: "mysql",
|
||||
Dsn: "root:123456@tcp(127.0.0.1:3306)/db_edge_log?charset=utf8mb4&timeout=30s",
|
||||
Prefix: "edge",
|
||||
Connections: struct {
|
||||
Pool int `yaml:"pool"`
|
||||
Max int `yaml:"max"`
|
||||
Life string `yaml:"life"`
|
||||
LifeDuration time.Duration `yaml:",omitempty"`
|
||||
}{},
|
||||
Models: struct {
|
||||
Package string `yaml:"package"`
|
||||
}{},
|
||||
}
|
||||
|
||||
db, err := dbs.NewInstanceFromConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var manager = models.SharedHTTPAccessLogManager
|
||||
err = manager.CreateTable(db, "accessLog_1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPAccessLogManager_FindTableNames(t *testing.T) {
|
||||
var config = &dbs.DBConfig{
|
||||
Driver: "mysql",
|
||||
Dsn: "root:123456@tcp(127.0.0.1:3306)/db_edge_log?charset=utf8mb4&timeout=30s",
|
||||
Prefix: "edge",
|
||||
Connections: struct {
|
||||
Pool int `yaml:"pool"`
|
||||
Max int `yaml:"max"`
|
||||
Life string `yaml:"life"`
|
||||
LifeDuration time.Duration `yaml:",omitempty"`
|
||||
}{},
|
||||
Models: struct {
|
||||
Package string `yaml:"package"`
|
||||
}{},
|
||||
}
|
||||
|
||||
db, err := dbs.NewInstanceFromConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
var before = time.Now()
|
||||
tables, err := models.SharedHTTPAccessLogManager.FindTables(db, "20220306")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, err := json.Marshal(tables)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(string(data))
|
||||
t.Log(time.Since(before).Seconds()*1000, "ms")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func TestHTTPAccessLogManager_FindTables(t *testing.T) {
|
||||
var config = &dbs.DBConfig{
|
||||
Driver: "mysql",
|
||||
Dsn: "root:123456@tcp(127.0.0.1:3306)/db_edge_log?charset=utf8mb4&timeout=30s",
|
||||
Prefix: "edge",
|
||||
Connections: struct {
|
||||
Pool int `yaml:"pool"`
|
||||
Max int `yaml:"max"`
|
||||
Life string `yaml:"life"`
|
||||
LifeDuration time.Duration `yaml:",omitempty"`
|
||||
}{},
|
||||
Models: struct {
|
||||
Package string `yaml:"package"`
|
||||
}{},
|
||||
}
|
||||
|
||||
db, err := dbs.NewInstanceFromConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
var before = time.Now()
|
||||
tables, err := models.SharedHTTPAccessLogManager.FindTables(db, "20220306")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, err := json.Marshal(tables)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(string(data))
|
||||
t.Log(time.Since(before).Seconds()*1000, "ms")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPAccessLogManager_FindTable(t *testing.T) {
|
||||
var config = &dbs.DBConfig{
|
||||
Driver: "mysql",
|
||||
Dsn: "root:123456@tcp(127.0.0.1:3306)/db_edge_log?charset=utf8mb4&timeout=30s",
|
||||
Prefix: "edge",
|
||||
Connections: struct {
|
||||
Pool int `yaml:"pool"`
|
||||
Max int `yaml:"max"`
|
||||
Life string `yaml:"life"`
|
||||
LifeDuration time.Duration `yaml:",omitempty"`
|
||||
}{},
|
||||
Models: struct {
|
||||
Package string `yaml:"package"`
|
||||
}{},
|
||||
}
|
||||
|
||||
db, err := dbs.NewInstanceFromConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
var before = time.Now()
|
||||
tableDef, err := models.SharedHTTPAccessLogManager.FindTable(db, "20220306", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, err := json.Marshal(tableDef)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(string(data))
|
||||
t.Log(time.Since(before).Seconds()*1000, "ms")
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,12 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 数据库相关服务
|
||||
// DBService 数据库相关服务
|
||||
type DBService struct {
|
||||
BaseService
|
||||
}
|
||||
|
||||
// 获取所有表信息
|
||||
// FindAllDBTables 获取所有表信息
|
||||
func (this *DBService) FindAllDBTables(ctx context.Context, req *pb.FindAllDBTablesRequest) (*pb.FindAllDBTablesResponse, error) {
|
||||
_, err := this.ValidateAdmin(ctx, 0)
|
||||
if err != nil {
|
||||
@@ -60,7 +60,7 @@ func (this *DBService) FindAllDBTables(ctx context.Context, req *pb.FindAllDBTab
|
||||
return &pb.FindAllDBTablesResponse{DbTables: pbTables}, nil
|
||||
}
|
||||
|
||||
// 删除表
|
||||
// DeleteDBTable 删除表
|
||||
func (this *DBService) DeleteDBTable(ctx context.Context, req *pb.DeleteDBTableRequest) (*pb.RPCSuccess, error) {
|
||||
_, err := this.ValidateAdmin(ctx, 0)
|
||||
if err != nil {
|
||||
@@ -84,7 +84,7 @@ func (this *DBService) DeleteDBTable(ctx context.Context, req *pb.DeleteDBTableR
|
||||
return this.Success()
|
||||
}
|
||||
|
||||
// 清空表
|
||||
// TruncateDBTable 清空表
|
||||
func (this *DBService) TruncateDBTable(ctx context.Context, req *pb.TruncateDBTableRequest) (*pb.RPCSuccess, error) {
|
||||
_, err := this.ValidateAdmin(ctx, 0)
|
||||
if err != nil {
|
||||
|
||||
@@ -45,7 +45,10 @@ func (this *SQLDump) Dump(db *dbs.DB) (result *SQLDumpResult, err error) {
|
||||
}
|
||||
for _, tableName := range tableNames {
|
||||
// 忽略一些分表
|
||||
if strings.HasPrefix(tableName, "edgeHTTPAccessLogs_") {
|
||||
if strings.HasPrefix(strings.ToLower(tableName), strings.ToLower("edgeHTTPAccessLogs_")) {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(strings.ToLower(tableName), strings.ToLower("edgeNSAccessLogs_")) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ func TestLogTask_loopClean(t *testing.T) {
|
||||
dbs.NotifyReady()
|
||||
|
||||
task := NewLogTask()
|
||||
err := task.loopClean(5)
|
||||
err := task.loopClean()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -103,23 +103,18 @@ func (this *ServerAccessLogCleaner) cleanDB(db *dbs.DB, endDay string) error {
|
||||
return errors.New("invalid column names: " + strings.Join(columnNames, ", "))
|
||||
}
|
||||
columnName := columnNames[0]
|
||||
var reg = regexp.MustCompile(`^(?i)(edgeHTTPAccessLogs|edgeNSAccessLogs)_(\d{8})(_\d{4})?$`)
|
||||
for _, one := range ones {
|
||||
tableName := one.GetString(columnName)
|
||||
var tableName = one.GetString(columnName)
|
||||
if len(tableName) == 0 {
|
||||
continue
|
||||
}
|
||||
ok, err := regexp.MatchString(`^(?i)(edgeHTTPAccessLogs|edgeNSAccessLogs)_(\d{8})$`, tableName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
if !reg.MatchString(tableName) {
|
||||
continue
|
||||
}
|
||||
index := strings.LastIndex(tableName, "_")
|
||||
if index < 0 {
|
||||
continue
|
||||
}
|
||||
day := tableName[index+1:]
|
||||
var matches = reg.FindStringSubmatch(tableName)
|
||||
var day = matches[2]
|
||||
|
||||
if day < endDay {
|
||||
_, err = db.Exec("DROP TABLE " + tableName)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package tasks
|
||||
package tasks_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/tasks"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
"testing"
|
||||
)
|
||||
@@ -8,7 +9,7 @@ import (
|
||||
func TestServerAccessLogCleaner_Loop(t *testing.T) {
|
||||
dbs.NotifyReady()
|
||||
|
||||
task := NewServerAccessLogCleaner()
|
||||
task := tasks.NewServerAccessLogCleaner()
|
||||
err := task.Loop()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
Reference in New Issue
Block a user