Files
2026-05-22 09:17:39 +08:00

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
}