Files
warpbox-dev/backend/libs/logging/logger.go
Daniel Legt 26619bacbc feat: add admin console, cleanup, and thumbnail workers
- Implement a token-authenticated admin console at `/admin` with overview metrics and file management.
- Add a background worker to periodically clean up expired boxes based on `WARPBOX_CLEANUP_EVERY`.
- Add a background worker to generate image and video thumbnails based on `WARPBOX_THUMBNAIL_EVERY`.
- Update file storage paths to use `@each@` and `@thumb@` prefixes to separate original files from thumbnails.
- Add severity fields to startup logs and update configuration templates.
2026-05-25 16:52:57 +03:00

106 lines
2.0 KiB
Go

package logging
import (
"context"
"encoding/json"
"io"
"log/slog"
"os"
"path/filepath"
"sync"
"time"
)
type Closer func() error
type jsonLineHandler struct {
mu *sync.Mutex
out io.Writer
attrs []slog.Attr
}
func New(dataDir string) (*slog.Logger, Closer, error) {
logDir := filepath.Join(dataDir, "logs")
if err := os.MkdirAll(logDir, 0o755); err != nil {
return nil, nil, err
}
path := filepath.Join(logDir, time.Now().Format("2006-01-02")+".log")
file, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
if err != nil {
return nil, nil, err
}
handler := &jsonLineHandler{
mu: &sync.Mutex{},
out: io.MultiWriter(os.Stdout, file),
}
return slog.New(handler), file.Close, nil
}
func (h *jsonLineHandler) Enabled(context.Context, slog.Level) bool {
return true
}
func (h *jsonLineHandler) Handle(_ context.Context, record slog.Record) error {
entry := map[string]any{
"date": record.Time.Format("2006-01-02"),
"time": record.Time.Format("15:04:05"),
"source": "app",
"code": 0,
"severity": severity(record.Level),
"log": record.Message,
}
for _, attr := range h.attrs {
applyAttr(entry, attr)
}
record.Attrs(func(attr slog.Attr) bool {
applyAttr(entry, attr)
return true
})
line, err := json.Marshal(entry)
if err != nil {
return err
}
h.mu.Lock()
defer h.mu.Unlock()
_, err = h.out.Write(append(line, '\n'))
return err
}
func (h *jsonLineHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
next := &jsonLineHandler{
mu: h.mu,
out: h.out,
attrs: append([]slog.Attr{}, h.attrs...),
}
next.attrs = append(next.attrs, attrs...)
return next
}
func (h *jsonLineHandler) WithGroup(string) slog.Handler {
return h
}
func applyAttr(entry map[string]any, attr slog.Attr) {
attr.Value = attr.Value.Resolve()
entry[attr.Key] = attr.Value.Any()
}
func severity(level slog.Level) string {
switch {
case level >= slog.LevelError:
return "error"
case level >= slog.LevelWarn:
return "warn"
case level <= slog.LevelDebug:
return "dev"
default:
return "dev"
}
}