update: 日志优化

This commit is contained in:
Kerwin
2025-10-28 11:07:00 +08:00
parent 53aebf2a15
commit 61beed6788
18 changed files with 991 additions and 75 deletions

View File

@@ -128,6 +128,11 @@ PORT=8080
# 时区配置 # 时区配置
TIMEZONE=Asia/Shanghai TIMEZONE=Asia/Shanghai
# 日志配置
LOG_LEVEL=INFO # 日志级别 (DEBUG, INFO, WARN, ERROR, FATAL)
DEBUG=false # 调试模式开关
STRUCTURED_LOG=false # 结构化日志开关 (JSON格式)
``` ```
--- ---

View File

@@ -1,7 +1,10 @@
package repo package repo
import ( import (
"time"
"github.com/ctwj/urldb/db/entity" "github.com/ctwj/urldb/db/entity"
"github.com/ctwj/urldb/utils"
"gorm.io/gorm" "gorm.io/gorm"
) )
@@ -66,20 +69,28 @@ func (r *CksRepositoryImpl) FindAll() ([]entity.Cks, error) {
// FindByID 根据ID查找Cks预加载Pan关联数据 // FindByID 根据ID查找Cks预加载Pan关联数据
func (r *CksRepositoryImpl) FindByID(id uint) (*entity.Cks, error) { func (r *CksRepositoryImpl) FindByID(id uint) (*entity.Cks, error) {
startTime := utils.GetCurrentTime()
var cks entity.Cks var cks entity.Cks
err := r.db.Preload("Pan").First(&cks, id).Error err := r.db.Preload("Pan").First(&cks, id).Error
queryDuration := time.Since(startTime)
if err != nil { if err != nil {
utils.Debug("FindByID失败: ID=%d, 错误=%v, 查询耗时=%v", id, err, queryDuration)
return nil, err return nil, err
} }
utils.Debug("FindByID成功: ID=%d, 查询耗时=%v", id, queryDuration)
return &cks, nil return &cks, nil
} }
func (r *CksRepositoryImpl) FindByIds(ids []uint) ([]*entity.Cks, error) { func (r *CksRepositoryImpl) FindByIds(ids []uint) ([]*entity.Cks, error) {
startTime := utils.GetCurrentTime()
var cks []*entity.Cks var cks []*entity.Cks
err := r.db.Preload("Pan").Where("id IN ?", ids).Find(&cks).Error err := r.db.Preload("Pan").Where("id IN ?", ids).Find(&cks).Error
queryDuration := time.Since(startTime)
if err != nil { if err != nil {
utils.Debug("FindByIds失败: IDs数量=%d, 错误=%v, 查询耗时=%v", len(ids), err, queryDuration)
return nil, err return nil, err
} }
utils.Debug("FindByIds成功: 找到%d个账号查询耗时=%v", len(cks), queryDuration)
return cks, nil return cks, nil
} }

View File

@@ -5,6 +5,7 @@ import (
"time" "time"
"github.com/ctwj/urldb/db/entity" "github.com/ctwj/urldb/db/entity"
"github.com/ctwj/urldb/utils"
"gorm.io/gorm" "gorm.io/gorm"
) )
@@ -201,6 +202,7 @@ func (r *ResourceRepositoryImpl) SearchByPanID(query string, panID uint, page, l
// SearchWithFilters 根据参数进行搜索 // SearchWithFilters 根据参数进行搜索
func (r *ResourceRepositoryImpl) SearchWithFilters(params map[string]interface{}) ([]entity.Resource, int64, error) { func (r *ResourceRepositoryImpl) SearchWithFilters(params map[string]interface{}) ([]entity.Resource, int64, error) {
startTime := utils.GetCurrentTime()
var resources []entity.Resource var resources []entity.Resource
var total int64 var total int64
@@ -318,8 +320,11 @@ func (r *ResourceRepositoryImpl) SearchWithFilters(params map[string]interface{}
offset := (page - 1) * pageSize offset := (page - 1) * pageSize
// 获取分页数据,按更新时间倒序 // 获取分页数据,按更新时间倒序
queryStart := utils.GetCurrentTime()
err := db.Order("updated_at DESC").Offset(offset).Limit(pageSize).Find(&resources).Error err := db.Order("updated_at DESC").Offset(offset).Limit(pageSize).Find(&resources).Error
fmt.Printf("查询结果: 总数=%d, 当前页数据量=%d, pageSize=%d\n", total, len(resources), pageSize) queryDuration := time.Since(queryStart)
totalDuration := time.Since(startTime)
utils.Debug("SearchWithFilters完成: 总数=%d, 当前页数据量=%d, 查询耗时=%v, 总耗时=%v", total, len(resources), queryDuration, totalDuration)
return resources, total, err return resources, total, err
} }
@@ -452,11 +457,15 @@ func (r *ResourceRepositoryImpl) GetResourcesForTransfer(panID uint, sinceTime t
// GetByURL 根据URL获取资源 // GetByURL 根据URL获取资源
func (r *ResourceRepositoryImpl) GetByURL(url string) (*entity.Resource, error) { func (r *ResourceRepositoryImpl) GetByURL(url string) (*entity.Resource, error) {
startTime := utils.GetCurrentTime()
var resource entity.Resource var resource entity.Resource
err := r.db.Where("url = ?", url).First(&resource).Error err := r.db.Where("url = ?", url).First(&resource).Error
queryDuration := time.Since(startTime)
if err != nil { if err != nil {
utils.Debug("GetByURL失败: URL=%s, 错误=%v, 查询耗时=%v", url, err, queryDuration)
return nil, err return nil, err
} }
utils.Debug("GetByURL成功: URL=%s, 查询耗时=%v", url, queryDuration)
return &resource, nil return &resource, nil
} }

View File

@@ -3,6 +3,7 @@ package repo
import ( import (
"fmt" "fmt"
"sync" "sync"
"time"
"github.com/ctwj/urldb/db/entity" "github.com/ctwj/urldb/db/entity"
"github.com/ctwj/urldb/utils" "github.com/ctwj/urldb/utils"
@@ -100,8 +101,11 @@ func (r *SystemConfigRepositoryImpl) UpsertConfigs(configs []entity.SystemConfig
// GetOrCreateDefault 获取配置或创建默认配置 // GetOrCreateDefault 获取配置或创建默认配置
func (r *SystemConfigRepositoryImpl) GetOrCreateDefault() ([]entity.SystemConfig, error) { func (r *SystemConfigRepositoryImpl) GetOrCreateDefault() ([]entity.SystemConfig, error) {
startTime := utils.GetCurrentTime()
configs, err := r.FindAll() configs, err := r.FindAll()
initialQueryDuration := time.Since(startTime)
if err != nil { if err != nil {
utils.Error("获取所有系统配置失败: %v耗时: %v", err, initialQueryDuration)
return nil, err return nil, err
} }
@@ -141,11 +145,16 @@ func (r *SystemConfigRepositoryImpl) GetOrCreateDefault() ([]entity.SystemConfig
{Key: entity.ConfigKeyQrCodeStyle, Value: entity.ConfigDefaultQrCodeStyle, Type: entity.ConfigTypeString}, {Key: entity.ConfigKeyQrCodeStyle, Value: entity.ConfigDefaultQrCodeStyle, Type: entity.ConfigTypeString},
} }
createStart := utils.GetCurrentTime()
err = r.UpsertConfigs(defaultConfigs) err = r.UpsertConfigs(defaultConfigs)
createDuration := time.Since(createStart)
if err != nil { if err != nil {
utils.Error("创建默认系统配置失败: %v耗时: %v", err, createDuration)
return nil, err return nil, err
} }
totalDuration := time.Since(startTime)
utils.Info("创建默认系统配置成功,数量: %d总耗时: %v", len(defaultConfigs), totalDuration)
return defaultConfigs, nil return defaultConfigs, nil
} }
@@ -198,17 +207,24 @@ func (r *SystemConfigRepositoryImpl) GetOrCreateDefault() ([]entity.SystemConfig
// 如果有缺失的配置项,则添加它们 // 如果有缺失的配置项,则添加它们
if len(missingConfigs) > 0 { if len(missingConfigs) > 0 {
upsertStart := utils.GetCurrentTime()
err = r.UpsertConfigs(missingConfigs) err = r.UpsertConfigs(missingConfigs)
upsertDuration := time.Since(upsertStart)
if err != nil { if err != nil {
utils.Error("添加缺失的系统配置失败: %v耗时: %v", err, upsertDuration)
return nil, err return nil, err
} }
utils.Debug("添加缺失的系统配置完成,数量: %d耗时: %v", len(missingConfigs), upsertDuration)
// 重新获取所有配置 // 重新获取所有配置
configs, err = r.FindAll() configs, err = r.FindAll()
if err != nil { if err != nil {
utils.Error("重新获取所有系统配置失败: %v", err)
return nil, err return nil, err
} }
} }
totalDuration := time.Since(startTime)
utils.Debug("GetOrCreateDefault完成总数: %d总耗时: %v", len(configs), totalDuration)
return configs, nil return configs, nil
} }

View File

@@ -1,7 +1,10 @@
package repo package repo
import ( import (
"time"
"github.com/ctwj/urldb/db/entity" "github.com/ctwj/urldb/db/entity"
"github.com/ctwj/urldb/utils"
"gorm.io/gorm" "gorm.io/gorm"
) )
@@ -58,8 +61,15 @@ func (r *TaskItemRepositoryImpl) DeleteByTaskID(taskID uint) error {
// GetByTaskIDAndStatus 根据任务ID和状态获取任务项 // GetByTaskIDAndStatus 根据任务ID和状态获取任务项
func (r *TaskItemRepositoryImpl) GetByTaskIDAndStatus(taskID uint, status string) ([]*entity.TaskItem, error) { func (r *TaskItemRepositoryImpl) GetByTaskIDAndStatus(taskID uint, status string) ([]*entity.TaskItem, error) {
startTime := utils.GetCurrentTime()
var items []*entity.TaskItem var items []*entity.TaskItem
err := r.db.Where("task_id = ? AND status = ?", taskID, status).Order("id ASC").Find(&items).Error err := r.db.Where("task_id = ? AND status = ?", taskID, status).Order("id ASC").Find(&items).Error
queryDuration := time.Since(startTime)
if err != nil {
utils.Error("GetByTaskIDAndStatus失败: 任务ID=%d, 状态=%s, 错误=%v, 查询耗时=%v", taskID, status, err, queryDuration)
return nil, err
}
utils.Debug("GetByTaskIDAndStatus成功: 任务ID=%d, 状态=%s, 数量=%d, 查询耗时=%v", taskID, status, len(items), queryDuration)
return items, err return items, err
} }
@@ -93,19 +103,36 @@ func (r *TaskItemRepositoryImpl) GetListByTaskID(taskID uint, page, pageSize int
// UpdateStatus 更新任务项状态 // UpdateStatus 更新任务项状态
func (r *TaskItemRepositoryImpl) UpdateStatus(id uint, status string) error { func (r *TaskItemRepositoryImpl) UpdateStatus(id uint, status string) error {
return r.db.Model(&entity.TaskItem{}).Where("id = ?", id).Update("status", status).Error startTime := utils.GetCurrentTime()
err := r.db.Model(&entity.TaskItem{}).Where("id = ?", id).Update("status", status).Error
updateDuration := time.Since(startTime)
if err != nil {
utils.Error("UpdateStatus失败: ID=%d, 状态=%s, 错误=%v, 更新耗时=%v", id, status, err, updateDuration)
return err
}
utils.Debug("UpdateStatus成功: ID=%d, 状态=%s, 更新耗时=%v", id, status, updateDuration)
return nil
} }
// UpdateStatusAndOutput 更新任务项状态和输出数据 // UpdateStatusAndOutput 更新任务项状态和输出数据
func (r *TaskItemRepositoryImpl) UpdateStatusAndOutput(id uint, status, outputData string) error { func (r *TaskItemRepositoryImpl) UpdateStatusAndOutput(id uint, status, outputData string) error {
return r.db.Model(&entity.TaskItem{}).Where("id = ?", id).Updates(map[string]interface{}{ startTime := utils.GetCurrentTime()
err := r.db.Model(&entity.TaskItem{}).Where("id = ?", id).Updates(map[string]interface{}{
"status": status, "status": status,
"output_data": outputData, "output_data": outputData,
}).Error }).Error
updateDuration := time.Since(startTime)
if err != nil {
utils.Error("UpdateStatusAndOutput失败: ID=%d, 状态=%s, 错误=%v, 更新耗时=%v", id, status, err, updateDuration)
return err
}
utils.Debug("UpdateStatusAndOutput成功: ID=%d, 状态=%s, 更新耗时=%v", id, status, updateDuration)
return nil
} }
// GetStatsByTaskID 获取任务项统计信息 // GetStatsByTaskID 获取任务项统计信息
func (r *TaskItemRepositoryImpl) GetStatsByTaskID(taskID uint) (map[string]int, error) { func (r *TaskItemRepositoryImpl) GetStatsByTaskID(taskID uint) (map[string]int, error) {
startTime := utils.GetCurrentTime()
var results []struct { var results []struct {
Status string Status string
Count int Count int
@@ -117,7 +144,9 @@ func (r *TaskItemRepositoryImpl) GetStatsByTaskID(taskID uint) (map[string]int,
Group("status"). Group("status").
Find(&results).Error Find(&results).Error
queryDuration := time.Since(startTime)
if err != nil { if err != nil {
utils.Error("GetStatsByTaskID失败: 任务ID=%d, 错误=%v, 查询耗时=%v", taskID, err, queryDuration)
return nil, err return nil, err
} }
@@ -134,12 +163,22 @@ func (r *TaskItemRepositoryImpl) GetStatsByTaskID(taskID uint) (map[string]int,
stats["total"] += result.Count stats["total"] += result.Count
} }
totalDuration := time.Since(startTime)
utils.Debug("GetStatsByTaskID成功: 任务ID=%d, 统计信息=%v, 查询耗时=%v, 总耗时=%v", taskID, stats, queryDuration, totalDuration)
return stats, nil return stats, nil
} }
// ResetProcessingItems 重置处理中的任务项为pending状态 // ResetProcessingItems 重置处理中的任务项为pending状态
func (r *TaskItemRepositoryImpl) ResetProcessingItems(taskID uint) error { func (r *TaskItemRepositoryImpl) ResetProcessingItems(taskID uint) error {
return r.db.Model(&entity.TaskItem{}). startTime := utils.GetCurrentTime()
err := r.db.Model(&entity.TaskItem{}).
Where("task_id = ? AND status = ?", taskID, "processing"). Where("task_id = ? AND status = ?", taskID, "processing").
Update("status", "pending").Error Update("status", "pending").Error
updateDuration := time.Since(startTime)
if err != nil {
utils.Error("ResetProcessingItems失败: 任务ID=%d, 错误=%v, 更新耗时=%v", taskID, err, updateDuration)
return err
}
utils.Debug("ResetProcessingItems成功: 任务ID=%d, 更新耗时=%v", taskID, updateDuration)
return nil
} }

View File

@@ -4,6 +4,7 @@ import (
"time" "time"
"github.com/ctwj/urldb/db/entity" "github.com/ctwj/urldb/db/entity"
"github.com/ctwj/urldb/utils"
"gorm.io/gorm" "gorm.io/gorm"
) )
@@ -35,11 +36,15 @@ func NewTaskRepository(db *gorm.DB) TaskRepository {
// GetByID 根据ID获取任务 // GetByID 根据ID获取任务
func (r *TaskRepositoryImpl) GetByID(id uint) (*entity.Task, error) { func (r *TaskRepositoryImpl) GetByID(id uint) (*entity.Task, error) {
startTime := utils.GetCurrentTime()
var task entity.Task var task entity.Task
err := r.db.First(&task, id).Error err := r.db.First(&task, id).Error
queryDuration := time.Since(startTime)
if err != nil { if err != nil {
utils.Debug("GetByID失败: ID=%d, 错误=%v, 查询耗时=%v", id, err, queryDuration)
return nil, err return nil, err
} }
utils.Debug("GetByID成功: ID=%d, 查询耗时=%v", id, queryDuration)
return &task, nil return &task, nil
} }
@@ -55,6 +60,7 @@ func (r *TaskRepositoryImpl) Delete(id uint) error {
// GetList 获取任务列表 // GetList 获取任务列表
func (r *TaskRepositoryImpl) GetList(page, pageSize int, taskType, status string) ([]*entity.Task, int64, error) { func (r *TaskRepositoryImpl) GetList(page, pageSize int, taskType, status string) ([]*entity.Task, int64, error) {
startTime := utils.GetCurrentTime()
var tasks []*entity.Task var tasks []*entity.Task
var total int64 var total int64
@@ -69,84 +75,171 @@ func (r *TaskRepositoryImpl) GetList(page, pageSize int, taskType, status string
} }
// 获取总数 // 获取总数
countStart := utils.GetCurrentTime()
err := query.Count(&total).Error err := query.Count(&total).Error
countDuration := time.Since(countStart)
if err != nil { if err != nil {
utils.Error("GetList获取总数失败: 错误=%v, 查询耗时=%v", err, countDuration)
return nil, 0, err return nil, 0, err
} }
// 分页查询 // 分页查询
offset := (page - 1) * pageSize offset := (page - 1) * pageSize
queryStart := utils.GetCurrentTime()
err = query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&tasks).Error err = query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&tasks).Error
queryDuration := time.Since(queryStart)
if err != nil { if err != nil {
utils.Error("GetList查询失败: 错误=%v, 查询耗时=%v", err, queryDuration)
return nil, 0, err return nil, 0, err
} }
totalDuration := time.Since(startTime)
utils.Debug("GetList完成: 任务类型=%s, 状态=%s, 页码=%d, 页面大小=%d, 总数=%d, 结果数=%d, 总耗时=%v", taskType, status, page, pageSize, total, len(tasks), totalDuration)
return tasks, total, nil return tasks, total, nil
} }
// UpdateStatus 更新任务状态 // UpdateStatus 更新任务状态
func (r *TaskRepositoryImpl) UpdateStatus(id uint, status string) error { func (r *TaskRepositoryImpl) UpdateStatus(id uint, status string) error {
return r.db.Model(&entity.Task{}).Where("id = ?", id).Update("status", status).Error startTime := utils.GetCurrentTime()
err := r.db.Model(&entity.Task{}).Where("id = ?", id).Update("status", status).Error
updateDuration := time.Since(startTime)
if err != nil {
utils.Error("UpdateStatus失败: ID=%d, 状态=%s, 错误=%v, 更新耗时=%v", id, status, err, updateDuration)
return err
}
utils.Debug("UpdateStatus成功: ID=%d, 状态=%s, 更新耗时=%v", id, status, updateDuration)
return nil
} }
// UpdateProgress 更新任务进度 // UpdateProgress 更新任务进度
func (r *TaskRepositoryImpl) UpdateProgress(id uint, progress float64, progressData string) error { func (r *TaskRepositoryImpl) UpdateProgress(id uint, progress float64, progressData string) error {
startTime := utils.GetCurrentTime()
// 检查progress和progress_data字段是否存在 // 检查progress和progress_data字段是否存在
var count int64 var count int64
err := r.db.Raw("SELECT COUNT(*) FROM information_schema.columns WHERE table_name = 'tasks' AND column_name = 'progress'").Count(&count).Error err := r.db.Raw("SELECT COUNT(*) FROM information_schema.columns WHERE table_name = 'tasks' AND column_name = 'progress'").Count(&count).Error
if err != nil || count == 0 { if err != nil || count == 0 {
// 如果检查失败或字段不存在只更新processed_items等现有字段 // 如果检查失败或字段不存在只更新processed_items等现有字段
return r.db.Model(&entity.Task{}).Where("id = ?", id).Updates(map[string]interface{}{ updateStart := utils.GetCurrentTime()
err := r.db.Model(&entity.Task{}).Where("id = ?", id).Updates(map[string]interface{}{
"processed_items": progress, // 使用progress作为processed_items的近似值 "processed_items": progress, // 使用progress作为processed_items的近似值
}).Error }).Error
updateDuration := time.Since(updateStart)
totalDuration := time.Since(startTime)
if err != nil {
utils.Error("UpdateProgress失败(字段不存在): ID=%d, 进度=%f, 错误=%v, 更新耗时=%v, 总耗时=%v", id, progress, err, updateDuration, totalDuration)
return err
}
utils.Debug("UpdateProgress成功(字段不存在): ID=%d, 进度=%f, 更新耗时=%v, 总耗时=%v", id, progress, updateDuration, totalDuration)
return nil
} }
// 字段存在,正常更新 // 字段存在,正常更新
return r.db.Model(&entity.Task{}).Where("id = ?", id).Updates(map[string]interface{}{ updateStart := utils.GetCurrentTime()
err = r.db.Model(&entity.Task{}).Where("id = ?", id).Updates(map[string]interface{}{
"progress": progress, "progress": progress,
"progress_data": progressData, "progress_data": progressData,
}).Error }).Error
updateDuration := time.Since(updateStart)
totalDuration := time.Since(startTime)
if err != nil {
utils.Error("UpdateProgress失败: ID=%d, 进度=%f, 错误=%v, 更新耗时=%v, 总耗时=%v", id, progress, err, updateDuration, totalDuration)
return err
}
utils.Debug("UpdateProgress成功: ID=%d, 进度=%f, 更新耗时=%v, 总耗时=%v", id, progress, updateDuration, totalDuration)
return nil
} }
// UpdateStatusAndMessage 更新任务状态和消息 // UpdateStatusAndMessage 更新任务状态和消息
func (r *TaskRepositoryImpl) UpdateStatusAndMessage(id uint, status, message string) error { func (r *TaskRepositoryImpl) UpdateStatusAndMessage(id uint, status, message string) error {
startTime := utils.GetCurrentTime()
// 检查message字段是否存在 // 检查message字段是否存在
var count int64 var count int64
err := r.db.Raw("SELECT COUNT(*) FROM information_schema.columns WHERE table_name = 'tasks' AND column_name = 'message'").Count(&count).Error err := r.db.Raw("SELECT COUNT(*) FROM information_schema.columns WHERE table_name = 'tasks' AND column_name = 'message'").Count(&count).Error
if err != nil { if err != nil {
// 如果检查失败,只更新状态 // 如果检查失败,只更新状态
return r.db.Model(&entity.Task{}).Where("id = ?", id).Update("status", status).Error updateStart := utils.GetCurrentTime()
err := r.db.Model(&entity.Task{}).Where("id = ?", id).Update("status", status).Error
updateDuration := time.Since(updateStart)
totalDuration := time.Since(startTime)
if err != nil {
utils.Error("UpdateStatusAndMessage失败(检查失败): ID=%d, 状态=%s, 错误=%v, 更新耗时=%v, 总耗时=%v", id, status, err, updateDuration, totalDuration)
return err
}
utils.Debug("UpdateStatusAndMessage成功(检查失败): ID=%d, 状态=%s, 更新耗时=%v, 总耗时=%v", id, status, updateDuration, totalDuration)
return nil
} }
if count > 0 { if count > 0 {
// message字段存在更新状态和消息 // message字段存在更新状态和消息
return r.db.Model(&entity.Task{}).Where("id = ?", id).Updates(map[string]interface{}{ updateStart := utils.GetCurrentTime()
err := r.db.Model(&entity.Task{}).Where("id = ?", id).Updates(map[string]interface{}{
"status": status, "status": status,
"message": message, "message": message,
}).Error }).Error
updateDuration := time.Since(updateStart)
totalDuration := time.Since(startTime)
if err != nil {
utils.Error("UpdateStatusAndMessage失败(字段存在): ID=%d, 状态=%s, 错误=%v, 更新耗时=%v, 总耗时=%v", id, status, err, updateDuration, totalDuration)
return err
}
utils.Debug("UpdateStatusAndMessage成功(字段存在): ID=%d, 状态=%s, 更新耗时=%v, 总耗时=%v", id, status, updateDuration, totalDuration)
return nil
} else { } else {
// message字段不存在只更新状态 // message字段不存在只更新状态
return r.db.Model(&entity.Task{}).Where("id = ?", id).Update("status", status).Error updateStart := utils.GetCurrentTime()
err := r.db.Model(&entity.Task{}).Where("id = ?", id).Update("status", status).Error
updateDuration := time.Since(updateStart)
totalDuration := time.Since(startTime)
if err != nil {
utils.Error("UpdateStatusAndMessage失败(字段不存在): ID=%d, 状态=%s, 错误=%v, 更新耗时=%v, 总耗时=%v", id, status, err, updateDuration, totalDuration)
return err
}
utils.Debug("UpdateStatusAndMessage成功(字段不存在): ID=%d, 状态=%s, 更新耗时=%v, 总耗时=%v", id, status, updateDuration, totalDuration)
return nil
} }
} }
// UpdateTaskStats 更新任务统计信息 // UpdateTaskStats 更新任务统计信息
func (r *TaskRepositoryImpl) UpdateTaskStats(id uint, processed, success, failed int) error { func (r *TaskRepositoryImpl) UpdateTaskStats(id uint, processed, success, failed int) error {
return r.db.Model(&entity.Task{}).Where("id = ?", id).Updates(map[string]interface{}{ startTime := utils.GetCurrentTime()
err := r.db.Model(&entity.Task{}).Where("id = ?", id).Updates(map[string]interface{}{
"processed_items": processed, "processed_items": processed,
"success_items": success, "success_items": success,
"failed_items": failed, "failed_items": failed,
}).Error }).Error
updateDuration := time.Since(startTime)
if err != nil {
utils.Error("UpdateTaskStats失败: ID=%d, 处理数=%d, 成功数=%d, 失败数=%d, 错误=%v, 更新耗时=%v", id, processed, success, failed, err, updateDuration)
return err
}
utils.Debug("UpdateTaskStats成功: ID=%d, 处理数=%d, 成功数=%d, 失败数=%d, 更新耗时=%v", id, processed, success, failed, updateDuration)
return nil
} }
// UpdateStartedAt 更新任务开始时间 // UpdateStartedAt 更新任务开始时间
func (r *TaskRepositoryImpl) UpdateStartedAt(id uint) error { func (r *TaskRepositoryImpl) UpdateStartedAt(id uint) error {
startTime := utils.GetCurrentTime()
now := time.Now() now := time.Now()
return r.db.Model(&entity.Task{}).Where("id = ?", id).Update("started_at", now).Error err := r.db.Model(&entity.Task{}).Where("id = ?", id).Update("started_at", now).Error
updateDuration := time.Since(startTime)
if err != nil {
utils.Error("UpdateStartedAt失败: ID=%d, 错误=%v, 更新耗时=%v", id, err, updateDuration)
return err
}
utils.Debug("UpdateStartedAt成功: ID=%d, 更新耗时=%v", id, updateDuration)
return nil
} }
// UpdateCompletedAt 更新任务完成时间 // UpdateCompletedAt 更新任务完成时间
func (r *TaskRepositoryImpl) UpdateCompletedAt(id uint) error { func (r *TaskRepositoryImpl) UpdateCompletedAt(id uint) error {
startTime := utils.GetCurrentTime()
now := time.Now() now := time.Now()
return r.db.Model(&entity.Task{}).Where("id = ?", id).Update("completed_at", now).Error err := r.db.Model(&entity.Task{}).Where("id = ?", id).Update("completed_at", now).Error
updateDuration := time.Since(startTime)
if err != nil {
utils.Error("UpdateCompletedAt失败: ID=%d, 错误=%v, 更新耗时=%v", id, err, updateDuration)
return err
}
utils.Debug("UpdateCompletedAt成功: ID=%d, 更新耗时=%v", id, updateDuration)
return nil
} }

132
docs/logging.md Normal file
View File

@@ -0,0 +1,132 @@
# 日志系统说明
## 概述
本项目使用自定义的日志系统,支持多种日志级别、环境差异化配置和结构化日志记录。
## 日志级别
日志系统支持以下级别(按严重程度递增):
1. **DEBUG** - 调试信息,用于开发和故障排除
2. **INFO** - 一般信息,记录系统正常运行状态
3. **WARN** - 警告信息,表示可能的问题但不影响系统运行
4. **ERROR** - 错误信息,表示系统错误但可以继续运行
5. **FATAL** - 致命错误,系统将退出
## 环境配置
### 日志级别配置
可以通过环境变量配置日志级别:
```bash
# 设置日志级别DEBUG, INFO, WARN, ERROR, FATAL
LOG_LEVEL=DEBUG
# 或者启用调试模式等同于DEBUG级别
DEBUG=true
```
默认情况下开发环境使用DEBUG级别生产环境使用INFO级别。
### 结构化日志
可以通过环境变量启用结构化日志JSON格式
```bash
# 启用结构化日志
STRUCTURED_LOG=true
```
## 使用方法
### 基本日志记录
```go
import "github.com/ctwj/urldb/utils"
// 基本日志记录
utils.Debug("调试信息: %s", debugInfo)
utils.Info("一般信息: %s", info)
utils.Warn("警告信息: %s", warning)
utils.Error("错误信息: %s", err)
utils.Fatal("致命错误: %s", fatalErr) // 程序将退出
```
### 结构化日志记录
结构化日志允许添加额外的字段信息,便于日志分析:
```go
// 带字段的结构化日志
utils.DebugWithFields(map[string]interface{}{
"user_id": 123,
"action": "login",
"ip": "192.168.1.1",
}, "用户登录调试信息")
utils.InfoWithFields(map[string]interface{}{
"task_id": 456,
"status": "completed",
"duration_ms": 1250,
}, "任务处理完成")
utils.ErrorWithFields(map[string]interface{}{
"error_code": 500,
"error": "database connection failed",
"component": "database",
}, "数据库连接失败: %v", err)
```
## 日志输出
日志默认输出到:
- 控制台(标准输出)
- 文件logs目录下的app_日期.log文件
日志文件支持轮转单个文件最大100MB最多保留5个备份文件日志文件最长保留30天。
## 最佳实践
1. **选择合适的日志级别**
- DEBUG详细的调试信息仅在开发和故障排除时使用
- INFO重要的业务流程和状态变更
- WARN可预期的问题和异常情况
- ERROR系统错误和异常
- FATAL系统无法继续运行的致命错误
2. **使用结构化日志**
- 对于需要后续分析的日志,使用结构化日志
- 添加有意义的字段如用户ID、任务ID、请求ID等
- 避免在字段中包含敏感信息
3. **性能监控**
- 记录关键操作的执行时间
- 使用duration_ms字段记录毫秒级耗时
4. **安全日志**
- 记录所有认证和授权相关的操作
- 包含客户端IP和用户信息
- 记录失败的访问尝试
## 示例
```go
// 性能监控示例
startTime := time.Now()
// 执行操作...
duration := time.Since(startTime)
utils.DebugWithFields(map[string]interface{}{
"operation": "database_query",
"duration_ms": duration.Milliseconds(),
}, "数据库查询完成,耗时: %v", duration)
// 安全日志示例
utils.InfoWithFields(map[string]interface{}{
"user_id": userID,
"ip": clientIP,
"action": "login",
"status": "success",
}, "用户登录成功 - 用户ID: %d, IP: %s", userID, clientIP)
```

View File

@@ -14,4 +14,9 @@ TIMEZONE=Asia/Shanghai
# 文件上传配置 # 文件上传配置
UPLOAD_DIR=./uploads UPLOAD_DIR=./uploads
MAX_FILE_SIZE=5MB MAX_FILE_SIZE=5MB
# 日志配置
LOG_LEVEL=INFO # 日志级别 (DEBUG, INFO, WARN, ERROR, FATAL)
DEBUG=false # 调试模式开关
STRUCTURED_LOG=false

View File

@@ -148,6 +148,11 @@ func (h *PublicAPIHandler) AddBatchResources(c *gin.Context) {
return return
} }
// 记录API访问安全日志
clientIP := c.ClientIP()
userAgent := c.GetHeader("User-Agent")
utils.Info("PublicAPI.AddBatchResources - API访问 - IP: %s, UserAgent: %s, 资源数量: %d", clientIP, userAgent, len(req.Resources))
// 收集所有待提交的URL去重 // 收集所有待提交的URL去重
urlSet := make(map[string]struct{}) urlSet := make(map[string]struct{})
for _, resource := range req.Resources { for _, resource := range req.Resources {
@@ -238,7 +243,9 @@ func (h *PublicAPIHandler) AddBatchResources(c *gin.Context) {
func (h *PublicAPIHandler) SearchResources(c *gin.Context) { func (h *PublicAPIHandler) SearchResources(c *gin.Context) {
startTime := time.Now() startTime := time.Now()
// 获取查询参数 // 记录API访问安全日志
clientIP := c.ClientIP()
userAgent := c.GetHeader("User-Agent")
keyword := c.Query("keyword") keyword := c.Query("keyword")
tag := c.Query("tag") tag := c.Query("tag")
category := c.Query("category") category := c.Query("category")
@@ -246,6 +253,9 @@ func (h *PublicAPIHandler) SearchResources(c *gin.Context) {
pageStr := c.DefaultQuery("page", "1") pageStr := c.DefaultQuery("page", "1")
pageSizeStr := c.DefaultQuery("page_size", "20") pageSizeStr := c.DefaultQuery("page_size", "20")
utils.Info("PublicAPI.SearchResources - API访问 - IP: %s, UserAgent: %s, Keyword: %s, Tag: %s, Category: %s, PanID: %s",
clientIP, userAgent, keyword, tag, category, panID)
page, err := strconv.Atoi(pageStr) page, err := strconv.Atoi(pageStr)
if err != nil || page < 1 { if err != nil || page < 1 {
page = 1 page = 1
@@ -402,9 +412,14 @@ func (h *PublicAPIHandler) SearchResources(c *gin.Context) {
func (h *PublicAPIHandler) GetHotDramas(c *gin.Context) { func (h *PublicAPIHandler) GetHotDramas(c *gin.Context) {
startTime := time.Now() startTime := time.Now()
// 记录API访问安全日志
clientIP := c.ClientIP()
userAgent := c.GetHeader("User-Agent")
pageStr := c.DefaultQuery("page", "1") pageStr := c.DefaultQuery("page", "1")
pageSizeStr := c.DefaultQuery("page_size", "20") pageSizeStr := c.DefaultQuery("page_size", "20")
utils.Info("PublicAPI.GetHotDramas - API访问 - IP: %s, UserAgent: %s", clientIP, userAgent)
page, err := strconv.Atoi(pageStr) page, err := strconv.Atoi(pageStr)
if err != nil || page < 1 { if err != nil || page < 1 {
page = 1 page = 1

View File

@@ -130,6 +130,10 @@ func UpdateSystemConfig(c *gin.Context) {
return return
} }
adminUsername, _ := c.Get("username")
clientIP, _ := c.Get("client_ip")
utils.Info("UpdateSystemConfig - 管理员更新系统配置 - 管理员: %s, IP: %s", adminUsername, clientIP)
// 调试信息 // 调试信息
utils.Info("接收到的配置请求: %+v", req) utils.Info("接收到的配置请求: %+v", req)
@@ -320,6 +324,10 @@ func ToggleAutoProcess(c *gin.Context) {
return return
} }
adminUsername, _ := c.Get("username")
clientIP, _ := c.Get("client_ip")
utils.Info("ToggleAutoProcess - 管理员切换自动处理配置 - 管理员: %s, 启用: %t, IP: %s", adminUsername, req.AutoProcessReadyResources, clientIP)
// 获取当前配置 // 获取当前配置
configs, err := repoManager.SystemConfigRepository.GetOrCreateDefault() configs, err := repoManager.SystemConfigRepository.GetOrCreateDefault()
if err != nil { if err != nil {

View File

@@ -51,6 +51,10 @@ func (h *TaskHandler) CreateBatchTransferTask(c *gin.Context) {
return return
} }
username, _ := c.Get("username")
clientIP, _ := c.Get("client_ip")
utils.Info("CreateBatchTransferTask - 用户创建批量转存任务 - 用户: %s, 任务标题: %s, 资源数量: %d, IP: %s", username, req.Title, len(req.Resources), clientIP)
utils.Debug("创建批量转存任务: %s资源数量: %d选择账号数量: %d", req.Title, len(req.Resources), len(req.SelectedAccounts)) utils.Debug("创建批量转存任务: %s资源数量: %d选择账号数量: %d", req.Title, len(req.Resources), len(req.SelectedAccounts))
// 构建任务配置 // 构建任务配置
@@ -124,6 +128,10 @@ func (h *TaskHandler) StartTask(c *gin.Context) {
return return
} }
username, _ := c.Get("username")
clientIP, _ := c.Get("client_ip")
utils.Info("StartTask - 用户启动任务 - 用户: %s, 任务ID: %d, IP: %s", username, taskID, clientIP)
err = h.taskManager.StartTask(uint(taskID)) err = h.taskManager.StartTask(uint(taskID))
if err != nil { if err != nil {
utils.Error("启动任务失败: %v", err) utils.Error("启动任务失败: %v", err)
@@ -147,6 +155,10 @@ func (h *TaskHandler) StopTask(c *gin.Context) {
return return
} }
username, _ := c.Get("username")
clientIP, _ := c.Get("client_ip")
utils.Info("StopTask - 用户停止任务 - 用户: %s, 任务ID: %d, IP: %s", username, taskID, clientIP)
err = h.taskManager.StopTask(uint(taskID)) err = h.taskManager.StopTask(uint(taskID))
if err != nil { if err != nil {
utils.Error("停止任务失败: %v", err) utils.Error("停止任务失败: %v", err)
@@ -170,6 +182,10 @@ func (h *TaskHandler) PauseTask(c *gin.Context) {
return return
} }
username, _ := c.Get("username")
clientIP, _ := c.Get("client_ip")
utils.Info("PauseTask - 用户暂停任务 - 用户: %s, 任务ID: %d, IP: %s", username, taskID, clientIP)
err = h.taskManager.PauseTask(uint(taskID)) err = h.taskManager.PauseTask(uint(taskID))
if err != nil { if err != nil {
utils.Error("暂停任务失败: %v", err) utils.Error("暂停任务失败: %v", err)
@@ -360,8 +376,13 @@ func (h *TaskHandler) DeleteTask(c *gin.Context) {
return return
} }
username, _ := c.Get("username")
clientIP, _ := c.Get("client_ip")
utils.Info("DeleteTask - 用户删除任务 - 用户: %s, 任务ID: %d, IP: %s", username, taskID, clientIP)
// 检查任务是否在运行 // 检查任务是否在运行
if h.taskManager.IsTaskRunning(uint(taskID)) { if h.taskManager.IsTaskRunning(uint(taskID)) {
utils.Warn("DeleteTask - 尝试删除正在运行的任务 - 用户: %s, 任务ID: %d, IP: %s", username, taskID, clientIP)
ErrorResponse(c, "任务正在运行中,无法删除", http.StatusBadRequest) ErrorResponse(c, "任务正在运行中,无法删除", http.StatusBadRequest)
return return
} }
@@ -383,6 +404,7 @@ func (h *TaskHandler) DeleteTask(c *gin.Context) {
} }
utils.Debug("任务删除成功: %d", taskID) utils.Debug("任务删除成功: %d", taskID)
utils.Info("DeleteTask - 任务删除成功 - 用户: %s, 任务ID: %d, IP: %s", username, taskID, clientIP)
SuccessResponse(c, gin.H{ SuccessResponse(c, gin.H{
"message": "任务删除成功", "message": "任务删除成功",
@@ -402,6 +424,10 @@ func (h *TaskHandler) CreateExpansionTask(c *gin.Context) {
return return
} }
username, _ := c.Get("username")
clientIP, _ := c.Get("client_ip")
utils.Info("CreateExpansionTask - 用户创建扩容任务 - 用户: %s, 账号ID: %d, IP: %s", username, req.PanAccountID, clientIP)
utils.Debug("创建扩容任务: 账号ID %d", req.PanAccountID) utils.Debug("创建扩容任务: 账号ID %d", req.PanAccountID)
// 获取账号信息,用于构建任务标题 // 获取账号信息,用于构建任务标题

View File

@@ -1,6 +1,7 @@
package handlers package handlers
import ( import (
"fmt"
"net/http" "net/http"
"strconv" "strconv"
@@ -8,6 +9,7 @@ import (
"github.com/ctwj/urldb/db/dto" "github.com/ctwj/urldb/db/dto"
"github.com/ctwj/urldb/db/entity" "github.com/ctwj/urldb/db/entity"
"github.com/ctwj/urldb/middleware" "github.com/ctwj/urldb/middleware"
"github.com/ctwj/urldb/utils"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@@ -20,18 +22,24 @@ func Login(c *gin.Context) {
return return
} }
clientIP, _ := c.Get("client_ip")
utils.Info("Login - 尝试登录 - 用户名: %s, IP: %s", req.Username, clientIP)
user, err := repoManager.UserRepository.FindByUsername(req.Username) user, err := repoManager.UserRepository.FindByUsername(req.Username)
if err != nil { if err != nil {
utils.Warn("Login - 用户不存在或密码错误 - 用户名: %s, IP: %s", req.Username, clientIP)
ErrorResponse(c, "用户名或密码错误", http.StatusUnauthorized) ErrorResponse(c, "用户名或密码错误", http.StatusUnauthorized)
return return
} }
if !user.IsActive { if !user.IsActive {
utils.Warn("Login - 账户已被禁用 - 用户名: %s, IP: %s", req.Username, clientIP)
ErrorResponse(c, "账户已被禁用", http.StatusUnauthorized) ErrorResponse(c, "账户已被禁用", http.StatusUnauthorized)
return return
} }
if !middleware.CheckPassword(req.Password, user.Password) { if !middleware.CheckPassword(req.Password, user.Password) {
utils.Warn("Login - 密码错误 - 用户名: %s, IP: %s", req.Username, clientIP)
ErrorResponse(c, "用户名或密码错误", http.StatusUnauthorized) ErrorResponse(c, "用户名或密码错误", http.StatusUnauthorized)
return return
} }
@@ -42,10 +50,13 @@ func Login(c *gin.Context) {
// 生成JWT令牌 // 生成JWT令牌
token, err := middleware.GenerateToken(user) token, err := middleware.GenerateToken(user)
if err != nil { if err != nil {
utils.Error("Login - 生成令牌失败 - 用户名: %s, IP: %s, Error: %v", req.Username, clientIP, err)
ErrorResponse(c, "生成令牌失败", http.StatusInternalServerError) ErrorResponse(c, "生成令牌失败", http.StatusInternalServerError)
return return
} }
utils.Info("Login - 登录成功 - 用户名: %s(ID:%d), IP: %s", req.Username, user.ID, clientIP)
response := dto.LoginResponse{ response := dto.LoginResponse{
Token: token, Token: token,
User: converter.ToUserResponse(user), User: converter.ToUserResponse(user),
@@ -62,9 +73,13 @@ func Register(c *gin.Context) {
return return
} }
clientIP, _ := c.Get("client_ip")
utils.Info("Register - 尝试注册 - 用户名: %s, 邮箱: %s, IP: %s", req.Username, req.Email, clientIP)
// 检查用户名是否已存在 // 检查用户名是否已存在
existingUser, _ := repoManager.UserRepository.FindByUsername(req.Username) existingUser, _ := repoManager.UserRepository.FindByUsername(req.Username)
if existingUser != nil { if existingUser != nil {
utils.Warn("Register - 用户名已存在 - 用户名: %s, IP: %s", req.Username, clientIP)
ErrorResponse(c, "用户名已存在", http.StatusBadRequest) ErrorResponse(c, "用户名已存在", http.StatusBadRequest)
return return
} }
@@ -72,6 +87,7 @@ func Register(c *gin.Context) {
// 检查邮箱是否已存在 // 检查邮箱是否已存在
existingEmail, _ := repoManager.UserRepository.FindByEmail(req.Email) existingEmail, _ := repoManager.UserRepository.FindByEmail(req.Email)
if existingEmail != nil { if existingEmail != nil {
utils.Warn("Register - 邮箱已存在 - 邮箱: %s, IP: %s", req.Email, clientIP)
ErrorResponse(c, "邮箱已存在", http.StatusBadRequest) ErrorResponse(c, "邮箱已存在", http.StatusBadRequest)
return return
} }
@@ -79,6 +95,7 @@ func Register(c *gin.Context) {
// 哈希密码 // 哈希密码
hashedPassword, err := middleware.HashPassword(req.Password) hashedPassword, err := middleware.HashPassword(req.Password)
if err != nil { if err != nil {
utils.Error("Register - 密码加密失败 - 用户名: %s, IP: %s, Error: %v", req.Username, clientIP, err)
ErrorResponse(c, "密码加密失败", http.StatusInternalServerError) ErrorResponse(c, "密码加密失败", http.StatusInternalServerError)
return return
} }
@@ -93,10 +110,13 @@ func Register(c *gin.Context) {
err = repoManager.UserRepository.Create(user) err = repoManager.UserRepository.Create(user)
if err != nil { if err != nil {
utils.Error("Register - 创建用户失败 - 用户名: %s, IP: %s, Error: %v", req.Username, clientIP, err)
ErrorResponse(c, err.Error(), http.StatusInternalServerError) ErrorResponse(c, err.Error(), http.StatusInternalServerError)
return return
} }
utils.Info("Register - 注册成功 - 用户名: %s(ID:%d), 邮箱: %s, IP: %s", req.Username, user.ID, req.Email, clientIP)
SuccessResponse(c, gin.H{ SuccessResponse(c, gin.H{
"message": "注册成功", "message": "注册成功",
"user": converter.ToUserResponse(user), "user": converter.ToUserResponse(user),
@@ -123,9 +143,14 @@ func CreateUser(c *gin.Context) {
return return
} }
adminUsername, _ := c.Get("username")
clientIP, _ := c.Get("client_ip")
utils.Info("CreateUser - 管理员创建用户 - 管理员: %s, 新用户名: %s, IP: %s", adminUsername, req.Username, clientIP)
// 检查用户名是否已存在 // 检查用户名是否已存在
existingUser, _ := repoManager.UserRepository.FindByUsername(req.Username) existingUser, _ := repoManager.UserRepository.FindByUsername(req.Username)
if existingUser != nil { if existingUser != nil {
utils.Warn("CreateUser - 用户名已存在 - 管理员: %s, 用户名: %s, IP: %s", adminUsername, req.Username, clientIP)
ErrorResponse(c, "用户名已存在", http.StatusBadRequest) ErrorResponse(c, "用户名已存在", http.StatusBadRequest)
return return
} }
@@ -133,6 +158,7 @@ func CreateUser(c *gin.Context) {
// 检查邮箱是否已存在 // 检查邮箱是否已存在
existingEmail, _ := repoManager.UserRepository.FindByEmail(req.Email) existingEmail, _ := repoManager.UserRepository.FindByEmail(req.Email)
if existingEmail != nil { if existingEmail != nil {
utils.Warn("CreateUser - 邮箱已存在 - 管理员: %s, 邮箱: %s, IP: %s", adminUsername, req.Email, clientIP)
ErrorResponse(c, "邮箱已存在", http.StatusBadRequest) ErrorResponse(c, "邮箱已存在", http.StatusBadRequest)
return return
} }
@@ -140,6 +166,7 @@ func CreateUser(c *gin.Context) {
// 哈希密码 // 哈希密码
hashedPassword, err := middleware.HashPassword(req.Password) hashedPassword, err := middleware.HashPassword(req.Password)
if err != nil { if err != nil {
utils.Error("CreateUser - 密码加密失败 - 管理员: %s, 用户名: %s, IP: %s, Error: %v", adminUsername, req.Username, clientIP, err)
ErrorResponse(c, "密码加密失败", http.StatusInternalServerError) ErrorResponse(c, "密码加密失败", http.StatusInternalServerError)
return return
} }
@@ -154,10 +181,13 @@ func CreateUser(c *gin.Context) {
err = repoManager.UserRepository.Create(user) err = repoManager.UserRepository.Create(user)
if err != nil { if err != nil {
utils.Error("CreateUser - 创建用户失败 - 管理员: %s, 用户名: %s, IP: %s, Error: %v", adminUsername, req.Username, clientIP, err)
ErrorResponse(c, err.Error(), http.StatusInternalServerError) ErrorResponse(c, err.Error(), http.StatusInternalServerError)
return return
} }
utils.Info("CreateUser - 用户创建成功 - 管理员: %s, 用户名: %s(ID:%d), 角色: %s, IP: %s", adminUsername, req.Username, user.ID, req.Role, clientIP)
SuccessResponse(c, gin.H{ SuccessResponse(c, gin.H{
"message": "用户创建成功", "message": "用户创建成功",
"user": converter.ToUserResponse(user), "user": converter.ToUserResponse(user),
@@ -179,12 +209,21 @@ func UpdateUser(c *gin.Context) {
return return
} }
adminUsername, _ := c.Get("username")
clientIP, _ := c.Get("client_ip")
utils.Info("UpdateUser - 管理员更新用户 - 管理员: %s, 目标用户ID: %d, IP: %s", adminUsername, id, clientIP)
user, err := repoManager.UserRepository.FindByID(uint(id)) user, err := repoManager.UserRepository.FindByID(uint(id))
if err != nil { if err != nil {
utils.Warn("UpdateUser - 目标用户不存在 - 管理员: %s, 用户ID: %d, IP: %s", adminUsername, id, clientIP)
ErrorResponse(c, "用户不存在", http.StatusNotFound) ErrorResponse(c, "用户不存在", http.StatusNotFound)
return return
} }
// 记录变更前的信息
oldInfo := fmt.Sprintf("用户名:%s,邮箱:%s,角色:%s,状态:%t", user.Username, user.Email, user.Role, user.IsActive)
utils.Debug("UpdateUser - 更新前用户信息 - 管理员: %s, 用户ID: %d, 信息: %s", adminUsername, id, oldInfo)
if req.Username != "" { if req.Username != "" {
user.Username = req.Username user.Username = req.Username
} }
@@ -198,10 +237,15 @@ func UpdateUser(c *gin.Context) {
err = repoManager.UserRepository.Update(user) err = repoManager.UserRepository.Update(user)
if err != nil { if err != nil {
utils.Error("UpdateUser - 更新用户失败 - 管理员: %s, 用户ID: %d, IP: %s, Error: %v", adminUsername, id, clientIP, err)
ErrorResponse(c, err.Error(), http.StatusInternalServerError) ErrorResponse(c, err.Error(), http.StatusInternalServerError)
return return
} }
// 记录变更后信息
newInfo := fmt.Sprintf("用户名:%s,邮箱:%s,角色:%s,状态:%t", user.Username, user.Email, user.Role, user.IsActive)
utils.Info("UpdateUser - 用户更新成功 - 管理员: %s, 用户ID: %d, 更新前: %s, 更新后: %s, IP: %s", adminUsername, id, oldInfo, newInfo, clientIP)
SuccessResponse(c, gin.H{"message": "用户更新成功"}) SuccessResponse(c, gin.H{"message": "用户更新成功"})
} }
@@ -220,8 +264,13 @@ func ChangePassword(c *gin.Context) {
return return
} }
adminUsername, _ := c.Get("username")
clientIP, _ := c.Get("client_ip")
utils.Info("ChangePassword - 管理员修改用户密码 - 管理员: %s, 目标用户ID: %d, IP: %s", adminUsername, id, clientIP)
user, err := repoManager.UserRepository.FindByID(uint(id)) user, err := repoManager.UserRepository.FindByID(uint(id))
if err != nil { if err != nil {
utils.Warn("ChangePassword - 目标用户不存在 - 管理员: %s, 用户ID: %d, IP: %s", adminUsername, id, clientIP)
ErrorResponse(c, "用户不存在", http.StatusNotFound) ErrorResponse(c, "用户不存在", http.StatusNotFound)
return return
} }
@@ -229,6 +278,7 @@ func ChangePassword(c *gin.Context) {
// 哈希新密码 // 哈希新密码
hashedPassword, err := middleware.HashPassword(req.NewPassword) hashedPassword, err := middleware.HashPassword(req.NewPassword)
if err != nil { if err != nil {
utils.Error("ChangePassword - 密码加密失败 - 管理员: %s, 用户ID: %d, IP: %s, Error: %v", adminUsername, id, clientIP, err)
ErrorResponse(c, "密码加密失败", http.StatusInternalServerError) ErrorResponse(c, "密码加密失败", http.StatusInternalServerError)
return return
} }
@@ -236,10 +286,13 @@ func ChangePassword(c *gin.Context) {
user.Password = hashedPassword user.Password = hashedPassword
err = repoManager.UserRepository.Update(user) err = repoManager.UserRepository.Update(user)
if err != nil { if err != nil {
utils.Error("ChangePassword - 更新密码失败 - 管理员: %s, 用户ID: %d, IP: %s, Error: %v", adminUsername, id, clientIP, err)
ErrorResponse(c, err.Error(), http.StatusInternalServerError) ErrorResponse(c, err.Error(), http.StatusInternalServerError)
return return
} }
utils.Info("ChangePassword - 密码修改成功 - 管理员: %s, 用户名: %s(ID:%d), IP: %s", adminUsername, user.Username, id, clientIP)
SuccessResponse(c, gin.H{"message": "密码修改成功"}) SuccessResponse(c, gin.H{"message": "密码修改成功"})
} }
@@ -252,12 +305,27 @@ func DeleteUser(c *gin.Context) {
return return
} }
adminUsername, _ := c.Get("username")
clientIP, _ := c.Get("client_ip")
utils.Info("DeleteUser - 管理员删除用户 - 管理员: %s, 目标用户ID: %d, IP: %s", adminUsername, id, clientIP)
// 先获取用户信息用于日志记录
user, err := repoManager.UserRepository.FindByID(uint(id))
if err != nil {
utils.Warn("DeleteUser - 目标用户不存在 - 管理员: %s, 用户ID: %d, IP: %s", adminUsername, id, clientIP)
ErrorResponse(c, "用户不存在", http.StatusNotFound)
return
}
err = repoManager.UserRepository.Delete(uint(id)) err = repoManager.UserRepository.Delete(uint(id))
if err != nil { if err != nil {
utils.Error("DeleteUser - 删除用户失败 - 管理员: %s, 用户ID: %d, IP: %s, Error: %v", adminUsername, id, clientIP, err)
ErrorResponse(c, err.Error(), http.StatusInternalServerError) ErrorResponse(c, err.Error(), http.StatusInternalServerError)
return return
} }
utils.Info("DeleteUser - 用户删除成功 - 管理员: %s, 用户名: %s(ID:%d), IP: %s", adminUsername, user.Username, id, clientIP)
SuccessResponse(c, gin.H{"message": "用户删除成功"}) SuccessResponse(c, gin.H{"message": "用户删除成功"})
} }
@@ -269,12 +337,18 @@ func GetProfile(c *gin.Context) {
return return
} }
username, _ := c.Get("username")
clientIP, _ := c.Get("client_ip")
utils.Info("GetProfile - 用户获取个人资料 - 用户名: %s(ID:%d), IP: %s", username, userID, clientIP)
user, err := repoManager.UserRepository.FindByID(userID.(uint)) user, err := repoManager.UserRepository.FindByID(userID.(uint))
if err != nil { if err != nil {
utils.Warn("GetProfile - 用户不存在 - 用户名: %s(ID:%d), IP: %s", username, userID, clientIP)
ErrorResponse(c, "用户不存在", http.StatusNotFound) ErrorResponse(c, "用户不存在", http.StatusNotFound)
return return
} }
response := converter.ToUserResponse(user) response := converter.ToUserResponse(user)
utils.Debug("GetProfile - 成功获取个人资料 - 用户名: %s(ID:%d), IP: %s", username, userID, clientIP)
SuccessResponse(c, response) SuccessResponse(c, response)
} }

View File

@@ -41,7 +41,6 @@ func main() {
if err := utils.InitLogger(nil); err != nil { if err := utils.InitLogger(nil); err != nil {
log.Fatal("初始化日志系统失败:", err) log.Fatal("初始化日志系统失败:", err)
} }
defer utils.GetLogger().Close()
// 加载环境变量 // 加载环境变量
if err := godotenv.Load(); err != nil { if err := godotenv.Load(); err != nil {

View File

@@ -27,11 +27,14 @@ type Claims struct {
func AuthMiddleware() gin.HandlerFunc { func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
authHeader := c.GetHeader("Authorization") authHeader := c.GetHeader("Authorization")
// utils.Info("AuthMiddleware - 收到请求: %s %s", c.Request.Method, c.Request.URL.Path) clientIP := c.ClientIP()
// utils.Info("AuthMiddleware - Authorization头: %s", authHeader) userAgent := c.Request.UserAgent()
utils.Debug("AuthMiddleware - 认证请求: %s %s, IP: %s, UserAgent: %s",
c.Request.Method, c.Request.URL.Path, clientIP, userAgent)
if authHeader == "" { if authHeader == "" {
utils.Error("AuthMiddleware - 未提供认证令牌") utils.Warn("AuthMiddleware - 未提供认证令牌 - IP: %s, Path: %s", clientIP, c.Request.URL.Path)
c.JSON(http.StatusUnauthorized, gin.H{"error": "未提供认证令牌"}) c.JSON(http.StatusUnauthorized, gin.H{"error": "未提供认证令牌"})
c.Abort() c.Abort()
return return
@@ -39,29 +42,31 @@ func AuthMiddleware() gin.HandlerFunc {
// 检查Bearer前缀 // 检查Bearer前缀
if !strings.HasPrefix(authHeader, "Bearer ") { if !strings.HasPrefix(authHeader, "Bearer ") {
// utils.Error("AuthMiddleware - 无效的认证格式: %s", authHeader) utils.Warn("AuthMiddleware - 无效的认证格式 - IP: %s, Header: %s", clientIP, authHeader)
c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的认证格式"}) c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的认证格式"})
c.Abort() c.Abort()
return return
} }
tokenString := strings.TrimPrefix(authHeader, "Bearer ") tokenString := strings.TrimPrefix(authHeader, "Bearer ")
// utils.Info("AuthMiddleware - 解析令牌: %s", tokenString[:10]+"...") utils.Debug("AuthMiddleware - 解析令牌: %s...", tokenString[:utils.Min(len(tokenString), 10)])
claims, err := parseToken(tokenString) claims, err := parseToken(tokenString)
if err != nil { if err != nil {
// utils.Error("AuthMiddleware - 令牌解析失败: %v", err) utils.Warn("AuthMiddleware - 令牌解析失败 - IP: %s, Error: %v", clientIP, err)
c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的令牌"}) c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的令牌"})
c.Abort() c.Abort()
return return
} }
// utils.Info("AuthMiddleware - 令牌验证成功,用户: %s, 角色: %s", claims.Username, claims.Role) utils.Info("AuthMiddleware - 认证成功 - 用户: %s(ID:%d), 角色: %s, IP: %s",
claims.Username, claims.UserID, claims.Role, clientIP)
// 将用户信息存储到上下文中 // 将用户信息存储到上下文中
c.Set("user_id", claims.UserID) c.Set("user_id", claims.UserID)
c.Set("username", claims.Username) c.Set("username", claims.Username)
c.Set("role", claims.Role) c.Set("role", claims.Role)
c.Set("client_ip", clientIP)
c.Next() c.Next()
} }
@@ -71,18 +76,23 @@ func AuthMiddleware() gin.HandlerFunc {
func AdminMiddleware() gin.HandlerFunc { func AdminMiddleware() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
role, exists := c.Get("role") role, exists := c.Get("role")
username, _ := c.Get("username")
clientIP, _ := c.Get("client_ip")
if !exists { if !exists {
// c.JSON(http.StatusUnauthorized, gin.H{"error": "未认证"}) utils.Warn("AdminMiddleware - 未认证访问管理员接口 - IP: %s, Path: %s", clientIP, c.Request.URL.Path)
c.Abort() c.Abort()
return return
} }
if role != "admin" { if role != "admin" {
// c.JSON(http.StatusForbidden, gin.H{"error": "需要管理员权限"}) utils.Warn("AdminMiddleware - 非管理员用户尝试访问管理员接口 - 用户: %s, 角色: %s, IP: %s, Path: %s",
username, role, clientIP, c.Request.URL.Path)
c.Abort() c.Abort()
return return
} }
utils.Debug("AdminMiddleware - 管理员访问接口 - 用户: %s, IP: %s, Path: %s", username, clientIP, c.Request.URL.Path)
c.Next() c.Next()
} }
} }

View File

@@ -53,25 +53,47 @@ type ExpansionOutput struct {
// Process 处理扩容任务项 // Process 处理扩容任务项
func (ep *ExpansionProcessor) Process(ctx context.Context, taskID uint, item *entity.TaskItem) error { func (ep *ExpansionProcessor) Process(ctx context.Context, taskID uint, item *entity.TaskItem) error {
utils.Info("开始处理扩容任务项: %d", item.ID) startTime := utils.GetCurrentTime()
utils.InfoWithFields(map[string]interface{}{
"task_item_id": item.ID,
"task_id": taskID,
}, "开始处理扩容任务项: %d", item.ID)
// 解析输入数据 // 解析输入数据
parseStart := utils.GetCurrentTime()
var input ExpansionInput var input ExpansionInput
if err := json.Unmarshal([]byte(item.InputData), &input); err != nil { if err := json.Unmarshal([]byte(item.InputData), &input); err != nil {
parseDuration := time.Since(parseStart)
utils.ErrorWithFields(map[string]interface{}{
"error": err.Error(),
"duration_ms": parseDuration.Milliseconds(),
}, "解析输入数据失败: %v耗时: %v", err, parseDuration)
return fmt.Errorf("解析输入数据失败: %v", err) return fmt.Errorf("解析输入数据失败: %v", err)
} }
parseDuration := time.Since(parseStart)
utils.DebugWithFields(map[string]interface{}{
"duration_ms": parseDuration.Milliseconds(),
}, "解析输入数据完成,耗时: %v", parseDuration)
// 验证输入数据 // 验证输入数据
validateStart := utils.GetCurrentTime()
if err := ep.validateInput(&input); err != nil { if err := ep.validateInput(&input); err != nil {
validateDuration := time.Since(validateStart)
utils.Error("输入数据验证失败: %v耗时: %v", err, validateDuration)
return fmt.Errorf("输入数据验证失败: %v", err) return fmt.Errorf("输入数据验证失败: %v", err)
} }
validateDuration := time.Since(validateStart)
utils.Debug("输入数据验证完成,耗时: %v", validateDuration)
// 检查账号是否已经扩容过 // 检查账号是否已经扩容过
checkExpansionStart := utils.GetCurrentTime()
exists, err := ep.checkExpansionExists(input.PanAccountID) exists, err := ep.checkExpansionExists(input.PanAccountID)
checkExpansionDuration := time.Since(checkExpansionStart)
if err != nil { if err != nil {
utils.Error("检查扩容记录失败: %v", err) utils.Error("检查扩容记录失败: %v耗时: %v", err, checkExpansionDuration)
return fmt.Errorf("检查扩容记录失败: %v", err) return fmt.Errorf("检查扩容记录失败: %v", err)
} }
utils.Debug("检查扩容记录完成,耗时: %v", checkExpansionDuration)
if exists { if exists {
output := ExpansionOutput{ output := ExpansionOutput{
@@ -89,7 +111,9 @@ func (ep *ExpansionProcessor) Process(ctx context.Context, taskID uint, item *en
} }
// 检查账号类型只支持quark账号 // 检查账号类型只支持quark账号
checkAccountTypeStart := utils.GetCurrentTime()
if err := ep.checkAccountType(input.PanAccountID); err != nil { if err := ep.checkAccountType(input.PanAccountID); err != nil {
checkAccountTypeDuration := time.Since(checkAccountTypeStart)
output := ExpansionOutput{ output := ExpansionOutput{
Success: false, Success: false,
Message: "账号类型不支持扩容", Message: "账号类型不支持扩容",
@@ -100,12 +124,16 @@ func (ep *ExpansionProcessor) Process(ctx context.Context, taskID uint, item *en
outputJSON, _ := json.Marshal(output) outputJSON, _ := json.Marshal(output)
item.OutputData = string(outputJSON) item.OutputData = string(outputJSON)
utils.Error("账号类型不支持扩容: %v", err) utils.Error("账号类型不支持扩容: %v耗时: %v", err, checkAccountTypeDuration)
return err return err
} }
checkAccountTypeDuration := time.Since(checkAccountTypeStart)
utils.Debug("检查账号类型完成,耗时: %v", checkAccountTypeDuration)
// 执行扩容操作(传入数据源) // 执行扩容操作(传入数据源)
expansionStart := utils.GetCurrentTime()
transferred, err := ep.performExpansion(ctx, input.PanAccountID, input.DataSource) transferred, err := ep.performExpansion(ctx, input.PanAccountID, input.DataSource)
expansionDuration := time.Since(expansionStart)
if err != nil { if err != nil {
output := ExpansionOutput{ output := ExpansionOutput{
Success: false, Success: false,
@@ -117,9 +145,10 @@ func (ep *ExpansionProcessor) Process(ctx context.Context, taskID uint, item *en
outputJSON, _ := json.Marshal(output) outputJSON, _ := json.Marshal(output)
item.OutputData = string(outputJSON) item.OutputData = string(outputJSON)
utils.Error("扩容任务项处理失败: %d, 错误: %v", item.ID, err) utils.Error("扩容任务项处理失败: %d, 错误: %v总耗时: %v", item.ID, err, expansionDuration)
return fmt.Errorf("扩容失败: %v", err) return fmt.Errorf("扩容失败: %v", err)
} }
utils.Debug("扩容操作完成,耗时: %v", expansionDuration)
// 扩容成功 // 扩容成功
output := ExpansionOutput{ output := ExpansionOutput{
@@ -132,27 +161,44 @@ func (ep *ExpansionProcessor) Process(ctx context.Context, taskID uint, item *en
outputJSON, _ := json.Marshal(output) outputJSON, _ := json.Marshal(output)
item.OutputData = string(outputJSON) item.OutputData = string(outputJSON)
utils.Info("扩容任务项处理完成: %d, 账号ID: %d", item.ID, input.PanAccountID) elapsedTime := time.Since(startTime)
utils.InfoWithFields(map[string]interface{}{
"task_item_id": item.ID,
"account_id": input.PanAccountID,
"duration_ms": elapsedTime.Milliseconds(),
}, "扩容任务项处理完成: %d, 账号ID: %d, 总耗时: %v", item.ID, input.PanAccountID, elapsedTime)
return nil return nil
} }
// validateInput 验证输入数据 // validateInput 验证输入数据
func (ep *ExpansionProcessor) validateInput(input *ExpansionInput) error { func (ep *ExpansionProcessor) validateInput(input *ExpansionInput) error {
startTime := utils.GetCurrentTime()
if input.PanAccountID == 0 { if input.PanAccountID == 0 {
utils.Error("账号ID验证失败账号ID不能为空耗时: %v", time.Since(startTime))
return fmt.Errorf("账号ID不能为空") return fmt.Errorf("账号ID不能为空")
} }
utils.Debug("输入数据验证完成,耗时: %v", time.Since(startTime))
return nil return nil
} }
// checkExpansionExists 检查账号是否已经扩容过 // checkExpansionExists 检查账号是否已经扩容过
func (ep *ExpansionProcessor) checkExpansionExists(panAccountID uint) (bool, error) { func (ep *ExpansionProcessor) checkExpansionExists(panAccountID uint) (bool, error) {
startTime := utils.GetCurrentTime()
// 查询所有expansion类型的任务 // 查询所有expansion类型的任务
tasksStart := utils.GetCurrentTime()
tasks, _, err := ep.repoMgr.TaskRepository.GetList(1, 1000, "expansion", "completed") tasks, _, err := ep.repoMgr.TaskRepository.GetList(1, 1000, "expansion", "completed")
tasksDuration := time.Since(tasksStart)
if err != nil { if err != nil {
utils.Error("获取扩容任务列表失败: %v耗时: %v", err, tasksDuration)
return false, fmt.Errorf("获取扩容任务列表失败: %v", err) return false, fmt.Errorf("获取扩容任务列表失败: %v", err)
} }
utils.Debug("获取扩容任务列表完成,找到 %d 个任务,耗时: %v", len(tasks), tasksDuration)
// 检查每个任务的配置中是否包含该账号ID // 检查每个任务的配置中是否包含该账号ID
checkStart := utils.GetCurrentTime()
for _, task := range tasks { for _, task := range tasks {
if task.Config != "" { if task.Config != "" {
var taskConfig map[string]interface{} var taskConfig map[string]interface{}
@@ -162,6 +208,8 @@ func (ep *ExpansionProcessor) checkExpansionExists(panAccountID uint) (bool, err
// 找到了该账号的扩容任务,检查任务状态 // 找到了该账号的扩容任务,检查任务状态
if task.Status == "completed" { if task.Status == "completed" {
// 如果任务已完成,说明已经扩容过 // 如果任务已完成,说明已经扩容过
checkDuration := time.Since(checkStart)
utils.Debug("检查扩容记录完成,账号已扩容,耗时: %v", checkDuration)
return true, nil return true, nil
} }
} }
@@ -169,40 +217,63 @@ func (ep *ExpansionProcessor) checkExpansionExists(panAccountID uint) (bool, err
} }
} }
} }
checkDuration := time.Since(checkStart)
utils.Debug("检查扩容记录完成,账号未扩容,耗时: %v", checkDuration)
totalDuration := time.Since(startTime)
utils.Debug("检查扩容记录完成,账号未扩容,总耗时: %v", totalDuration)
return false, nil return false, nil
} }
// checkAccountType 检查账号类型只支持quark账号 // checkAccountType 检查账号类型只支持quark账号
func (ep *ExpansionProcessor) checkAccountType(panAccountID uint) error { func (ep *ExpansionProcessor) checkAccountType(panAccountID uint) error {
startTime := utils.GetCurrentTime()
// 获取账号信息 // 获取账号信息
accountStart := utils.GetCurrentTime()
cks, err := ep.repoMgr.CksRepository.FindByID(panAccountID) cks, err := ep.repoMgr.CksRepository.FindByID(panAccountID)
accountDuration := time.Since(accountStart)
if err != nil { if err != nil {
utils.Error("获取账号信息失败: %v耗时: %v", err, accountDuration)
return fmt.Errorf("获取账号信息失败: %v", err) return fmt.Errorf("获取账号信息失败: %v", err)
} }
utils.Debug("获取账号信息完成,耗时: %v", accountDuration)
// 检查是否为quark账号 // 检查是否为quark账号
serviceCheckStart := utils.GetCurrentTime()
if cks.ServiceType != "quark" { if cks.ServiceType != "quark" {
serviceCheckDuration := time.Since(serviceCheckStart)
utils.Error("账号类型检查失败,当前账号类型: %s耗时: %v", cks.ServiceType, serviceCheckDuration)
return fmt.Errorf("只支持quark账号扩容当前账号类型: %s", cks.ServiceType) return fmt.Errorf("只支持quark账号扩容当前账号类型: %s", cks.ServiceType)
} }
serviceCheckDuration := time.Since(serviceCheckStart)
utils.Debug("账号类型检查完成为quark账号耗时: %v", serviceCheckDuration)
totalDuration := time.Since(startTime)
utils.Debug("账号类型检查完成,总耗时: %v", totalDuration)
return nil return nil
} }
// performExpansion 执行扩容操作 // performExpansion 执行扩容操作
func (ep *ExpansionProcessor) performExpansion(ctx context.Context, panAccountID uint, dataSource map[string]interface{}) ([]TransferredResource, error) { func (ep *ExpansionProcessor) performExpansion(ctx context.Context, panAccountID uint, dataSource map[string]interface{}) ([]TransferredResource, error) {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
startTime := utils.GetCurrentTime()
utils.Info("执行扩容操作账号ID: %d, 数据源: %v", panAccountID, dataSource) utils.Info("执行扩容操作账号ID: %d, 数据源: %v", panAccountID, dataSource)
transferred := []TransferredResource{} transferred := []TransferredResource{}
// 获取账号信息 // 获取账号信息
accountStart := utils.GetCurrentTime()
account, err := ep.repoMgr.CksRepository.FindByID(panAccountID) account, err := ep.repoMgr.CksRepository.FindByID(panAccountID)
accountDuration := time.Since(accountStart)
if err != nil { if err != nil {
utils.Error("获取账号信息失败: %v耗时: %v", err, accountDuration)
return nil, fmt.Errorf("获取账号信息失败: %v", err) return nil, fmt.Errorf("获取账号信息失败: %v", err)
} }
utils.Debug("获取账号信息完成,耗时: %v", accountDuration)
// 创建网盘服务工厂 // 创建网盘服务工厂
serviceStart := utils.GetCurrentTime()
factory := pan.NewPanFactory() factory := pan.NewPanFactory()
service, err := factory.CreatePanServiceByType(pan.Quark, &pan.PanConfig{ service, err := factory.CreatePanServiceByType(pan.Quark, &pan.PanConfig{
URL: "", URL: "",
@@ -210,10 +281,13 @@ func (ep *ExpansionProcessor) performExpansion(ctx context.Context, panAccountID
IsType: 0, IsType: 0,
Cookie: account.Ck, Cookie: account.Ck,
}) })
serviceDuration := time.Since(serviceStart)
if err != nil { if err != nil {
utils.Error("创建网盘服务失败: %v耗时: %v", err, serviceDuration)
return nil, fmt.Errorf("创建网盘服务失败: %v", err) return nil, fmt.Errorf("创建网盘服务失败: %v", err)
} }
service.SetCKSRepository(ep.repoMgr.CksRepository, *account) service.SetCKSRepository(ep.repoMgr.CksRepository, *account)
utils.Debug("创建网盘服务完成,耗时: %v", serviceDuration)
// 定义扩容分类列表(按优先级排序) // 定义扩容分类列表(按优先级排序)
categories := []string{ categories := []string{
@@ -246,11 +320,14 @@ func (ep *ExpansionProcessor) performExpansion(ctx context.Context, panAccountID
utils.Info("开始处理分类: %s", category) utils.Info("开始处理分类: %s", category)
// 获取该分类的资源 // 获取该分类的资源
resourcesStart := utils.GetCurrentTime()
resources, err := ep.getHotResources(category) resources, err := ep.getHotResources(category)
resourcesDuration := time.Since(resourcesStart)
if err != nil { if err != nil {
utils.Error("获取分类 %s 的资源失败: %v", category, err) utils.Error("获取分类 %s 的资源失败: %v耗时: %v", category, err, resourcesDuration)
continue continue
} }
utils.Debug("获取分类 %s 的资源完成,耗时: %v", category, resourcesDuration)
if len(resources) == 0 { if len(resources) == 0 {
utils.Info("分类 %s 没有可用资源,跳过", category) utils.Info("分类 %s 没有可用资源,跳过", category)
@@ -269,11 +346,14 @@ func (ep *ExpansionProcessor) performExpansion(ctx context.Context, panAccountID
} }
// 检查是否还有存储空间 // 检查是否还有存储空间
storageCheckStart := utils.GetCurrentTime()
hasSpace, err := ep.checkStorageSpace(service, &account.Ck) hasSpace, err := ep.checkStorageSpace(service, &account.Ck)
storageCheckDuration := time.Since(storageCheckStart)
if err != nil { if err != nil {
utils.Error("检查存储空间失败: %v", err) utils.Error("检查存储空间失败: %v耗时: %v", err, storageCheckDuration)
return transferred, fmt.Errorf("检查存储空间失败: %v", err) return transferred, fmt.Errorf("检查存储空间失败: %v", err)
} }
utils.Debug("检查存储空间完成,耗时: %v", storageCheckDuration)
if !hasSpace { if !hasSpace {
utils.Info("存储空间不足,停止扩容,但保存已转存的资源") utils.Info("存储空间不足,停止扩容,但保存已转存的资源")
@@ -282,24 +362,30 @@ func (ep *ExpansionProcessor) performExpansion(ctx context.Context, panAccountID
} }
// 获取资源 , dataSourceType, thirdPartyURL // 获取资源 , dataSourceType, thirdPartyURL
resourceGetStart := utils.GetCurrentTime()
resource, err := ep.getResourcesByHot(resource, dataSourceType, thirdPartyURL, *account, service) resource, err := ep.getResourcesByHot(resource, dataSourceType, thirdPartyURL, *account, service)
resourceGetDuration := time.Since(resourceGetStart)
if resource == nil || err != nil { if resource == nil || err != nil {
if resource != nil { if resource != nil {
utils.Error("获取资源失败: %s, 错误: %v", resource.Title, err) utils.Error("获取资源失败: %s, 错误: %v耗时: %v", resource.Title, err, resourceGetDuration)
} else { } else {
utils.Error("获取资源失败, 错误: %v", err) utils.Error("获取资源失败, 错误: %v耗时: %v", err, resourceGetDuration)
} }
totalFailed++ totalFailed++
continue continue
} }
utils.Debug("获取资源完成,耗时: %v", resourceGetDuration)
// 执行转存 // 执行转存
transferStart := utils.GetCurrentTime()
saveURL, err := ep.transferResource(ctx, service, resource, *account) saveURL, err := ep.transferResource(ctx, service, resource, *account)
transferDuration := time.Since(transferStart)
if err != nil { if err != nil {
utils.Error("转存资源失败: %s, 错误: %v", resource.Title, err) utils.Error("转存资源失败: %s, 错误: %v耗时: %v", resource.Title, err, transferDuration)
totalFailed++ totalFailed++
continue continue
} }
utils.Debug("转存资源完成,耗时: %v", transferDuration)
// 随机休眠1-3秒避免请求过于频繁 // 随机休眠1-3秒避免请求过于频繁
sleepDuration := time.Duration(rand.Intn(3)+1) * time.Second sleepDuration := time.Duration(rand.Intn(3)+1) * time.Second
@@ -324,7 +410,8 @@ func (ep *ExpansionProcessor) performExpansion(ctx context.Context, panAccountID
utils.Info("分类 %s 处理完成,转存 %d 个资源", category, transferredCount) utils.Info("分类 %s 处理完成,转存 %d 个资源", category, transferredCount)
} }
utils.Info("扩容完成,总共转存: %d 个资源,失败: %d 个资源", totalTransferred, totalFailed) elapsedTime := time.Since(startTime)
utils.Info("扩容完成,总共转存: %d 个资源,失败: %d 个资源,总耗时: %v", totalTransferred, totalFailed, elapsedTime)
return transferred, nil return transferred, nil
} }
@@ -335,27 +422,45 @@ func (ep *ExpansionProcessor) getResourcesByHot(
entity entity.Cks, entity entity.Cks,
service pan.PanService, service pan.PanService,
) (*entity.Resource, error) { ) (*entity.Resource, error) {
startTime := utils.GetCurrentTime()
if dataSourceType == "third-party" && thirdPartyURL != "" { if dataSourceType == "third-party" && thirdPartyURL != "" {
// 从第三方API获取资源 // 从第三方API获取资源
return ep.getResourcesFromThirdPartyAPI(resource, thirdPartyURL) thirdPartyStart := utils.GetCurrentTime()
result, err := ep.getResourcesFromThirdPartyAPI(resource, thirdPartyURL)
thirdPartyDuration := time.Since(thirdPartyStart)
utils.Debug("从第三方API获取资源完成耗时: %v", thirdPartyDuration)
return result, err
} }
// 从内部数据库获取资源 // 从内部数据库获取资源
return ep.getResourcesFromInternalDB(resource, entity, service) internalStart := utils.GetCurrentTime()
result, err := ep.getResourcesFromInternalDB(resource, entity, service)
internalDuration := time.Since(internalStart)
utils.Debug("从内部数据库获取资源完成,耗时: %v", internalDuration)
totalDuration := time.Since(startTime)
utils.Debug("获取资源完成: %s总耗时: %v", resource.Title, totalDuration)
return result, err
} }
// getResourcesFromInternalDB 根据 HotDrama 的title 获取数据库中资源,并且资源的类型和 account 的资源类型一致 // getResourcesFromInternalDB 根据 HotDrama 的title 获取数据库中资源,并且资源的类型和 account 的资源类型一致
func (ep *ExpansionProcessor) getResourcesFromInternalDB(HotDrama *entity.HotDrama, account entity.Cks, service pan.PanService) (*entity.Resource, error) { func (ep *ExpansionProcessor) getResourcesFromInternalDB(HotDrama *entity.HotDrama, account entity.Cks, service pan.PanService) (*entity.Resource, error) {
startTime := utils.GetCurrentTime()
// 修改配置 isType = 1 只检测,不转存 // 修改配置 isType = 1 只检测,不转存
configStart := utils.GetCurrentTime()
service.UpdateConfig(&pan.PanConfig{ service.UpdateConfig(&pan.PanConfig{
URL: "", URL: "",
ExpiredType: 0, ExpiredType: 0,
IsType: 1, IsType: 1,
Cookie: account.Ck, Cookie: account.Ck,
}) })
utils.Debug("更新服务配置完成,耗时: %v", time.Since(configStart))
panID := account.PanID panID := account.PanID
// 1. 搜索标题 // 1. 搜索标题
searchStart := utils.GetCurrentTime()
params := map[string]interface{}{ params := map[string]interface{}{
"search": HotDrama.Title, "search": HotDrama.Title,
"pan_id": panID, "pan_id": panID,
@@ -364,11 +469,15 @@ func (ep *ExpansionProcessor) getResourcesFromInternalDB(HotDrama *entity.HotDra
"page_size": 10, "page_size": 10,
} }
resources, _, err := ep.repoMgr.ResourceRepository.SearchWithFilters(params) resources, _, err := ep.repoMgr.ResourceRepository.SearchWithFilters(params)
searchDuration := time.Since(searchStart)
if err != nil { if err != nil {
utils.Error("搜索资源失败: %v耗时: %v", err, searchDuration)
return nil, fmt.Errorf("搜索资源失败: %v", err) return nil, fmt.Errorf("搜索资源失败: %v", err)
} }
utils.Debug("搜索资源完成,找到 %d 个资源,耗时: %v", len(resources), searchDuration)
// 检查结果是否有效,通过服务验证 // 检查结果是否有效,通过服务验证
validateStart := utils.GetCurrentTime()
for _, res := range resources { for _, res := range resources {
if res.IsValid && res.URL != "" { if res.IsValid && res.URL != "" {
// 使用服务验证资源是否可转存 // 使用服务验证资源是否可转存
@@ -376,38 +485,59 @@ func (ep *ExpansionProcessor) getResourcesFromInternalDB(HotDrama *entity.HotDra
if shareID != "" { if shareID != "" {
result, err := service.Transfer(shareID) result, err := service.Transfer(shareID)
if err == nil && result != nil && result.Success { if err == nil && result != nil && result.Success {
validateDuration := time.Since(validateStart)
utils.Debug("验证资源成功: %s耗时: %v", res.Title, validateDuration)
return &res, nil return &res, nil
} }
} }
} }
} }
validateDuration := time.Since(validateStart)
utils.Debug("验证资源完成,未找到有效资源,耗时: %v", validateDuration)
totalDuration := time.Since(startTime)
utils.Debug("从内部数据库获取资源完成: %s总耗时: %v", HotDrama.Title, totalDuration)
// 3. 没有有效资源,返回错误信息 // 3. 没有有效资源,返回错误信息
return nil, fmt.Errorf("未找到有效的资源") return nil, fmt.Errorf("未找到有效的资源")
} }
// getResourcesFromInternalDB 从内部数据库获取资源 // getResourcesFromInternalDB 从内部数据库获取资源
func (ep *ExpansionProcessor) getHotResources(category string) ([]*entity.HotDrama, error) { func (ep *ExpansionProcessor) getHotResources(category string) ([]*entity.HotDrama, error) {
startTime := utils.GetCurrentTime()
// 获取该分类下sub_type为"排行"的资源 // 获取该分类下sub_type为"排行"的资源
rankedStart := utils.GetCurrentTime()
dramas, _, err := ep.repoMgr.HotDramaRepository.FindByCategoryAndSubType(category, "排行", 1, 20) dramas, _, err := ep.repoMgr.HotDramaRepository.FindByCategoryAndSubType(category, "排行", 1, 20)
rankedDuration := time.Since(rankedStart)
if err != nil { if err != nil {
utils.Error("获取分类 %s 的排行资源失败: %v耗时: %v", category, err, rankedDuration)
return nil, fmt.Errorf("获取分类 %s 的资源失败: %v", category, err) return nil, fmt.Errorf("获取分类 %s 的资源失败: %v", category, err)
} }
utils.Debug("获取分类 %s 的排行资源完成,找到 %d 个资源,耗时: %v", category, len(dramas), rankedDuration)
// 如果没有找到"排行"类型的资源,尝试获取该分类下的所有资源 // 如果没有找到"排行"类型的资源,尝试获取该分类下的所有资源
if len(dramas) == 0 { if len(dramas) == 0 {
allStart := utils.GetCurrentTime()
dramas, _, err = ep.repoMgr.HotDramaRepository.FindByCategory(category, 1, 20) dramas, _, err = ep.repoMgr.HotDramaRepository.FindByCategory(category, 1, 20)
allDuration := time.Since(allStart)
if err != nil { if err != nil {
utils.Error("获取分类 %s 的所有资源失败: %v耗时: %v", category, err, allDuration)
return nil, fmt.Errorf("获取分类 %s 的资源失败: %v", category, err) return nil, fmt.Errorf("获取分类 %s 的资源失败: %v", category, err)
} }
utils.Debug("获取分类 %s 的所有资源完成,找到 %d 个资源,耗时: %v", category, len(dramas), allDuration)
} }
// 转换为指针数组 // 转换为指针数组
convertStart := utils.GetCurrentTime()
result := make([]*entity.HotDrama, len(dramas)) result := make([]*entity.HotDrama, len(dramas))
for i := range dramas { for i := range dramas {
result[i] = &dramas[i] result[i] = &dramas[i]
} }
convertDuration := time.Since(convertStart)
utils.Debug("转换资源数组完成,耗时: %v", convertDuration)
totalDuration := time.Since(startTime)
utils.Debug("获取热门资源完成: 分类 %s总数 %d总耗时: %v", category, len(result), totalDuration)
return result, nil return result, nil
} }
@@ -423,50 +553,69 @@ func (ep *ExpansionProcessor) getResourcesFromThirdPartyAPI(resource *entity.Hot
// checkStorageSpace 检查存储空间是否足够 // checkStorageSpace 检查存储空间是否足够
func (ep *ExpansionProcessor) checkStorageSpace(service pan.PanService, ck *string) (bool, error) { func (ep *ExpansionProcessor) checkStorageSpace(service pan.PanService, ck *string) (bool, error) {
startTime := utils.GetCurrentTime()
userInfoStart := utils.GetCurrentTime()
userInfo, err := service.GetUserInfo(ck) userInfo, err := service.GetUserInfo(ck)
userInfoDuration := time.Since(userInfoStart)
if err != nil { if err != nil {
utils.Error("获取用户信息失败: %v", err) utils.Error("获取用户信息失败: %v耗时: %v", err, userInfoDuration)
// 如果无法获取用户信息,假设还有空间继续 // 如果无法获取用户信息,假设还有空间继续
return true, nil return true, nil
} }
utils.Debug("获取用户信息完成,耗时: %v", userInfoDuration)
// 检查是否还有足够的空间保留至少10GB空间 // 检查是否还有足够的空间保留至少10GB空间
const reservedSpaceGB = 100 const reservedSpaceGB = 100
reservedSpaceBytes := int64(reservedSpaceGB * 1024 * 1024 * 1024) reservedSpaceBytes := int64(reservedSpaceGB * 1024 * 1024 * 1024)
if userInfo.TotalSpace-userInfo.UsedSpace <= reservedSpaceBytes { if userInfo.TotalSpace-userInfo.UsedSpace <= reservedSpaceBytes {
utils.Info("存储空间不足,已使用: %d bytes总容量: %d bytes", utils.Info("存储空间不足,已使用: %d bytes总容量: %d bytes,检查耗时: %v",
userInfo.UsedSpace, userInfo.TotalSpace) userInfo.UsedSpace, userInfo.TotalSpace, time.Since(startTime))
return false, nil return false, nil
} }
totalDuration := time.Since(startTime)
utils.Debug("存储空间检查完成,有足够空间,耗时: %v", totalDuration)
return true, nil return true, nil
} }
// transferResource 执行单个资源的转存 // transferResource 执行单个资源的转存
func (ep *ExpansionProcessor) transferResource(ctx context.Context, service pan.PanService, res *entity.Resource, account entity.Cks) (string, error) { func (ep *ExpansionProcessor) transferResource(ctx context.Context, service pan.PanService, res *entity.Resource, account entity.Cks) (string, error) {
startTime := utils.GetCurrentTime()
// 修改配置 isType = 0 转存 // 修改配置 isType = 0 转存
configStart := utils.GetCurrentTime()
service.UpdateConfig(&pan.PanConfig{ service.UpdateConfig(&pan.PanConfig{
URL: "", URL: "",
ExpiredType: 0, ExpiredType: 0,
IsType: 0, IsType: 0,
Cookie: account.Ck, Cookie: account.Ck,
}) })
utils.Debug("更新服务配置完成,耗时: %v", time.Since(configStart))
// 如果没有URL跳过转存 // 如果没有URL跳过转存
if res.URL == "" { if res.URL == "" {
utils.Error("资源 %s 没有有效的URL", res.URL)
return "", fmt.Errorf("资源 %s 没有有效的URL", res.URL) return "", fmt.Errorf("资源 %s 没有有效的URL", res.URL)
} }
// 提取分享ID // 提取分享ID
extractStart := utils.GetCurrentTime()
shareID, _ := pan.ExtractShareId(res.URL) shareID, _ := pan.ExtractShareId(res.URL)
extractDuration := time.Since(extractStart)
if shareID == "" { if shareID == "" {
utils.Error("无法从URL %s 提取分享ID耗时: %v", res.URL, extractDuration)
return "", fmt.Errorf("无法从URL %s 提取分享ID", res.URL) return "", fmt.Errorf("无法从URL %s 提取分享ID", res.URL)
} }
utils.Debug("提取分享ID完成: %s耗时: %v", shareID, extractDuration)
// 执行转存 // 执行转存
transferStart := utils.GetCurrentTime()
result, err := service.Transfer(shareID) result, err := service.Transfer(shareID)
transferDuration := time.Since(transferStart)
if err != nil { if err != nil {
utils.Error("转存失败: %v耗时: %v", err, transferDuration)
return "", fmt.Errorf("转存失败: %v", err) return "", fmt.Errorf("转存失败: %v", err)
} }
@@ -475,10 +624,12 @@ func (ep *ExpansionProcessor) transferResource(ctx context.Context, service pan.
if result != nil { if result != nil {
errorMsg = result.Message errorMsg = result.Message
} }
utils.Error("转存结果失败: %s耗时: %v", errorMsg, time.Since(transferStart))
return "", fmt.Errorf("转存失败: %s", errorMsg) return "", fmt.Errorf("转存失败: %s", errorMsg)
} }
// 提取转存链接 // 提取转存链接
extractURLStart := utils.GetCurrentTime()
var saveURL string var saveURL string
if result.Data != nil { if result.Data != nil {
if data, ok := result.Data.(map[string]interface{}); ok { if data, ok := result.Data.(map[string]interface{}); ok {
@@ -490,11 +641,14 @@ func (ep *ExpansionProcessor) transferResource(ctx context.Context, service pan.
if saveURL == "" { if saveURL == "" {
saveURL = result.ShareURL saveURL = result.ShareURL
} }
if saveURL == "" { if saveURL == "" {
extractURLDuration := time.Since(extractURLStart)
utils.Error("转存成功但未获取到分享链接,耗时: %v", extractURLDuration)
return "", fmt.Errorf("转存成功但未获取到分享链接") return "", fmt.Errorf("转存成功但未获取到分享链接")
} }
totalDuration := time.Since(startTime)
utils.Debug("转存资源完成: %s -> %s总耗时: %v", res.Title, saveURL, totalDuration)
return saveURL, nil return saveURL, nil
} }

View File

@@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"sync" "sync"
"time"
"github.com/ctwj/urldb/db/entity" "github.com/ctwj/urldb/db/entity"
"github.com/ctwj/urldb/db/repo" "github.com/ctwj/urldb/db/repo"
@@ -90,7 +91,9 @@ func (tm *TaskManager) StartTask(taskID uint) error {
// 启动后台任务 // 启动后台任务
go tm.processTask(ctx, task, processor) go tm.processTask(ctx, task, processor)
utils.Info("StartTask: 任务 %d 启动成功", taskID) utils.InfoWithFields(map[string]interface{}{
"task_id": taskID,
}, "StartTask: 任务 %d 启动成功", taskID)
return nil return nil
} }
@@ -185,14 +188,19 @@ func (tm *TaskManager) StopTask(taskID uint) error {
// processTask 处理任务 // processTask 处理任务
func (tm *TaskManager) processTask(ctx context.Context, task *entity.Task, processor TaskProcessor) { func (tm *TaskManager) processTask(ctx context.Context, task *entity.Task, processor TaskProcessor) {
startTime := utils.GetCurrentTime()
defer func() { defer func() {
tm.mu.Lock() tm.mu.Lock()
delete(tm.running, task.ID) delete(tm.running, task.ID)
tm.mu.Unlock() tm.mu.Unlock()
utils.Debug("processTask: 任务 %d 处理完成,清理资源", task.ID) elapsedTime := time.Since(startTime)
utils.Info("processTask: 任务 %d 处理完成,耗时: %v清理资源", task.ID, elapsedTime)
}() }()
utils.Debug("processTask: 开始处理任务: %d, 类型: %s", task.ID, task.Type) utils.InfoWithFields(map[string]interface{}{
"task_id": task.ID,
"task_type": task.Type,
}, "processTask: 开始处理任务: %d, 类型: %s", task.ID, task.Type)
// 更新任务状态为运行中 // 更新任务状态为运行中
err := tm.repoMgr.TaskRepository.UpdateStatus(task.ID, "running") err := tm.repoMgr.TaskRepository.UpdateStatus(task.ID, "running")
@@ -208,7 +216,9 @@ func (tm *TaskManager) processTask(ctx context.Context, task *entity.Task, proce
} }
// 获取任务项统计信息,用于计算正确的进度 // 获取任务项统计信息,用于计算正确的进度
statsStart := utils.GetCurrentTime()
stats, err := tm.repoMgr.TaskItemRepository.GetStatsByTaskID(task.ID) stats, err := tm.repoMgr.TaskItemRepository.GetStatsByTaskID(task.ID)
statsDuration := time.Since(statsStart)
if err != nil { if err != nil {
utils.Error("获取任务项统计失败: %v", err) utils.Error("获取任务项统计失败: %v", err)
stats = map[string]int{ stats = map[string]int{
@@ -218,14 +228,20 @@ func (tm *TaskManager) processTask(ctx context.Context, task *entity.Task, proce
"completed": 0, "completed": 0,
"failed": 0, "failed": 0,
} }
} else {
utils.Debug("获取任务项统计完成,耗时: %v", statsDuration)
} }
// 获取待处理的任务项 // 获取待处理的任务项
itemsStart := utils.GetCurrentTime()
items, err := tm.repoMgr.TaskItemRepository.GetByTaskIDAndStatus(task.ID, "pending") items, err := tm.repoMgr.TaskItemRepository.GetByTaskIDAndStatus(task.ID, "pending")
itemsDuration := time.Since(itemsStart)
if err != nil { if err != nil {
utils.Error("获取任务项失败: %v", err) utils.Error("获取任务项失败: %v", err)
tm.markTaskFailed(task.ID, fmt.Sprintf("获取任务项失败: %v", err)) tm.markTaskFailed(task.ID, fmt.Sprintf("获取任务项失败: %v", err))
return return
} else {
utils.Debug("获取任务项完成,数量: %d耗时: %v", len(items), itemsDuration)
} }
// 计算总任务项数和已完成的项数 // 计算总任务项数和已完成的项数
@@ -236,10 +252,14 @@ func (tm *TaskManager) processTask(ctx context.Context, task *entity.Task, proce
// 如果当前批次有处理中的任务项重置它们为pending状态服务器重启恢复 // 如果当前批次有处理中的任务项重置它们为pending状态服务器重启恢复
if processingItems > 0 { if processingItems > 0 {
utils.Debug("任务 %d 发现 %d 个处理中的任务项重置为pending状态", task.ID, processingItems) utils.Info("任务 %d 发现 %d 个处理中的任务项重置为pending状态", task.ID, processingItems)
resetStart := utils.GetCurrentTime()
err = tm.repoMgr.TaskItemRepository.ResetProcessingItems(task.ID) err = tm.repoMgr.TaskItemRepository.ResetProcessingItems(task.ID)
resetDuration := time.Since(resetStart)
if err != nil { if err != nil {
utils.Error("重置处理中任务项失败: %v", err) utils.Error("重置处理中任务项失败: %v", err)
} else {
utils.Debug("重置处理中任务项完成,耗时: %v", resetDuration)
} }
// 重新获取待处理的任务项 // 重新获取待处理的任务项
items, err = tm.repoMgr.TaskItemRepository.GetByTaskIDAndStatus(task.ID, "pending") items, err = tm.repoMgr.TaskItemRepository.GetByTaskIDAndStatus(task.ID, "pending")
@@ -258,21 +278,35 @@ func (tm *TaskManager) processTask(ctx context.Context, task *entity.Task, proce
utils.Debug("任务 %d 统计信息: 总计=%d, 已完成=%d, 已失败=%d, 待处理=%d", utils.Debug("任务 %d 统计信息: 总计=%d, 已完成=%d, 已失败=%d, 待处理=%d",
task.ID, totalItems, completedItems, failedItems, currentBatchItems) task.ID, totalItems, completedItems, failedItems, currentBatchItems)
for _, item := range items { // 记录处理开始时间
batchStartTime := utils.GetCurrentTime()
for i, item := range items {
select { select {
case <-ctx.Done(): case <-ctx.Done():
utils.Debug("任务 %d 被取消", task.ID) utils.Debug("任务 %d 被取消", task.ID)
return return
default: default:
// 记录单个任务项处理开始时间
itemStartTime := utils.GetCurrentTime()
// 处理单个任务项 // 处理单个任务项
err := tm.processTaskItem(ctx, task.ID, item, processor) err := tm.processTaskItem(ctx, task.ID, item, processor)
processedItems++ processedItems++
// 记录单个任务项处理耗时
itemDuration := time.Since(itemStartTime)
if err != nil { if err != nil {
failedItems++ failedItems++
utils.Error("处理任务项 %d 失败: %v", item.ID, err) utils.ErrorWithFields(map[string]interface{}{
"task_item_id": item.ID,
"error": err.Error(),
"duration_ms": itemDuration.Milliseconds(),
}, "处理任务项 %d 失败: %v耗时: %v", item.ID, err, itemDuration)
} else { } else {
successItems++ successItems++
utils.Info("处理任务项 %d 成功,耗时: %v", item.ID, itemDuration)
} }
// 更新任务进度(基于总任务项数) // 更新任务进度(基于总任务项数)
@@ -280,9 +314,21 @@ func (tm *TaskManager) processTask(ctx context.Context, task *entity.Task, proce
progress := float64(processedItems) / float64(totalItems) * 100 progress := float64(processedItems) / float64(totalItems) * 100
tm.updateTaskProgress(task.ID, progress, processedItems, successItems, failedItems) tm.updateTaskProgress(task.ID, progress, processedItems, successItems, failedItems)
} }
// 每处理10个任务项记录一次批处理进度
if (i+1)%10 == 0 || i == len(items)-1 {
batchDuration := time.Since(batchStartTime)
utils.Info("任务 %d 批处理进度: 已处理 %d/%d 项,成功 %d 项,失败 %d 项,当前批处理耗时: %v",
task.ID, processedItems, totalItems, successItems, failedItems, batchDuration)
}
} }
} }
// 记录整个批处理耗时
batchDuration := time.Since(batchStartTime)
utils.Info("任务 %d 批处理完成: 总计 %d 项,成功 %d 项,失败 %d 项,总耗时: %v",
task.ID, len(items), successItems, failedItems, batchDuration)
// 任务完成 // 任务完成
status := "completed" status := "completed"
message := fmt.Sprintf("任务完成,共处理 %d 项,成功 %d 项,失败 %d 项", processedItems, successItems, failedItems) message := fmt.Sprintf("任务完成,共处理 %d 项,成功 %d 项,失败 %d 项", processedItems, successItems, failedItems)
@@ -308,25 +354,41 @@ func (tm *TaskManager) processTask(ctx context.Context, task *entity.Task, proce
} }
} }
utils.Info("任务 %d 处理完成: %s", task.ID, message) utils.InfoWithFields(map[string]interface{}{
"task_id": task.ID,
"message": message,
}, "任务 %d 处理完成: %s", task.ID, message)
} }
// processTaskItem 处理单个任务项 // processTaskItem 处理单个任务项
func (tm *TaskManager) processTaskItem(ctx context.Context, taskID uint, item *entity.TaskItem, processor TaskProcessor) error { func (tm *TaskManager) processTaskItem(ctx context.Context, taskID uint, item *entity.TaskItem, processor TaskProcessor) error {
itemStartTime := utils.GetCurrentTime()
utils.Debug("开始处理任务项: %d (任务ID: %d)", item.ID, taskID)
// 更新任务项状态为处理中 // 更新任务项状态为处理中
updateStart := utils.GetCurrentTime()
err := tm.repoMgr.TaskItemRepository.UpdateStatus(item.ID, "processing") err := tm.repoMgr.TaskItemRepository.UpdateStatus(item.ID, "processing")
updateDuration := time.Since(updateStart)
if err != nil { if err != nil {
utils.Error("更新任务项状态失败: %v耗时: %v", err, updateDuration)
return fmt.Errorf("更新任务项状态失败: %v", err) return fmt.Errorf("更新任务项状态失败: %v", err)
} else {
utils.Debug("更新任务项状态为处理中完成,耗时: %v", updateDuration)
} }
// 处理任务项 // 处理任务项
processStart := utils.GetCurrentTime()
err = processor.Process(ctx, taskID, item) err = processor.Process(ctx, taskID, item)
processDuration := time.Since(processStart)
if err != nil { if err != nil {
// 处理失败 // 处理失败
utils.Error("处理任务项 %d 失败: %v处理耗时: %v", item.ID, err, processDuration)
outputData := map[string]interface{}{ outputData := map[string]interface{}{
"error": err.Error(), "error": err.Error(),
"time": utils.GetCurrentTime(), "time": utils.GetCurrentTime(),
"duration_ms": processDuration.Milliseconds(),
} }
outputJSON, _ := json.Marshal(outputData) outputJSON, _ := json.Marshal(outputData)
@@ -338,25 +400,49 @@ func (tm *TaskManager) processTaskItem(ctx context.Context, taskID uint, item *e
} }
// 处理成功 // 处理成功
utils.Info("处理任务项 %d 成功,处理耗时: %v", item.ID, processDuration)
// 如果处理器已经设置了 output_data比如 ExpansionProcessor则不覆盖 // 如果处理器已经设置了 output_data比如 ExpansionProcessor则不覆盖
var outputJSON string var outputJSON string
if item.OutputData == "" { if item.OutputData == "" {
outputData := map[string]interface{}{ outputData := map[string]interface{}{
"success": true, "success": true,
"time": utils.GetCurrentTime(), "time": utils.GetCurrentTime(),
"duration_ms": processDuration.Milliseconds(),
} }
outputBytes, _ := json.Marshal(outputData) outputBytes, _ := json.Marshal(outputData)
outputJSON = string(outputBytes) outputJSON = string(outputBytes)
} else { } else {
// 使用处理器设置的 output_data // 使用处理器设置的 output_data,并添加处理时间信息
outputJSON = item.OutputData var existingOutput map[string]interface{}
if json.Unmarshal([]byte(item.OutputData), &existingOutput) == nil {
existingOutput["duration_ms"] = processDuration.Milliseconds()
outputBytes, _ := json.Marshal(existingOutput)
outputJSON = string(outputBytes)
} else {
// 如果无法解析现有输出,保留原样并添加时间信息
outputData := map[string]interface{}{
"original_output": item.OutputData,
"success": true,
"time": utils.GetCurrentTime(),
"duration_ms": processDuration.Milliseconds(),
}
outputBytes, _ := json.Marshal(outputData)
outputJSON = string(outputBytes)
}
} }
updateSuccessStart := utils.GetCurrentTime()
err = tm.repoMgr.TaskItemRepository.UpdateStatusAndOutput(item.ID, "completed", outputJSON) err = tm.repoMgr.TaskItemRepository.UpdateStatusAndOutput(item.ID, "completed", outputJSON)
updateSuccessDuration := time.Since(updateSuccessStart)
if err != nil { if err != nil {
utils.Error("更新成功任务项状态失败: %v", err) utils.Error("更新成功任务项状态失败: %v耗时: %v", err, updateSuccessDuration)
} else {
utils.Debug("更新成功任务项状态完成,耗时: %v", updateSuccessDuration)
} }
itemDuration := time.Since(itemStartTime)
utils.Debug("任务项 %d 处理完成,总耗时: %v", item.ID, itemDuration)
return nil return nil
} }

View File

@@ -51,20 +51,42 @@ type TransferOutput struct {
// Process 处理转存任务项 // Process 处理转存任务项
func (tp *TransferProcessor) Process(ctx context.Context, taskID uint, item *entity.TaskItem) error { func (tp *TransferProcessor) Process(ctx context.Context, taskID uint, item *entity.TaskItem) error {
utils.Info("开始处理转存任务项: %d", item.ID) startTime := utils.GetCurrentTime()
utils.InfoWithFields(map[string]interface{}{
"task_item_id": item.ID,
"task_id": taskID,
}, "开始处理转存任务项: %d", item.ID)
// 解析输入数据 // 解析输入数据
parseStart := utils.GetCurrentTime()
var input TransferInput var input TransferInput
if err := json.Unmarshal([]byte(item.InputData), &input); err != nil { if err := json.Unmarshal([]byte(item.InputData), &input); err != nil {
parseDuration := time.Since(parseStart)
utils.ErrorWithFields(map[string]interface{}{
"error": err.Error(),
"duration_ms": parseDuration.Milliseconds(),
}, "解析输入数据失败: %v耗时: %v", err, parseDuration)
return fmt.Errorf("解析输入数据失败: %v", err) return fmt.Errorf("解析输入数据失败: %v", err)
} }
parseDuration := time.Since(parseStart)
utils.DebugWithFields(map[string]interface{}{
"duration_ms": parseDuration.Milliseconds(),
}, "解析输入数据完成,耗时: %v", parseDuration)
// 验证输入数据 // 验证输入数据
validateStart := utils.GetCurrentTime()
if err := tp.validateInput(&input); err != nil { if err := tp.validateInput(&input); err != nil {
validateDuration := time.Since(validateStart)
utils.Error("输入数据验证失败: %v耗时: %v", err, validateDuration)
return fmt.Errorf("输入数据验证失败: %v", err) return fmt.Errorf("输入数据验证失败: %v", err)
} }
validateDuration := time.Since(validateStart)
utils.DebugWithFields(map[string]interface{}{
"duration_ms": validateDuration.Milliseconds(),
}, "输入数据验证完成,耗时: %v", validateDuration)
// 获取任务配置中的账号信息 // 获取任务配置中的账号信息
configStart := utils.GetCurrentTime()
var selectedAccounts []uint var selectedAccounts []uint
task, err := tp.repoMgr.TaskRepository.GetByID(taskID) task, err := tp.repoMgr.TaskRepository.GetByID(taskID)
if err == nil && task.Config != "" { if err == nil && task.Config != "" {
@@ -79,15 +101,21 @@ func (tp *TransferProcessor) Process(ctx context.Context, taskID uint, item *ent
} }
} }
} }
configDuration := time.Since(configStart)
utils.Debug("获取任务配置完成,耗时: %v", configDuration)
if len(selectedAccounts) == 0 { if len(selectedAccounts) == 0 {
utils.Error("失败: %v", "没有指定转存账号") utils.Error("失败: %v", "没有指定转存账号")
} }
// 检查资源是否已存在 // 检查资源是否已存在
checkStart := utils.GetCurrentTime()
exists, existingResource, err := tp.checkResourceExists(input.URL) exists, existingResource, err := tp.checkResourceExists(input.URL)
checkDuration := time.Since(checkStart)
if err != nil { if err != nil {
utils.Error("检查资源是否存在失败: %v", err) utils.Error("检查资源是否存在失败: %v耗时: %v", err, checkDuration)
} else {
utils.Debug("检查资源是否存在完成,耗时: %v", checkDuration)
} }
if exists { if exists {
@@ -107,19 +135,26 @@ func (tp *TransferProcessor) Process(ctx context.Context, taskID uint, item *ent
outputJSON, _ := json.Marshal(output) outputJSON, _ := json.Marshal(output)
item.OutputData = string(outputJSON) item.OutputData = string(outputJSON)
utils.Info("资源已存在且有转存链接,跳过转存: %s", input.Title) elapsedTime := time.Since(startTime)
utils.Info("资源已存在且有转存链接,跳过转存: %s总耗时: %v", input.Title, elapsedTime)
return nil return nil
} }
} }
// 查询出 账号列表 // 查询出 账号列表
cksStart := utils.GetCurrentTime()
cks, err := tp.repoMgr.CksRepository.FindByIds(selectedAccounts) cks, err := tp.repoMgr.CksRepository.FindByIds(selectedAccounts)
cksDuration := time.Since(cksStart)
if err != nil { if err != nil {
utils.Error("读取账号失败: %v", err) utils.Error("读取账号失败: %v耗时: %v", err, cksDuration)
} else {
utils.Debug("读取账号完成,账号数量: %d耗时: %v", len(cks), cksDuration)
} }
// 执行转存操作 // 执行转存操作
transferStart := utils.GetCurrentTime()
resourceID, saveURL, err := tp.performTransfer(ctx, &input, cks) resourceID, saveURL, err := tp.performTransfer(ctx, &input, cks)
transferDuration := time.Since(transferStart)
if err != nil { if err != nil {
// 转存失败,更新输出数据 // 转存失败,更新输出数据
output := TransferOutput{ output := TransferOutput{
@@ -131,7 +166,13 @@ func (tp *TransferProcessor) Process(ctx context.Context, taskID uint, item *ent
outputJSON, _ := json.Marshal(output) outputJSON, _ := json.Marshal(output)
item.OutputData = string(outputJSON) item.OutputData = string(outputJSON)
utils.Error("转存任务项处理失败: %d, 错误: %v", item.ID, err) elapsedTime := time.Since(startTime)
utils.ErrorWithFields(map[string]interface{}{
"task_item_id": item.ID,
"error": err.Error(),
"duration_ms": transferDuration.Milliseconds(),
"total_ms": elapsedTime.Milliseconds(),
}, "转存任务项处理失败: %d, 错误: %v转存耗时: %v总耗时: %v", item.ID, err, transferDuration, elapsedTime)
return fmt.Errorf("转存失败: %v", err) return fmt.Errorf("转存失败: %v", err)
} }
@@ -146,7 +187,8 @@ func (tp *TransferProcessor) Process(ctx context.Context, taskID uint, item *ent
outputJSON, _ := json.Marshal(output) outputJSON, _ := json.Marshal(output)
item.OutputData = string(outputJSON) item.OutputData = string(outputJSON)
utils.Error("转存任务项处理失败: %d, 未获取到分享链接", item.ID) elapsedTime := time.Since(startTime)
utils.Error("转存任务项处理失败: %d, 未获取到分享链接,总耗时: %v", item.ID, elapsedTime)
return fmt.Errorf("转存成功但未获取到分享链接") return fmt.Errorf("转存成功但未获取到分享链接")
} }
@@ -161,7 +203,14 @@ func (tp *TransferProcessor) Process(ctx context.Context, taskID uint, item *ent
outputJSON, _ := json.Marshal(output) outputJSON, _ := json.Marshal(output)
item.OutputData = string(outputJSON) item.OutputData = string(outputJSON)
utils.Info("转存任务项处理完成: %d, 资源ID: %d, 转存链接: %s", item.ID, resourceID, saveURL) elapsedTime := time.Since(startTime)
utils.InfoWithFields(map[string]interface{}{
"task_item_id": item.ID,
"resource_id": resourceID,
"save_url": saveURL,
"transfer_duration_ms": transferDuration.Milliseconds(),
"total_duration_ms": elapsedTime.Milliseconds(),
}, "转存任务项处理完成: %d, 资源ID: %d, 转存链接: %s转存耗时: %v总耗时: %v", item.ID, resourceID, saveURL, transferDuration, elapsedTime)
return nil return nil
} }

View File

@@ -1,12 +1,14 @@
package utils package utils
import ( import (
"encoding/json"
"fmt" "fmt"
"io" "io"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings"
"sync" "sync"
"time" "time"
) )
@@ -40,6 +42,16 @@ func (l LogLevel) String() string {
} }
} }
// StructuredLogEntry 结构化日志条目
type StructuredLogEntry struct {
Timestamp time.Time `json:"timestamp"`
Level string `json:"level"`
Message string `json:"message"`
Caller string `json:"caller"`
Module string `json:"module"`
Fields map[string]interface{} `json:"fields,omitempty"`
}
// Logger 统一日志器 // Logger 统一日志器
type Logger struct { type Logger struct {
debugLogger *log.Logger debugLogger *log.Logger
@@ -63,22 +75,72 @@ type LogConfig struct {
EnableConsole bool // 是否启用控制台输出 EnableConsole bool // 是否启用控制台输出
EnableFile bool // 是否启用文件输出 EnableFile bool // 是否启用文件输出
EnableRotation bool // 是否启用日志轮转 EnableRotation bool // 是否启用日志轮转
StructuredLog bool // 是否启用结构化日志格式
} }
// DefaultConfig 默认配置 // DefaultConfig 默认配置
func DefaultConfig() *LogConfig { func DefaultConfig() *LogConfig {
// 从环境变量获取日志级别默认为INFO
logLevel := getLogLevelFromEnv()
return &LogConfig{ return &LogConfig{
LogDir: "logs", LogDir: "logs",
LogLevel: INFO, LogLevel: logLevel,
MaxFileSize: 100, // 100MB MaxFileSize: 100, // 100MB
MaxBackups: 5, MaxBackups: 5,
MaxAge: 30, // 30天 MaxAge: 30, // 30天
EnableConsole: true, EnableConsole: true,
EnableFile: true, EnableFile: true,
EnableRotation: true, EnableRotation: true,
StructuredLog: os.Getenv("STRUCTURED_LOG") == "true", // 从环境变量控制结构化日志
} }
} }
// getLogLevelFromEnv 从环境变量获取日志级别
func getLogLevelFromEnv() LogLevel {
envLogLevel := os.Getenv("LOG_LEVEL")
envDebug := os.Getenv("DEBUG")
// 如果设置了DEBUG环境变量为true则使用DEBUG级别
if envDebug == "true" || envDebug == "1" {
return DEBUG
}
// 根据LOG_LEVEL环境变量设置日志级别
switch strings.ToUpper(envLogLevel) {
case "DEBUG":
return DEBUG
case "INFO":
return INFO
case "WARN", "WARNING":
return WARN
case "ERROR":
return ERROR
case "FATAL":
return FATAL
default:
// 根据运行环境设置默认级别开发环境DEBUG生产环境INFO
if isDevelopment() {
return DEBUG
}
return INFO
}
}
// isDevelopment 判断是否为开发环境
func isDevelopment() bool {
env := os.Getenv("GO_ENV")
return env == "development" || env == "dev" || env == "local" || env == "test"
}
// getEnvironment 获取当前环境类型
func (l *Logger) getEnvironment() string {
if isDevelopment() {
return "development"
}
return "production"
}
var ( var (
globalLogger *Logger globalLogger *Logger
onceLogger sync.Once onceLogger sync.Once
@@ -135,6 +197,11 @@ func NewLogger(config *LogConfig) (*Logger, error) {
go logger.startRotationCheck() go logger.startRotationCheck()
} }
// 打印日志配置信息
logger.Info("日志系统初始化完成 - 级别: %s, 环境: %s",
config.LogLevel.String(),
logger.getEnvironment())
return logger, nil return logger, nil
} }
@@ -200,26 +267,55 @@ func (l *Logger) log(level LogLevel, format string, args ...interface{}) {
line = 0 line = 0
} }
// 提取文件名 // 提取文件名作为模块名
fileName := filepath.Base(file) fileName := filepath.Base(file)
moduleName := strings.TrimSuffix(fileName, filepath.Ext(fileName))
// 格式化消息 // 格式化消息
message := fmt.Sprintf(format, args...) message := fmt.Sprintf(format, args...)
// 添加调用位置信息 // 添加调用位置信息
fullMessage := fmt.Sprintf("[%s:%d] %s", fileName, line, message) caller := fmt.Sprintf("%s:%d", fileName, line)
if l.config.StructuredLog {
// 结构化日志格式
entry := StructuredLogEntry{
Timestamp: GetCurrentTime(),
Level: level.String(),
Message: message,
Caller: caller,
Module: moduleName,
}
jsonBytes, err := json.Marshal(entry)
if err != nil {
// 如果JSON序列化失败回退到普通格式
fullMessage := fmt.Sprintf("[%s] [%s:%d] %s", level.String(), fileName, line, message)
l.logToLevel(level, fullMessage)
return
}
l.logToLevel(level, string(jsonBytes))
} else {
// 普通文本格式
fullMessage := fmt.Sprintf("[%s] [%s:%d] %s", level.String(), fileName, line, message)
l.logToLevel(level, fullMessage)
}
}
// logToLevel 根据级别输出日志
func (l *Logger) logToLevel(level LogLevel, message string) {
switch level { switch level {
case DEBUG: case DEBUG:
l.debugLogger.Println(fullMessage) l.debugLogger.Println(message)
case INFO: case INFO:
l.infoLogger.Println(fullMessage) l.infoLogger.Println(message)
case WARN: case WARN:
l.warnLogger.Println(fullMessage) l.warnLogger.Println(message)
case ERROR: case ERROR:
l.errorLogger.Println(fullMessage) l.errorLogger.Println(message)
case FATAL: case FATAL:
l.fatalLogger.Println(fullMessage) l.fatalLogger.Println(message)
os.Exit(1) os.Exit(1)
} }
} }
@@ -328,15 +424,83 @@ func (l *Logger) cleanOldLogs() {
} }
} }
// Close 关闭日志器 // Min 返回两个整数中的较小值
func (l *Logger) Close() error { func Min(a, b int) int {
l.mu.Lock() if a < b {
defer l.mu.Unlock() return a
}
if l.file != nil { return b
return l.file.Close() }
// 结构化日志方法
func (l *Logger) DebugWithFields(fields map[string]interface{}, format string, args ...interface{}) {
l.logWithFields(DEBUG, fields, format, args...)
}
func (l *Logger) InfoWithFields(fields map[string]interface{}, format string, args ...interface{}) {
l.logWithFields(INFO, fields, format, args...)
}
func (l *Logger) WarnWithFields(fields map[string]interface{}, format string, args ...interface{}) {
l.logWithFields(WARN, fields, format, args...)
}
func (l *Logger) ErrorWithFields(fields map[string]interface{}, format string, args ...interface{}) {
l.logWithFields(ERROR, fields, format, args...)
}
func (l *Logger) FatalWithFields(fields map[string]interface{}, format string, args ...interface{}) {
l.logWithFields(FATAL, fields, format, args...)
}
// logWithFields 带字段的结构化日志方法
func (l *Logger) logWithFields(level LogLevel, fields map[string]interface{}, format string, args ...interface{}) {
if level < l.config.LogLevel {
return
}
// 获取调用者信息
_, file, line, ok := runtime.Caller(2)
if !ok {
file = "unknown"
line = 0
}
// 提取文件名作为模块名
fileName := filepath.Base(file)
moduleName := strings.TrimSuffix(fileName, filepath.Ext(fileName))
// 格式化消息
message := fmt.Sprintf(format, args...)
// 添加调用位置信息
caller := fmt.Sprintf("%s:%d", fileName, line)
if l.config.StructuredLog {
// 结构化日志格式
entry := StructuredLogEntry{
Timestamp: GetCurrentTime(),
Level: level.String(),
Message: message,
Caller: caller,
Module: moduleName,
Fields: fields,
}
jsonBytes, err := json.Marshal(entry)
if err != nil {
// 如果JSON序列化失败回退到普通格式
fullMessage := fmt.Sprintf("[%s] [%s:%d] %s - Fields: %v", level.String(), fileName, line, message, fields)
l.logToLevel(level, fullMessage)
return
}
l.logToLevel(level, string(jsonBytes))
} else {
// 普通文本格式
fullMessage := fmt.Sprintf("[%s] [%s:%d] %s - Fields: %v", level.String(), fileName, line, message, fields)
l.logToLevel(level, fullMessage)
} }
return nil
} }
// 全局便捷函数 // 全局便捷函数
@@ -359,3 +523,24 @@ func Error(format string, args ...interface{}) {
func Fatal(format string, args ...interface{}) { func Fatal(format string, args ...interface{}) {
GetLogger().Fatal(format, args...) GetLogger().Fatal(format, args...)
} }
// 全局结构化日志便捷函数
func DebugWithFields(fields map[string]interface{}, format string, args ...interface{}) {
GetLogger().DebugWithFields(fields, format, args...)
}
func InfoWithFields(fields map[string]interface{}, format string, args ...interface{}) {
GetLogger().InfoWithFields(fields, format, args...)
}
func WarnWithFields(fields map[string]interface{}, format string, args ...interface{}) {
GetLogger().WarnWithFields(fields, format, args...)
}
func ErrorWithFields(fields map[string]interface{}, format string, args ...interface{}) {
GetLogger().ErrorWithFields(fields, format, args...)
}
func FatalWithFields(fields map[string]interface{}, format string, args ...interface{}) {
GetLogger().FatalWithFields(fields, format, args...)
}