enabled file logging with log rotation

This commit is contained in:
2024-03-25 11:32:11 +01:00
parent e7639c2720
commit bf2e24009e
4 changed files with 135 additions and 68 deletions

15
main.go
View File

@@ -30,6 +30,9 @@ var (
userFromEnv = os.Getenv("USERNAME")
passFromEnv = os.Getenv("PASSWORD")
logFile string
enableFileLogging bool
//go:embed frontend/dist/index.html
//go:embed frontend/dist/assets/*
frontend embed.FS
@@ -47,6 +50,9 @@ func init() {
flag.StringVar(&sessionFilePath, "session", ".", "session file path")
flag.StringVar(&localDatabasePath, "db", "local.db", "local database path")
flag.BoolVar(&enableFileLogging, "fl", false, "enable outputting logs to a file")
flag.StringVar(&logFile, "lf", "yt-dlp-webui.log", "set log file location")
flag.BoolVar(&requireAuth, "auth", false, "Enable RPC authentication")
flag.StringVar(&username, "user", userFromEnv, "Username required for auth")
flag.StringVar(&password, "pass", passFromEnv, "Password required for auth")
@@ -79,5 +85,12 @@ func main() {
log.Println(cli.BgRed, "config", cli.Reset, err)
}
server.RunBlocking(c.Host, c.Port, frontend, localDatabasePath)
server.RunBlocking(&server.RunConfig{
Host: c.Host,
Port: c.Port,
App: frontend,
DBPath: localDatabasePath,
FileLogging: enableFileLogging,
LogFile: logFile,
})
}

View File

@@ -0,0 +1,80 @@
package logging
import (
"compress/gzip"
"io"
"os"
"sync"
"time"
)
// implements io.Writer interface
type LogRotateWriter struct {
mu sync.Mutex
fd *os.File
filename string
}
func NewRotableLogger(filename string) (*LogRotateWriter, error) {
fd, err := os.Create(filename)
if err != nil {
return nil, err
}
w := &LogRotateWriter{filename: filename, fd: fd}
return w, nil
}
func (w *LogRotateWriter) Write(b []byte) (int, error) {
w.mu.Lock()
defer w.mu.Unlock()
return w.fd.Write(b)
}
func (w *LogRotateWriter) Rotate() error {
var err error
w.mu.Lock()
gzFile, err := os.Create(w.filename + "." + time.Now().Format(time.RFC3339) + ".gz")
if err != nil {
return err
}
data, err := io.ReadAll(w.fd)
if err != nil {
return err
}
defer func() {
w.mu.Unlock()
w.gzipLog(gzFile, &data)
}()
_, err = os.Stat(w.filename)
if err != nil {
return err
}
if w.fd != nil {
err = w.fd.Close()
w.fd = nil
if err != nil {
return err
}
}
err = os.Remove(w.filename)
if err != nil {
return err
}
w.fd, err = os.Create(w.filename)
return err
}
func (w *LogRotateWriter) gzipLog(wr io.Writer, data *[]byte) error {
if _, err := gzip.NewWriter(wr).Write(*data); err != nil {
return err
}
return nil
}

View File

@@ -30,6 +30,15 @@ import (
_ "modernc.org/sqlite"
)
type RunConfig struct {
Host string
Port int
App fs.FS
DBPath string
LogFile string
FileLogging bool
}
type serverConfig struct {
frontend fs.FS
logger *slog.Logger
@@ -40,19 +49,37 @@ type serverConfig struct {
mq *internal.MessageQueue
}
func RunBlocking(host string, port int, frontend fs.FS, dbPath string) {
func RunBlocking(cfg *RunConfig) {
var mdb internal.MemoryDB
logWriters := []io.Writer{
os.Stdout,
logging.NewObservableLogger(),
}
if cfg.FileLogging {
logger, err := logging.NewRotableLogger(cfg.LogFile)
if err != nil {
panic(err)
}
go func() {
for {
time.Sleep(time.Hour * 24)
logger.Rotate()
}
}()
logWriters = append(logWriters, logger)
}
logger := slog.New(
slog.NewTextHandler(
io.MultiWriter(os.Stdout, logging.NewObservableLogger()),
&slog.HandlerOptions{},
),
slog.NewTextHandler(io.MultiWriter(logWriters...), &slog.HandlerOptions{}),
)
mdb.Restore(logger)
db, err := sql.Open("sqlite", dbPath)
db, err := sql.Open("sqlite", cfg.DBPath)
if err != nil {
logger.Error("failed to open database", slog.String("err", err.Error()))
}
@@ -66,10 +93,10 @@ func RunBlocking(host string, port int, frontend fs.FS, dbPath string) {
go mq.Subscriber()
srv := newServer(serverConfig{
frontend: frontend,
frontend: cfg.App,
logger: logger,
host: host,
port: port,
host: cfg.Host,
port: cfg.Port,
mdb: &mdb,
mq: mq,
db: db,
@@ -79,11 +106,13 @@ func RunBlocking(host string, port int, frontend fs.FS, dbPath string) {
go autoPersist(time.Minute*5, &mdb, logger)
network := "tcp"
address := fmt.Sprintf("%s:%d", host, port)
if strings.HasPrefix(host, "/") {
address := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)
if strings.HasPrefix(cfg.Host, "/") {
network = "unix"
address = host
address = cfg.Host
}
listener, err := net.Listen(network, address)
if err != nil {
logger.Error("failed to listen", slog.String("err", err.Error()))

View File

@@ -1,55 +0,0 @@
package utils
import (
"io"
"io/fs"
"os"
"path/filepath"
"time"
"github.com/marcopeocchi/yt-dlp-web-ui/server/config"
)
func LogRotate() (*os.File, error) {
logs := findLogs()
for _, log := range logs {
logfd, err := os.Open(log)
if err != nil {
return nil, err
}
gzWriter, err := os.Create(log + ".gz")
if err != nil {
return nil, err
}
_, err = io.Copy(gzWriter, logfd)
if err != nil {
return nil, err
}
}
logfile := time.Now().String() + ".log"
config.Instance().CurrentLogFile = logfile
return os.Create(logfile)
}
func findLogs() []string {
var (
logfiles []string
root = config.Instance().LogPath
)
filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if filepath.Ext(d.Name()) == ".log" {
logfiles = append(logfiles, path)
}
return nil
})
return logfiles
}