| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433 |
- package controllers
- import (
- "Tech/common"
- "encoding/json"
- "os"
- "path/filepath"
- "strings"
- beego "github.com/beego/beego/v2/server/web"
- "github.com/sirupsen/logrus"
- )
- type LogController struct {
- beego.Controller
- }
- // LogList 获取日志文件列表
- func (c *LogController) LogList() {
- // 验证用户权限
- userName := c.Ctx.Input.Header("userName")
- if userName == "" {
- c.Ctx.Output.SetStatus(401)
- c.Data["json"] = map[string]interface{}{
- "code": 401,
- "msg": "未提供认证信息",
- }
- c.ServeJSON()
- return
- }
- // 记录操作日志
- clientIP := c.Ctx.Input.IP()
- common.LogUserAction("", userName, "log_list", "system", logrus.Fields{
- "ip": clientIP,
- })
- // 获取日志文件信息
- logFiles, err := common.GetLogFilesInfo()
- if err != nil {
- c.Ctx.Output.SetStatus(500)
- c.Data["json"] = map[string]interface{}{
- "code": 500,
- "msg": "获取日志文件信息失败: " + err.Error(),
- }
- c.ServeJSON()
- return
- }
- c.Data["json"] = map[string]interface{}{
- "code": 200,
- "data": logFiles,
- "msg": "获取成功",
- }
- c.ServeJSON()
- }
- // LogView 查看日志文件内容
- func (c *LogController) LogView() {
- // 验证用户权限
- userName := c.Ctx.Input.Header("userName")
- if userName == "" {
- c.Ctx.Output.SetStatus(401)
- c.Data["json"] = map[string]interface{}{
- "code": 401,
- "msg": "未提供认证信息",
- }
- c.ServeJSON()
- return
- }
- type LogViewRequest struct {
- FileName string `json:"fileName"`
- Lines int `json:"lines"` // 读取的行数,默认100
- Offset int `json:"offset"` // 从末尾开始的偏移量,默认0
- }
- var req LogViewRequest
- if err := json.Unmarshal(c.Ctx.Input.RequestBody, &req); err != nil {
- c.Ctx.Output.SetStatus(400)
- c.Data["json"] = map[string]interface{}{
- "code": 400,
- "msg": "请求参数格式错误: " + err.Error(),
- }
- c.ServeJSON()
- return
- }
- // 设置默认值
- if req.Lines <= 0 {
- req.Lines = 100
- }
- // 验证文件名,防止路径遍历攻击
- if strings.Contains(req.FileName, "..") || strings.Contains(req.FileName, "/") || strings.Contains(req.FileName, "\\") {
- c.Ctx.Output.SetStatus(400)
- c.Data["json"] = map[string]interface{}{
- "code": 400,
- "msg": "非法的文件名",
- }
- c.ServeJSON()
- return
- }
- // 检查文件是否是日志文件
- if !strings.HasPrefix(req.FileName, "info") && !strings.HasPrefix(req.FileName, "warn") && !strings.HasPrefix(req.FileName, "error") || !strings.HasSuffix(req.FileName, ".log") && !strings.HasSuffix(req.FileName, ".log.gz") {
- c.Ctx.Output.SetStatus(400)
- c.Data["json"] = map[string]interface{}{
- "code": 400,
- "msg": "非日志文件",
- }
- c.ServeJSON()
- return
- }
- // 构建文件完整路径
- logDir := filepath.Join(".", "log")
- filePath := filepath.Join(logDir, req.FileName)
- // 检查文件是否存在
- if _, err := os.Stat(filePath); os.IsNotExist(err) {
- c.Ctx.Output.SetStatus(404)
- c.Data["json"] = map[string]interface{}{
- "code": 404,
- "msg": "文件不存在",
- }
- c.ServeJSON()
- return
- }
- // 读取文件内容
- var content string
- var err error
- if strings.HasSuffix(req.FileName, ".gz") {
- // 压缩文件需要特殊处理,这里简化处理,返回提示信息
- content = "压缩文件暂不支持在线查看,请下载后查看"
- } else {
- content, err = readLastLines(filePath, req.Lines, req.Offset)
- if err != nil {
- c.Ctx.Output.SetStatus(500)
- c.Data["json"] = map[string]interface{}{
- "code": 500,
- "msg": "读取文件失败: " + err.Error(),
- }
- c.ServeJSON()
- return
- }
- }
- // 记录操作日志
- clientIP := c.Ctx.Input.IP()
- common.LogUserAction("", userName, "log_view", "system", logrus.Fields{
- "ip": clientIP,
- "file_name": req.FileName,
- "lines": req.Lines,
- "offset": req.Offset,
- })
- c.Data["json"] = map[string]interface{}{
- "code": 200,
- "data": map[string]interface{}{
- "content": content,
- "file": req.FileName,
- },
- "msg": "获取成功",
- }
- c.ServeJSON()
- }
- // LogDownload 下载日志文件
- func (c *LogController) LogDownload() {
- // 验证用户权限
- userName := c.Ctx.Input.Header("userName")
- if userName == "" {
- c.Ctx.Output.SetStatus(401)
- c.Data["json"] = map[string]interface{}{
- "code": 401,
- "msg": "未提供认证信息",
- }
- c.ServeJSON()
- return
- }
- fileName := c.GetString("fileName")
- if fileName == "" {
- c.Ctx.Output.SetStatus(400)
- c.Data["json"] = map[string]interface{}{
- "code": 400,
- "msg": "文件名不能为空",
- }
- c.ServeJSON()
- return
- }
- // 验证文件名,防止路径遍历攻击
- if strings.Contains(fileName, "..") || strings.Contains(fileName, "/") || strings.Contains(fileName, "\\") {
- c.Ctx.Output.SetStatus(400)
- c.Data["json"] = map[string]interface{}{
- "code": 400,
- "msg": "非法的文件名",
- }
- c.ServeJSON()
- return
- }
- // 检查文件是否是日志文件
- if !strings.HasPrefix(fileName, "info") && !strings.HasPrefix(fileName, "warn") && !strings.HasPrefix(fileName, "error") || !strings.HasSuffix(fileName, ".log") && !strings.HasSuffix(fileName, ".log.gz") {
- c.Ctx.Output.SetStatus(400)
- c.Data["json"] = map[string]interface{}{
- "code": 400,
- "msg": "非日志文件",
- }
- c.ServeJSON()
- return
- }
- // 构建文件完整路径
- logDir := filepath.Join(".", "log")
- filePath := filepath.Join(logDir, fileName)
- // 检查文件是否存在
- if _, err := os.Stat(filePath); os.IsNotExist(err) {
- c.Ctx.Output.SetStatus(404)
- c.Data["json"] = map[string]interface{}{
- "code": 404,
- "msg": "文件不存在",
- }
- c.ServeJSON()
- return
- }
- // 记录操作日志
- clientIP := c.Ctx.Input.IP()
- common.LogUserAction("", userName, "log_download", "system", logrus.Fields{
- "ip": clientIP,
- "file_name": fileName,
- })
- // 设置下载响应头
- c.Ctx.Output.Download(filePath, fileName)
- }
- // LogLevelSet 设置日志级别
- func (c *LogController) LogLevelSet() {
- // 验证用户权限
- userName := c.Ctx.Input.Header("userName")
- if userName == "" {
- c.Ctx.Output.SetStatus(401)
- c.Data["json"] = map[string]interface{}{
- "code": 401,
- "msg": "未提供认证信息",
- }
- c.ServeJSON()
- return
- }
- type LogLevelRequest struct {
- Level string `json:"level"`
- }
- var req LogLevelRequest
- if err := json.Unmarshal(c.Ctx.Input.RequestBody, &req); err != nil {
- c.Ctx.Output.SetStatus(400)
- c.Data["json"] = map[string]interface{}{
- "code": 400,
- "msg": "请求参数格式错误: " + err.Error(),
- }
- c.ServeJSON()
- return
- }
- // 验证日志级别
- validLevels := map[string]bool{
- "trace": true,
- "debug": true,
- "info": true,
- "warn": true,
- "error": true,
- "fatal": true,
- "panic": true,
- }
- if !validLevels[req.Level] {
- c.Ctx.Output.SetStatus(400)
- c.Data["json"] = map[string]interface{}{
- "code": 400,
- "msg": "无效的日志级别",
- }
- c.ServeJSON()
- return
- }
- // 设置日志级别
- err := common.SetLogLevel(req.Level)
- if err != nil {
- c.Ctx.Output.SetStatus(500)
- c.Data["json"] = map[string]interface{}{
- "code": 500,
- "msg": "设置日志级别失败: " + err.Error(),
- }
- c.ServeJSON()
- return
- }
- // 记录操作日志
- clientIP := c.Ctx.Input.IP()
- common.LogUserAction("", userName, "log_level_set", "system", logrus.Fields{
- "ip": clientIP,
- "level": req.Level,
- })
- c.Data["json"] = map[string]interface{}{
- "code": 200,
- "msg": "设置成功",
- }
- c.ServeJSON()
- }
- // LogDelete 删除日志文件
- func (c *LogController) LogDelete() {
- // 验证用户权限
- userName := c.Ctx.Input.Header("userName")
- if userName == "" {
- c.Ctx.Output.SetStatus(401)
- c.Data["json"] = map[string]interface{}{
- "code": 401,
- "msg": "未提供认证信息",
- }
- c.ServeJSON()
- return
- }
- type LogDeleteRequest struct {
- FileName string `json:"fileName"`
- }
- var req LogDeleteRequest
- if err := json.Unmarshal(c.Ctx.Input.RequestBody, &req); err != nil {
- c.Ctx.Output.SetStatus(400)
- c.Data["json"] = map[string]interface{}{
- "code": 400,
- "msg": "请求参数格式错误: " + err.Error(),
- }
- c.ServeJSON()
- return
- }
- // 验证文件名,防止路径遍历攻击
- if strings.Contains(req.FileName, "..") || strings.Contains(req.FileName, "/") || strings.Contains(req.FileName, "\\") {
- c.Ctx.Output.SetStatus(400)
- c.Data["json"] = map[string]interface{}{
- "code": 400,
- "msg": "非法的文件名",
- }
- c.ServeJSON()
- return
- }
- // 检查文件是否是日志文件
- if !strings.HasPrefix(req.FileName, "info") && !strings.HasPrefix(req.FileName, "warn") && !strings.HasPrefix(req.FileName, "error") || !strings.HasSuffix(req.FileName, ".log") && !strings.HasSuffix(req.FileName, ".log.gz") {
- c.Ctx.Output.SetStatus(400)
- c.Data["json"] = map[string]interface{}{
- "code": 400,
- "msg": "非日志文件",
- }
- c.ServeJSON()
- return
- }
- // 构建文件完整路径
- logDir := filepath.Join(".", "log")
- filePath := filepath.Join(logDir, req.FileName)
- // 检查文件是否存在
- if _, err := os.Stat(filePath); os.IsNotExist(err) {
- c.Ctx.Output.SetStatus(404)
- c.Data["json"] = map[string]interface{}{
- "code": 404,
- "msg": "文件不存在",
- }
- c.ServeJSON()
- return
- }
- // 删除文件
- err := os.Remove(filePath)
- if err != nil {
- c.Ctx.Output.SetStatus(500)
- c.Data["json"] = map[string]interface{}{
- "code": 500,
- "msg": "删除文件失败: " + err.Error(),
- }
- c.ServeJSON()
- return
- }
- // 记录操作日志
- clientIP := c.Ctx.Input.IP()
- common.LogUserAction("", userName, "log_delete", "system", logrus.Fields{
- "ip": clientIP,
- "file_name": req.FileName,
- })
- c.Data["json"] = map[string]interface{}{
- "code": 200,
- "msg": "删除成功",
- }
- c.ServeJSON()
- }
- // readLastLines 读取文件最后N行
- func readLastLines(filePath string, lines int, offset int) (string, error) {
- // 读取文件内容
- content, err := os.ReadFile(filePath)
- if err != nil {
- return "", err
- }
- // 按行分割
- allLines := strings.Split(string(content), "\n")
-
- // 计算起始索引
- startIndex := len(allLines) - lines - offset
- if startIndex < 0 {
- startIndex = 0
- }
- // 截取需要的行
- resultLines := allLines[startIndex : len(allLines)-offset]
-
- return strings.Join(resultLines, "\n"), nil
- }
|