mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-10-12 01:58:46 +02:00
feat(cli): keep a log journal on disk with file headers and an upload limit
ctrld keeps a second log file, ctrld-journal.log, next to ctrld.log. The journal holds all warnings and errors and each event with the field journal=true. It stays on disk after a start of the service and after a self-upgrade. Each log file starts with a Log header line. Each log send upload starts with the same line, made at send time, and has a limit of about 16 MB.
This commit is contained in:
1 parent
d1690545f9
commit
8934e09428
41 files changed
+5661
-495
No files matched your search
@@ -0,0 +1,73 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/Control-D-Inc/ctrld/internal/router"
|
||||
)
|
||||
|
||||
// logPathFile is the open log_path file. The header renderer and the log
|
||||
// readers need it, and both run outside this file.
|
||||
var logPathFile atomic.Pointer[rotatingFile]
|
||||
|
||||
// pendingLogPathRotation holds the rotation of a start-time backup. The
|
||||
// logger reaches no file at that point, so initLogging reports it later.
|
||||
var pendingLogPathRotation atomic.Pointer[logRotation]
|
||||
|
||||
// openLogPathWriter opens the log_path file of this run and stores it for the
|
||||
// header renderer and the log readers. It returns nil when no log_path is set.
|
||||
// A start calls it more than once, so a call for the file that is already open
|
||||
// keeps that file: a second open would put a second header in it.
|
||||
func openLogPathWriter(doBackup bool) *rotatingFile {
|
||||
logFilePath := normalizeLogFilePath(cfg.Service.LogPath)
|
||||
if logFilePath == "" {
|
||||
return nil
|
||||
}
|
||||
budget := debugLogBudget(&cfg.Service, router.Name() != "")
|
||||
rf := logPathFile.Load()
|
||||
if rf == nil || rf.currentPath() != logFilePath {
|
||||
rf = newLogPathFile(logFilePath, budget)
|
||||
} else {
|
||||
rf.setBudget(budget)
|
||||
}
|
||||
if doBackup {
|
||||
backupLogPathFile(rf)
|
||||
}
|
||||
return rf
|
||||
}
|
||||
|
||||
// newLogPathFile opens path and puts it in place of the file of an earlier
|
||||
// call. A ctrld that cannot write the log file its operator asked for leaves
|
||||
// no record of the run, so the open failure ends the process.
|
||||
func newLogPathFile(path string, budget logBudget) *rotatingFile {
|
||||
rf, err := newRotatingFile(path, budget, nil)
|
||||
if err != nil {
|
||||
mainLog.Load().Error().Msgf("failed to create log file: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
rf.onRotate = emitLogRotated
|
||||
if old := logPathFile.Swap(rf); old != nil {
|
||||
_ = old.close()
|
||||
}
|
||||
return rf
|
||||
}
|
||||
|
||||
// backupLogPathFile moves the file of an earlier run away, so no run
|
||||
// overwrites another.
|
||||
func backupLogPathFile(rf *rotatingFile) {
|
||||
if !logFileHasContent(rf.currentPath()) {
|
||||
return
|
||||
}
|
||||
record, err := rf.rotateNow()
|
||||
if err != nil {
|
||||
mainLog.Load().Error().Msgf("could not backup old log file: %v", err)
|
||||
return
|
||||
}
|
||||
pendingLogPathRotation.Store(record)
|
||||
}
|
||||
|
||||
func logFileHasContent(path string) bool {
|
||||
st, err := os.Stat(path)
|
||||
return err == nil && st.Size() > 0
|
||||
}
|
||||
Reference in new issue
Block a user