From 73e9681f2fa7adde34325d181ad2af441ecd2d28 Mon Sep 17 00:00:00 2001 From: evilolipop Date: Tue, 5 Mar 2024 14:59:45 +0800 Subject: [PATCH 1/6] feat: rotate file --- go.mod | 11 +++++ go.sum | 10 +++++ rotate.go | 12 ++++++ rotate_file.go | 102 ++++++++++++++++++++++++++++++++++++++++++++ rotate_file_test.go | 25 +++++++++++ rotate_test.go | 7 +++ 6 files changed, 167 insertions(+) create mode 100644 go.mod create mode 100644 go.sum create mode 100644 rotate.go create mode 100644 rotate_file.go create mode 100644 rotate_file_test.go create mode 100644 rotate_test.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4bf26f6 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module github.com/qazwsxedckll/logh + +go 1.22.0 + +require github.com/stretchr/testify v1.9.0 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..60ce688 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/rotate.go b/rotate.go new file mode 100644 index 0000000..ddea727 --- /dev/null +++ b/rotate.go @@ -0,0 +1,12 @@ +package logh + +import ( + "io" + "log/slog" +) + +func NewRotateJSONHandler(rollSize int, flushInterval int, opts *slog.HandlerOptions) (slog.Handler, error) { + var w io.Writer + + return slog.NewJSONHandler(w, opts), nil +} diff --git a/rotate_file.go b/rotate_file.go new file mode 100644 index 0000000..084395a --- /dev/null +++ b/rotate_file.go @@ -0,0 +1,102 @@ +package logh + +import ( + "fmt" + "os" + "time" +) + +type RotateFile struct { + filepath string + file *os.File + + rotateSize int + rotateInterval time.Duration + checkEveryN int + + written int + lastRotate time.Time + count int +} + +func NewRotateFile(directory string, basename string, rotateSize int, opts ...Option) (*RotateFile, error) { + err := os.MkdirAll(directory, 0o755) + if err != nil { + return nil, err + } + + rf := &RotateFile{ + filepath: directory + "/" + basename, + rotateSize: rotateSize, + rotateInterval: time.Hour * 24, + checkEveryN: 1024, + } + + for _, opt := range opts { + opt(rf) + } + + rf.Rotate() + + return rf, nil +} + +func (r *RotateFile) Write(p []byte) (int, error) { + n, err := r.file.Write(p) + r.written += n + + if r.written > r.rotateSize { + r.Rotate() + } else { + r.count++ + if r.count >= r.checkEveryN { + r.count = 0 + if time.Now().After(r.lastRotate.Add(r.rotateInterval)) { + r.Rotate() + } + } + } + + return n, err +} + +func (r *RotateFile) logFileName() (string, time.Time) { + hostname, err := os.Hostname() + if err != nil { + hostname = "unknownhost" + } + now := time.Now() + return r.filepath + "." + now.Format("20060102-150405") + "." + hostname + "." + fmt.Sprint(os.Getpid()) + ".log", now +} + +func (r *RotateFile) Rotate() { + filename, now := r.logFileName() + + if now.After(r.lastRotate) { + file, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644) + if err != nil { + panic(fmt.Sprintf("Failed to open log file %s: %s", filename, err)) + } + + if r.file != nil { + // TODO: error handling? + r.file.Close() + } + r.file = file + r.lastRotate = now + } +} + +type Option func(*RotateFile) + +func WithCheckEveryN(n int) Option { + return func(r *RotateFile) { + r.checkEveryN = n + } +} + +func WithRotateInterval(d time.Duration) Option { + return func(r *RotateFile) { + r.rotateInterval = d + } +} diff --git a/rotate_file_test.go b/rotate_file_test.go new file mode 100644 index 0000000..d64f838 --- /dev/null +++ b/rotate_file_test.go @@ -0,0 +1,25 @@ +package logh + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewRotateFile(t *testing.T) { + path, err := os.MkdirTemp("", "loghtest") + require.NoError(t, err) + + path += "/TestNewRotateFile" + file, err := NewRotateFile(path, "test", 10) + require.NoError(t, err) + require.NotNil(t, file) + + dir, err := os.ReadDir(path) + require.NoError(t, err) + require.Len(t, dir, 1) + + err = os.RemoveAll(path) + require.NoError(t, err) +} diff --git a/rotate_test.go b/rotate_test.go new file mode 100644 index 0000000..a918bdd --- /dev/null +++ b/rotate_test.go @@ -0,0 +1,7 @@ +package logh + +// func TestRotateJSONHandler(t *testing.T) { +// handler, err := NewRotateJSONHandler(0, 0, nil) +// require.NoError(t, err) + +// } From c51d124ecd098e3b7a2cb4ba269ed1a9b6717ce0 Mon Sep 17 00:00:00 2001 From: evilolipop Date: Tue, 5 Mar 2024 17:44:45 +0800 Subject: [PATCH 2/6] test: test rotate by size --- rotate_file.go | 2 +- rotate_file_test.go | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/rotate_file.go b/rotate_file.go index 084395a..69e6df5 100644 --- a/rotate_file.go +++ b/rotate_file.go @@ -66,7 +66,7 @@ func (r *RotateFile) logFileName() (string, time.Time) { hostname = "unknownhost" } now := time.Now() - return r.filepath + "." + now.Format("20060102-150405") + "." + hostname + "." + fmt.Sprint(os.Getpid()) + ".log", now + return r.filepath + "." + now.Format("20060102-150405.000000000") + "." + hostname + "." + fmt.Sprint(os.Getpid()) + ".log", now } func (r *RotateFile) Rotate() { diff --git a/rotate_file_test.go b/rotate_file_test.go index d64f838..b0f6e3d 100644 --- a/rotate_file_test.go +++ b/rotate_file_test.go @@ -10,6 +10,7 @@ import ( func TestNewRotateFile(t *testing.T) { path, err := os.MkdirTemp("", "loghtest") require.NoError(t, err) + defer os.RemoveAll(path) path += "/TestNewRotateFile" file, err := NewRotateFile(path, "test", 10) @@ -19,7 +20,22 @@ func TestNewRotateFile(t *testing.T) { dir, err := os.ReadDir(path) require.NoError(t, err) require.Len(t, dir, 1) +} + +func TestRotateSize(t *testing.T) { + path, err := os.MkdirTemp("", "loghtest-TestWrite") + require.NoError(t, err) + defer os.RemoveAll(path) + + file, err := NewRotateFile(path, "test", 10) + require.NoError(t, err) - err = os.RemoveAll(path) + _, err = file.Write([]byte("12345678901234567890")) + require.NoError(t, err) + _, err = file.Write([]byte("12345678901234567890")) + require.NoError(t, err) + + dir, err := os.ReadDir(path) require.NoError(t, err) + require.Len(t, dir, 3) } From ce197680b509f01f0e6a4ca1808f5fda0233dad3 Mon Sep 17 00:00:00 2001 From: evilolipop Date: Tue, 5 Mar 2024 17:45:27 +0800 Subject: [PATCH 3/6] refactor: make rotate private --- rotate_file.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rotate_file.go b/rotate_file.go index 69e6df5..80436d7 100644 --- a/rotate_file.go +++ b/rotate_file.go @@ -36,7 +36,7 @@ func NewRotateFile(directory string, basename string, rotateSize int, opts ...Op opt(rf) } - rf.Rotate() + rf.rotate() return rf, nil } @@ -46,13 +46,13 @@ func (r *RotateFile) Write(p []byte) (int, error) { r.written += n if r.written > r.rotateSize { - r.Rotate() + r.rotate() } else { r.count++ if r.count >= r.checkEveryN { r.count = 0 if time.Now().After(r.lastRotate.Add(r.rotateInterval)) { - r.Rotate() + r.rotate() } } } @@ -69,7 +69,7 @@ func (r *RotateFile) logFileName() (string, time.Time) { return r.filepath + "." + now.Format("20060102-150405.000000000") + "." + hostname + "." + fmt.Sprint(os.Getpid()) + ".log", now } -func (r *RotateFile) Rotate() { +func (r *RotateFile) rotate() { filename, now := r.logFileName() if now.After(r.lastRotate) { From 731b5d2af590d71ac3521c33485390bf238bd0c1 Mon Sep 17 00:00:00 2001 From: evilolipop Date: Wed, 6 Mar 2024 11:09:51 +0800 Subject: [PATCH 4/6] test: add test rotate interval --- rotate_file_test.go | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/rotate_file_test.go b/rotate_file_test.go index b0f6e3d..e29b5ce 100644 --- a/rotate_file_test.go +++ b/rotate_file_test.go @@ -3,6 +3,7 @@ package logh import ( "os" "testing" + "time" "github.com/stretchr/testify/require" ) @@ -23,7 +24,7 @@ func TestNewRotateFile(t *testing.T) { } func TestRotateSize(t *testing.T) { - path, err := os.MkdirTemp("", "loghtest-TestWrite") + path, err := os.MkdirTemp("", "loghtest-TestRotateSize") require.NoError(t, err) defer os.RemoveAll(path) @@ -39,3 +40,31 @@ func TestRotateSize(t *testing.T) { require.NoError(t, err) require.Len(t, dir, 3) } + +func TestRotateInterval(t *testing.T) { + path, err := os.MkdirTemp("", "loghtest-TestRotateInterval") + require.NoError(t, err) + defer os.RemoveAll(path) + + file, err := NewRotateFile(path, "test", 1024, WithCheckEveryN(2), WithRotateInterval(1*time.Second)) + require.NoError(t, err) + + _, err = file.Write([]byte("1")) + require.NoError(t, err) + _, err = file.Write([]byte("2")) + require.NoError(t, err) + + dir, err := os.ReadDir(path) + require.NoError(t, err) + require.Len(t, dir, 1) + + time.Sleep(1 * time.Second) + _, err = file.Write([]byte("1")) + require.NoError(t, err) + _, err = file.Write([]byte("2")) + require.NoError(t, err) + + dir, err = os.ReadDir(path) + require.NoError(t, err) + require.Len(t, dir, 2) +} From f88ece46e3db6066a4239391a907d02bcce51d52 Mon Sep 17 00:00:00 2001 From: evilolipop Date: Wed, 6 Mar 2024 14:15:12 +0800 Subject: [PATCH 5/6] fix: fix nil option --- rotate.go | 10 ++++++---- rotate_file.go | 4 +++- rotate_file_test.go | 2 +- rotate_test.go | 17 +++++++++++++---- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/rotate.go b/rotate.go index ddea727..c650d2d 100644 --- a/rotate.go +++ b/rotate.go @@ -1,12 +1,14 @@ package logh import ( - "io" "log/slog" ) -func NewRotateJSONHandler(rollSize int, flushInterval int, opts *slog.HandlerOptions) (slog.Handler, error) { - var w io.Writer +func NewRotateJSONHandler(directory string, basename string, rollSize int, opts *slog.HandlerOptions, options ...Option) (slog.Handler, error) { + file, err := NewRotateFile(directory, basename, rollSize, options...) + if err != nil { + return nil, err + } - return slog.NewJSONHandler(w, opts), nil + return slog.NewJSONHandler(file, opts), nil } diff --git a/rotate_file.go b/rotate_file.go index 80436d7..efe474d 100644 --- a/rotate_file.go +++ b/rotate_file.go @@ -33,7 +33,9 @@ func NewRotateFile(directory string, basename string, rotateSize int, opts ...Op } for _, opt := range opts { - opt(rf) + if opt != nil { + opt(rf) + } } rf.rotate() diff --git a/rotate_file_test.go b/rotate_file_test.go index e29b5ce..2df7d25 100644 --- a/rotate_file_test.go +++ b/rotate_file_test.go @@ -14,7 +14,7 @@ func TestNewRotateFile(t *testing.T) { defer os.RemoveAll(path) path += "/TestNewRotateFile" - file, err := NewRotateFile(path, "test", 10) + file, err := NewRotateFile(path, "test", 10, nil) require.NoError(t, err) require.NotNil(t, file) diff --git a/rotate_test.go b/rotate_test.go index a918bdd..4aafbfc 100644 --- a/rotate_test.go +++ b/rotate_test.go @@ -1,7 +1,16 @@ package logh -// func TestRotateJSONHandler(t *testing.T) { -// handler, err := NewRotateJSONHandler(0, 0, nil) -// require.NoError(t, err) +import ( + "log/slog" + "testing" -// } + "github.com/stretchr/testify/require" +) + +func TestRotateJSONHandler(t *testing.T) { + handler, err := NewRotateJSONHandler("test", "test", 10, nil, nil) + require.NoError(t, err) + + logger := slog.New(handler) + logger.Info("test") +} From c3519e150d59d01ad2a2b482f9d3c9d289fa3f42 Mon Sep 17 00:00:00 2001 From: evilolipop Date: Wed, 6 Mar 2024 14:16:17 +0800 Subject: [PATCH 6/6] test: remove test --- rotate_test.go | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 rotate_test.go diff --git a/rotate_test.go b/rotate_test.go deleted file mode 100644 index 4aafbfc..0000000 --- a/rotate_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package logh - -import ( - "log/slog" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestRotateJSONHandler(t *testing.T) { - handler, err := NewRotateJSONHandler("test", "test", 10, nil, nil) - require.NoError(t, err) - - logger := slog.New(handler) - logger.Info("test") -}