diff --git a/always_new_file_writer.go b/always_new_file_writer.go new file mode 100644 index 0000000..e0f1f6f --- /dev/null +++ b/always_new_file_writer.go @@ -0,0 +1,42 @@ +package log + +import ( + "fmt" + "os" + "path" + "time" +) + +// AlwaysNewFileWriter create new log for every process +type AlwaysNewFileWriter struct { + Name string + file *os.File +} + +func (w *AlwaysNewFileWriter) Write(p []byte) (n int, err error) { + if w.file == nil { + w.openFile() + } + + return w.file.Write(p) +} + +func (w *AlwaysNewFileWriter) openFile() (err error) { + name := fmt.Sprintf("%s.%s", w.Name, time.Now().Format("20060102_150405")) + + // remove symbol link if exist + os.Remove(w.Name) + + // create symbol + err = os.Symlink(path.Base(name), w.Name) + if err != nil { + return err + } + + w.file, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0644) + if err != nil { + return err + } + + return nil +} diff --git a/always_new_file_writer_test.go b/always_new_file_writer_test.go new file mode 100644 index 0000000..b5778e3 --- /dev/null +++ b/always_new_file_writer_test.go @@ -0,0 +1,16 @@ +package log + +import ( + "testing" +) + +func TestAlwaysNewFileWriter(t *testing.T) { + log := New(&AlwaysNewFileWriter{ + Name: "/tmp/test.log", + }) + log.SetTimeLayout("15:04:05.999") + log.SetName("main") + for i := 0; i < 100; i++ { + log.Info("i = %d", i) + } +} diff --git a/composite_writers.go b/composite_writers.go new file mode 100644 index 0000000..c8863b9 --- /dev/null +++ b/composite_writers.go @@ -0,0 +1,14 @@ +package log + +import ( + "io" +) + +type CompositeWriters []io.Writer + +func (ws *CompositeWriters) Write(p []byte) (n int, err error) { + for _, w := range *ws { + n, err = w.Write(p) + } + return +} diff --git a/composite_writers_test.go b/composite_writers_test.go new file mode 100644 index 0000000..bb85679 --- /dev/null +++ b/composite_writers_test.go @@ -0,0 +1,20 @@ +package log + +import ( + "os" + "testing" +) + +func TestCompositeWriters(t *testing.T) { + log := New(&CompositeWriters{ + os.Stdout, + &DailyFileWriter{ + Name: "/tmp/test.log", + }, + }) + log.SetTimeLayout("15:04:05.999") + log.SetName("main") + for i := 0; i < 5; i++ { + log.Info("i = %d", i) + } +} diff --git a/daily_file_writer.go b/daily_file_writer.go new file mode 100644 index 0000000..ab97214 --- /dev/null +++ b/daily_file_writer.go @@ -0,0 +1,51 @@ +package log + +import ( + "fmt" + "os" + "path" + "time" +) + +// DailyFileWriter create new log for every day +type DailyFileWriter struct { + Name string + file *os.File + nextDayTime int64 +} + +func (w *DailyFileWriter) Write(p []byte) (n int, err error) { + now := time.Now() + + if w.file == nil { + w.openFile(&now) + } else if now.Unix() >= w.nextDayTime { + w.file.Close() + w.openFile(&now) + } + + return w.file.Write(p) +} + +func (w *DailyFileWriter) openFile(now *time.Time) (err error) { + name := fmt.Sprintf("%s.%s", w.Name, now.Format("2006-01-02")) + + // remove symbol link if exist + os.Remove(w.Name) + + // create symbol + err = os.Symlink(path.Base(name), w.Name) + if err != nil { + return err + } + + w.file, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) + if err != nil { + return err + } + + year, month, day := now.Date() + w.nextDayTime = time.Date(year, month, day+1, 0, 0, 0, 0, now.Location()).Unix() + + return nil +} diff --git a/daily_file_writer_test.go b/daily_file_writer_test.go new file mode 100644 index 0000000..f14add3 --- /dev/null +++ b/daily_file_writer_test.go @@ -0,0 +1,16 @@ +package log + +import ( + "testing" +) + +func TestDailyFileWriter(t *testing.T) { + log := New(&DailyFileWriter{ + Name: "/tmp/test.log", + }) + log.SetTimeLayout("15:04:05.999") + log.SetName("main") + for i := 0; i < 100; i++ { + log.Info("i = %d", i) + } +} diff --git a/fixed_size_file_writer.go b/fixed_size_file_writer.go new file mode 100644 index 0000000..3d61861 --- /dev/null +++ b/fixed_size_file_writer.go @@ -0,0 +1,106 @@ +package log + +import ( + "fmt" + "math" + "os" + "path" +) + +// FixedSizeFileWriter create new log if log size exceed +type FixedSizeFileWriter struct { + Name string + MaxSize int64 + MaxCount int + + file *os.File + currentSize int64 +} + +func (w *FixedSizeFileWriter) Write(p []byte) (n int, err error) { + if w.file == nil { + w.openCurrentFile() + } else if w.currentSize > w.MaxSize { + w.file.Close() + w.openNextFile() + } + + w.currentSize += int64(len(p)) + + return w.file.Write(p) +} + +func (w *FixedSizeFileWriter) openCurrentFile() error { + name, err := os.Readlink(w.Name) + if err != nil { + name = w.getAvailableFileName() + + // create a symlink + err = os.Symlink(path.Base(name), w.Name) + if err != nil { + return err + } + } else { + // convert to abs path + name = path.Join(path.Dir(w.Name), name) + } + + w.file, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) + if err != nil { + return err + } + + stat, err := w.file.Stat() + if err != nil { + return err + } + w.currentSize = stat.Size() + + return nil +} + +func (w *FixedSizeFileWriter) openNextFile() (err error) { + name := w.getAvailableFileName() + + // remove symbol link + err = os.Remove(w.Name) + if err != nil { + return err + } + + // create symbol + err = os.Symlink(path.Base(name), w.Name) + if err != nil { + return err + } + + w.file, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return err + } + + w.currentSize = 0 + + return nil +} + +// get available file or oldest file +func (w *FixedSizeFileWriter) getAvailableFileName() string { + var oldestTime int64 = math.MaxInt64 + var oldestName string + + for i := 0; i < w.MaxCount; i++ { + name := fmt.Sprintf("%s.%d", w.Name, i) + stat, err := os.Stat(name) + if err != nil { + return name + } + + if fTime := stat.ModTime().Unix(); fTime < oldestTime { + oldestTime = fTime + oldestName = name + } + } + + return oldestName +} diff --git a/fixed_size_file_writer_test.go b/fixed_size_file_writer_test.go new file mode 100644 index 0000000..c7e5aa3 --- /dev/null +++ b/fixed_size_file_writer_test.go @@ -0,0 +1,18 @@ +package log + +import ( + "testing" +) + +func TestFixedSizeFileWriter(t *testing.T) { + log := New(&FixedSizeFileWriter{ + Name: "/tmp/test.log", + MaxSize: 1024 * 3, + MaxCount: 5, + }) + log.SetTimeLayout("15:04:05.999") + log.SetName("main") + for i := 0; i < 100; i++ { + log.Info("i = %d", i) + } +}