nsfwapp/backend/log.go
2026-06-17 09:24:18 +02:00

280 lines
5.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// backend\log.go
package main
import (
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"sync"
"time"
)
var (
appLogMu sync.Mutex
appLogFile *os.File
)
func appLogPath() string {
exePath, _ := os.Executable()
exeDir := ""
if exePath != "" {
exeDir = filepath.Dir(exePath)
}
if exeDir == "" {
exeDir, _ = os.Getwd()
}
return filepath.Join(exeDir, "recorder.log")
}
func openAppLogFile() (*os.File, error) {
path := appLogPath()
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return nil, err
}
return os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
}
func initAppLog() {
appLogMu.Lock()
defer appLogMu.Unlock()
if appLogFile != nil {
return
}
f, err := openAppLogFile()
if err != nil {
// Wichtig: hier NICHT appLogln verwenden, weil appLogMu schon gelockt ist.
fmt.Println("⚠️ recorder log konnte nicht geöffnet werden:", err)
return
}
appLogFile = f
_, _ = fmt.Fprintf(
appLogFile,
"\n\n--- Recorder Start %s ---\nlog=%s\n",
time.Now().Format(time.RFC3339),
appLogPath(),
)
_ = appLogFile.Sync()
}
func closeAppLog() {
appLogMu.Lock()
defer appLogMu.Unlock()
if appLogFile == nil {
return
}
_, _ = fmt.Fprintf(
appLogFile,
"--- Recorder Stop %s ---\n",
time.Now().Format(time.RFC3339),
)
_ = appLogFile.Sync()
_ = appLogFile.Close()
appLogFile = nil
}
func appLogWriter() io.Writer {
initAppLog()
appLogMu.Lock()
defer appLogMu.Unlock()
if appLogFile != nil {
return appLogFile
}
return os.Stderr
}
func appLogWriteLine(line string) {
line = strings.TrimRight(line, "\r\n")
ts := time.Now().Format("2006-01-02 15:04:05")
out := fmt.Sprintf("[%s] %s\n", ts, line)
appLogMu.Lock()
defer appLogMu.Unlock()
fmt.Print(out)
if appLogFile != nil {
_, _ = appLogFile.WriteString(out)
_ = appLogFile.Sync()
}
}
func appLogln(args ...any) {
appLogWriteLine(fmt.Sprintln(args...))
}
func appLogf(format string, args ...any) {
appLogWriteLine(fmt.Sprintf(format, args...))
}
// appErrorLogSuppressed unterdrückt das automatische ❌-Logging für bekannte,
// erwartbare und sehr häufige Fehler (z.B. abgelaufene HLS-Edge-URLs während
// eines Stream-Reconnects). Der Fehler wird weiterhin zurückgegeben, damit die
// Retry-/Reconnect-Logik unverändert funktioniert nur die Log-Zeile entfällt.
func appErrorLogSuppressed(msg string) bool {
m := strings.ToLower(msg)
return strings.Contains(m, "leere hls url") ||
strings.Contains(m, "http 403")
}
func appErrorf(format string, args ...any) error {
err := fmt.Errorf(format, args...)
if !appErrorLogSuppressed(err.Error()) {
appLogln("❌", err)
}
return err
}
// Nur zentraler Ersatz für fmt.Sprintf.
// Loggt absichtlich NICHT, weil Sprintf oft nur UI-/Command-Strings baut.
func appSprintf(format string, args ...any) string {
return fmt.Sprintf(format, args...)
}
// Zentraler Ersatz für fmt.Sprint.
func appSprint(args ...any) string {
return fmt.Sprint(args...)
}
func appLogHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Nur GET erlaubt", http.StatusMethodNotAllowed)
return
}
path := appLogPath()
maxBytes := int64(64 * 1024)
switch strings.TrimSpace(r.URL.Query().Get("maxBytes")) {
case "16384":
maxBytes = 16 * 1024
case "32768":
maxBytes = 32 * 1024
case "131072":
maxBytes = 128 * 1024
case "262144":
maxBytes = 256 * 1024
}
info, err := os.Stat(path)
if err != nil {
if os.IsNotExist(err) {
writeJSON(w, http.StatusOK, map[string]any{
"ok": false,
"path": path,
"exists": false,
"log": "",
"error": "Logdatei existiert noch nicht.",
})
return
}
writeJSON(w, http.StatusOK, map[string]any{
"ok": false,
"path": path,
"exists": false,
"log": "",
"error": err.Error(),
})
return
}
if info.IsDir() {
writeJSON(w, http.StatusOK, map[string]any{
"ok": false,
"path": path,
"exists": true,
"log": "",
"error": "Logpfad ist ein Ordner.",
})
return
}
f, err := os.Open(path)
if err != nil {
writeJSON(w, http.StatusOK, map[string]any{
"ok": false,
"path": path,
"exists": true,
"log": "",
"error": err.Error(),
})
return
}
defer f.Close()
size := info.Size()
offset := int64(0)
if size > maxBytes {
offset = size - maxBytes
}
buf := make([]byte, size-offset)
if len(buf) > 0 {
if _, err := f.ReadAt(buf, offset); err != nil && err != io.EOF {
writeJSON(w, http.StatusOK, map[string]any{
"ok": false,
"path": path,
"exists": true,
"log": "",
"error": err.Error(),
})
return
}
}
logText := string(buf)
if offset > 0 {
logText = "… Log gekürzt, zeige die letzten " + fmt.Sprint(maxBytes/1024) + " KB …\n\n" + logText
}
writeJSON(w, http.StatusOK, map[string]any{
"ok": true,
"path": path,
"exists": true,
"sizeBytes": size,
"log": logText,
})
}
func clearAppLog() {
appLogMu.Lock()
defer appLogMu.Unlock()
if appLogFile != nil {
_ = appLogFile.Close()
appLogFile = nil
}
path := appLogPath()
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
fmt.Println("⚠️ recorder log dir konnte nicht erstellt werden:", err)
return
}
if err := os.WriteFile(path, []byte{}, 0644); err != nil {
fmt.Println("⚠️ recorder log konnte nicht geleert werden:", err)
}
}