package frpLogger import ( "bufio" "fmt" "io" "os" "strings" "time" ) func (s *InstanceLogStreamer) streamLogFile(logFilePath string) { s.streamLogFileSince(logFilePath, time.Time{}) } func (s *InstanceLogStreamer) streamLogFileSince(logFilePath string, sinceTime time.Time) { s.sendInfo(fmt.Sprintf("Streaming log file: %s", logFilePath)) if _, err := os.Stat(logFilePath); os.IsNotExist(err) { s.sendError(fmt.Sprintf("Log file not found: %s", logFilePath)) return } file, err := os.Open(logFilePath) if err != nil { s.sendError(fmt.Sprintf("Failed to open log file: %v", err)) return } defer file.Close() if !sinceTime.IsZero() { s.sendInfo(fmt.Sprintf("Filtering logs since: %s", sinceTime.Format("2006-01-02 15:04:05"))) } s.streamLogFileFromBeginning(file, sinceTime) } func (s *InstanceLogStreamer) streamLogFileFromBeginning(file *os.File, sinceTime time.Time) { reader := bufio.NewReader(file) for { select { case <-s.stopChan: s.sendInfo("Log streaming stopped") return default: line, err := reader.ReadString('\n') if err != nil { if err == io.EOF { time.Sleep(100 * time.Millisecond) continue } s.sendError(fmt.Sprintf("Error reading log file: %v", err)) return } line = strings.TrimSpace(line) if line != "" { lineTime := s.parseLogLineTime(line) if lineTime.IsZero() || lineTime.After(sinceTime) || lineTime.Equal(sinceTime) { s.sendLog(s.parseLogLevel(line), line) } } } } } func (s *InstanceLogStreamer) parseLogLineTime(line string) time.Time { layouts := []string{ "2006/01/02 15:04:05", "2006-01-02 15:04:05", "2006-01-02T15:04:05", time.RFC3339, time.RFC3339Nano, } for _, layout := range layouts { minLen := len(layout) if len(line) >= minLen { t, err := time.Parse(layout, strings.TrimSpace(line[:minLen])) if err == nil { return t } } } return time.Time{} } func (s *InstanceLogStreamer) parseLogLevel(line string) string { lowerLine := strings.ToLower(line) if strings.Contains(lowerLine, "error") || strings.Contains(lowerLine, "err") { return "ERROR" } if strings.Contains(lowerLine, "warn") || strings.Contains(lowerLine, "warning") { return "WARN" } if strings.Contains(lowerLine, "debug") { return "DEBUG" } if strings.Contains(lowerLine, "info") { return "INFO" } return "INFO" }