|
@@ -8,89 +8,185 @@ import (
|
|
|
"github.com/gorilla/websocket"
|
|
|
"github.com/hpcloud/tail"
|
|
|
"github.com/pkg/errors"
|
|
|
+ "github.com/spf13/cast"
|
|
|
"io"
|
|
|
"log"
|
|
|
"net/http"
|
|
|
+ "os"
|
|
|
"path/filepath"
|
|
|
)
|
|
|
|
|
|
+const (
|
|
|
+ PageSize = 128 * 1024
|
|
|
+)
|
|
|
+
|
|
|
type controlStruct struct {
|
|
|
- Fetch string `json:"fetch"`
|
|
|
Type string `json:"type"`
|
|
|
ConfName string `json:"conf_name"`
|
|
|
ServerIdx int `json:"server_idx"`
|
|
|
DirectiveIdx int `json:"directive_idx"`
|
|
|
}
|
|
|
|
|
|
-func tailNginxLog(ws *websocket.Conn, controlChan chan controlStruct, errChan chan error) {
|
|
|
- defer func() {
|
|
|
- if err := recover(); err != nil {
|
|
|
- log.Println("tailNginxLog recovery", err)
|
|
|
- _ = ws.WriteMessage(websocket.TextMessage, err.([]byte))
|
|
|
+type nginxLogPageResp struct {
|
|
|
+ Content string `json:"content"`
|
|
|
+ Page int64 `json:"page"`
|
|
|
+}
|
|
|
+
|
|
|
+func GetNginxLogPage(c *gin.Context) {
|
|
|
+ page := cast.ToInt64(c.Query("page"))
|
|
|
+ if page < 0 {
|
|
|
+ page = 0
|
|
|
+ }
|
|
|
+
|
|
|
+ var control controlStruct
|
|
|
+ if !BindAndValid(c, &control) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ logPath, err := getLogPath(&control)
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ log.Println("error GetNginxLogPage", err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ f, err := os.Open(logPath)
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ c.JSON(http.StatusOK, nginxLogPageResp{})
|
|
|
+ log.Println("error GetNginxLogPage open file", err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ logFileStat, err := os.Stat(logPath)
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ c.JSON(http.StatusOK, nginxLogPageResp{})
|
|
|
+ log.Println("error GetNginxLogPage stat", err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ totalPage := logFileStat.Size() / PageSize
|
|
|
+
|
|
|
+ if logFileStat.Size()%PageSize > 0 {
|
|
|
+ totalPage++
|
|
|
+ }
|
|
|
+
|
|
|
+ var buf []byte
|
|
|
+ var offset int64
|
|
|
+ if page == 0 {
|
|
|
+ page = totalPage
|
|
|
+ }
|
|
|
+
|
|
|
+ buf = make([]byte, PageSize)
|
|
|
+ offset = (page - 1) * PageSize
|
|
|
+
|
|
|
+ // seek
|
|
|
+ _, err = f.Seek(offset, io.SeekStart)
|
|
|
+ if err != nil && err != io.EOF {
|
|
|
+ c.JSON(http.StatusOK, nginxLogPageResp{})
|
|
|
+ log.Println("error GetNginxLogPage seek", err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ n, err := f.Read(buf)
|
|
|
+
|
|
|
+ if err != nil && err != io.EOF {
|
|
|
+ c.JSON(http.StatusOK, nginxLogPageResp{})
|
|
|
+ log.Println("error GetNginxLogPage read buf", err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ c.JSON(http.StatusOK, nginxLogPageResp{
|
|
|
+ Page: page,
|
|
|
+ Content: string(buf[:n]),
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+func getLogPath(control *controlStruct) (logPath string, err error) {
|
|
|
+ switch control.Type {
|
|
|
+ case "site":
|
|
|
+ var config *nginx.NgxConfig
|
|
|
+ path := filepath.Join(nginx.GetNginxConfPath("sites-available"), control.ConfName)
|
|
|
+ config, err = nginx.ParseNgxConfig(path)
|
|
|
+ if err != nil {
|
|
|
+ err = errors.Wrap(err, "error parsing ngx config")
|
|
|
return
|
|
|
}
|
|
|
- }()
|
|
|
|
|
|
- control := <-controlChan
|
|
|
+ if control.ServerIdx >= len(config.Servers) {
|
|
|
+ err = errors.New("serverIdx out of range")
|
|
|
+ return
|
|
|
+ }
|
|
|
|
|
|
- for {
|
|
|
- var seek tail.SeekInfo
|
|
|
- if control.Fetch != "all" {
|
|
|
- seek.Offset = 0
|
|
|
- seek.Whence = io.SeekEnd
|
|
|
- }
|
|
|
- var logPath string
|
|
|
- switch control.Type {
|
|
|
- case "site":
|
|
|
- path := filepath.Join(nginx.GetNginxConfPath("sites-available"), control.ConfName)
|
|
|
- config, err := nginx.ParseNgxConfig(path)
|
|
|
- if err != nil {
|
|
|
- errChan <- errors.Wrap(err, "error parsing ngx config")
|
|
|
- return
|
|
|
- }
|
|
|
+ if control.DirectiveIdx >= len(config.Servers[control.ServerIdx].Directives) {
|
|
|
+ err = errors.New("DirectiveIdx out of range")
|
|
|
+ return
|
|
|
+ }
|
|
|
|
|
|
- if control.ServerIdx >= len(config.Servers) {
|
|
|
- errChan <- errors.New("serverIdx out of range")
|
|
|
- return
|
|
|
- }
|
|
|
+ directive := config.Servers[control.ServerIdx].Directives[control.DirectiveIdx]
|
|
|
|
|
|
- if control.DirectiveIdx >= len(config.Servers[control.ServerIdx].Directives) {
|
|
|
- errChan <- errors.New("DirectiveIdx out of range")
|
|
|
- return
|
|
|
- }
|
|
|
+ switch directive.Directive {
|
|
|
+ case "access_log", "error_log":
|
|
|
+ // ok
|
|
|
+ default:
|
|
|
+ err = errors.New("directive.Params neither access_log nor error_log")
|
|
|
+ return
|
|
|
+ }
|
|
|
|
|
|
- directive := config.Servers[control.ServerIdx].Directives[control.DirectiveIdx]
|
|
|
+ if directive.Params == "" {
|
|
|
+ err = errors.New("directive.Params is empty")
|
|
|
+ return
|
|
|
+ }
|
|
|
|
|
|
- switch directive.Directive {
|
|
|
- case "access_log", "error_log":
|
|
|
- // ok
|
|
|
- default:
|
|
|
- errChan <- errors.New("directive.Params neither access_log nor error_log")
|
|
|
- return
|
|
|
- }
|
|
|
+ logPath = directive.Params
|
|
|
|
|
|
- if directive.Params == "" {
|
|
|
- errChan <- errors.New("directive.Params is empty")
|
|
|
- return
|
|
|
- }
|
|
|
+ case "error":
|
|
|
+ if settings.NginxLogSettings.ErrorLogPath == "" {
|
|
|
+ err = errors.New("settings.NginxLogSettings.ErrorLogPath is empty," +
|
|
|
+ " see https://github.com/0xJacky/nginx-ui/wiki/Nginx-Log-Configuration for more information")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ logPath = settings.NginxLogSettings.ErrorLogPath
|
|
|
|
|
|
- logPath = directive.Params
|
|
|
+ default:
|
|
|
+ if settings.NginxLogSettings.AccessLogPath == "" {
|
|
|
+ err = errors.New("settings.NginxLogSettings.AccessLogPath is empty," +
|
|
|
+ " see https://github.com/0xJacky/nginx-ui/wiki/Nginx-Log-Configuration for more information")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ logPath = settings.NginxLogSettings.AccessLogPath
|
|
|
+ }
|
|
|
|
|
|
- case "error":
|
|
|
- if settings.NginxLogSettings.ErrorLogPath == "" {
|
|
|
- errChan <- errors.New("settings.NginxLogSettings.ErrorLogPath is empty," +
|
|
|
- " see https://github.com/0xJacky/nginx-ui/wiki/Nginx-Log-Configuration for more information")
|
|
|
- return
|
|
|
- }
|
|
|
- logPath = settings.NginxLogSettings.ErrorLogPath
|
|
|
+ return
|
|
|
+}
|
|
|
|
|
|
- default:
|
|
|
- if settings.NginxLogSettings.AccessLogPath == "" {
|
|
|
- errChan <- errors.New("settings.NginxLogSettings.AccessLogPath is empty," +
|
|
|
- " see https://github.com/0xJacky/nginx-ui/wiki/Nginx-Log-Configuration for more information")
|
|
|
+func tailNginxLog(ws *websocket.Conn, controlChan chan controlStruct, errChan chan error) {
|
|
|
+ defer func() {
|
|
|
+ if err := recover(); err != nil {
|
|
|
+ log.Println("tailNginxLog recovery", err)
|
|
|
+ err = ws.WriteMessage(websocket.TextMessage, err.([]byte))
|
|
|
+ if err != nil {
|
|
|
+ log.Println(err)
|
|
|
return
|
|
|
}
|
|
|
- logPath = settings.NginxLogSettings.AccessLogPath
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }()
|
|
|
+
|
|
|
+ control := <-controlChan
|
|
|
+
|
|
|
+ for {
|
|
|
+ logPath, err := getLogPath(&control)
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ errChan <- err
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ seek := tail.SeekInfo{
|
|
|
+ Offset: 0,
|
|
|
+ Whence: io.SeekEnd,
|
|
|
}
|
|
|
|
|
|
// Create a tail
|