gitea, notion webhook
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

100 lines
2.8 KiB

package logging
import (
"os"
"path/filepath"
"strings"
"sync"
"time"
)
// dailyWriter는 날짜별 로그 파일(<dir>/<prefix>-YYYY-MM-DD<ext>)에 기록하고,
// 날짜가 바뀌면 새 파일로 전환하며 retention일보다 오래된 파일을 삭제한다.
// 외부 의존성 없이 표준 라이브러리만 사용(저사양 타깃 정책 — 회전을 위해 lumberjack 등을 쓰지 않음).
type dailyWriter struct {
mu sync.Mutex
dir string
prefix string // 예: "app"
ext string // 예: ".log"
retention int // 보존 일수(오늘 포함)
curDate string
file *os.File
}
// newDailyWriter는 logFile(예: logs/app.log)을 기준으로 날짜별 writer를 만든다.
// → logs/app-2026-06-11.log 형태로 기록.
func newDailyWriter(logFile string, retention int) (*dailyWriter, error) {
if retention <= 0 {
retention = 7
}
base := filepath.Base(logFile)
ext := filepath.Ext(base) // ".log" (없으면 "")
w := &dailyWriter{
dir: filepath.Dir(logFile),
prefix: strings.TrimSuffix(base, ext),
ext: ext,
retention: retention,
}
if err := w.rotate(time.Now()); err != nil {
return nil, err
}
return w, nil
}
func (w *dailyWriter) filename(date string) string {
return filepath.Join(w.dir, w.prefix+"-"+date+w.ext)
}
func (w *dailyWriter) Write(p []byte) (int, error) {
w.mu.Lock()
defer w.mu.Unlock()
if err := w.rotate(time.Now()); err != nil {
return 0, err
}
return w.file.Write(p)
}
// rotate은 now 날짜의 파일을 연다. 이미 같은 날짜 파일이 열려 있으면 아무것도 안 한다.
// (newDailyWriter에서 최초 1회, 이후 Write에서 lock 보유 상태로 호출)
func (w *dailyWriter) rotate(now time.Time) error {
date := now.Format("2006-01-02")
if w.file != nil && date == w.curDate {
return nil
}
if w.dir != "" && w.dir != "." {
if err := os.MkdirAll(w.dir, 0o755); err != nil {
return err
}
}
f, err := os.OpenFile(w.filename(date), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
if err != nil {
return err
}
if w.file != nil {
_ = w.file.Close()
}
w.file = f
w.curDate = date
w.cleanup(now)
return nil
}
// cleanup은 retention일치(오늘 포함)만 남기고 오래된 날짜 파일을 삭제한다.
func (w *dailyWriter) cleanup(now time.Time) {
cutoff := now.AddDate(0, 0, -(w.retention - 1)).Format("2006-01-02")
matches, err := filepath.Glob(filepath.Join(w.dir, w.prefix+"-*"+w.ext))
if err != nil {
return
}
for _, path := range matches {
name := filepath.Base(path)
ds := strings.TrimSuffix(strings.TrimPrefix(name, w.prefix+"-"), w.ext)
if _, err := time.Parse("2006-01-02", ds); err != nil {
continue // 날짜 형식이 아니면 우리 파일이 아님 — 건드리지 않음
}
if ds < cutoff { // YYYY-MM-DD는 사전식 비교 = 날짜순 비교
_ = os.Remove(path)
}
}
}