Commit 1fc283e0 by 刘豪

更新日志

parent a9711d52
package code
import (
"fmt"
"github.com/gorilla/websocket"
"github.com/kataras/iris/v12"
"log"
"net/url"
"os"
"strings"
"time"
"web_log/config"
"web_log/utils"
)
type User struct {
......@@ -20,45 +20,44 @@ func GetApp(directory string, deBug bool) *iris.Application {
w := &WebHandler{directory}
app.Get("/", func(ctx iris.Context) {
w.loginhandle(ctx, nil)
w.loginHandle(ctx, nil)
})
app.Post("/login", func(ctx iris.Context) {
var user User
ctx.ReadForm(&user)
//user.Username = "zzl"
//user.Password = "Lx@520m!@@#"
if ok2, msg := VerifyUser(user.Username, user.Password); ok2 {
ctx.SetCookieKV("sessionid", msg)
ctx.Redirect("/index", 301)
} else {
fmt.Println("验证不通过")
w.loginhandle(ctx, msg)
utils.Log.Warn("验证不通过")
w.loginHandle(ctx, msg)
}
})
if !deBug {
app.Use(func(ctx iris.Context) {
sessionid := ctx.GetCookie("sessionid")
ok1, msg := VerifySessionID(sessionid)
fmt.Println(msg)
utils.Log.Info(msg)
if ok1 {
ctx.Next()
} else {
w.loginhandle(ctx, "未登录")
w.loginHandle(ctx, "未登录")
}
})
}
app.Get("/index", w.indexhandle)
app.Get("/ws", genCtxHand(directory, manager.filemap))
app.Get("/index", w.indexHandle)
app.Get("/ws", getCtxHand(manager.filemap))
app.Get("/wsSearchLog", getSearchLogResult)
app.Get("/searchLog", w.searchLogHandle)
app.Get("/downLog", w.downLogHandle)
go manager.start()
return app
}
// file_map -> {filename : {client: True}}
func genCtxHand(directory string, file_map map[string]map[Client]bool) func(ctx iris.Context) {
// getCtxHand 获取单个文件的最后五百行内容,并开启额外协程监控文件是否更新以达到实时加载的目的
func getCtxHand(fileMap map[string]map[Client]bool) func(ctx iris.Context) {
f := func(ctx iris.Context) {
w := ctx.ResponseWriter()
r := ctx.Request()
......@@ -68,46 +67,120 @@ func genCtxHand(directory string, file_map map[string]map[Client]bool) func(ctx
if err != nil {
return
}
cli := &Client{time.Now().String(), ws, make(chan []byte, 1024), ""}
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)
queryForm, err := url.ParseQuery(r.URL.RawQuery)
if err != nil {
utils.Log.Errorf("解析参数错误, %s", err.Error())
return
}
lastcontent_msg, err := return_init_str(file_path, 500)
if err != nil {
log.Fatal(err)
return
}
cli.Initstr = lastcontent_msg
// 此文件路径是完整的文件路径
filePath := strings.ReplaceAll(queryForm["file"][0], "\\", "")
// 建立ws客户端
cli := &Client{time.Now().String(), ws, make(chan []byte, 1024), ""}
// 监控文件
if _, ok := file_map[filename]; ok {
file_map[filename][*cli] = true
fmt.Printf("%s has been monitored\n", filename)
//加载最后500行全部内容
content := logResult(nil, filePath, "500")
cli.Initstr = content
// 判断该文件的client对象是否已存在,不存在则新开监控的协程
if _, ok := fileMap[filePath]; ok {
fileMap[filePath][*cli] = true
utils.Log.Infof("%s has been monitored", filePath)
} else {
go monitor(file_path, filename)
// 监控文件
go monitor(filePath, cli)
l := make(map[Client]bool)
l[*cli] = true
file_map[filename] = l
fmt.Printf("Add a new monitored file [%s]\n", filename)
fileMap[filePath] = l
utils.Log.Infof("Add a new monitored file [%s]", filePath)
}
//注册client并开始传输信息
manager.register <- cli
go cli.read()
//go cli.read()
cli.write()
}
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
import (
"github.com/gorilla/websocket"
"html/template"
"log"
"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.WriteHeader(200)
tmpl, err := template.ParseFiles(template_path)
if err != nil {
log.Println("err:", err)
utils.Log.Errorf("return_res错误: %s", err.Error())
return
}
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"
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(200)
tmpl, err := template.ParseFiles(template_path)
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
}
tmpl.Execute(w, str_file)
......
......@@ -4,16 +4,17 @@ import (
"bufio"
"bytes"
"io"
"log"
"os"
"strings"
"web_log/utils"
)
// 读取最后五百行数据
func return_init_str(file_path string, lines_num int) (string, error) {
lines := int64(lines_num)
file, err := os.Open(file_path)
if err != nil {
log.Println(err)
utils.Log.Errorf("打开文件失败: %s", err.Error())
return "", err
}
fileInfo, _ := file.Stat()
......@@ -39,7 +40,7 @@ func return_init_str(file_path string, lines_num int) (string, error) {
continue
}
if err != nil {
log.Println("Read file error:", err)
utils.Log.Errorf("Read file error: %s", err.Error())
return "", err
}
strs := strings.Split(string(data[:n]), "\n")
......
......@@ -12,6 +12,7 @@ import (
"regexp"
"sync"
. "web_log/config"
"web_log/utils"
)
//var userMap = make(map[string]string)
......@@ -33,7 +34,7 @@ func init() {
//初始化读取配置文件
func ReadUserConfig(path string) *MainConfig {
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() {
allConfigs, mainConfig := LoadConfig(path)
......@@ -164,17 +165,17 @@ func CutWord(s string) string {
func LoadConfig(path string) (Configs, *MainConfig) {
buf, err := ioutil.ReadFile(path)
if err != nil {
log.Panicln("load config conf failed: ", err)
utils.Log.Panicf("load config conf failed: %s", err.Error())
}
mainConfig := &MainConfig{}
err = json.Unmarshal(buf, mainConfig)
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)
err = json.Unmarshal(buf, &allConfigs)
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
......
package code
import (
"log"
"os"
"time"
"web_log/utils"
)
// 监控日志文件
func monitor(filePath string, filename string) {
func monitor(filePath string, cli *Client) {
defer func() {
if err := recover(); err != nil {
log.Printf("[seelog] error:%+v", err)
utils.Log.Errorf("defer [seelog] error:%+v", err)
}
}()
var fileInfo os.FileInfo
......@@ -18,45 +18,54 @@ func monitor(filePath string, filename string) {
for i := 1; i <= 10; i++ {
fileInfo, err = os.Stat(filePath)
if err != nil {
log.Printf("[seelog] error:%v", err.Error())
utils.Log.Errorf("循环Stat [seelog] error:%v", err.Error())
continue
}
break
}
offset := fileInfo.Size()
startMonitorTime := time.Now().Unix()
for {
fileInfo, err = os.Stat(filePath)
if err != nil {
log.Printf("[seelog] error:%v", err.Error())
continue
utils.Log.Errorf("Stat [seelog] error:%v", err.Error())
return
}
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())
utils.Log.Errorf("Open [seelog] error:%v", err.Error())
continue
}
_, err = file.Seek(offset, 0)
if err != nil {
log.Printf("[seelog] error:%v", err.Error())
utils.Log.Errorf("Seek [seelog] error:%v", err.Error())
continue
}
_, err = file.Read(msg)
if err != nil {
log.Printf("[seelog] error:%v", err.Error())
utils.Log.Errorf("Read [seelog] error:%v", err.Error())
continue
}
str_msg := string(msg)
s := filename + "$$" + str_msg
strMsg := string(msg)
s := filePath + "$$" + strMsg
manager.broadcast <- []byte(s)
offset = newOffset
file.Close()
}
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
import (
"fmt"
"github.com/kataras/iris/v12"
"os"
"net/http"
"strings"
"web_log/utils"
)
......@@ -11,9 +11,7 @@ type WebHandler struct {
Directory string
}
type Datafile struct {
Htmltitle string
CurFiles []FileInfo
UrlLocation string
CurFiles []FileInfo
}
type FileInfo struct {
......@@ -22,56 +20,75 @@ type FileInfo struct {
LastModify string
}
type SearchLog struct {
LogResult string
Keyword string
LogPath string
Servers string
}
var base_path = "web/views/base.html"
func (wbh *WebHandler) loginhandle(ctx iris.Context, data interface{}) {
return_res(ctx.ResponseWriter(), "web/views/login.html", data)
// loginHandle 登录界面
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)
if err != nil {
return_error_res(ctx.ResponseWriter(), filename)
return
}
fmt.Printf("view log [%s]\n", file)
return_res(ctx.ResponseWriter(), "web/views/page.html", str_filepath)
// indexHandle 主界面
func (wbh *WebHandler) indexHandle(ctx iris.Context) {
filePath := ctx.URLParam("filePath")
filePath = wbh.Directory + filePath
isFile := utils.IsDir(filePath)
if isFile != true {
returnRes(ctx.ResponseWriter(), "web/views/page.html", filePath)
return
} else {
var htmltitle, urlloction string
dir := wbh.Directory
if innerip == "" && server_direc == "" && file == "" {
htmltitle = "选择服务器"
urlloction = "?innerip="
}
if innerip != "" && server_direc == "" && file == "" {
htmltitle = "选择服务"
urlloction = "&server_direc="
dir += "/" + innerip
}
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"]})
}
filesInfo, err := utils.PathFileInfo(filePath)
if err != nil {
returnErrorRes(ctx.ResponseWriter(), filePath)
return
}
var fs = make([]FileInfo, 0)
isLogFile := false
for _, v := range filesInfo {
if !utils.IsDir(filePath + "/" + v["file_name"]) {
isLogFile = true
}
data := Datafile{Htmltitle: htmltitle, CurFiles: fs, UrlLocation: urlloction}
return_res(ctx.ResponseWriter(), base_path, data)
fs = append(fs, FileInfo{Name: v["file_name"], Size: v["file_size"], LastModify: v["last_modify"]})
}
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
import (
"fmt"
"github.com/gorilla/websocket"
"log"
"strings"
"time"
"web_log/utils"
)
// websocket客户端
......@@ -19,9 +19,9 @@ type Client struct {
type clientManager struct {
clients map[*Client]bool
broadcast chan []byte
register chan *Client
unregister chan *Client
filemap map[string]map[Client]bool
register chan *Client //注册通道
unregister chan *Client //注销通道
filemap map[string]map[Client]bool //存储文件对象对应的客户端对象
}
var manager = clientManager{
......@@ -37,40 +37,42 @@ func getfilemap() map[string]map[Client]bool {
return file_map
}
// 死循环,负责处理通道
func (manager *clientManager) start() {
defer func() {
if err := recover(); err != nil {
log.Printf("[seelog] error:%+v", err)
utils.Log.Errorf("[seelog] error:%+v", err)
}
}()
for {
select {
// 注册通道接收到客户端对象
case conn := <-manager.register:
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:
if _, ok := manager.clients[conn]; ok {
close(conn.Send)
delete(manager.clients, conn)
for _, map_tmp := range manager.filemap {
delete(map_tmp, *conn)
close(conn.Send) //关闭发送通道
delete(manager.clients, conn) //删除存储对象
for _, mapTmp := range manager.filemap {
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:
str_msg := string(message)
tmp := strings.Split(str_msg, "$$")
strMsg := string(message)
tmp := strings.Split(strMsg, "$$")
filename := tmp[0]
msg := tmp[1]
map_tmp := manager.filemap[filename]
for conn, _ := range map_tmp {
mapTmp := manager.filemap[filename]
for conn, _ := range mapTmp {
select {
// 将消息发送到客户端的Send通道
case conn.Send <- []byte(msg):
default:
close(conn.Send)
delete(manager.clients, &conn)
}
}
......@@ -86,14 +88,14 @@ func (manager *clientManager) start() {
}
}
// ws 写方法
func (c *Client) write() {
//defer func() {
// manager.unregister <- c
// c.Socket.Close()
//}()
// 写消息
if err := c.Socket.WriteMessage(websocket.TextMessage, []byte(c.Initstr)); err != nil {
return
}
// 死循环,监听ws客户端的发送通道,接收到关闭信号则退出循环
for {
select {
case message, ok := <-c.Send:
......@@ -106,6 +108,7 @@ func (c *Client) write() {
}
}
//ws 读方法,接收前段通过同一个ws客户端发过来的消息
func (c *Client) read() {
defer func() {
manager.unregister <- c
......@@ -114,74 +117,18 @@ func (c *Client) read() {
for {
_, reply, err := c.Socket.ReadMessage()
if err != nil {
fmt.Println("Error! Can't receive message...")
utils.Log.Error("Error! Can't receive message...")
break
}
// 前端会有定时器,定时发送heart消息
if string(reply) != "heart" {
manager.unregister <- c
c.Socket.Close()
} else {
fmt.Println("heart package...")
}
//reply = []byte
//time.Sleep(2000 * time.Millisecond)
time.Sleep(5000 * time.Millisecond)
//jsonMessage, _ := json.Marshal(&Message{Sender: c.Id, Content: string(message)})
//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 {
return strings.Replace(dir, "\\", "/", -1)
}
func InitConfig() {
flag.StringVar(&Directory, "d", "/data/inotify_logs", "监听目录")
flag.StringVar(&Directory, "d", "/data", "监听目录")
flag.StringVar(&Port, "p", "9997", "监听端口")
flag.Parse()
_, err := os.Stat(Directory)
......
{
"weblog": "824d8eb068e6917d21e8d39db9edffe7"
"weblog": "e10adc3949ba59abbe56e057f20f883e"
}
\ No newline at end of file
......@@ -4,29 +4,35 @@ go 1.14
require (
github.com/CloudyKit/jet/v3 v3.0.1 // indirect
github.com/gin-gonic/gin v1.6.3 // indirect
github.com/go-playground/validator/v10 v10.4.1 // indirect
github.com/golang/protobuf v1.4.3 // indirect
github.com/gorilla/websocket v1.4.2
github.com/ajg/form v1.5.1 // indirect
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 // indirect
github.com/google/go-querystring v1.1.0 // indirect
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/schema v0.0.6 // 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/iris/v12 v12.1.8
github.com/klauspost/compress v1.11.4 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-colorable v0.1.12 // 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/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/sergi/go-diff v1.2.0 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/ugorji/go v1.2.2 // indirect
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
golang.org/x/net v0.0.0-20201224014010-6772e930b67b
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 // indirect
golang.org/x/text v0.3.4 // indirect
google.golang.org/protobuf v1.25.0 // indirect
github.com/smartystreets/goconvey v1.7.2 // indirect
github.com/valyala/fasthttp v1.35.0 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect
github.com/yudai/gojsondiff v1.0.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/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0
)
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
github.com/CloudyKit/jet/v3 v3.0.1 h1:LUBMIJtW92Fqi+fOqXbGsT/xKiwNWjYktaNAASPE7E4=
github.com/CloudyKit/jet/v3 v3.0.1/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398 h1:WDC6ySpJzbxGWFh4aMxFFC28wwGp5pEuoTtvA4q/qQ4=
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible h1:Ppm0npCCsmuR9oQaBtRuZcmILVE74aXE+AmrJj8L2ns=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 h1:DddqAaWDpywytcG8w/qoQ5sAN8X12d3Z3koB0C3Rxsc=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8=
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127 h1:0gkP6mzaMqkmpcJYCFOLkIBwI7xFExG03bbkOkCvUPI=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/iris-contrib/blackfriday v2.0.0+incompatible h1:o5sHQHHm0ToHUlAJSTjW9UWicjJSDDauOOQ2AHuIVp4=
github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk=
github.com/iris-contrib/jade v1.1.4 h1:WoYdfyJFfZIUgqNAeOyRfTNQZOksSlZ6+FnXR3AEpX0=
github.com/iris-contrib/jade v1.1.4/go.mod h1:EDqR+ur9piDl6DUgs6qRrlfzmlx/D5UybogqrXvJTBE=
github.com/iris-contrib/pongo2 v0.0.1 h1:zGP7pW51oi5eQZMIlGA3I+FHY9/HOQWDB+572yin0to=
github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g=
github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=
github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8=
github.com/kataras/golog v0.1.6 h1:jEqEQCm+4B4M4/CgzrEWNj9iUST0hGZXDqAPMVnxWMw=
github.com/kataras/golog v0.1.6/go.mod h1:jOSQ+C5fUqsNSwurB/oAHq1IFSb0KI3l6GMa7xB6dZA=
github.com/kataras/iris/v12 v12.1.8 h1:O3gJasjm7ZxpxwTH8tApZsvf274scSGQAUpNe47c37U=
github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE=
github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE=
github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro=
github.com/kataras/pio v0.0.10 h1:b0qtPUqOpM2O+bqa5wr2O6dN4cQNwSmFd6HQqgVae0g=
github.com/kataras/pio v0.0.10/go.mod h1:gS3ui9xSD+lAUpbYnjOGiQyY7sUMJO+EHpiRzhtZ5no=
github.com/kataras/sitemap v0.0.5 h1:4HCONX5RLgVy6G4RkYOV3vKNcma9p236LdGOipJsaFE=
github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8=
github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U=
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
github.com/microcosm-cc/bluemonday v1.0.4 h1:p0L+CTpo/PLFdkoPcJemLXG+fpMD7pYOoDEq1axMbGg=
github.com/microcosm-cc/bluemonday v1.0.4/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.1.3 h1:e/3Cwtogj0HA+25nMP1jCMDIf8RtRYbGwGGuBIFztkc=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk=
github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.35.0 h1:wwkR8mZn2NbigFsaw2Zj5r+xkmzjbrA/lyTmiSlal/Y=
github.com/valyala/fasthttp v1.35.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI=
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
......@@ -3,6 +3,8 @@ package main
import (
"fmt"
"github.com/kataras/iris/v12"
"go.uber.org/zap/zapcore"
"runtime"
"web_log/code"
"web_log/config"
"web_log/utils"
......@@ -12,6 +14,12 @@ import (
func main() {
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
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
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"math"
"os"
"os/exec"
"runtime"
"sort"
"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) {
_, err := os.Stat(path)
if err == nil {
......@@ -55,6 +68,7 @@ func Unwrap(num int64, retain int) float64 {
func PathFileInfo(path string) ([]map[string]string, error) {
files, err := ioutil.ReadDir(path)
if err != nil {
fmt.Println(err.Error())
return nil, err
}
var filesinfo = make([]map[string]interface{}, 0)
......@@ -96,3 +110,62 @@ func RunEnv() bool {
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 @@
</head>
<body>
<div style="font-size: 20px" >{{ .Htmltitle }}</div>
<div style="font-size: 20px" >请选择</div>
<div id="show" style="overflow-y: scroll;">
</div>
......@@ -23,24 +23,28 @@
if (1024>sz){
sz = sz.toFixed(3)
sz +='K'
}else {
} else {
sz /= 1024
sz = sz.toFixed(3)
sz+= 'M'
}
var f_name = "<label class='tmp' style='margin-right: 1px'>" + {{ .Name }} + "</label>"
{{/* 如果 url里面没有server_direc这个参数,说明还没进到选文件的页面,也就是展示的列表全是文件夹,文件夹不予显示大小*/}}
if ( window.location.href.indexOf("server_direc")==-1){
sz = ""
}
var f_size = "<label class='size' style='margin-right: 100px'>" + sz + "</label>"
var f_size = "<label class='size' style='margin-right: 100px'></label>"
var f_lastmodify ="<label>" + (new Date(lastmodify*1000)).toLocaleString() + "</label>"
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 }}
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>
......@@ -57,26 +61,26 @@
margin: 0;
background-color: #cccccc;
}
a:active,
a:visited,
a:link {
display: block;
text-decoration: none;
margin: 6px 10px 6px 0px;
padding: 2px 6px 2px 6px;
color: #00527f;
background-color: #d9e8f3;
border: 1px solid #004266;
}
a:hover {
color: red;
background-color: #8cbbda;
}
/*a:active,*/
/*a:visited,*/
/*a:link {*/
/* display: block;*/
/* text-decoration: none;*/
/* margin: 6px 10px 6px 0px;*/
/* padding: 2px 6px 2px 6px;*/
/* color: #00527f;*/
/* background-color: #d9e8f3;*/
/* border: 1px solid #004266;*/
/*}*/
/*a:hover {*/
/* color: red;*/
/* background-color: #8cbbda;*/
/*}*/
label{
display:inline-block;
width:200px;
text-align:right;
text-align:left;
}
.tmp{
display:inline-block;
......@@ -84,6 +88,15 @@
text-align:left;
}
#file {
padding: 10px;
border: 1px solid #004266;
background-color:#d9e8f3;
display: block;
margin:6px 10px 6px 0px;
color:#00527f;
}
</style >
</body>
</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 @@
var ws = new WebSocket("ws://" + window.location.host + "/ws?file={{ . }}");
ws.onmessage = function (e) {
console.log(filterText)
snsArr = e.data.split(/[(\r\n)\r\n]+/);
snsArr = e.data.split(/[\r\n]/);
snsArr.forEach((item, index) => {
if (!item) {
snsArr.splice(index, 1);//删除空项
......@@ -26,23 +25,26 @@
})
snsArr.forEach((item, index) => {
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 {
$('#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 () {
$('#status').css("background-color", "red").text("链接断开")
console.log("onclose")
reConnect()
}
ws.onopen = function () {
$('#status').css("background-color", "chartreuse").text("连接成功")
$('#clear').click()
// $('#clear').click()
console.log("onopen")
}
ws.onerror = function (e) {
$('#status').css("background-color", "red").text("链接断开")
console.log("onerror")
}
// function send_heart() {
// ws.send("heart")
......@@ -84,11 +86,11 @@
$('#filter').on('input', function () {
filterText = $('#filter').val()
$('#log pre').each(function () {
$('#log span').each(function () {
$(this).attr("style", "display:block;color: white;font-size: 15px;margin: 3px")
})
if (filterText != ""){
$('#log pre').each(function () {
$('#log span').each(function () {
if ($(this).text().indexOf(filterText) == -1) {
$(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