New Features and safe log
This commit is contained in:
105
panel_log_limiter.go
Normal file
105
panel_log_limiter.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultPanelLogMaxBytes int64 = 1 * 1024 * 1024
|
||||
defaultPanelLogCheckEvery = 10 * time.Second
|
||||
)
|
||||
|
||||
type panelLogResetResponse struct {
|
||||
OK bool `json:"ok"`
|
||||
Path string `json:"path"`
|
||||
MaxBytes int64 `json:"max_bytes"`
|
||||
}
|
||||
|
||||
func panelLogFilePath() string {
|
||||
path := strings.TrimSpace(os.Getenv("PANEL_LOG_FILE"))
|
||||
if path == "" {
|
||||
path = defaultPanelLogFile
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func panelLogMaxBytes() int64 {
|
||||
raw := strings.TrimSpace(os.Getenv("PANEL_LOG_MAX_BYTES"))
|
||||
if raw == "" {
|
||||
return defaultPanelLogMaxBytes
|
||||
}
|
||||
n, err := strconv.ParseInt(raw, 10, 64)
|
||||
if err != nil || n <= 0 {
|
||||
return defaultPanelLogMaxBytes
|
||||
}
|
||||
// Do not allow a tiny limit that would cause continuous truncation.
|
||||
if n < 64*1024 {
|
||||
return 64 * 1024
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func startPanelLogLimiter() {
|
||||
path := panelLogFilePath()
|
||||
maxBytes := panelLogMaxBytes()
|
||||
if path == "" || maxBytes <= 0 {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
_ = enforcePanelLogLimit(path, maxBytes)
|
||||
ticker := time.NewTicker(defaultPanelLogCheckEvery)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
_ = enforcePanelLogLimit(path, maxBytes)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func enforcePanelLogLimit(path string, maxBytes int64) error {
|
||||
st, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
if st.Size() <= maxBytes {
|
||||
return nil
|
||||
}
|
||||
return truncatePanelLog(path, maxBytes, "automatic 1 MiB log limit")
|
||||
}
|
||||
|
||||
func truncatePanelLog(path string, maxBytes int64, reason string) error {
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = fmt.Fprintf(f, "%s sshpanel: panel log cleaned (%s, max=%d bytes)\n", time.Now().Format(time.RFC3339), reason, maxBytes)
|
||||
return err
|
||||
}
|
||||
|
||||
func handleSystemLogsReset(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
path := panelLogFilePath()
|
||||
maxBytes := panelLogMaxBytes()
|
||||
if err := truncatePanelLog(path, maxBytes, "manual clean from admin panel"); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(panelLogResetResponse{OK: true, Path: path, MaxBytes: maxBytes})
|
||||
}
|
||||
Reference in New Issue
Block a user