mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-11-08 01:44:03 +01:00
Added wip log rotate feature
- Added log rotate function interface - Added darwin amd64 support in make file (Intel Macs) - Added log summary and error API
This commit is contained in:
@@ -18,9 +18,10 @@ import (
|
||||
*/
|
||||
|
||||
type Logger struct {
|
||||
Prefix string //Prefix for log files
|
||||
LogFolder string //Folder to store the log file
|
||||
CurrentLogFile string //Current writing filename
|
||||
Prefix string //Prefix for log files
|
||||
LogFolder string //Folder to store the log file
|
||||
CurrentLogFile string //Current writing filename
|
||||
RotateOption RotateOption //Options for log rotation, see rotate.go
|
||||
logger *log.Logger
|
||||
file *os.File
|
||||
}
|
||||
|
||||
127
src/mod/info/logger/rotate.go
Normal file
127
src/mod/info/logger/rotate.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RotateOption struct {
|
||||
Enabled bool //Whether log rotation is enabled
|
||||
MaxSize int64 //Maximum size of the log file in bytes before rotation (e.g. 10 * 1024 * 1024 for 10MB)
|
||||
MaxBackups int //Maximum number of backup files to keep
|
||||
Compress bool //Whether to compress the rotated files
|
||||
BackupDir string //Directory to store backup files, if empty, use the same directory as the log file
|
||||
}
|
||||
|
||||
func (l *Logger) LogNeedRotate(filename string) bool {
|
||||
if !l.RotateOption.Enabled {
|
||||
return false
|
||||
}
|
||||
info, err := os.Stat(filename)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return info.Size() >= l.RotateOption.MaxSize
|
||||
}
|
||||
|
||||
func (l *Logger) RotateLog() error {
|
||||
if !l.RotateOption.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
needRotate := l.LogNeedRotate(l.CurrentLogFile)
|
||||
if !needRotate {
|
||||
return nil
|
||||
}
|
||||
|
||||
//Close current file
|
||||
if l.file != nil {
|
||||
l.file.Close()
|
||||
}
|
||||
|
||||
// Determine backup directory
|
||||
backupDir := l.RotateOption.BackupDir
|
||||
if backupDir == "" {
|
||||
backupDir = filepath.Dir(l.CurrentLogFile)
|
||||
}
|
||||
|
||||
// Ensure backup directory exists
|
||||
if err := os.MkdirAll(backupDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate rotated filename with timestamp
|
||||
timestamp := time.Now().Format("20060102-150405")
|
||||
baseName := filepath.Base(l.CurrentLogFile)
|
||||
rotatedName := fmt.Sprintf("%s.%s", baseName, timestamp)
|
||||
rotatedPath := filepath.Join(backupDir, rotatedName)
|
||||
|
||||
// Rename current log file to rotated file
|
||||
if err := os.Rename(l.CurrentLogFile, rotatedPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Optionally compress the rotated file
|
||||
if l.RotateOption.Compress {
|
||||
if err := compressFile(rotatedPath); err != nil {
|
||||
return err
|
||||
}
|
||||
// Remove the uncompressed rotated file after compression
|
||||
os.Remove(rotatedPath)
|
||||
rotatedPath += ".gz"
|
||||
}
|
||||
|
||||
// Remove old backups if exceeding MaxBackups
|
||||
if l.RotateOption.MaxBackups > 0 {
|
||||
files, err := filepath.Glob(filepath.Join(backupDir, baseName+".*"))
|
||||
if err == nil && len(files) > l.RotateOption.MaxBackups {
|
||||
sort.Slice(files, func(i, j int) bool {
|
||||
return files[i] < files[j]
|
||||
})
|
||||
for _, old := range files[:len(files)-l.RotateOption.MaxBackups] {
|
||||
os.Remove(old)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reopen a new log file
|
||||
file, err := os.OpenFile(l.CurrentLogFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.file = file
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// compressFile compresses the given file using zip format and creates a .gz file.
|
||||
func compressFile(filename string) error {
|
||||
zipFilename := filename + ".gz"
|
||||
outFile, err := os.Create(zipFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer outFile.Close()
|
||||
|
||||
zipWriter := zip.NewWriter(outFile)
|
||||
defer zipWriter.Close()
|
||||
|
||||
fileToCompress, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fileToCompress.Close()
|
||||
|
||||
w, err := zipWriter.Create(filepath.Base(filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(w, fileToCompress)
|
||||
return err
|
||||
}
|
||||
@@ -126,6 +126,52 @@ func (v *Viewer) HandleReadLogSummary(w http.ResponseWriter, r *http.Request) {
|
||||
utils.SendJSONResponse(w, summary)
|
||||
}
|
||||
|
||||
func (v *Viewer) HandleLogErrorSummary(w http.ResponseWriter, r *http.Request) {
|
||||
filename, err := utils.GetPara(r, "file")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "invalid filename given")
|
||||
return
|
||||
}
|
||||
|
||||
content, err := v.LoadLogFile(strings.TrimSpace(filepath.Base(filename)))
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
//Generate the error summary for log that is request and non 100 - 200 range status code
|
||||
errorLines := [][]string{}
|
||||
lines := strings.Split(content, "\n")
|
||||
for _, line := range lines {
|
||||
if strings.TrimSpace(line) == "" {
|
||||
continue
|
||||
}
|
||||
// Only process router logs with a status code not in 1xx or 2xx
|
||||
if strings.Contains(line, "[router:") {
|
||||
//Extract date time from the line
|
||||
timestamp := ""
|
||||
if strings.HasPrefix(line, "[") && strings.Contains(line, "]") {
|
||||
timestamp = line[1:strings.Index(line, "]")]
|
||||
}
|
||||
|
||||
//Trim out the request metadata
|
||||
line = line[strings.LastIndex(line, "]")+1:]
|
||||
fields := strings.Fields(strings.TrimSpace(line))
|
||||
|
||||
if len(fields) > 0 {
|
||||
statusStr := fields[2]
|
||||
if len(statusStr) == 3 && (statusStr[0] != '1' && statusStr[0] != '2' && statusStr[0] != '3') {
|
||||
fieldsWithTimestamp := append([]string{timestamp}, strings.Fields(strings.TrimSpace(line))...)
|
||||
errorLines = append(errorLines, fieldsWithTimestamp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
js, _ := json.Marshal(errorLines)
|
||||
utils.SendJSONResponse(w, string(js))
|
||||
}
|
||||
|
||||
/*
|
||||
Log Access Functions
|
||||
*/
|
||||
@@ -322,104 +368,3 @@ func (v *Viewer) LoadLogSummary(filename string) (string, error) {
|
||||
return "", errors.New("log file not found")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Log examples:
|
||||
|
||||
[2025-08-18 21:02:15.664246] [router:host-http] [origin:test1.localhost] [client: 127.0.0.1] [useragent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0] GET /system/file_system/listDirHash?dir=s2%3A%2FMusic%2FMusic%20Bank%2FYear%202025%2F08-2025%2F 200
|
||||
[2025-08-18 21:02:20.682091] [router:host-http] [origin:test1.localhost] [client: 127.0.0.1] [useragent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0] GET /system/file_system/listDirHash?dir=s2%3A%2FMusic%2FMusic%20Bank%2FYear%202025%2F08-2025%2F 200
|
||||
[2025-08-18 21:02:20.725569] [internal] [system:info] mDNS scan result updated
|
||||
[2025-08-17 20:24:38.669488] [uptime-monitor] [system:info] Uptime updated - 1755433478
|
||||
[2025-08-17 20:25:08.247535] [internal] [system:info] mDNS scan result updated
|
||||
[2025-08-17 20:29:38.669187] [uptime-monitor] [system:info] Uptime updated - 1755433778
|
||||
[2025-08-17 20:34:38.669090] [uptime-monitor] [system:info] Uptime updated - 1755434078
|
||||
[2025-08-17 20:39:38.668610] [uptime-monitor] [system:info] Uptime updated - 1755434378
|
||||
[2025-08-17 20:40:08.248890] [internal] [system:info] mDNS scan result updated
|
||||
[2025-08-17 20:44:38.669058] [uptime-monitor] [system:info] Uptime updated - 1755434678
|
||||
[2025-08-17 20:49:38.669340] [uptime-monitor] [system:info] Uptime updated - 1755434978
|
||||
[2025-08-17 20:54:38.668785] [uptime-monitor] [system:info] Uptime updated - 1755435278
|
||||
[2025-08-17 20:55:08.247715] [internal] [system:info] mDNS scan result updated
|
||||
[2025-08-17 20:59:38.668575] [uptime-monitor] [system:info] Uptime updated - 1755435578
|
||||
[2025-08-17 21:04:38.669637] [uptime-monitor] [system:info] Uptime updated - 1755435878
|
||||
[2025-08-17 21:09:38.669109] [uptime-monitor] [system:info] Uptime updated - 1755436178
|
||||
[2025-08-17 21:10:08.247618] [internal] [system:info] mDNS scan result updated
|
||||
[2025-08-17 21:14:38.668828] [uptime-monitor] [system:info] Uptime updated - 1755436478
|
||||
[2025-08-17 21:19:38.669091] [uptime-monitor] [system:info] Uptime updated - 1755436778
|
||||
[2025-08-17 21:24:38.668830] [uptime-monitor] [system:info] Uptime updated - 1755437078
|
||||
[2025-08-17 21:25:08.246931] [internal] [system:info] mDNS scan result updated
|
||||
[2025-08-17 21:29:38.673217] [uptime-monitor] [system:info] Uptime updated - 1755437378
|
||||
[2025-08-17 21:34:38.668883] [uptime-monitor] [system:info] Uptime updated - 1755437678
|
||||
[2025-08-17 21:39:38.668980] [uptime-monitor] [system:info] Uptime updated - 1755437978
|
||||
[2025-08-17 21:40:08.266062] [internal] [system:info] mDNS scan result updated
|
||||
[2025-08-17 21:44:38.669150] [uptime-monitor] [system:info] Uptime updated - 1755438278
|
||||
[2025-08-17 21:49:38.668640] [uptime-monitor] [system:info] Uptime updated - 1755438578
|
||||
[2025-08-17 21:54:38.669275] [uptime-monitor] [system:info] Uptime updated - 1755438878
|
||||
[2025-08-17 21:55:08.266425] [internal] [system:info] mDNS scan result updated
|
||||
[2025-08-17 21:59:38.668861] [uptime-monitor] [system:info] Uptime updated - 1755439178
|
||||
[2025-08-17 22:04:38.668840] [uptime-monitor] [system:info] Uptime updated - 1755439478
|
||||
[2025-08-17 22:09:38.668798] [uptime-monitor] [system:info] Uptime updated - 1755439778
|
||||
[2025-08-17 22:10:08.266417] [internal] [system:info] mDNS scan result updated
|
||||
[2025-08-17 22:14:38.669122] [uptime-monitor] [system:info] Uptime updated - 1755440078
|
||||
[2025-08-17 22:19:38.668810] [uptime-monitor] [system:info] Uptime updated - 1755440378
|
||||
[2025-08-17 22:21:35.947519] [netstat] [system:info] Netstats listener stopped
|
||||
[2025-08-17 22:21:35.947519] [internal] [system:info] Shutting down Zoraxy
|
||||
[2025-08-17 22:21:35.947519] [internal] [system:info] Closing Netstats Listener
|
||||
[2025-08-17 22:21:35.970526] [plugin-manager] [system:error] plugin com.example.restful-example encounted a fatal error. Disabling plugin...: exit status 0xc000013a
|
||||
[2025-08-17 22:21:35.970526] [plugin-manager] [system:error] plugin org.aroz.zoraxy.api_call_example encounted a fatal error. Disabling plugin...: exit status 0xc000013a
|
||||
[2025-08-17 22:21:36.250929] [internal] [system:info] Closing Statistic Collector
|
||||
[2025-08-17 22:21:36.318808] [internal] [system:info] Stopping mDNS Discoverer (might take a few minutes)
|
||||
[2025-08-17 22:21:36.319829] [internal] [system:info] Shutting down load balancer
|
||||
[2025-08-17 22:21:36.319829] [internal] [system:info] Closing Certificates Auto Renewer
|
||||
[2025-08-17 22:21:36.319829] [internal] [system:info] Closing Access Controller
|
||||
[2025-08-17 22:21:36.319829] [internal] [system:info] Shutting down plugin manager
|
||||
[2025-08-17 22:21:36.319829] [internal] [system:info] Cleaning up tmp files
|
||||
[2025-08-17 22:21:36.328033] [internal] [system:info] Stopping system database
|
||||
[2025-08-18 20:31:49.673182] [database] [system:info] Using BoltDB as the database backend
|
||||
[2025-08-18 20:31:49.784069] [auth] [system:info] Authentication session key loaded from database
|
||||
[2025-08-18 20:31:50.290804] [LoadBalancer] [system:info] Upstream state cache ticker started
|
||||
[2025-08-18 20:31:50.510300] [static-webserv] [system:info] Static Web Server started. Listeing on :5487
|
||||
[2025-08-18 20:31:51.017433] [internal] [system:info] Starting ACME handler
|
||||
[2025-08-18 20:31:51.022545] [cert-renew] [system:info] ACME early renew set to 30 days and check interval set to 86400 seconds
|
||||
[2025-08-18 20:31:51.073031] [plugin-manager] [system:info] Hot reload ticker started
|
||||
[2025-08-18 20:31:51.357203] [plugin-manager] [system:info] Loaded plugin: API Call Example Plugin
|
||||
[2025-08-18 20:31:51.357782] [plugin-manager] [system:info] Generated API key for plugin API Call Example Plugin
|
||||
[2025-08-18 20:31:51.358293] [plugin-manager] [system:info] Starting plugin API Call Example Plugin at :5974
|
||||
[2025-08-18 20:31:51.406867] [plugin-manager] [system:info] [API Call Example Plugin:13316] Starting API Call Example Plugin on 127.0.0.1:5974
|
||||
[2025-08-18 20:31:51.466380] [plugin-manager] [system:info] Plugin list synced from plugin store
|
||||
[2025-08-18 20:31:51.662866] [plugin-manager] [system:info] Loaded plugin: Restful Example
|
||||
[2025-08-18 20:31:51.663383] [plugin-manager] [system:info] Starting plugin Restful Example at :5874
|
||||
[2025-08-18 20:31:51.688641] [plugin-manager] [system:info] [Restful Example:10500] Restful-example started at http://127.0.0.1:5874
|
||||
[2025-08-18 20:31:51.721309] [plugin-manager] [system:info] Plugin hash generated for: org.aroz.zoraxy.api_call_example
|
||||
[2025-08-18 20:31:51.777523] [plugin-manager] [system:info] Plugin hash generated for: com.example.restful-example
|
||||
[2025-08-18 20:31:51.789497] [internal] [system:info] Inbound port not set. Using default (443)
|
||||
[2025-08-18 20:31:51.789497] [internal] [system:info] TLS mode enabled. Serving proxy request with TLS
|
||||
[2025-08-18 20:31:51.789497] [internal] [system:info] Development mode enabled. Using no-store Cache Control policy
|
||||
[2025-08-18 20:31:51.790016] [internal] [system:info] Force latest TLS mode disabled. Minimum TLS version is set to v1.0
|
||||
[2025-08-18 20:31:51.790016] [internal] [system:info] Port 80 listener enabled
|
||||
[2025-08-18 20:31:51.790016] [internal] [system:info] Force HTTPS mode enabled
|
||||
[2025-08-18 20:31:51.825385] [proxy-config] [system:info] *.yami.localhost -> 192.168.0.16:8080 routing rule loaded
|
||||
[2025-08-18 20:31:51.833567] [proxy-config] [system:info] a.localhost -> imuslab.com routing rule loaded
|
||||
[2025-08-18 20:31:51.849358] [proxy-config] [system:info] aroz.localhost -> 192.168.0.16:8080 routing rule loaded
|
||||
[2025-08-18 20:31:51.852977] [proxy-config] [system:info] auth.localhost -> localhost:5488 routing rule loaded
|
||||
[2025-08-18 20:31:51.866792] [proxy-config] [system:info] debug.localhost -> dc.imuslab.com:8080 routing rule loaded
|
||||
[2025-08-18 20:31:51.878091] [proxy-config] [system:info] peer.localhost -> 192.168.0.16:8080 routing rule loaded
|
||||
[2025-08-18 20:31:51.887843] [proxy-config] [system:info] / -> 127.0.0.1:5487 routing rule loaded
|
||||
[2025-08-18 20:31:51.895039] [proxy-config] [system:info] test.imuslab.com -> 192.168.1.202:8443 routing rule loaded
|
||||
[2025-08-18 20:31:51.909917] [proxy-config] [system:info] test.imuslab.internal -> 127.0.0.1:80 routing rule loaded
|
||||
[2025-08-18 20:31:51.922685] [proxy-config] [system:info] test.localhost -> alanyeung.co routing rule loaded
|
||||
[2025-08-18 20:31:51.937314] [proxy-config] [system:info] webdav.localhost -> 127.0.0.1:80/redirect routing rule loaded
|
||||
[2025-08-18 20:31:52.239414] [dprouter] [system:info] Starting HTTP-to-HTTPS redirector (port 80)
|
||||
[2025-08-18 20:31:52.239414] [internal] [system:info] Dynamic Reverse Proxy service started
|
||||
[2025-08-18 20:31:52.239414] [dprouter] [system:info] Reverse proxy service started in the background (TLS mode)
|
||||
[2025-08-18 20:31:52.289262] [internal] [system:info] Zoraxy started. Visit control panel at http://localhost:8000
|
||||
[2025-08-18 20:31:52.289262] [internal] [system:info] Assigned temporary port:36951
|
||||
[2025-08-18 20:31:54.513995] [internal] [system:info] Uptime Monitor background service started
|
||||
[2025-08-18 20:32:20.725596] [internal] [system:info] mDNS Startup scan completed
|
||||
[2025-08-18 20:36:52.239883] [uptime-monitor] [system:info] Uptime updated - 1755520612
|
||||
[2025-08-18 20:56:14.160166] [router:host-http] [origin:test1.localhost] [client: 127.0.0.1] [useragent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0] GET /system/file_system/preference?key=file_explorer/theme 200
|
||||
[2025-08-18 20:56:14.160166] [router:host-http] [origin:test1.localhost] [client: 127.0.0.1] [useragent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0] GET /system/file_system/preference?key=file_explorer/listmode 200
|
||||
[2025-08-18 20:56:14.160166] [router:host-http] [origin:test1.localhost] [client: 127.0.0.1] [useragent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0] GET /system/file_system/preference?key=file_explorer/listmode 200
|
||||
[2025-08-18 20:56:14.170270] [router:host-http] [origin:test1.localhost] [client: 127.0.0.1] [useragent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0] GET /system/file_system/listRoots?user=true 200
|
||||
[2025-08-18 20:56:14.171752] [router:host-http] [origin:test1.localhost] [client: 127.0.0.1] [useragent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0] GET /system/id/requestInfo 200
|
||||
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user