package starlog import ( "errors" "os" "path/filepath" "time" "b612.me/starmap" ) var archMap starmap.StarMapKV func init() { archMap = starmap.NewStarMap() } type Archive interface { ShouldArchiveNow(*StarLogger, string, os.FileInfo) bool NextLogFilePath(*StarLogger, string, os.FileInfo) string ArchiveLogFilePath(*StarLogger, string, os.FileInfo) string Interval() int64 HookBeforArchive() func(*StarLogger, string, string, os.FileInfo) error //archivePath;currentPath HookAfterArchive() func(*StarLogger, string, string, os.FileInfo) error //archivePath;currentPath } type logfileinfo struct { fullpath string pointer *os.File } func SetLogFile(path string, logger *StarLogger, appendMode bool) error { var fileMode int if appendMode { fileMode = os.O_APPEND | os.O_CREATE | os.O_WRONLY } else { fileMode = os.O_CREATE | os.O_WRONLY | os.O_TRUNC } fullpath, err := filepath.Abs(path) if err != nil { return err } if !appendMode && Exists(fullpath) { os.Remove(fullpath) } fp, err := os.OpenFile(fullpath, fileMode, 0644) if err != nil { return err } if archMap.MustGet(logger.logcore.id) != nil { logger.SetSwitching(true) err := archMap.MustGet(logger.logcore.id).(logfileinfo).pointer.Close() if err != nil { logger.logcore.output = nil logger.SetSwitching(false) return err } err = archMap.Delete(logger.logcore.id) if err != nil { logger.logcore.output = nil logger.SetSwitching(false) return err } } err = archMap.Store(logger.logcore.id, logfileinfo{ fullpath: fullpath, pointer: fp, }) if err != nil { fp.Close() logger.logcore.output = nil logger.SetSwitching(false) return err } logger.SetSwitching(true) logger.logcore.output = fp logger.SetSwitching(false) return nil } func CloseWithSwitching(logger *StarLogger) error { if archMap.MustGet(logger.logcore.id) != nil { logger.SetSwitching(true) err := archMap.MustGet(logger.logcore.id).(logfileinfo).pointer.Close() if err != nil { logger.logcore.output = nil return err } err = archMap.Delete(logger.logcore.id) if err != nil { return err } } return nil } func Close(logger *StarLogger) error { defer logger.SetSwitching(false) return CloseWithSwitching(logger) } func GetLogFileInfo(logger *StarLogger) (os.FileInfo, error) { if archMap.MustGet(logger.logcore.id) != nil { return archMap.MustGet(logger.logcore.id).(logfileinfo).pointer.Stat() } return nil, errors.New("logger don't have a register logfile") } func StartArchive(logger *StarLogger, arch Archive) error { if archMap.MustGet("arch"+logger.logcore.id) != nil { return errors.New("already running") } stopChan := make(chan int) archMap.Store("arch"+logger.logcore.id, stopChan) go func(stopChan chan int, arch Archive, logger *StarLogger) { for { select { case <-stopChan: return case <-time.After(time.Millisecond * time.Duration(1000*arch.Interval())): } fileinfo, err := GetLogFileInfo(logger) if err != nil { logger.Errorf("cannot get log file info,reason is %v\n", err) continue } if archMap.MustGet(logger.logcore.id) == nil { logger.Errorf("cannot get log core info from the map:no such keys\n") continue } fullpath := archMap.MustGet(logger.logcore.id).(logfileinfo).fullpath if !arch.ShouldArchiveNow(logger, fullpath, fileinfo) { continue } newLogPath := arch.NextLogFilePath(logger, fullpath, fileinfo) archiveLogPath := arch.ArchiveLogFilePath(logger, fullpath, fileinfo) if arch.HookBeforArchive() != nil { if err := arch.HookBeforArchive()(logger, archiveLogPath, fullpath, fileinfo); err != nil { logger.Errorf("error occur while executing hook before archive,detail is %v\n", err) continue } } err = CloseWithSwitching(logger) if err != nil { continue } err = os.Rename(fullpath, archiveLogPath) if err != nil { continue } if err := SetLogFile(newLogPath, logger, false); err != nil { logger.Errorf("error occur while executing coverting new log file,detail is %v\n", err) continue } else { logger.Debugln("Archive Log Success") } fileinfo, err = GetLogFileInfo(logger) if err != nil { logger.Errorf("cannot get new log core info from the map:no such keys\n") continue } if arch.HookAfterArchive() != nil { if err := arch.HookAfterArchive()(logger, archiveLogPath, newLogPath, fileinfo); err != nil { logger.Errorf("error occur while executing hook after archive,detail is %v\n", err) continue } } } }(stopChan, arch, logger) return nil } func IsArchiveRun(logger *StarLogger) bool { if archMap.MustGet("arch"+logger.logcore.id) == nil { return false } return true } func StopArchive(logger *StarLogger) { if archMap.MustGet("arch"+logger.logcore.id) == nil { return } archMap.MustGet("arch" + logger.logcore.id).(chan int) <- 1 } type ArchiveByDate struct { interval int64 checkInterval int64 baseFileStyle string archiveStyle string lastSwitchTime time.Time changeArchiveName bool hookBefore func(*StarLogger, string, string, os.FileInfo) error hookAfter func(*StarLogger, string, string, os.FileInfo) error } func (abd *ArchiveByDate) ShouldArchiveNow(l *StarLogger, fullpath string, info os.FileInfo) bool { if abd.lastSwitchTime.IsZero() { abd.lastSwitchTime = GetFileCreationTime(info) } sub := time.Now().Unix() - abd.lastSwitchTime.Unix() if sub >= abd.interval || abd.interval-sub <= abd.checkInterval/2 { return true } return false } func (abd *ArchiveByDate) NextLogFilePath(l *StarLogger, oldpath string, info os.FileInfo) string { var newName string dir := filepath.Dir(oldpath) if !abd.changeArchiveName { newName = abd.baseFileStyle + time.Now().Format(abd.archiveStyle) } else { newName = abd.baseFileStyle } return filepath.Join(dir, newName) } func (abd *ArchiveByDate) ArchiveLogFilePath(l *StarLogger, oldpath string, info os.FileInfo) string { var newName string dir := filepath.Dir(oldpath) if abd.changeArchiveName { newName = filepath.Base(abd.baseFileStyle) + time.Now().Format(abd.archiveStyle) } else { newName = abd.baseFileStyle } return filepath.Join(dir, newName) } func (abd *ArchiveByDate) Interval() int64 { return abd.checkInterval } func (abd *ArchiveByDate) HookBeforArchive() func(*StarLogger, string, string, os.FileInfo) error { return abd.hookBefore } func (abd *ArchiveByDate) HookAfterArchive() func(*StarLogger, string, string, os.FileInfo) error { return func(logger *StarLogger, s string, s2 string, info os.FileInfo) error { abd.lastSwitchTime = time.Now() if abd.hookAfter != nil { return abd.hookAfter(logger, s, s2, info) } return nil } } func (abd *ArchiveByDate) SetHookBeforArchive(f func(*StarLogger, string, string, os.FileInfo) error) { abd.hookBefore = f } func (abd *ArchiveByDate) SetHookAfterArchive(f func(*StarLogger, string, string, os.FileInfo) error) { abd.hookAfter = f } func NewArchiveByDate(archInterval int64, checkInterval int64, baseFileName string, archiveFileName string, changeArchiveName bool, hookbefore func(*StarLogger, string, string, os.FileInfo) error, hookafter func(*StarLogger, string, string, os.FileInfo) error) *ArchiveByDate { return &ArchiveByDate{ interval: archInterval, checkInterval: checkInterval, changeArchiveName: changeArchiveName, baseFileStyle: baseFileName, archiveStyle: archiveFileName, hookBefore: hookbefore, hookAfter: hookafter, } } type ArchiveBySize struct { size int64 checkInterval int64 changeArchiveName bool baseFileStyle string archiveStyle string hookBefore func(*StarLogger, string, string, os.FileInfo) error hookAfter func(*StarLogger, string, string, os.FileInfo) error } func (abd *ArchiveBySize) ShouldArchiveNow(l *StarLogger, fullpath string, info os.FileInfo) bool { if info.Size() > abd.size { return true } return false } func (abd *ArchiveBySize) NextLogFilePath(l *StarLogger, oldpath string, info os.FileInfo) string { var newName string dir := filepath.Dir(oldpath) if !abd.changeArchiveName { newName = abd.baseFileStyle + time.Now().Format(abd.archiveStyle) } else { newName = abd.baseFileStyle } return filepath.Join(dir, newName) } func (abd *ArchiveBySize) ArchiveLogFilePath(l *StarLogger, oldpath string, info os.FileInfo) string { var newName string dir := filepath.Dir(oldpath) if abd.changeArchiveName { newName = filepath.Base(abd.baseFileStyle) + time.Now().Format(abd.archiveStyle) } else { newName = abd.baseFileStyle } return filepath.Join(dir, newName) } func (abd *ArchiveBySize) Interval() int64 { return abd.checkInterval } func (abd *ArchiveBySize) HookBeforArchive() func(*StarLogger, string, string, os.FileInfo) error { return abd.hookBefore } func (abd *ArchiveBySize) HookAfterArchive() func(*StarLogger, string, string, os.FileInfo) error { return abd.hookAfter } func (abd *ArchiveBySize) SetHookBeforArchive(f func(*StarLogger, string, string, os.FileInfo) error) { abd.hookBefore = f } func (abd *ArchiveBySize) SetHookAfterArchive(f func(*StarLogger, string, string, os.FileInfo) error) { abd.hookAfter = f } func NewArchiveBySize(size int64, checkInterval int64, baseFileStyle, archiveFileStyle string, changeArchiveFileName bool, hookbefore func(*StarLogger, string, string, os.FileInfo) error, hookafter func(*StarLogger, string, string, os.FileInfo) error) *ArchiveBySize { return &ArchiveBySize{ size: size, checkInterval: checkInterval, baseFileStyle: baseFileStyle, archiveStyle: archiveFileStyle, hookBefore: hookbefore, hookAfter: hookafter, changeArchiveName: changeArchiveFileName, } } type ArchiveByDateSize struct { interval int64 size int64 checkInterval int64 changeArchiveName bool lastSwitchTime time.Time baseFileStyle string archiveStyle string hookBefore func(*StarLogger, string, string, os.FileInfo) error hookAfter func(*StarLogger, string, string, os.FileInfo) error } func (abd *ArchiveByDateSize) ShouldArchiveNow(l *StarLogger, fullpath string, info os.FileInfo) bool { if abd.lastSwitchTime.IsZero() { abd.lastSwitchTime = GetFileCreationTime(info) } if info.Size() > abd.size { return true } sub := time.Now().Unix() - abd.lastSwitchTime.Unix() if sub >= abd.interval || abd.interval-sub <= abd.checkInterval/2 { return true } return false } func (abd *ArchiveByDateSize) NextLogFilePath(l *StarLogger, oldpath string, info os.FileInfo) string { var newName string dir := filepath.Dir(oldpath) if !abd.changeArchiveName { newName = abd.baseFileStyle + time.Now().Format(abd.archiveStyle) } else { newName = abd.baseFileStyle } return filepath.Join(dir, newName) } func (abd *ArchiveByDateSize) ArchiveLogFilePath(l *StarLogger, oldpath string, info os.FileInfo) string { var newName string dir := filepath.Dir(oldpath) if abd.changeArchiveName { newName = filepath.Base(abd.baseFileStyle) + time.Now().Format(abd.archiveStyle) } else { newName = abd.baseFileStyle } return filepath.Join(dir, newName) } func (abd *ArchiveByDateSize) Interval() int64 { return abd.checkInterval } func (abd *ArchiveByDateSize) HookBeforArchive() func(*StarLogger, string, string, os.FileInfo) error { return abd.hookBefore } func (abd *ArchiveByDateSize) HookAfterArchive() func(*StarLogger, string, string, os.FileInfo) error { return func(logger *StarLogger, s string, s2 string, info os.FileInfo) error { abd.lastSwitchTime = time.Now() if abd.hookAfter != nil { return abd.hookAfter(logger, s, s2, info) } return nil } } func (abd *ArchiveByDateSize) SetHookBeforArchive(f func(*StarLogger, string, string, os.FileInfo) error) { abd.hookBefore = f } func (abd *ArchiveByDateSize) SetHookAfterArchive(f func(*StarLogger, string, string, os.FileInfo) error) { abd.hookAfter = f } func NewArchiveByDateSize(size int64, interval int64, checkInterval int64, baseFileStyle, archiveFileStyle string, changeArchiveFileName bool, hookbefore func(*StarLogger, string, string, os.FileInfo) error, hookafter func(*StarLogger, string, string, os.FileInfo) error) *ArchiveByDateSize { return &ArchiveByDateSize{ size: size, interval: interval, checkInterval: checkInterval, baseFileStyle: baseFileStyle, archiveStyle: archiveFileStyle, hookBefore: hookbefore, hookAfter: hookafter, changeArchiveName: changeArchiveFileName, } }