nginx_log.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. package nginx_log
  2. import (
  3. "io"
  4. "net/http"
  5. "os"
  6. "strings"
  7. "github.com/0xJacky/Nginx-UI/internal/nginx_log"
  8. "github.com/0xJacky/Nginx-UI/internal/translation"
  9. "github.com/gin-gonic/gin"
  10. "github.com/pkg/errors"
  11. "github.com/spf13/cast"
  12. "github.com/uozi-tech/cosy"
  13. "github.com/uozi-tech/cosy/logger"
  14. )
  15. const (
  16. // PageSize defines the size of log chunks returned by the API
  17. PageSize = 128 * 1024
  18. )
  19. // controlStruct represents the request parameters for getting log content
  20. type controlStruct struct {
  21. Type string `json:"type"` // Type of log: "access" or "error"
  22. LogPath string `json:"log_path"` // Path to the log file
  23. }
  24. // nginxLogPageResp represents the response format for log content
  25. type nginxLogPageResp struct {
  26. Content string `json:"content"` // Log content
  27. Page int64 `json:"page"` // Current page number
  28. Error *translation.Container `json:"error,omitempty"` // Error message if any
  29. }
  30. // GetNginxLogPage handles retrieving a page of log content from a log file
  31. func GetNginxLogPage(c *gin.Context) {
  32. page := cast.ToInt64(c.Query("page"))
  33. if page < 0 {
  34. page = 0
  35. }
  36. var control controlStruct
  37. if !cosy.BindAndValid(c, &control) {
  38. return
  39. }
  40. logPath, err := getLogPath(&control)
  41. if err != nil {
  42. c.JSON(http.StatusInternalServerError, nginxLogPageResp{
  43. Error: translation.C(err.Error()),
  44. })
  45. logger.Error(err)
  46. return
  47. }
  48. logFileStat, err := os.Stat(logPath)
  49. if err != nil {
  50. c.JSON(http.StatusInternalServerError, nginxLogPageResp{
  51. Error: translation.C(err.Error()),
  52. })
  53. logger.Error(err)
  54. return
  55. }
  56. if !logFileStat.Mode().IsRegular() {
  57. c.JSON(http.StatusInternalServerError, nginxLogPageResp{
  58. Error: translation.C("Log file %{log_path} is not a regular file. "+
  59. "If you are using nginx-ui in docker container, please refer to "+
  60. "https://nginxui.com/zh_CN/guide/config-nginx-log.html for more information.",
  61. map[string]any{
  62. "log_path": logPath,
  63. }),
  64. })
  65. logger.Errorf("log file is not a regular file: %s", logPath)
  66. return
  67. }
  68. // to fix: seek invalid argument #674
  69. if logFileStat.Size() == 0 {
  70. c.JSON(http.StatusOK, nginxLogPageResp{
  71. Page: 1,
  72. Content: "",
  73. })
  74. return
  75. }
  76. f, err := os.Open(logPath)
  77. if err != nil {
  78. c.JSON(http.StatusInternalServerError, nginxLogPageResp{
  79. Error: translation.C(err.Error()),
  80. })
  81. logger.Error(err)
  82. return
  83. }
  84. defer f.Close()
  85. totalPage := logFileStat.Size() / PageSize
  86. if logFileStat.Size()%PageSize > 0 {
  87. totalPage++
  88. }
  89. var buf []byte
  90. var offset int64
  91. if page == 0 {
  92. page = totalPage
  93. }
  94. buf = make([]byte, PageSize)
  95. offset = (page - 1) * PageSize
  96. // seek to the correct position in the file
  97. _, err = f.Seek(offset, io.SeekStart)
  98. if err != nil && err != io.EOF {
  99. c.JSON(http.StatusInternalServerError, nginxLogPageResp{
  100. Error: translation.C(err.Error()),
  101. })
  102. logger.Error(err)
  103. return
  104. }
  105. n, err := f.Read(buf)
  106. if err != nil && !errors.Is(err, io.EOF) {
  107. c.JSON(http.StatusInternalServerError, nginxLogPageResp{
  108. Error: translation.C(err.Error()),
  109. })
  110. logger.Error(err)
  111. return
  112. }
  113. c.JSON(http.StatusOK, nginxLogPageResp{
  114. Page: page,
  115. Content: string(buf[:n]),
  116. })
  117. }
  118. // GetLogList returns a list of Nginx log files
  119. func GetLogList(c *gin.Context) {
  120. filters := []func(*nginx_log.NginxLogCache) bool{}
  121. if logType := c.Query("type"); logType != "" {
  122. filters = append(filters, func(entry *nginx_log.NginxLogCache) bool {
  123. return entry.Type == logType
  124. })
  125. }
  126. if name := c.Query("name"); name != "" {
  127. filters = append(filters, func(entry *nginx_log.NginxLogCache) bool {
  128. return strings.Contains(entry.Name, name)
  129. })
  130. }
  131. if path := c.Query("path"); path != "" {
  132. filters = append(filters, func(entry *nginx_log.NginxLogCache) bool {
  133. return strings.Contains(entry.Path, path)
  134. })
  135. }
  136. data := nginx_log.GetAllLogs(filters...)
  137. orderBy := c.DefaultQuery("sort_by", "name")
  138. sort := c.DefaultQuery("order", "desc")
  139. data = nginx_log.Sort(orderBy, sort, data)
  140. c.JSON(http.StatusOK, gin.H{
  141. "data": data,
  142. })
  143. }