mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-20 04:42:33 +02:00

* feat: idle sleep for proxmox LXCs * refactor: replace deprecated docker api types * chore(api): remove debug task list endpoint * refactor: move servemux to gphttp/servemux; favicon.go to v1/favicon * refactor: introduce Pool interface, move agent_pool to agent module * refactor: simplify api code * feat: introduce debug api * refactor: remove net.URL and net.CIDR types, improved unmarshal handling * chore: update Makefile for debug build tag, update README * chore: add gperr.Unwrap method * feat: relative time and duration formatting * chore: add ROOT_DIR environment variable, refactor * migration: move homepage override and icon cache to $BASE_DIR/data, add migration code * fix: nil dereference on marshalling service health * fix: wait for route deletion * chore: enhance tasks debuggability * feat: stdout access logger and MultiWriter * fix(agent): remove agent properly on verify error * fix(metrics): disk exclusion logic and added corresponding tests * chore: update schema and prettify, fix package.json and Makefile * fix: I/O buffer not being shrunk before putting back to pool * feat: enhanced error handling module * chore: deps upgrade * feat: better value formatting and handling --------- Co-authored-by: yusing <yusing@6uo.me>
128 lines
2.5 KiB
Go
128 lines
2.5 KiB
Go
package accesslog
|
|
|
|
import (
|
|
"bytes"
|
|
ioPkg "io"
|
|
"time"
|
|
)
|
|
|
|
func (l *AccessLogger) rotate() (err error) {
|
|
io, ok := l.io.(supportRotate)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
// Get retention configuration
|
|
config := l.Config().Retention
|
|
var shouldKeep func(t time.Time, lineCount int) bool
|
|
|
|
if config.Last > 0 {
|
|
shouldKeep = func(_ time.Time, lineCount int) bool {
|
|
return lineCount < int(config.Last)
|
|
}
|
|
} else if config.Days > 0 {
|
|
cutoff := time.Now().AddDate(0, 0, -int(config.Days))
|
|
shouldKeep = func(t time.Time, _ int) bool {
|
|
return !t.IsZero() && !t.Before(cutoff)
|
|
}
|
|
} else {
|
|
return nil // No retention policy set
|
|
}
|
|
|
|
s := NewBackScanner(io, defaultChunkSize)
|
|
nRead := 0
|
|
nLines := 0
|
|
for s.Scan() {
|
|
nRead += len(s.Bytes()) + 1
|
|
nLines++
|
|
t := ParseLogTime(s.Bytes())
|
|
if !shouldKeep(t, nLines) {
|
|
break
|
|
}
|
|
}
|
|
if s.Err() != nil {
|
|
return s.Err()
|
|
}
|
|
|
|
beg := int64(nRead)
|
|
if _, err := io.Seek(-beg, ioPkg.SeekEnd); err != nil {
|
|
return err
|
|
}
|
|
buf := make([]byte, nRead)
|
|
if _, err := io.Read(buf); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := l.writeTruncate(buf); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (l *AccessLogger) writeTruncate(buf []byte) (err error) {
|
|
io, ok := l.io.(supportRotate)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
// Seek to beginning and truncate
|
|
if _, err := io.Seek(0, 0); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Write buffer back to file
|
|
nWritten, err := l.buffered.Write(buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = l.buffered.Flush(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Truncate file
|
|
if err = io.Truncate(int64(nWritten)); err != nil {
|
|
return err
|
|
}
|
|
|
|
// check bytes written == buffer size
|
|
if nWritten != len(buf) {
|
|
return ioPkg.ErrShortWrite
|
|
}
|
|
return
|
|
}
|
|
|
|
const timeLen = len(`"time":"`)
|
|
|
|
var timeJSON = []byte(`"time":"`)
|
|
|
|
func ParseLogTime(line []byte) (t time.Time) {
|
|
if len(line) == 0 {
|
|
return
|
|
}
|
|
|
|
if i := bytes.Index(line, timeJSON); i != -1 { // JSON format
|
|
var jsonStart = i + timeLen
|
|
var jsonEnd = i + timeLen + len(LogTimeFormat)
|
|
if len(line) < jsonEnd {
|
|
return
|
|
}
|
|
timeStr := line[jsonStart:jsonEnd]
|
|
t, _ = time.Parse(LogTimeFormat, string(timeStr))
|
|
return
|
|
}
|
|
|
|
// Common/Combined format
|
|
// Format: <virtual host> <host ip> - - [02/Jan/2006:15:04:05 -0700] ...
|
|
start := bytes.IndexByte(line, '[')
|
|
if start == -1 {
|
|
return
|
|
}
|
|
end := bytes.IndexByte(line[start:], ']')
|
|
if end == -1 {
|
|
return
|
|
}
|
|
end += start // adjust end position relative to full line
|
|
|
|
timeStr := line[start+1 : end]
|
|
t, _ = time.Parse(LogTimeFormat, string(timeStr)) // ignore error
|
|
return
|
|
}
|