119 lines
2.6 KiB
Go
119 lines
2.6 KiB
Go
package logweb
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
"xk-hy-transit-go/internal/hy"
|
|
)
|
|
|
|
var dateRE = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`)
|
|
|
|
var validKinds = map[string]bool{"app": true, "pull": true, "push": true}
|
|
|
|
// LogMeta 日志文件元信息。
|
|
type LogMeta struct {
|
|
Kind string `json:"kind"`
|
|
KindLabel string `json:"kindLabel,omitempty"`
|
|
Date string `json:"date"`
|
|
Path string `json:"path"`
|
|
Size int64 `json:"size"`
|
|
Lines int `json:"lines"`
|
|
Modified string `json:"modified"`
|
|
}
|
|
|
|
func logFilePath(logDir, kind, date string) (string, error) {
|
|
if !validKinds[kind] {
|
|
return "", fmt.Errorf("invalid kind")
|
|
}
|
|
if !dateRE.MatchString(date) {
|
|
return "", fmt.Errorf("invalid date")
|
|
}
|
|
root, err := filepath.Abs(logDir)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
p := filepath.Join(root, kind+"-"+date+".log")
|
|
p, err = filepath.Abs(p)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if !strings.HasPrefix(p, root+string(os.PathSeparator)) && p != root {
|
|
return "", fmt.Errorf("path escape")
|
|
}
|
|
return p, nil
|
|
}
|
|
|
|
// ListLogDates 列出某类型日志可用日期(降序)。
|
|
func ListLogDates(logDir, kind string) ([]string, error) {
|
|
if !validKinds[kind] {
|
|
return nil, fmt.Errorf("invalid kind")
|
|
}
|
|
entries, err := os.ReadDir(logDir)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
prefix := kind + "-"
|
|
suffix := ".log"
|
|
var dates []string
|
|
for _, e := range entries {
|
|
if e.IsDir() {
|
|
continue
|
|
}
|
|
name := e.Name()
|
|
if strings.HasPrefix(name, prefix) && strings.HasSuffix(name, suffix) {
|
|
d := strings.TrimSuffix(strings.TrimPrefix(name, prefix), suffix)
|
|
if dateRE.MatchString(d) {
|
|
dates = append(dates, d)
|
|
}
|
|
}
|
|
}
|
|
// simple sort desc
|
|
for i := 0; i < len(dates); i++ {
|
|
for j := i + 1; j < len(dates); j++ {
|
|
if dates[j] > dates[i] {
|
|
dates[i], dates[j] = dates[j], dates[i]
|
|
}
|
|
}
|
|
}
|
|
return dates, nil
|
|
}
|
|
|
|
// ReadLogFile 读取日志全文。
|
|
func ReadLogFile(logDir, kind, date string) (*LogMeta, []string, error) {
|
|
p, err := logFilePath(logDir, kind, date)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
info, err := os.Stat(p)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
b, err := os.ReadFile(p)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
content := string(b)
|
|
var lines []string
|
|
if content != "" {
|
|
lines = strings.Split(strings.TrimRight(content, "\n"), "\n")
|
|
}
|
|
meta := &LogMeta{
|
|
Kind: kind,
|
|
KindLabel: hy.LabelLogKind(kind),
|
|
Date: date,
|
|
Path: p,
|
|
Size: info.Size(),
|
|
Lines: len(lines),
|
|
Modified: info.ModTime().Format(time.RFC3339),
|
|
}
|
|
return meta, lines, nil
|
|
}
|