Skip to content

Commit b594326

Browse files
committed
fix: resolve silent log loss and optimize performance via incremental parsing
1 parent ec55071 commit b594326

1 file changed

Lines changed: 107 additions & 29 deletions

File tree

main.go

Lines changed: 107 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,18 @@ type CaddyLog struct {
3434
}
3535

3636
var (
37-
isTerminal bool
38-
totalLines int
39-
lastSize int64
40-
startTime time.Time
41-
lastH, lastW int
42-
lastCPUTime int64
43-
lastSampleTime time.Time
44-
cachedLogs []CaddyLog
45-
cachedStats LogStats
46-
remoteIP bool
37+
isTerminal bool
38+
totalLines int
39+
lastSize int64
40+
startTime time.Time
41+
lastH, lastW int
42+
lastCPUTime int64
43+
lastSampleTime time.Time
44+
cachedLogs []CaddyLog
45+
cachedStats LogStats
46+
remoteIP bool
47+
lastParsedOffset int64
48+
parseLeftover string
4749
)
4850

4951
type LogStats struct {
@@ -173,17 +175,41 @@ func getLastLines(filePath string, n int) ([]string, int64) {
173175
var cursor int64 = 0
174176
bufSize := int64(4096)
175177
if fileSize < bufSize { bufSize = fileSize }
178+
buf := make([]byte, bufSize)
179+
180+
var leftover string
176181
for cursor < fileSize {
182+
readSize := bufSize
177183
cursor += bufSize
178-
if cursor > fileSize { cursor = fileSize }
184+
if cursor > fileSize {
185+
readSize = fileSize - (cursor - bufSize)
186+
cursor = fileSize
187+
}
188+
179189
file.Seek(fileSize-cursor, io.SeekStart)
180-
buf := make([]byte, bufSize)
181-
file.Read(buf)
182-
chunk := strings.Split(string(buf), "\n")
183-
if len(lines) == 0 && chunk[len(chunk)-1] == "" { chunk = chunk[:len(chunk)-1] }
190+
c, err := file.Read(buf[:readSize])
191+
if c <= 0 || err != nil { break }
192+
193+
content := string(buf[:c]) + leftover
194+
chunk := strings.Split(content, "\n")
195+
196+
if fileSize-cursor > 0 {
197+
leftover = chunk[0]
198+
chunk = chunk[1:]
199+
} else {
200+
leftover = ""
201+
}
202+
203+
if len(lines) == 0 && len(chunk) > 0 && chunk[len(chunk)-1] == "" {
204+
chunk = chunk[:len(chunk)-1]
205+
}
206+
184207
lines = append(chunk, lines...)
185208
if len(lines) > n { return lines[len(lines)-n:], fileSize }
186209
}
210+
if leftover != "" {
211+
lines = append([]string{leftover}, lines...)
212+
}
187213
return lines, fileSize
188214
}
189215

@@ -331,23 +357,75 @@ func main() {
331357

332358
func processLogs(filePath string, lCount int, ha bool, e bool, all bool, f string, host string, count bool, dash bool, width int, shouldUpdate bool) {
333359
if shouldUpdate {
334-
fetchCount := lCount * 10
335-
if all { fetchCount = totalLines }
336-
if fetchCount < 10 { fetchCount = 10 }
337-
rawLines, _ := getLastLines(filePath, fetchCount)
338-
newLogs := make([]CaddyLog, 0, len(rawLines))
339-
for _, line := range rawLines {
340-
if line == "" { continue }
341-
var l CaddyLog
342-
if err := json.Unmarshal([]byte(line), &l); err == nil {
343-
if !remoteIP && l.Request.ClientIP != "" {
344-
l.Request.RemoteIP = l.Request.ClientIP
360+
info, err := os.Stat(filePath)
361+
if err == nil {
362+
currentSize := info.Size()
363+
// Si premier run ou fichier tronqué/vidé
364+
if lastParsedOffset == 0 || currentSize < lastParsedOffset {
365+
fetchCount := lCount * 10
366+
if all { fetchCount = totalLines }
367+
if fetchCount < 10 { fetchCount = 10 }
368+
rawLines, _ := getLastLines(filePath, fetchCount)
369+
newLogs := make([]CaddyLog, 0, len(rawLines))
370+
for _, line := range rawLines {
371+
if line == "" { continue }
372+
var l CaddyLog
373+
if err := json.Unmarshal([]byte(line), &l); err == nil {
374+
if !remoteIP && l.Request.ClientIP != "" {
375+
l.Request.RemoteIP = l.Request.ClientIP
376+
}
377+
newLogs = append(newLogs, l)
378+
}
379+
}
380+
cachedLogs = newLogs
381+
lastParsedOffset = currentSize
382+
parseLeftover = ""
383+
} else if currentSize > lastParsedOffset {
384+
// Lecture incrémentale
385+
f, err := os.Open(filePath)
386+
if err == nil {
387+
defer f.Close()
388+
f.Seek(lastParsedOffset, io.SeekStart)
389+
buf := make([]byte, 32*1024)
390+
var content string
391+
for {
392+
c, err := f.Read(buf)
393+
if c > 0 { content += string(buf[:c]) }
394+
if err != nil { break }
395+
}
396+
397+
content = parseLeftover + content
398+
lines := strings.Split(content, "\n")
399+
400+
if len(lines) > 0 {
401+
parseLeftover = lines[len(lines)-1]
402+
lines = lines[:len(lines)-1]
403+
}
404+
405+
for _, line := range lines {
406+
if line == "" { continue }
407+
var l CaddyLog
408+
if err := json.Unmarshal([]byte(line), &l); err == nil {
409+
if !remoteIP && l.Request.ClientIP != "" {
410+
l.Request.RemoteIP = l.Request.ClientIP
411+
}
412+
cachedLogs = append(cachedLogs, l)
413+
}
414+
}
415+
416+
// Tronquer cachedLogs pour ne pas accumuler indéfiniment en mémoire
417+
limit := lCount * 10
418+
if all { limit = totalLines }
419+
if limit < 100 { limit = 100 }
420+
if len(cachedLogs) > limit {
421+
cachedLogs = cachedLogs[len(cachedLogs)-limit:]
422+
}
423+
424+
lastParsedOffset = currentSize
345425
}
346-
newLogs = append(newLogs, l)
347426
}
427+
cachedStats = calculateStats(cachedLogs)
348428
}
349-
cachedLogs = newLogs
350-
cachedStats = calculateStats(cachedLogs)
351429
}
352430

353431
if isTerminal || count {

0 commit comments

Comments
 (0)