Commit 1fc283e0 by 刘豪

更新日志

parent a9711d52
package code package code
import ( import (
"fmt" "github.com/gorilla/websocket"
"github.com/kataras/iris/v12" "github.com/kataras/iris/v12"
"log"
"net/url" "net/url"
"os"
"strings" "strings"
"time" "time"
"web_log/config"
"web_log/utils"
) )
type User struct { type User struct {
...@@ -20,45 +20,44 @@ func GetApp(directory string, deBug bool) *iris.Application { ...@@ -20,45 +20,44 @@ func GetApp(directory string, deBug bool) *iris.Application {
w := &WebHandler{directory} w := &WebHandler{directory}
app.Get("/", func(ctx iris.Context) { app.Get("/", func(ctx iris.Context) {
w.loginhandle(ctx, nil) w.loginHandle(ctx, nil)
}) })
app.Post("/login", func(ctx iris.Context) { app.Post("/login", func(ctx iris.Context) {
var user User var user User
ctx.ReadForm(&user) ctx.ReadForm(&user)
//user.Username = "zzl"
//user.Password = "Lx@520m!@@#"
if ok2, msg := VerifyUser(user.Username, user.Password); ok2 { if ok2, msg := VerifyUser(user.Username, user.Password); ok2 {
ctx.SetCookieKV("sessionid", msg) ctx.SetCookieKV("sessionid", msg)
ctx.Redirect("/index", 301) ctx.Redirect("/index", 301)
} else { } else {
fmt.Println("验证不通过") utils.Log.Warn("验证不通过")
w.loginhandle(ctx, msg) w.loginHandle(ctx, msg)
} }
}) })
if !deBug { if !deBug {
app.Use(func(ctx iris.Context) { app.Use(func(ctx iris.Context) {
sessionid := ctx.GetCookie("sessionid") sessionid := ctx.GetCookie("sessionid")
ok1, msg := VerifySessionID(sessionid) ok1, msg := VerifySessionID(sessionid)
fmt.Println(msg) utils.Log.Info(msg)
if ok1 { if ok1 {
ctx.Next() ctx.Next()
} else { } else {
w.loginhandle(ctx, "未登录") w.loginHandle(ctx, "未登录")
} }
}) })
} }
app.Get("/index", w.indexhandle) app.Get("/index", w.indexHandle)
app.Get("/ws", genCtxHand(directory, manager.filemap)) app.Get("/ws", getCtxHand(manager.filemap))
app.Get("/wsSearchLog", getSearchLogResult)
app.Get("/searchLog", w.searchLogHandle)
app.Get("/downLog", w.downLogHandle)
go manager.start() go manager.start()
return app return app
} }
// file_map -> {filename : {client: True}} // getCtxHand 获取单个文件的最后五百行内容,并开启额外协程监控文件是否更新以达到实时加载的目的
func genCtxHand(directory string, file_map map[string]map[Client]bool) func(ctx iris.Context) { func getCtxHand(fileMap map[string]map[Client]bool) func(ctx iris.Context) {
f := func(ctx iris.Context) { f := func(ctx iris.Context) {
w := ctx.ResponseWriter() w := ctx.ResponseWriter()
r := ctx.Request() r := ctx.Request()
...@@ -68,46 +67,120 @@ func genCtxHand(directory string, file_map map[string]map[Client]bool) func(ctx ...@@ -68,46 +67,120 @@ func genCtxHand(directory string, file_map map[string]map[Client]bool) func(ctx
if err != nil { if err != nil {
return return
} }
cli := &Client{time.Now().String(), ws, make(chan []byte, 1024), ""} queryForm, err := url.ParseQuery(r.URL.RawQuery)
queryform, err := url.ParseQuery(r.URL.RawQuery)
if err != nil {
log.Fatal(err)
}
filename := queryform["file"][0]
//初始监听时加载最后500行全部内容
file_path := directory
for _, s := range strings.Split(filename, "~~") {
file_path += "/" + s
}
_, err = os.Stat(file_path)
if err != nil { if err != nil {
utils.Log.Errorf("解析参数错误, %s", err.Error())
return return
} }
lastcontent_msg, err := return_init_str(file_path, 500) // 此文件路径是完整的文件路径
if err != nil { filePath := strings.ReplaceAll(queryForm["file"][0], "\\", "")
log.Fatal(err) // 建立ws客户端
return cli := &Client{time.Now().String(), ws, make(chan []byte, 1024), ""}
}
cli.Initstr = lastcontent_msg
// 监控文件 //加载最后500行全部内容
if _, ok := file_map[filename]; ok { content := logResult(nil, filePath, "500")
file_map[filename][*cli] = true cli.Initstr = content
fmt.Printf("%s has been monitored\n", filename)
// 判断该文件的client对象是否已存在,不存在则新开监控的协程
if _, ok := fileMap[filePath]; ok {
fileMap[filePath][*cli] = true
utils.Log.Infof("%s has been monitored", filePath)
} else { } else {
go monitor(file_path, filename) // 监控文件
go monitor(filePath, cli)
l := make(map[Client]bool) l := make(map[Client]bool)
l[*cli] = true l[*cli] = true
file_map[filename] = l fileMap[filePath] = l
fmt.Printf("Add a new monitored file [%s]\n", filename) utils.Log.Infof("Add a new monitored file [%s]", filePath)
} }
//注册client并开始传输信息 //注册client并开始传输信息
manager.register <- cli manager.register <- cli
go cli.read() //go cli.read()
cli.write() cli.write()
} }
return f return f
} }
// logResult 负责执行linux命令获取结果
func logResult(ctx iris.Context, filePath, lineNums string) string {
// 通过ctx是否为nil来判断是否是批量查询
if ctx == nil {
// 二进制文件直接用grep过滤全部将会很慢
// 不用-a是因为此处过来关键词是空字符串,且文件不确定是否为二进制文件,目的是过滤最后的全部行,如果用-a,grep命令不会报错,将会在此处卡很久
//所以此处命令作用相当于判断是否为二进制文件,
cmd := `grep "" ` + filePath + ` | tail -n ` + lineNums
logResult, err := utils.GetLinuxResult(cmd)
if err != nil {
return "执行过滤命令出错, 命令:" + cmd + ",错误原因:" + err.Error()
}
// 判断日志文件是否为二进制文件,二进制文件结果会返回Binary file,是的话再用strings命令
if strings.Contains(logResult, "Binary file") {
cmd = `strings ` + filePath + ` | tail -n ` + lineNums
logResult, err = utils.GetLinuxResult(cmd)
if err != nil {
return "执行过滤命令出错, 命令:" + cmd + ",错误原因:" + err.Error()
}
}
return logResult
}
keyword := ctx.URLParam("keyword")
filePath = strings.TrimSpace(ctx.URLParam("filePath"))
logs := ctx.URLParam("logs")
logArr := strings.Split(logs, ",")
logResults := ""
haveContent := false
cmd := ""
// 遍历所有日志文件
for index, logName := range logArr {
//-a 防止日志文件是二进制文件查询出错
if keyword == "" {
cmd = `grep "" ` + config.Directory + filePath + "/" + logName + ` | tail -n ` + lineNums
} else {
cmd = `grep -a ` + keyword + ` ` + config.Directory + filePath + "/" + logName + ` | tail -n ` + lineNums
}
logResult, err := utils.GetLinuxResult(cmd)
if err != nil {
return "执行过滤命令出错, 命令:" + cmd + ",错误原因:" + err.Error()
}
// 判断日志文件是否为二进制文件,二进制文件结果会返回Binary file,是的话再用strings命令
if strings.Contains(logResult, "Binary file") {
cmd = `strings ` + config.Directory + filePath + "/" + logName + ` | tail -n ` + lineNums
logResult, err = utils.GetLinuxResult(cmd)
if err != nil {
return "执行过滤命令出错, 命令:" + cmd + ",错误原因:" + err.Error()
}
}
if logResult != "" {
haveContent = true
}
if index == len(logArr)-1 {
logResults += logResult
} else {
logResults += logResult + "\n"
}
}
if haveContent == false {
logResults = "查询结果为空, 命令:" + cmd
}
return logResults
}
// 获取批量查询结果
func getSearchLogResult(ctx iris.Context) {
w := ctx.ResponseWriter()
r := ctx.Request()
// 完成ws协议的握手操作
// Upgrade:websocket
ws, err := upGrader.Upgrade(w, r, nil)
if err != nil {
return
}
cli := &Client{time.Now().String(), ws, make(chan []byte, 1024), ""}
content := logResult(ctx, "", "200")
cli.Initstr = content
//manager.clients[cli] = false
_ = cli.Socket.WriteMessage(websocket.TextMessage, []byte(content))
_ = cli.Socket.Close()
//manager.unregister <- cli
}
...@@ -3,27 +3,39 @@ package code ...@@ -3,27 +3,39 @@ package code
import ( import (
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"html/template" "html/template"
"log"
"net/http" "net/http"
"web_log/utils"
) )
func return_res(w http.ResponseWriter, template_path string, data interface{}) { func returnRes(w http.ResponseWriter, template_path string, data interface{}) {
w.Header().Set("Content-Type", "text/html") w.Header().Set("Content-Type", "text/html")
w.WriteHeader(200) w.WriteHeader(200)
tmpl, err := template.ParseFiles(template_path) tmpl, err := template.ParseFiles(template_path)
if err != nil { if err != nil {
log.Println("err:", err) utils.Log.Errorf("return_res错误: %s", err.Error())
return return
} }
tmpl.Execute(w, data) tmpl.Execute(w, data)
} }
func return_error_res(w http.ResponseWriter, str_file string) { func returnErrorRes(w http.ResponseWriter, str_file string) {
template_path := "web/views/file_not_exist.html" template_path := "web/views/file_not_exist.html"
w.Header().Set("Content-Type", "text/html") w.Header().Set("Content-Type", "text/html")
w.WriteHeader(200) w.WriteHeader(200)
tmpl, err := template.ParseFiles(template_path) tmpl, err := template.ParseFiles(template_path)
if err != nil { if err != nil {
log.Println("err:", err) utils.Log.Errorf("return_error_res错误: %s", err.Error())
return
}
tmpl.Execute(w, str_file)
}
func returnDownErrorRes(w http.ResponseWriter, str_file string) {
template_path := "web/views/down_file_error.html"
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(200)
tmpl, err := template.ParseFiles(template_path)
if err != nil {
utils.Log.Errorf("return_error_res错误: %s", err.Error())
return return
} }
tmpl.Execute(w, str_file) tmpl.Execute(w, str_file)
......
...@@ -4,16 +4,17 @@ import ( ...@@ -4,16 +4,17 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"io" "io"
"log"
"os" "os"
"strings" "strings"
"web_log/utils"
) )
// 读取最后五百行数据
func return_init_str(file_path string, lines_num int) (string, error) { func return_init_str(file_path string, lines_num int) (string, error) {
lines := int64(lines_num) lines := int64(lines_num)
file, err := os.Open(file_path) file, err := os.Open(file_path)
if err != nil { if err != nil {
log.Println(err) utils.Log.Errorf("打开文件失败: %s", err.Error())
return "", err return "", err
} }
fileInfo, _ := file.Stat() fileInfo, _ := file.Stat()
...@@ -39,7 +40,7 @@ func return_init_str(file_path string, lines_num int) (string, error) { ...@@ -39,7 +40,7 @@ func return_init_str(file_path string, lines_num int) (string, error) {
continue continue
} }
if err != nil { if err != nil {
log.Println("Read file error:", err) utils.Log.Errorf("Read file error: %s", err.Error())
return "", err return "", err
} }
strs := strings.Split(string(data[:n]), "\n") strs := strings.Split(string(data[:n]), "\n")
......
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"regexp" "regexp"
"sync" "sync"
. "web_log/config" . "web_log/config"
"web_log/utils"
) )
//var userMap = make(map[string]string) //var userMap = make(map[string]string)
...@@ -33,7 +34,7 @@ func init() { ...@@ -33,7 +34,7 @@ func init() {
//初始化读取配置文件 //初始化读取配置文件
func ReadUserConfig(path string) *MainConfig { func ReadUserConfig(path string) *MainConfig {
if conf != nil && path != UserConfigPath { if conf != nil && path != UserConfigPath {
log.Printf("the config is already initialized, oldPath=%s, path=%s", UserConfigPath, path) utils.Log.Errorf("the config is already initialized, oldPath=%s, path=%s", UserConfigPath, path)
} }
instanceOnce.Do(func() { instanceOnce.Do(func() {
allConfigs, mainConfig := LoadConfig(path) allConfigs, mainConfig := LoadConfig(path)
...@@ -164,17 +165,17 @@ func CutWord(s string) string { ...@@ -164,17 +165,17 @@ func CutWord(s string) string {
func LoadConfig(path string) (Configs, *MainConfig) { func LoadConfig(path string) (Configs, *MainConfig) {
buf, err := ioutil.ReadFile(path) buf, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
log.Panicln("load config conf failed: ", err) utils.Log.Panicf("load config conf failed: %s", err.Error())
} }
mainConfig := &MainConfig{} mainConfig := &MainConfig{}
err = json.Unmarshal(buf, mainConfig) err = json.Unmarshal(buf, mainConfig)
if err != nil { if err != nil {
log.Panicln("decode config file failed:", string(buf), err) utils.Log.Panicf("decode config file failed:", string(buf), err.Error())
} }
allConfigs := make(Configs, 0) allConfigs := make(Configs, 0)
err = json.Unmarshal(buf, &allConfigs) err = json.Unmarshal(buf, &allConfigs)
if err != nil { if err != nil {
log.Panicln("decode config file failed:", string(buf), err) utils.Log.Panicf("decode config file failed:", string(buf), err.Error())
} }
return allConfigs, mainConfig return allConfigs, mainConfig
......
package code package code
import ( import (
"log"
"os" "os"
"time" "time"
"web_log/utils"
) )
// 监控日志文件 // 监控日志文件
func monitor(filePath string, filename string) { func monitor(filePath string, cli *Client) {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
log.Printf("[seelog] error:%+v", err) utils.Log.Errorf("defer [seelog] error:%+v", err)
} }
}() }()
var fileInfo os.FileInfo var fileInfo os.FileInfo
...@@ -18,45 +18,54 @@ func monitor(filePath string, filename string) { ...@@ -18,45 +18,54 @@ func monitor(filePath string, filename string) {
for i := 1; i <= 10; i++ { for i := 1; i <= 10; i++ {
fileInfo, err = os.Stat(filePath) fileInfo, err = os.Stat(filePath)
if err != nil { if err != nil {
log.Printf("[seelog] error:%v", err.Error()) utils.Log.Errorf("循环Stat [seelog] error:%v", err.Error())
continue continue
} }
break break
} }
offset := fileInfo.Size() offset := fileInfo.Size()
startMonitorTime := time.Now().Unix()
for { for {
fileInfo, err = os.Stat(filePath) fileInfo, err = os.Stat(filePath)
if err != nil { if err != nil {
log.Printf("[seelog] error:%v", err.Error()) utils.Log.Errorf("Stat [seelog] error:%v", err.Error())
continue return
} }
newOffset := fileInfo.Size() newOffset := fileInfo.Size()
// 根据文件大小来判断是否新内容
if offset < newOffset { if offset < newOffset {
msg := make([]byte, newOffset-offset) msg := make([]byte, newOffset-offset)
file, err := os.Open(filePath) file, err := os.Open(filePath)
if err != nil { if err != nil {
log.Printf("[seelog] error:%v", err.Error()) utils.Log.Errorf("Open [seelog] error:%v", err.Error())
continue continue
} }
_, err = file.Seek(offset, 0) _, err = file.Seek(offset, 0)
if err != nil { if err != nil {
log.Printf("[seelog] error:%v", err.Error()) utils.Log.Errorf("Seek [seelog] error:%v", err.Error())
continue
} }
_, err = file.Read(msg) _, err = file.Read(msg)
if err != nil { if err != nil {
log.Printf("[seelog] error:%v", err.Error()) utils.Log.Errorf("Read [seelog] error:%v", err.Error())
continue
} }
str_msg := string(msg) strMsg := string(msg)
s := filename + "$$" + str_msg s := filePath + "$$" + strMsg
manager.broadcast <- []byte(s) manager.broadcast <- []byte(s)
offset = newOffset offset = newOffset
file.Close() file.Close()
} }
offset = newOffset offset = newOffset
time.Sleep(200 * time.Millisecond) // 防止协程开启太多
if startMonitorTime < (time.Now().Unix() - 3600) {
utils.Log.Info("超过1小时限制时间,退出协程")
return
} else {
time.Sleep(500 * time.Millisecond)
}
} }
} }
package code package code
import ( import (
"fmt"
"github.com/kataras/iris/v12" "github.com/kataras/iris/v12"
"os" "net/http"
"strings"
"web_log/utils" "web_log/utils"
) )
...@@ -11,9 +11,7 @@ type WebHandler struct { ...@@ -11,9 +11,7 @@ type WebHandler struct {
Directory string Directory string
} }
type Datafile struct { type Datafile struct {
Htmltitle string CurFiles []FileInfo
CurFiles []FileInfo
UrlLocation string
} }
type FileInfo struct { type FileInfo struct {
...@@ -22,56 +20,75 @@ type FileInfo struct { ...@@ -22,56 +20,75 @@ type FileInfo struct {
LastModify string LastModify string
} }
type SearchLog struct {
LogResult string
Keyword string
LogPath string
Servers string
}
var base_path = "web/views/base.html" var base_path = "web/views/base.html"
func (wbh *WebHandler) loginhandle(ctx iris.Context, data interface{}) { // loginHandle 登录界面
return_res(ctx.ResponseWriter(), "web/views/login.html", data) func (wbh *WebHandler) loginHandle(ctx iris.Context, data interface{}) {
returnRes(ctx.ResponseWriter(), "web/views/login.html", data)
} }
func (wbh *WebHandler) indexhandle(ctx iris.Context) {
innerip := ctx.URLParam("innerip")
server_direc := ctx.URLParam("server_direc")
file := ctx.URLParam("filename")
if innerip != "" && server_direc != "" && file != "" {
str_filepath := innerip + "~~" + server_direc + "~~" + file
filename := innerip + "/" + server_direc + "/" + file
_, err := os.Stat(wbh.Directory + "/" + filename) // indexHandle 主界面
if err != nil { func (wbh *WebHandler) indexHandle(ctx iris.Context) {
return_error_res(ctx.ResponseWriter(), filename) filePath := ctx.URLParam("filePath")
return filePath = wbh.Directory + filePath
} isFile := utils.IsDir(filePath)
fmt.Printf("view log [%s]\n", file) if isFile != true {
return_res(ctx.ResponseWriter(), "web/views/page.html", str_filepath) returnRes(ctx.ResponseWriter(), "web/views/page.html", filePath)
return return
} else { }
var htmltitle, urlloction string filesInfo, err := utils.PathFileInfo(filePath)
dir := wbh.Directory if err != nil {
if innerip == "" && server_direc == "" && file == "" { returnErrorRes(ctx.ResponseWriter(), filePath)
htmltitle = "选择服务器" return
urlloction = "?innerip=" }
} var fs = make([]FileInfo, 0)
if innerip != "" && server_direc == "" && file == "" { isLogFile := false
htmltitle = "选择服务" for _, v := range filesInfo {
urlloction = "&server_direc=" if !utils.IsDir(filePath + "/" + v["file_name"]) {
dir += "/" + innerip isLogFile = true
}
if innerip != "" && server_direc != "" && file == "" {
htmltitle = "选择文件"
urlloction = "&filename="
dir += "/" + innerip + "/" + server_direc
}
filesinfo, err := utils.PathFileInfo(dir)
if err != nil {
return_error_res(ctx.ResponseWriter(), dir)
}
var fs = make([]FileInfo, 0)
for _, v := range filesinfo {
fs = append(fs, FileInfo{Name: v["file_name"], Size: v["file_size"], LastModify: v["last_modify"]})
} }
data := Datafile{Htmltitle: htmltitle, CurFiles: fs, UrlLocation: urlloction} fs = append(fs, FileInfo{Name: v["file_name"], Size: v["file_size"], LastModify: v["last_modify"]})
return_res(ctx.ResponseWriter(), base_path, data)
} }
data := Datafile{CurFiles: fs}
if isLogFile == false {
returnRes(ctx.ResponseWriter(), base_path, data)
} else {
returnRes(ctx.ResponseWriter(), "web/views/base_search.html", data)
}
}
// searchLogHandle 过滤界面
func (wbh *WebHandler) searchLogHandle(ctx iris.Context) {
returnRes(ctx.ResponseWriter(), "web/views/search_log.html", nil)
return
}
// downLogHandle 过滤界面
func (wbh *WebHandler) downLogHandle(ctx iris.Context) {
filePath := strings.TrimSpace(ctx.URLParam("filePath"))
filePath = wbh.Directory + filePath
utils.Log.Infof(filePath)
fileBody, readErr := utils.ReadFile(filePath)
if readErr != nil {
utils.Log.Errorf("读取文件失败:%s, %v", filePath, readErr.Error())
returnDownErrorRes(ctx.ResponseWriter(), readErr.Error())
return
}
//_ = strings.Split(logs, ",")
//body := []byte("数据测试")
ctx.ResponseWriter().Header().Set("Content-Type", "application/octet-stream")
ctx.ResponseWriter().WriteHeader(http.StatusOK)
_, err := ctx.ResponseWriter().Write(fileBody)
if err != nil {
utils.Log.Errorf("写入数据失败:%s, %v", filePath, err.Error())
returnDownErrorRes(ctx.ResponseWriter(), err.Error())
}
return
} }
package code package code
import ( import (
"fmt"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"log"
"strings" "strings"
"time"
"web_log/utils"
) )
// websocket客户端 // websocket客户端
...@@ -19,9 +19,9 @@ type Client struct { ...@@ -19,9 +19,9 @@ type Client struct {
type clientManager struct { type clientManager struct {
clients map[*Client]bool clients map[*Client]bool
broadcast chan []byte broadcast chan []byte
register chan *Client register chan *Client //注册通道
unregister chan *Client unregister chan *Client //注销通道
filemap map[string]map[Client]bool filemap map[string]map[Client]bool //存储文件对象对应的客户端对象
} }
var manager = clientManager{ var manager = clientManager{
...@@ -37,40 +37,42 @@ func getfilemap() map[string]map[Client]bool { ...@@ -37,40 +37,42 @@ func getfilemap() map[string]map[Client]bool {
return file_map return file_map
} }
// 死循环,负责处理通道
func (manager *clientManager) start() { func (manager *clientManager) start() {
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
log.Printf("[seelog] error:%+v", err) utils.Log.Errorf("[seelog] error:%+v", err)
} }
}() }()
for { for {
select { select {
// 注册通道接收到客户端对象
case conn := <-manager.register: case conn := <-manager.register:
manager.clients[conn] = true manager.clients[conn] = true
fmt.Printf("A client connect, current clients count is %d \n", len(manager.clients)) utils.Log.Infof("A client connect, current clients count is %d", len(manager.clients))
// 注销通道接收到客户端对象
case conn := <-manager.unregister: case conn := <-manager.unregister:
if _, ok := manager.clients[conn]; ok { if _, ok := manager.clients[conn]; ok {
close(conn.Send) close(conn.Send) //关闭发送通道
delete(manager.clients, conn) delete(manager.clients, conn) //删除存储对象
for _, map_tmp := range manager.filemap { for _, mapTmp := range manager.filemap {
delete(map_tmp, *conn) delete(mapTmp, *conn) // 从fileMap删除客户端对象
} }
fmt.Printf("A client disconnect, current clients count is %d \n", len(manager.clients)) utils.Log.Infof("A client disconnect, current clients count is %d", len(manager.clients))
} }
// 消息通道接收到消息
case message := <-manager.broadcast: case message := <-manager.broadcast:
str_msg := string(message) strMsg := string(message)
tmp := strings.Split(str_msg, "$$") tmp := strings.Split(strMsg, "$$")
filename := tmp[0] filename := tmp[0]
msg := tmp[1] msg := tmp[1]
map_tmp := manager.filemap[filename] mapTmp := manager.filemap[filename]
for conn, _ := range map_tmp { for conn, _ := range mapTmp {
select { select {
// 将消息发送到客户端的Send通道
case conn.Send <- []byte(msg): case conn.Send <- []byte(msg):
default:
close(conn.Send)
delete(manager.clients, &conn)
} }
} }
...@@ -86,14 +88,14 @@ func (manager *clientManager) start() { ...@@ -86,14 +88,14 @@ func (manager *clientManager) start() {
} }
} }
// ws 写方法
func (c *Client) write() { func (c *Client) write() {
//defer func() {
// manager.unregister <- c // 写消息
// c.Socket.Close()
//}()
if err := c.Socket.WriteMessage(websocket.TextMessage, []byte(c.Initstr)); err != nil { if err := c.Socket.WriteMessage(websocket.TextMessage, []byte(c.Initstr)); err != nil {
return return
} }
// 死循环,监听ws客户端的发送通道,接收到关闭信号则退出循环
for { for {
select { select {
case message, ok := <-c.Send: case message, ok := <-c.Send:
...@@ -106,6 +108,7 @@ func (c *Client) write() { ...@@ -106,6 +108,7 @@ func (c *Client) write() {
} }
} }
//ws 读方法,接收前段通过同一个ws客户端发过来的消息
func (c *Client) read() { func (c *Client) read() {
defer func() { defer func() {
manager.unregister <- c manager.unregister <- c
...@@ -114,74 +117,18 @@ func (c *Client) read() { ...@@ -114,74 +117,18 @@ func (c *Client) read() {
for { for {
_, reply, err := c.Socket.ReadMessage() _, reply, err := c.Socket.ReadMessage()
if err != nil { if err != nil {
fmt.Println("Error! Can't receive message...") utils.Log.Error("Error! Can't receive message...")
break break
} }
// 前端会有定时器,定时发送heart消息
if string(reply) != "heart" { if string(reply) != "heart" {
manager.unregister <- c manager.unregister <- c
c.Socket.Close() c.Socket.Close()
} else {
fmt.Println("heart package...")
} }
//reply = []byte //reply = []byte
//time.Sleep(2000 * time.Millisecond) time.Sleep(5000 * time.Millisecond)
//jsonMessage, _ := json.Marshal(&Message{Sender: c.Id, Content: string(message)}) //jsonMessage, _ := json.Marshal(&Message{Sender: c.Id, Content: string(message)})
//manager.broadcast <- jsonMessage //manager.broadcast <- jsonMessage
} }
} }
//func (c *client) monitor(filePath string, filename string) {
// defer func() {
// if err := recover(); err != nil {
// log.Printf("[seelog] error:%+v", err)
// }
// }()
// var fileInfo os.FileInfo
// var err error
// for i := 1; i <= 10; i++ {
// fileInfo, err = os.Stat(filePath)
// if err != nil {
// log.Printf("[seelog] error:%v", err.Error())
// continue
// }
// break
// }
//
// offset := fileInfo.Size()
// for {
// fileInfo, err = os.Stat(filePath)
// if err != nil {
// log.Printf("[seelog] error:%v", err.Error())
// continue
// }
// newOffset := fileInfo.Size()
// if offset < newOffset {
// msg := make([]byte, newOffset-offset)
// file, err := os.Open(filePath)
// if err != nil {
// log.Printf("[seelog] error:%v", err.Error())
// continue
// }
// _, err = file.Seek(offset, 0)
// if err != nil {
// log.Printf("[seelog] error:%v", err.Error())
// }
//
// _, err = file.Read(msg)
// if err != nil {
// log.Printf("[seelog] error:%v", err.Error())
// }
// str_msg := string(msg)
// s := filename + "$$" + str_msg
//
// manager.broadcast <- []byte(s)
// offset = newOffset
// file.Close()
// }
// offset = newOffset
// time.Sleep(200 * time.Millisecond)
// }
//
//}
...@@ -20,7 +20,7 @@ func GetCurrentPath() string { ...@@ -20,7 +20,7 @@ func GetCurrentPath() string {
return strings.Replace(dir, "\\", "/", -1) return strings.Replace(dir, "\\", "/", -1)
} }
func InitConfig() { func InitConfig() {
flag.StringVar(&Directory, "d", "/data/inotify_logs", "监听目录") flag.StringVar(&Directory, "d", "/data", "监听目录")
flag.StringVar(&Port, "p", "9997", "监听端口") flag.StringVar(&Port, "p", "9997", "监听端口")
flag.Parse() flag.Parse()
_, err := os.Stat(Directory) _, err := os.Stat(Directory)
......
{ {
"weblog": "824d8eb068e6917d21e8d39db9edffe7" "weblog": "e10adc3949ba59abbe56e057f20f883e"
} }
\ No newline at end of file
...@@ -4,29 +4,35 @@ go 1.14 ...@@ -4,29 +4,35 @@ go 1.14
require ( require (
github.com/CloudyKit/jet/v3 v3.0.1 // indirect github.com/CloudyKit/jet/v3 v3.0.1 // indirect
github.com/gin-gonic/gin v1.6.3 // indirect github.com/ajg/form v1.5.1 // indirect
github.com/go-playground/validator/v10 v10.4.1 // indirect github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect
github.com/golang/protobuf v1.4.3 // indirect github.com/google/go-querystring v1.1.0 // indirect
github.com/gorilla/websocket v1.4.2 github.com/gorilla/websocket v1.5.0
github.com/imkira/go-interpol v1.1.0 // indirect
github.com/iris-contrib/jade v1.1.4 // indirect github.com/iris-contrib/jade v1.1.4 // indirect
github.com/iris-contrib/schema v0.0.6 // indirect github.com/iris-contrib/schema v0.0.6 // indirect
github.com/json-iterator/go v1.1.10 // indirect github.com/json-iterator/go v1.1.10 // indirect
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect
github.com/kataras/golog v0.1.6 // indirect github.com/kataras/golog v0.1.6 // indirect
github.com/kataras/iris/v12 v12.1.8 github.com/kataras/iris/v12 v12.1.8
github.com/klauspost/compress v1.11.4 // indirect github.com/mattn/go-colorable v0.1.12 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/microcosm-cc/bluemonday v1.0.4 // indirect github.com/microcosm-cc/bluemonday v1.0.4 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/moul/http2curl v1.0.0 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/gomega v1.19.0 // indirect
github.com/ryanuber/columnize v2.1.2+incompatible // indirect github.com/ryanuber/columnize v2.1.2+incompatible // indirect
github.com/sergi/go-diff v1.2.0 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/ugorji/go v1.2.2 // indirect github.com/smartystreets/goconvey v1.7.2 // indirect
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect github.com/valyala/fasthttp v1.35.0 // indirect
golang.org/x/net v0.0.0-20201224014010-6772e930b67b github.com/xeipuuv/gojsonschema v1.2.0 // indirect
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 // indirect github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect
golang.org/x/text v0.3.4 // indirect github.com/yudai/gojsondiff v1.0.0 // indirect
google.golang.org/protobuf v1.25.0 // indirect github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
github.com/yudai/pp v2.0.1+incompatible // indirect
go.uber.org/zap v1.21.0
gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
) )
This diff is collapsed. Click to expand it.
...@@ -3,6 +3,8 @@ package main ...@@ -3,6 +3,8 @@ package main
import ( import (
"fmt" "fmt"
"github.com/kataras/iris/v12" "github.com/kataras/iris/v12"
"go.uber.org/zap/zapcore"
"runtime"
"web_log/code" "web_log/code"
"web_log/config" "web_log/config"
"web_log/utils" "web_log/utils"
...@@ -12,6 +14,12 @@ import ( ...@@ -12,6 +14,12 @@ import (
func main() { func main() {
config.InitConfig() config.InitConfig()
sysType := runtime.GOOS
if sysType == "windows" {
utils.LogSetUp(zapcore.DebugLevel, false)
} else {
utils.LogSetUp(zapcore.DebugLevel, true)
}
addrs := "0.0.0.0:" + config.Port addrs := "0.0.0.0:" + config.Port
fmt.Printf("查看日志目录: %s \n用户配置文件: %s\n", config.Directory, config.UserConfigPath) fmt.Printf("查看日志目录: %s \n用户配置文件: %s\n", config.Directory, config.UserConfigPath)
......
package utils
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
"os"
"time"
)
var Log *zap.SugaredLogger //在性能很好但不是很关键的上下文中,使用SugaredLogger。它比其他结构化日志记录包快4-10倍,并且支持结构化和printf风格的日志记录。
//在每一微秒和每一次内存分配都很重要的上下文中,使用Logger。它甚至比SugaredLogger更快,内存分配次数也更少,但它只支持强类型的结构化日志记录。
func LogSetUp(logLevel zapcore.Level, isLinux bool) {
config := zapcore.EncoderConfig{
MessageKey: "msg", //结构化(json)输出:msg的key
LevelKey: "level", //结构化(json)输出:日志级别的key(INFO,WARN,ERROR等)
TimeKey: "time", //结构化(json)输出:时间的key(INFO,WARN,ERROR等)
CallerKey: "file", //结构化(json)输出:打印日志的文件对应的Key
StacktraceKey: "stacktrace", //结构化(json)输出:堆栈信息的key
EncodeLevel: zapcore.CapitalLevelEncoder, //将日志级别转换成大写(INFO,WARN,ERROR等)
EncodeCaller: zapcore.ShortCallerEncoder, //采用短文件路径编码输出(test/main.go:14 )
EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(t.Format("2006-01-02 15:04:05"))
}, //输出的时间格式
EncodeDuration: func(d time.Duration, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendInt64(int64(d) / 1000000)
},
}
encoder := zapcore.NewJSONEncoder(config)
debugLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl == zapcore.DebugLevel
})
infoLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl == zapcore.InfoLevel
})
//自定义日志级别:自定义Error级别
errorLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl >= zapcore.WarnLevel && lvl >= logLevel
})
var core zapcore.Core
// 获取io.Writer的实现
if isLinux {
logPath := "/data/liexin_logs/web_log/"
debugWriter := getLogWriter(logPath + "debug.log")
infoWriter := getLogWriter(logPath + "info.log")
errorWriter := getLogWriter(logPath + "error.log")
// 实现多个输出
core = zapcore.NewTee(
zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(debugWriter), debugLevel),
zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(infoWriter), infoLevel), //将info写入infoPath
zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(errorWriter), errorLevel), //warn及以上写入errPath
)
} else {
core = zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout)), logLevel) //将日志输出到控制台
}
Log = zap.New(core, zap.AddCaller(), zap.AddStacktrace(zap.ErrorLevel)).Sugar()
return
}
func getLogWriter(filename string) zapcore.WriteSyncer {
lumberJackLogger := zapcore.AddSync(&lumberjack.Logger{
Filename: filename,
MaxSize: 100,
MaxBackups: 10,
MaxAge: 1,
Compress: false,
LocalTime: true,
})
return lumberJackLogger
}
package utils package utils
import ( import (
"bufio"
"fmt"
"io"
"io/ioutil" "io/ioutil"
"math" "math"
"os" "os"
"os/exec"
"runtime" "runtime"
"sort" "sort"
"strconv" "strconv"
) )
// 判断所给路径是否为文件夹
func IsDir(path string) bool {
s, err := os.Stat(path)
if err != nil {
return false
}
return s.IsDir()
}
func PathExists(path string) (bool, error) { func PathExists(path string) (bool, error) {
_, err := os.Stat(path) _, err := os.Stat(path)
if err == nil { if err == nil {
...@@ -55,6 +68,7 @@ func Unwrap(num int64, retain int) float64 { ...@@ -55,6 +68,7 @@ func Unwrap(num int64, retain int) float64 {
func PathFileInfo(path string) ([]map[string]string, error) { func PathFileInfo(path string) ([]map[string]string, error) {
files, err := ioutil.ReadDir(path) files, err := ioutil.ReadDir(path)
if err != nil { if err != nil {
fmt.Println(err.Error())
return nil, err return nil, err
} }
var filesinfo = make([]map[string]interface{}, 0) var filesinfo = make([]map[string]interface{}, 0)
...@@ -96,3 +110,62 @@ func RunEnv() bool { ...@@ -96,3 +110,62 @@ func RunEnv() bool {
return true return true
} }
} }
// 使用linux命令获取结果
func GetLinuxResult(linuxCmd string) (string, error) {
cmd := exec.Command("/bin/bash", "-c", linuxCmd)
//创建获取命令输出管道
stdout, err := cmd.StdoutPipe()
if err != nil {
fmt.Printf("Error:can not obtain stdout pipe for command:%s\n", err)
return "", err
}
//执行命令
if err := cmd.Start(); err != nil {
fmt.Println("Error:The command is err,", err)
return "", err
}
//读取所有输出
bytes, err := ioutil.ReadAll(stdout)
if err != nil {
fmt.Println("ReadAll Stdout:", err.Error())
return "", err
}
if err := cmd.Wait(); err != nil {
fmt.Println("wait:", err.Error())
return "", err
}
//fmt.Printf("stdout:\n\n %s", bytes)
return string(bytes), nil
}
func ReadFile(filePath string) ([]byte, error) {
// 创建句柄
fi, err := os.Open(filePath)
if err != nil {
return nil, err
}
defer fi.Close()
// 创建 Reader
r := bufio.NewReader(fi)
s := ""
// 每次读取 1024 个字节
buf := make([]byte, 1024)
for {
//func (b *Reader) Read(p []byte) (n int, err error) {}
n, err := r.Read(buf)
if err != nil && err != io.EOF {
return nil, err
}
if n == 0 {
break
}
s += string(buf[:n])
}
return []byte(s), nil
}
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
</head> </head>
<body> <body>
<div style="font-size: 20px" >{{ .Htmltitle }}</div> <div style="font-size: 20px" >请选择</div>
<div id="show" style="overflow-y: scroll;"> <div id="show" style="overflow-y: scroll;">
</div> </div>
...@@ -23,24 +23,28 @@ ...@@ -23,24 +23,28 @@
if (1024>sz){ if (1024>sz){
sz = sz.toFixed(3) sz = sz.toFixed(3)
sz +='K' sz +='K'
}else { } else {
sz /= 1024 sz /= 1024
sz = sz.toFixed(3) sz = sz.toFixed(3)
sz+= 'M' sz+= 'M'
} }
var f_name = "<label class='tmp' style='margin-right: 1px'>" + {{ .Name }} + "</label>" var f_name = "<label class='tmp' style='margin-right: 1px'>" + {{ .Name }} + "</label>"
{{/* 如果 url里面没有server_direc这个参数,说明还没进到选文件的页面,也就是展示的列表全是文件夹,文件夹不予显示大小*/}} var f_size = "<label class='size' style='margin-right: 100px'></label>"
if ( window.location.href.indexOf("server_direc")==-1){
sz = ""
}
var f_size = "<label class='size' style='margin-right: 100px'>" + sz + "</label>"
var f_lastmodify ="<label>" + (new Date(lastmodify*1000)).toLocaleString() + "</label>" var f_lastmodify ="<label>" + (new Date(lastmodify*1000)).toLocaleString() + "</label>"
var f_info = f_name+f_size+f_lastmodify var f_info = f_name+f_size+f_lastmodify
$('#show').append("<a style='padding: 10px' href=" + window.location.href + {{ $.UrlLocation }} +{{ .Name }} + ">"+ f_info +"</a>") $('#show').append("<a id='file' onclick='fileClick(this)'>"+ f_info +"</a>")
{{ end }} {{ end }}
function fileClick(ele) {
const text = $(ele).find(".tmp")[0].innerText
if ( window.location.href.indexOf("filePath")===-1){
window.location.href += "?filePath=/" + text
} else {
window.location.href = window.location.href + "/" + text
}
}
</script> </script>
...@@ -57,26 +61,26 @@ ...@@ -57,26 +61,26 @@
margin: 0; margin: 0;
background-color: #cccccc; background-color: #cccccc;
} }
a:active, /*a:active,*/
a:visited, /*a:visited,*/
a:link { /*a:link {*/
display: block; /* display: block;*/
text-decoration: none; /* text-decoration: none;*/
margin: 6px 10px 6px 0px; /* margin: 6px 10px 6px 0px;*/
padding: 2px 6px 2px 6px; /* padding: 2px 6px 2px 6px;*/
color: #00527f; /* color: #00527f;*/
background-color: #d9e8f3; /* background-color: #d9e8f3;*/
border: 1px solid #004266; /* border: 1px solid #004266;*/
} /*}*/
a:hover { /*a:hover {*/
color: red; /* color: red;*/
background-color: #8cbbda; /* background-color: #8cbbda;*/
} /*}*/
label{ label{
display:inline-block; display:inline-block;
width:200px; width:200px;
text-align:right; text-align:left;
} }
.tmp{ .tmp{
display:inline-block; display:inline-block;
...@@ -84,6 +88,15 @@ ...@@ -84,6 +88,15 @@
text-align:left; text-align:left;
} }
#file {
padding: 10px;
border: 1px solid #004266;
background-color:#d9e8f3;
display: block;
margin:6px 10px 6px 0px;
color:#00527f;
}
</style > </style >
</body> </body>
</html> </html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>选择</title>
<script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<div style="font-size: 20px" >请选择</div>
<div id="show" style="overflow-y: scroll;">
</div>
<label ></label>
<script>
String.prototype.endWith=function(endStr){undefined
const d = this.length - endStr.length;
return (d>=0&&this.lastIndexOf(endStr)===d);
}
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (let i=0; i<vars.length; i++) {
const pair = vars[i].split("=");
if(pair[0] === variable){return pair[1];}
}
return false;
}
function downLoad(url) {
const xhr = new XMLHttpRequest();
xhr.open("get", url, true);
xhr.responseType = "blob";
xhr.onload = function () {
const blob = this.response;
const fileName = url.split("/")[url.split("/").length - 2]+"/"+url.split("/")[url.split("/").length - 1]; // 导出文件名
let a = document.createElement("a");
a.download = fileName;
a.style.display = "none";
a.href = window.URL.createObjectURL(blob);
a.target = "_blank";
document.body.appendChild(a);
a.click();
URL.revokeObjectURL(a.href); // 释放URL 对象
document.body.removeChild(a); // 删除 a 标签
};
xhr.send();
}
$("#show").height(document.documentElement.scrollHeight-50)
$('#show').append("<span><label class='key'>过滤关键词</label><input id='keyword' type='text'><button id='but'>过滤</button><button id='download'>下载文件</button></span><label class='desc'>请输入关键词,查询方法采用grep原生方法, 下载文件反应时间和文件大小有关,请耐心等待</label>")
//
{{ range .CurFiles }}
var lastmodify = {{ .LastModify }}
var sz = {{ .Size }}
sz = sz/1024
if (1024>sz){
sz = sz.toFixed(3)
sz +='K'
} else {
sz /= 1024
sz = sz.toFixed(3)
sz+= 'M'
}
var f_name = "<label class='name'>" + {{ .Name }}+"</label>"
var f_size = "<label class='size' style='margin-right: 100px'>" + sz + "</label>"
var f_lastmodify ="<label>" + (new Date(lastmodify*1000)).toLocaleString() + "</label>"
var f_info = f_name+f_size+f_lastmodify
$('#show').append("<span class='checkSpan'><input type='checkbox' name='check' class='tmp' style='margin-right: 1px'><a id='file' onclick='fileClick(this)'>"+f_info+"</a></span>")
{{ end }}
$("#but").click(function () {
const checkSpanArr = document.getElementsByClassName("checkSpan");
const logs = [];
for (let i = 0; i < checkSpanArr.length; i++) {
const tmp = checkSpanArr[i].getElementsByClassName("tmp")[0];
if (tmp.checked === true){
logs.push(checkSpanArr[i].getElementsByClassName("name")[0].textContent)
}
}
if (logs.length === 0){
alert("请勾选需要查询的日志")
return
}
const keyword = encodeURIComponent(document.getElementById("keyword").value)
const filePath = getQueryVariable("filePath")
window.location.href = "/searchLog?keyword=" + keyword + "&filePath="+filePath+"&logs=" + logs
});
$("#download").click(function () {
const checkSpanArr = document.getElementsByClassName("checkSpan");
const logs = [];
for (let i = 0; i < checkSpanArr.length; i++) {
const tmp = checkSpanArr[i].getElementsByClassName("tmp")[0];
if (tmp.checked === true){
logs.push(checkSpanArr[i].getElementsByClassName("name")[0].textContent)
const size = checkSpanArr[i].getElementsByClassName("size")[0].textContent;
if (size.indexOf("M")!==-1){
if (parseFloat(size) > 20){
alert("暂不支持下载超过20M的日志")
return
}
}
}
}
if (logs.length === 0){
alert("请勾选需要下载的日志")
return
}
if (logs.length > 1){
alert("请只勾选一个需要下载的日志")
return
}
const filePath = getQueryVariable("filePath")
var href = "http://" + window.location.host + "/downLog?filePath="+filePath+"/"+logs[0]
downLoad(href)
// window.open(href);
});
function fileClick(ele) {
const text = $(ele).find(".name")[0].innerText
console.log(text)
if ( window.location.href.indexOf("filePath")===-1){
window.location.href += "?filePath=/" + text
} else {
window.location.href = window.location.href + "/" + text
}
}
</script>
<style>
html{
width: 100%;
height: 100%;
overflow: hidden;
}
body{
width: 100%;
height: 100%;
font-family: 'Open Sans',sans-serif;
margin: 0;
background-color: #cccccc;
}
#keyword{
background-color: lightyellow;
color: black;
font-size: medium;
height: 25px;
width: 300px;
margin-left: 22px;
}
#log_path{
background-color: lightyellow;
color: black;
font-size: medium;
height: 25px;
width: 500px;
margin-left: 22px;
}
#but{
width: 58px;
height: 30px;
margin-left: 22px;
font-size: 15px;
background-color: lightblue
}
#download{
width: 116px;
height: 30px;
margin-left: 22px;
font-size: 15px;
background-color: lightblue
}
label{
display:inline-block;
width:200px;
text-align:right;
}
.key {
display:inline-block;
width:auto;
margin-left: 10px;
text-align:right;
}
.log {
display:inline-block;
width:auto;
margin-left: 30px;
text-align:right;
}
.tmp{
display:inline-block;
width:25px;
text-align:left;
}
.checkSpan {
padding: 10px;
border: 1px solid #004266;
background-color:#d9e8f3;
display: block;
margin:6px 10px 6px 0px;
color:#00527f;
}
.name {
width: 500px;
text-align: left;
}
#file {
color: #4bbdff;
}
.desc {
margin-left: 20px;
width: auto;
}
</style >
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
alert("下载文件出错: {{ . }}重定向到首页")
window.location.href = "index"
</script>
</body>
</html>
\ No newline at end of file
...@@ -17,8 +17,7 @@ ...@@ -17,8 +17,7 @@
var ws = new WebSocket("ws://" + window.location.host + "/ws?file={{ . }}"); var ws = new WebSocket("ws://" + window.location.host + "/ws?file={{ . }}");
ws.onmessage = function (e) { ws.onmessage = function (e) {
console.log(filterText) snsArr = e.data.split(/[\r\n]/);
snsArr = e.data.split(/[(\r\n)\r\n]+/);
snsArr.forEach((item, index) => { snsArr.forEach((item, index) => {
if (!item) { if (!item) {
snsArr.splice(index, 1);//删除空项 snsArr.splice(index, 1);//删除空项
...@@ -26,23 +25,26 @@ ...@@ -26,23 +25,26 @@
}) })
snsArr.forEach((item, index) => { snsArr.forEach((item, index) => {
if (out && (filterText == "" || item.indexOf(filterText) != -1)) { if (out && (filterText == "" || item.indexOf(filterText) != -1)) {
$('#log').append("<pre style='color: white;font-size: 15px;margin: 3px'>" + item + "</pre>").scrollTop($('#log')[0].scrollHeight) $('#log').append("<span style='color: white;font-size: 15px;margin: 3px'>" + item + "</span><br><br>").scrollTop($('#log')[0].scrollHeight)
}else { }else {
$('#log').append("<pre style='display:none;color: white;font-size: 15px;margin: 3px'>" + item + "</pre>").scrollTop($('#log')[0].scrollHeight) $('#log').append("<span style='display:none;color: white;font-size: 15px;margin: 3px'>" + item + "</span><br><br>").scrollTop($('#log')[0].scrollHeight)
} }
}) })
}; };
ws.onclose = function () { ws.onclose = function () {
$('#status').css("background-color", "red").text("链接断开") $('#status').css("background-color", "red").text("链接断开")
console.log("onclose")
reConnect() reConnect()
} }
ws.onopen = function () { ws.onopen = function () {
$('#status').css("background-color", "chartreuse").text("连接成功") $('#status').css("background-color", "chartreuse").text("连接成功")
$('#clear').click() // $('#clear').click()
console.log("onopen")
} }
ws.onerror = function (e) { ws.onerror = function (e) {
$('#status').css("background-color", "red").text("链接断开") $('#status').css("background-color", "red").text("链接断开")
console.log("onerror")
} }
// function send_heart() { // function send_heart() {
// ws.send("heart") // ws.send("heart")
...@@ -84,11 +86,11 @@ ...@@ -84,11 +86,11 @@
$('#filter').on('input', function () { $('#filter').on('input', function () {
filterText = $('#filter').val() filterText = $('#filter').val()
$('#log pre').each(function () { $('#log span').each(function () {
$(this).attr("style", "display:block;color: white;font-size: 15px;margin: 3px") $(this).attr("style", "display:block;color: white;font-size: 15px;margin: 3px")
}) })
if (filterText != ""){ if (filterText != ""){
$('#log pre').each(function () { $('#log span').each(function () {
if ($(this).text().indexOf(filterText) == -1) { if ($(this).text().indexOf(filterText) == -1) {
$(this).attr("style", "display:none") $(this).attr("style", "display:none")
} }
......
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>查询日志</title>
<link rel="shortcut icon" href="http://www.eiguo.cn/favicon.ico" type="image/x-icon"/>
<script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/html2canvas/0.5.0-beta4/html2canvas.min.js"></script>
</head>
<body>
<div id="show" style="overflow-y: scroll;"></div>
<div id="searchResult"></div>
<script>
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] === variable){return pair[1];}
}
return false;
}
const keyword = decodeURI(getQueryVariable("keyword"));
const filePath = getQueryVariable("filePath");
const logs = getQueryVariable("logs");
$('#show').append("<span><label class='key'>过滤关键词</label><input id='keyword' type='text' value='"+keyword+"'><button id='but'>过滤</button></span>")
const ws = new WebSocket("ws://" + window.location.host + "/wsSearchLog?keyword="+keyword+"&filePath="+filePath+"&logs="+logs);
ws.onmessage = function (e) {
let snsArr = e.data.split(/[\r\n]/);
snsArr.forEach((item, index) => {
if (!item) {
snsArr.splice(index, 1);//删除空项
}
})
snsArr.forEach((item, index) => {
$('#searchResult').append("<span style='color: white;font-size: 15px;margin: 3px'>" + item + "</span><br><br>").scrollTop($('#searchResult')[0].scrollHeight)
})
}
$("#but").click(function () {
const butKeyword = encodeURIComponent(document.getElementById("keyword").value)
window.location.href = "/searchLog?keyword=" + butKeyword + "&filePath="+filePath+"&logs=" + logs
});
</script>
</body>
<style>
body {
margin-left: 2%
}
#searchResult {
width: 98%;
height: 100%;
background-color: #181818;
border: 1px #ccc solid;
overflow-y: scroll;
margin-top: 10px;
padding-left: 12px;
float: left;
}
#keyword{
background-color: lightyellow;
color: black;
font-size: medium;
height: 25px;
width: 300px;
margin-left: 22px;
}
#but{
width: 58px;
height: 30px;
margin-left: 22px;
font-size: 15px;
background-color: lightblue
}
label{
display:inline-block;
width:200px;
text-align:right;
}
.key {
display:inline-block;
width:auto;
margin-left: 10px;
text-align:right;
}
.log {
display:inline-block;
width:auto;
margin-left: 30px;
text-align:right;
}
</style>
</html>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment