日志.go 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. package controllers
  2. import (
  3. "Tech/common"
  4. "encoding/json"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. beego "github.com/beego/beego/v2/server/web"
  9. "github.com/sirupsen/logrus"
  10. )
  11. type LogController struct {
  12. beego.Controller
  13. }
  14. // LogList 获取日志文件列表
  15. func (c *LogController) LogList() {
  16. // 验证用户权限
  17. userName := c.Ctx.Input.Header("userName")
  18. if userName == "" {
  19. c.Ctx.Output.SetStatus(401)
  20. c.Data["json"] = map[string]interface{}{
  21. "code": 401,
  22. "msg": "未提供认证信息",
  23. }
  24. c.ServeJSON()
  25. return
  26. }
  27. // 记录操作日志
  28. clientIP := c.Ctx.Input.IP()
  29. common.LogUserAction("", userName, "log_list", "system", logrus.Fields{
  30. "ip": clientIP,
  31. })
  32. // 获取日志文件信息
  33. logFiles, err := common.GetLogFilesInfo()
  34. if err != nil {
  35. c.Ctx.Output.SetStatus(500)
  36. c.Data["json"] = map[string]interface{}{
  37. "code": 500,
  38. "msg": "获取日志文件信息失败: " + err.Error(),
  39. }
  40. c.ServeJSON()
  41. return
  42. }
  43. c.Data["json"] = map[string]interface{}{
  44. "code": 200,
  45. "data": logFiles,
  46. "msg": "获取成功",
  47. }
  48. c.ServeJSON()
  49. }
  50. // LogView 查看日志文件内容
  51. func (c *LogController) LogView() {
  52. // 验证用户权限
  53. userName := c.Ctx.Input.Header("userName")
  54. if userName == "" {
  55. c.Ctx.Output.SetStatus(401)
  56. c.Data["json"] = map[string]interface{}{
  57. "code": 401,
  58. "msg": "未提供认证信息",
  59. }
  60. c.ServeJSON()
  61. return
  62. }
  63. type LogViewRequest struct {
  64. FileName string `json:"fileName"`
  65. Lines int `json:"lines"` // 读取的行数,默认100
  66. Offset int `json:"offset"` // 从末尾开始的偏移量,默认0
  67. }
  68. var req LogViewRequest
  69. if err := json.Unmarshal(c.Ctx.Input.RequestBody, &req); err != nil {
  70. c.Ctx.Output.SetStatus(400)
  71. c.Data["json"] = map[string]interface{}{
  72. "code": 400,
  73. "msg": "请求参数格式错误: " + err.Error(),
  74. }
  75. c.ServeJSON()
  76. return
  77. }
  78. // 设置默认值
  79. if req.Lines <= 0 {
  80. req.Lines = 100
  81. }
  82. // 验证文件名,防止路径遍历攻击
  83. if strings.Contains(req.FileName, "..") || strings.Contains(req.FileName, "/") || strings.Contains(req.FileName, "\\") {
  84. c.Ctx.Output.SetStatus(400)
  85. c.Data["json"] = map[string]interface{}{
  86. "code": 400,
  87. "msg": "非法的文件名",
  88. }
  89. c.ServeJSON()
  90. return
  91. }
  92. // 检查文件是否是日志文件
  93. 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") {
  94. c.Ctx.Output.SetStatus(400)
  95. c.Data["json"] = map[string]interface{}{
  96. "code": 400,
  97. "msg": "非日志文件",
  98. }
  99. c.ServeJSON()
  100. return
  101. }
  102. // 构建文件完整路径
  103. logDir := filepath.Join(".", "log")
  104. filePath := filepath.Join(logDir, req.FileName)
  105. // 检查文件是否存在
  106. if _, err := os.Stat(filePath); os.IsNotExist(err) {
  107. c.Ctx.Output.SetStatus(404)
  108. c.Data["json"] = map[string]interface{}{
  109. "code": 404,
  110. "msg": "文件不存在",
  111. }
  112. c.ServeJSON()
  113. return
  114. }
  115. // 读取文件内容
  116. var content string
  117. var err error
  118. if strings.HasSuffix(req.FileName, ".gz") {
  119. // 压缩文件需要特殊处理,这里简化处理,返回提示信息
  120. content = "压缩文件暂不支持在线查看,请下载后查看"
  121. } else {
  122. content, err = readLastLines(filePath, req.Lines, req.Offset)
  123. if err != nil {
  124. c.Ctx.Output.SetStatus(500)
  125. c.Data["json"] = map[string]interface{}{
  126. "code": 500,
  127. "msg": "读取文件失败: " + err.Error(),
  128. }
  129. c.ServeJSON()
  130. return
  131. }
  132. }
  133. // 记录操作日志
  134. clientIP := c.Ctx.Input.IP()
  135. common.LogUserAction("", userName, "log_view", "system", logrus.Fields{
  136. "ip": clientIP,
  137. "file_name": req.FileName,
  138. "lines": req.Lines,
  139. "offset": req.Offset,
  140. })
  141. c.Data["json"] = map[string]interface{}{
  142. "code": 200,
  143. "data": map[string]interface{}{
  144. "content": content,
  145. "file": req.FileName,
  146. },
  147. "msg": "获取成功",
  148. }
  149. c.ServeJSON()
  150. }
  151. // LogDownload 下载日志文件
  152. func (c *LogController) LogDownload() {
  153. // 验证用户权限
  154. userName := c.Ctx.Input.Header("userName")
  155. if userName == "" {
  156. c.Ctx.Output.SetStatus(401)
  157. c.Data["json"] = map[string]interface{}{
  158. "code": 401,
  159. "msg": "未提供认证信息",
  160. }
  161. c.ServeJSON()
  162. return
  163. }
  164. fileName := c.GetString("fileName")
  165. if fileName == "" {
  166. c.Ctx.Output.SetStatus(400)
  167. c.Data["json"] = map[string]interface{}{
  168. "code": 400,
  169. "msg": "文件名不能为空",
  170. }
  171. c.ServeJSON()
  172. return
  173. }
  174. // 验证文件名,防止路径遍历攻击
  175. if strings.Contains(fileName, "..") || strings.Contains(fileName, "/") || strings.Contains(fileName, "\\") {
  176. c.Ctx.Output.SetStatus(400)
  177. c.Data["json"] = map[string]interface{}{
  178. "code": 400,
  179. "msg": "非法的文件名",
  180. }
  181. c.ServeJSON()
  182. return
  183. }
  184. // 检查文件是否是日志文件
  185. if !strings.HasPrefix(fileName, "info") && !strings.HasPrefix(fileName, "warn") && !strings.HasPrefix(fileName, "error") || !strings.HasSuffix(fileName, ".log") && !strings.HasSuffix(fileName, ".log.gz") {
  186. c.Ctx.Output.SetStatus(400)
  187. c.Data["json"] = map[string]interface{}{
  188. "code": 400,
  189. "msg": "非日志文件",
  190. }
  191. c.ServeJSON()
  192. return
  193. }
  194. // 构建文件完整路径
  195. logDir := filepath.Join(".", "log")
  196. filePath := filepath.Join(logDir, fileName)
  197. // 检查文件是否存在
  198. if _, err := os.Stat(filePath); os.IsNotExist(err) {
  199. c.Ctx.Output.SetStatus(404)
  200. c.Data["json"] = map[string]interface{}{
  201. "code": 404,
  202. "msg": "文件不存在",
  203. }
  204. c.ServeJSON()
  205. return
  206. }
  207. // 记录操作日志
  208. clientIP := c.Ctx.Input.IP()
  209. common.LogUserAction("", userName, "log_download", "system", logrus.Fields{
  210. "ip": clientIP,
  211. "file_name": fileName,
  212. })
  213. // 设置下载响应头
  214. c.Ctx.Output.Download(filePath, fileName)
  215. }
  216. // LogLevelSet 设置日志级别
  217. func (c *LogController) LogLevelSet() {
  218. // 验证用户权限
  219. userName := c.Ctx.Input.Header("userName")
  220. if userName == "" {
  221. c.Ctx.Output.SetStatus(401)
  222. c.Data["json"] = map[string]interface{}{
  223. "code": 401,
  224. "msg": "未提供认证信息",
  225. }
  226. c.ServeJSON()
  227. return
  228. }
  229. type LogLevelRequest struct {
  230. Level string `json:"level"`
  231. }
  232. var req LogLevelRequest
  233. if err := json.Unmarshal(c.Ctx.Input.RequestBody, &req); err != nil {
  234. c.Ctx.Output.SetStatus(400)
  235. c.Data["json"] = map[string]interface{}{
  236. "code": 400,
  237. "msg": "请求参数格式错误: " + err.Error(),
  238. }
  239. c.ServeJSON()
  240. return
  241. }
  242. // 验证日志级别
  243. validLevels := map[string]bool{
  244. "trace": true,
  245. "debug": true,
  246. "info": true,
  247. "warn": true,
  248. "error": true,
  249. "fatal": true,
  250. "panic": true,
  251. }
  252. if !validLevels[req.Level] {
  253. c.Ctx.Output.SetStatus(400)
  254. c.Data["json"] = map[string]interface{}{
  255. "code": 400,
  256. "msg": "无效的日志级别",
  257. }
  258. c.ServeJSON()
  259. return
  260. }
  261. // 设置日志级别
  262. err := common.SetLogLevel(req.Level)
  263. if err != nil {
  264. c.Ctx.Output.SetStatus(500)
  265. c.Data["json"] = map[string]interface{}{
  266. "code": 500,
  267. "msg": "设置日志级别失败: " + err.Error(),
  268. }
  269. c.ServeJSON()
  270. return
  271. }
  272. // 记录操作日志
  273. clientIP := c.Ctx.Input.IP()
  274. common.LogUserAction("", userName, "log_level_set", "system", logrus.Fields{
  275. "ip": clientIP,
  276. "level": req.Level,
  277. })
  278. c.Data["json"] = map[string]interface{}{
  279. "code": 200,
  280. "msg": "设置成功",
  281. }
  282. c.ServeJSON()
  283. }
  284. // LogDelete 删除日志文件
  285. func (c *LogController) LogDelete() {
  286. // 验证用户权限
  287. userName := c.Ctx.Input.Header("userName")
  288. if userName == "" {
  289. c.Ctx.Output.SetStatus(401)
  290. c.Data["json"] = map[string]interface{}{
  291. "code": 401,
  292. "msg": "未提供认证信息",
  293. }
  294. c.ServeJSON()
  295. return
  296. }
  297. type LogDeleteRequest struct {
  298. FileName string `json:"fileName"`
  299. }
  300. var req LogDeleteRequest
  301. if err := json.Unmarshal(c.Ctx.Input.RequestBody, &req); err != nil {
  302. c.Ctx.Output.SetStatus(400)
  303. c.Data["json"] = map[string]interface{}{
  304. "code": 400,
  305. "msg": "请求参数格式错误: " + err.Error(),
  306. }
  307. c.ServeJSON()
  308. return
  309. }
  310. // 验证文件名,防止路径遍历攻击
  311. if strings.Contains(req.FileName, "..") || strings.Contains(req.FileName, "/") || strings.Contains(req.FileName, "\\") {
  312. c.Ctx.Output.SetStatus(400)
  313. c.Data["json"] = map[string]interface{}{
  314. "code": 400,
  315. "msg": "非法的文件名",
  316. }
  317. c.ServeJSON()
  318. return
  319. }
  320. // 检查文件是否是日志文件
  321. 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") {
  322. c.Ctx.Output.SetStatus(400)
  323. c.Data["json"] = map[string]interface{}{
  324. "code": 400,
  325. "msg": "非日志文件",
  326. }
  327. c.ServeJSON()
  328. return
  329. }
  330. // 构建文件完整路径
  331. logDir := filepath.Join(".", "log")
  332. filePath := filepath.Join(logDir, req.FileName)
  333. // 检查文件是否存在
  334. if _, err := os.Stat(filePath); os.IsNotExist(err) {
  335. c.Ctx.Output.SetStatus(404)
  336. c.Data["json"] = map[string]interface{}{
  337. "code": 404,
  338. "msg": "文件不存在",
  339. }
  340. c.ServeJSON()
  341. return
  342. }
  343. // 删除文件
  344. err := os.Remove(filePath)
  345. if err != nil {
  346. c.Ctx.Output.SetStatus(500)
  347. c.Data["json"] = map[string]interface{}{
  348. "code": 500,
  349. "msg": "删除文件失败: " + err.Error(),
  350. }
  351. c.ServeJSON()
  352. return
  353. }
  354. // 记录操作日志
  355. clientIP := c.Ctx.Input.IP()
  356. common.LogUserAction("", userName, "log_delete", "system", logrus.Fields{
  357. "ip": clientIP,
  358. "file_name": req.FileName,
  359. })
  360. c.Data["json"] = map[string]interface{}{
  361. "code": 200,
  362. "msg": "删除成功",
  363. }
  364. c.ServeJSON()
  365. }
  366. // readLastLines 读取文件最后N行
  367. func readLastLines(filePath string, lines int, offset int) (string, error) {
  368. // 读取文件内容
  369. content, err := os.ReadFile(filePath)
  370. if err != nil {
  371. return "", err
  372. }
  373. // 按行分割
  374. allLines := strings.Split(string(content), "\n")
  375. // 计算起始索引
  376. startIndex := len(allLines) - lines - offset
  377. if startIndex < 0 {
  378. startIndex = 0
  379. }
  380. // 截取需要的行
  381. resultLines := allLines[startIndex : len(allLines)-offset]
  382. return strings.Join(resultLines, "\n"), nil
  383. }