106 lines
2.2 KiB
Go
106 lines
2.2 KiB
Go
package logging
|
|
|
|
import (
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
|
|
"luxtools-client/internal/config"
|
|
)
|
|
|
|
const (
|
|
logBufferMaxBytes = 256 * 1024
|
|
logFileMaxBytes = 5 * 1024 * 1024
|
|
)
|
|
|
|
// Buffer stores recent log output for control pages.
|
|
type Buffer struct {
|
|
mu sync.Mutex
|
|
buf []byte
|
|
max int
|
|
}
|
|
|
|
func newBuffer(max int) *Buffer {
|
|
return &Buffer{max: max}
|
|
}
|
|
|
|
func (b *Buffer) Write(p []byte) (int, error) {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
|
|
if len(p) > b.max {
|
|
p = p[len(p)-b.max:]
|
|
}
|
|
b.buf = append(b.buf, p...)
|
|
if len(b.buf) > b.max {
|
|
b.buf = b.buf[len(b.buf)-b.max:]
|
|
}
|
|
return len(p), nil
|
|
}
|
|
|
|
// Bytes returns a copy of the buffered log content.
|
|
func (b *Buffer) Bytes() []byte {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
if len(b.buf) == 0 {
|
|
return nil
|
|
}
|
|
copyBuf := make([]byte, len(b.buf))
|
|
copy(copyBuf, b.buf)
|
|
return copyBuf
|
|
}
|
|
|
|
func logFilePath() (string, error) {
|
|
configPath, err := config.ConfigPath()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return filepath.Join(filepath.Dir(configPath), "luxtools-client.log"), nil
|
|
}
|
|
|
|
func prepareLogFile(path string, maxBytes int64) (*os.File, error) {
|
|
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
|
return nil, err
|
|
}
|
|
if info, err := os.Stat(path); err == nil && info.Size() > maxBytes {
|
|
_ = os.Remove(path + ".old")
|
|
_ = os.Rename(path, path+".old")
|
|
}
|
|
return os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
|
|
}
|
|
|
|
// Setup builds loggers, a buffer, and optional file logging.
|
|
func Setup() (*log.Logger, *log.Logger, *Buffer, string, func()) {
|
|
logStore := newBuffer(logBufferMaxBytes)
|
|
logPath, logErr := logFilePath()
|
|
var logFile *os.File
|
|
if logErr == nil {
|
|
if f, err := prepareLogFile(logPath, logFileMaxBytes); err == nil {
|
|
logFile = f
|
|
} else {
|
|
logPath = ""
|
|
}
|
|
} else {
|
|
logPath = ""
|
|
}
|
|
|
|
infoWriters := []io.Writer{logStore, os.Stdout}
|
|
errWriters := []io.Writer{logStore, os.Stderr}
|
|
if logFile != nil {
|
|
infoWriters = append(infoWriters, logFile)
|
|
errWriters = append(errWriters, logFile)
|
|
}
|
|
|
|
infoLog := log.New(io.MultiWriter(infoWriters...), "", log.LstdFlags)
|
|
errLog := log.New(io.MultiWriter(errWriters...), "ERROR: ", log.LstdFlags)
|
|
|
|
cleanup := func() {}
|
|
if logFile != nil {
|
|
cleanup = func() { _ = logFile.Close() }
|
|
}
|
|
|
|
return infoLog, errLog, logStore, logPath, cleanup
|
|
}
|