mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-10-07 07:00:52 +08:00
fea: common filerotate
This commit is contained in:
220
pkg/filerotate/filerotate.go
Normal file
220
pkg/filerotate/filerotate.go
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
package filerotate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
DidClose func(path string, didRotate bool)
|
||||||
|
PathIfShouldRotate func(creationTime time.Time, now time.Time) string
|
||||||
|
}
|
||||||
|
|
||||||
|
type File struct {
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
|
// Path is the path of the current file
|
||||||
|
Path string
|
||||||
|
|
||||||
|
creationTime time.Time
|
||||||
|
|
||||||
|
//Location *time.Location
|
||||||
|
|
||||||
|
config Config
|
||||||
|
file *os.File
|
||||||
|
|
||||||
|
// position in the file of last Write or Write2, exposed for tests
|
||||||
|
lastWritePos int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsSameDay(t1, t2 time.Time) bool {
|
||||||
|
return t1.YearDay() == t2.YearDay()
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsSameHour(t1, t2 time.Time) bool {
|
||||||
|
return t1.YearDay() == t2.YearDay() && t1.Hour() == t2.Hour()
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(config *Config) (*File, error) {
|
||||||
|
if nil == config {
|
||||||
|
return nil, fmt.Errorf("must provide config")
|
||||||
|
}
|
||||||
|
if config.PathIfShouldRotate == nil {
|
||||||
|
return nil, fmt.Errorf("must provide config.ShouldRotate")
|
||||||
|
}
|
||||||
|
file := &File{
|
||||||
|
config: *config,
|
||||||
|
}
|
||||||
|
err := file.reopenIfNeeded()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeDailyRotateInDir(dir string, fileNameSuffix string) func(time.Time, time.Time) string {
|
||||||
|
return func(creationTime time.Time, now time.Time) string {
|
||||||
|
if IsSameDay(creationTime, now) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
name := now.Format("20060102")
|
||||||
|
if fileNameSuffix != "" {
|
||||||
|
name = strings.Replace(fileNameSuffix, "$T", name, 1)
|
||||||
|
//name = name + "-" + fileNameSuffix
|
||||||
|
} else {
|
||||||
|
name += ".txt"
|
||||||
|
}
|
||||||
|
return filepath.Join(dir, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeHourlyRotateInDir(dir string, fileNameSuffix string) func(time.Time, time.Time) string {
|
||||||
|
return func(creationTime time.Time, now time.Time) string {
|
||||||
|
if IsSameHour(creationTime, now) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
name := now.Format("20060102_15")
|
||||||
|
if fileNameSuffix != "" {
|
||||||
|
name = strings.Replace(fileNameSuffix, "$T", name, 1)
|
||||||
|
//name = name + "-" + fileNameSuffix
|
||||||
|
} else {
|
||||||
|
name += ".txt"
|
||||||
|
}
|
||||||
|
return filepath.Join(dir, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDaily creates a new file, rotating daily in a given directory
|
||||||
|
func NewDaily(dir string, fileUniqueName string, didClose func(path string, didRotate bool)) (*File, error) {
|
||||||
|
daily := MakeDailyRotateInDir(dir, fileUniqueName)
|
||||||
|
config := Config{
|
||||||
|
DidClose: didClose,
|
||||||
|
PathIfShouldRotate: daily,
|
||||||
|
}
|
||||||
|
return New(&config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHourly creates a new file, rotating hourly in a given directory
|
||||||
|
func NewHourly(dir string, logUniqueName string, didClose func(path string, didRotate bool)) (*File, error) {
|
||||||
|
hourly := MakeHourlyRotateInDir(dir, logUniqueName)
|
||||||
|
config := Config{
|
||||||
|
DidClose: didClose,
|
||||||
|
PathIfShouldRotate: hourly,
|
||||||
|
}
|
||||||
|
return New(&config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) close(didRotate bool) error {
|
||||||
|
if f.file == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := f.file.Close()
|
||||||
|
f.file = nil
|
||||||
|
if err == nil && f.config.DidClose != nil {
|
||||||
|
f.config.DidClose(f.Path, didRotate)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func nowInMaybeLocation(loc *time.Location) time.Time {
|
||||||
|
now := time.Now()
|
||||||
|
if loc != nil {
|
||||||
|
now = now.In(loc)
|
||||||
|
}
|
||||||
|
return now
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (f *File) open(path string) error {
|
||||||
|
f.Path = path
|
||||||
|
f.creationTime = time.Now()
|
||||||
|
// we can't assume that the dir for the file already exists
|
||||||
|
dir := filepath.Dir(f.Path)
|
||||||
|
err := os.MkdirAll(dir, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// would be easier to open with os.O_APPEND but Seek() doesn't work in that case
|
||||||
|
flag := os.O_CREATE | os.O_WRONLY
|
||||||
|
f.file, err = os.OpenFile(f.Path, flag, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = f.file.Seek(0, io.SeekEnd)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) reopenIfNeeded() error {
|
||||||
|
now := time.Now()
|
||||||
|
newPath := f.config.PathIfShouldRotate(f.creationTime, now)
|
||||||
|
if newPath == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := f.close(true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return f.open(newPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) write(d []byte, sync bool) (int64, int, error) {
|
||||||
|
err := f.reopenIfNeeded()
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
f.lastWritePos, err = f.file.Seek(0, io.SeekCurrent)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
n, err := f.file.Write(d)
|
||||||
|
if err != nil {
|
||||||
|
return 0, n, err
|
||||||
|
}
|
||||||
|
if sync {
|
||||||
|
err = f.file.Sync()
|
||||||
|
}
|
||||||
|
return f.lastWritePos, n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes data to a file
|
||||||
|
func (f *File) Write(d []byte) (int, error) {
|
||||||
|
f.Lock()
|
||||||
|
defer f.Unlock()
|
||||||
|
|
||||||
|
_, n, err := f.write(d, false)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write2 writes data to a file, optionally syncs to disk. To enable users to later
|
||||||
|
// seek to where the data was written, it returns offset at which the data was
|
||||||
|
// written, number of bytes and error.
|
||||||
|
// You can get path of the file from f.Path
|
||||||
|
func (f *File) Write2(d []byte, sync bool) (int64, int, error) {
|
||||||
|
f.Lock()
|
||||||
|
defer f.Unlock()
|
||||||
|
|
||||||
|
writtenAtPos, n, err := f.write(d, sync)
|
||||||
|
return writtenAtPos, n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) Close() error {
|
||||||
|
f.Lock()
|
||||||
|
defer f.Unlock()
|
||||||
|
|
||||||
|
return f.close(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync commits the current contents of the file to stable storage.
|
||||||
|
func (f *File) Sync() error {
|
||||||
|
f.Lock()
|
||||||
|
defer f.Unlock()
|
||||||
|
|
||||||
|
return f.file.Sync()
|
||||||
|
}
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"m7s.live/pro/pkg"
|
"m7s.live/pro/pkg"
|
||||||
|
"m7s.live/pro/pkg/filerotate"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@@ -56,7 +57,7 @@ type Transformer struct {
|
|||||||
m7s.DefaultTransformer
|
m7s.DefaultTransformer
|
||||||
TransRule
|
TransRule
|
||||||
logFileName string
|
logFileName string
|
||||||
logFile *os.File
|
logFile *filerotate.File
|
||||||
ffmpeg *exec.Cmd
|
ffmpeg *exec.Cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,11 +144,11 @@ func (t *Transformer) Start() (err error) {
|
|||||||
t.SetDescription("cmd", args)
|
t.SetDescription("cmd", args)
|
||||||
t.SetDescription("config", t.TransRule)
|
t.SetDescription("config", t.TransRule)
|
||||||
//t.BufReader.Dump, err = os.OpenFile("dump.flv", os.O_CREATE|os.O_WRONLY, 0644)
|
//t.BufReader.Dump, err = os.OpenFile("dump.flv", os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
t.logFileName = fmt.Sprintf("logs/transcode_%s_%s.log", strings.ReplaceAll(t.TransformJob.StreamPath, "/", "_"), time.Now().Format("20060102"))
|
t.logFileName = fmt.Sprintf("transcode_%s_$T.log", strings.ReplaceAll(t.TransformJob.StreamPath, "/", "_"))
|
||||||
t.ffmpeg = exec.CommandContext(t, "ffmpeg", args...)
|
t.ffmpeg = exec.CommandContext(t, "ffmpeg", args...)
|
||||||
if t.logFileName != "" {
|
if t.logFileName != "" {
|
||||||
t.SetDescription("log", t.logFileName)
|
t.SetDescription("log", t.logFileName)
|
||||||
t.logFile, err = os.OpenFile(t.logFileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
t.logFile, err = filerotate.NewDaily("logs", t.logFileName, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("Could not create transcode log", "err", err)
|
t.Error("Could not create transcode log", "err", err)
|
||||||
return err
|
return err
|
||||||
|
Reference in New Issue
Block a user