feat(logs): add initial log loading and tab management improvements
- Implement fetchLastLines API to load initial log entries - Add addInitialLogs function to useSSE hook for prepending logs - Improve tab handling with better closable behavior and state management - Add backend support for fetching last N lines from log files
This commit is contained in:
+68
-2
@@ -6,12 +6,14 @@ import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"bufio"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
@@ -28,6 +30,9 @@ var (
|
||||
username string
|
||||
)
|
||||
|
||||
// Lista global de arquivos de log (apenas argumentos após flags)
|
||||
var logFiles []string
|
||||
|
||||
// init() é executado antes de main(). É o lugar ideal para configurar flags.
|
||||
func init() {
|
||||
flag.StringVar(&port, "port", ":8080", "Porta para o servidor web (ex: :8080, 9090)")
|
||||
@@ -118,7 +123,67 @@ func handleSSE(w http.ResponseWriter, r *http.Request) {
|
||||
// handleFiles lista os arquivos de log disponíveis
|
||||
func handleFiles(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(os.Args[1:])
|
||||
json.NewEncoder(w).Encode(logFiles)
|
||||
}
|
||||
|
||||
func handleLastLines(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
file := r.URL.Query().Get("file")
|
||||
nParam := r.URL.Query().Get("n")
|
||||
n := 200
|
||||
if nParam != "" {
|
||||
fmt.Sscanf(nParam, "%d", &n)
|
||||
}
|
||||
allowed := false
|
||||
for _, f := range logFiles {
|
||||
if f == file {
|
||||
allowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !allowed {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte("arquivo inválido"))
|
||||
return
|
||||
}
|
||||
entries, err := getLastLines(file, n)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte("erro ao ler arquivo"))
|
||||
return
|
||||
}
|
||||
json.NewEncoder(w).Encode(entries)
|
||||
}
|
||||
|
||||
func getLastLines(filename string, n int) ([]LogEntry, error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
scanner := bufio.NewScanner(f)
|
||||
buf := make([]string, 0, n)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if len(buf) == n {
|
||||
buf = buf[1:]
|
||||
}
|
||||
buf = append(buf, line)
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tsRe1 := regexp.MustCompile(`\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}`)
|
||||
tsRe2 := regexp.MustCompile(`\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}`)
|
||||
entries := make([]LogEntry, 0, len(buf))
|
||||
for _, line := range buf {
|
||||
t := tsRe1.FindString(line)
|
||||
if t == "" {
|
||||
t = tsRe2.FindString(line)
|
||||
}
|
||||
entries = append(entries, LogEntry{Filename: filename, Line: strings.TrimRight(line, "\r"), Timestamp: t})
|
||||
}
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
// startTailing inicia o monitoramento dos arquivos
|
||||
@@ -184,7 +249,7 @@ func requestAuth(w http.ResponseWriter) {
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
logFiles := flag.Args()
|
||||
logFiles = flag.Args()
|
||||
if len(logFiles) < 1 {
|
||||
fmt.Println("Uso: web-tail-pro [opções] <arquivo1.log> <arquivo2.log> ...")
|
||||
fmt.Println("\nOpções:")
|
||||
@@ -212,6 +277,7 @@ func main() {
|
||||
mux.Handle("/", http.FileServer(http.FS(distFS)))
|
||||
mux.HandleFunc("/logs", handleSSE)
|
||||
mux.HandleFunc("/api/files", handleFiles)
|
||||
mux.HandleFunc("/api/last", handleLastLines)
|
||||
|
||||
protectedHandler := authMiddleware(mux)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user