From fc7d8d22b460543255f586cb366d2ff346fbbeab Mon Sep 17 00:00:00 2001 From: evilsocket Date: Sun, 28 Oct 2018 12:43:38 +0100 Subject: [PATCH] new: added log rotation (closes #374) --- Gopkg.lock | 6 +- Gopkg.toml | 2 +- modules/events_stream.go | 54 +++++++++++++++++- modules/events_view.go | 51 +++++++++++++++++ .../github.com/evilsocket/islazy/zip/files.go | 55 +++++++++++++++++++ 5 files changed, 161 insertions(+), 7 deletions(-) create mode 100644 vendor/github.com/evilsocket/islazy/zip/files.go diff --git a/Gopkg.lock b/Gopkg.lock index 3531ab81..d2014a90 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -59,7 +59,7 @@ revision = "f58a169a71a51037728990b2d3597a14f56b525b" [[projects]] - digest = "1:bfbb93ae71a839764d94c1ba50f3868ecc576871274cf29db1903bf32efce620" + digest = "1:b44604e0c8da17e780203bfd9388d65db31d4ee039347ea92788412e7737a08c" name = "github.com/evilsocket/islazy" packages = [ "data", @@ -71,8 +71,8 @@ "zip", ] pruneopts = "UT" - revision = "e86063a516840e32de87d39c214a6937b4d22e04" - version = "v1.8.0" + revision = "be7e564817af1379a350998577006745039f599f" + version = "v1.9.0" [[projects]] branch = "master" diff --git a/Gopkg.toml b/Gopkg.toml index be2ca61c..cab5afa3 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -25,7 +25,7 @@ # unused-packages = true [[constraint]] name = "github.com/evilsocket/islazy" - version = "1.8.0" + version = "1.9.0" [[constraint]] branch = "master" diff --git a/modules/events_stream.go b/modules/events_stream.go index 1e7a682e..18dbc5c9 100644 --- a/modules/events_stream.go +++ b/modules/events_stream.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "strconv" + "sync" "time" "github.com/bettercap/bettercap/log" @@ -14,9 +15,20 @@ import ( "github.com/evilsocket/islazy/tui" ) +type rotation struct { + sync.Mutex + Enabled bool + Compress bool + Format string + How string + Period int +} + type EventsStream struct { session.SessionModule + outputName string output *os.File + rotation rotation ignoreList *IgnoreList waitFor string waitChan chan *session.Event @@ -117,6 +129,28 @@ func NewEventsStream(s *session.Session) *EventsStream { "", "If not empty, events will be written to this file instead of the standard output.")) + stream.AddParam(session.NewBoolParameter("events.stream.output.rotate", + "true", + "If true will enable log rotation.")) + + stream.AddParam(session.NewBoolParameter("events.stream.output.rotate.compress", + "true", + "If true will enable log rotation compression.")) + + stream.AddParam(session.NewStringParameter("events.stream.output.rotate.how", + "size", + "(size|time)", + "Rotate by 'size' or 'time'.")) + + stream.AddParam(session.NewStringParameter("events.stream.output.rotate.format", + "2006-01-02 15:04:05", + "", + "Datetime format to use for log rotation file names.")) + + stream.AddParam(session.NewIntParameter("events.stream.output.rotate.when", + "10485760", + "File size or time duration in seconds for log rotation.")) + stream.AddParam(session.NewBoolParameter("events.stream.http.request.dump", "false", "If true all HTTP requests will be dumped.")) @@ -146,10 +180,24 @@ func (s *EventsStream) Configure() (err error) { if err, output = s.StringParam("events.stream.output"); err == nil { if output == "" { s.output = os.Stdout - } else if output, err = fs.Expand(output); err == nil { - s.output, err = os.OpenFile(output, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + } else if s.outputName, err = fs.Expand(output); err == nil { + s.output, err = os.OpenFile(s.outputName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) } - } else if err, s.dumpHttpReqs = s.BoolParam("events.stream.http.request.dump"); err != nil { + } + + if err, s.rotation.Enabled = s.BoolParam("events.stream.output.rotate"); err != nil { + return err + } else if err, s.rotation.Compress = s.BoolParam("events.stream.output.rotate.compress"); err != nil { + return err + } else if err, s.rotation.Format = s.StringParam("events.stream.output.rotate.format"); err != nil { + return err + } else if err, s.rotation.How = s.StringParam("events.stream.output.rotate.how"); err != nil { + return err + } else if err, s.rotation.Period = s.IntParam("events.stream.output.rotate.when"); err != nil { + return err + } + + if err, s.dumpHttpReqs = s.BoolParam("events.stream.http.request.dump"); err != nil { return err } else if err, s.dumpHttpResp = s.BoolParam("events.stream.http.response.dump"); err != nil { return err diff --git a/modules/events_view.go b/modules/events_view.go index 71a22784..b2837c57 100644 --- a/modules/events_view.go +++ b/modules/events_view.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "strings" + "time" "github.com/bettercap/bettercap/network" "github.com/bettercap/bettercap/session" @@ -11,6 +12,7 @@ import ( "github.com/google/go-github/github" "github.com/evilsocket/islazy/tui" + "github.com/evilsocket/islazy/zip" ) const eventTimeFormat = "15:04:05" @@ -152,6 +154,53 @@ func (s *EventsStream) viewUpdateEvent(e session.Event) { *update.HTMLURL) } +func (s *EventsStream) doRotation() { + if s.output == os.Stdout { + return + } else if !s.rotation.Enabled { + return + } + + s.rotation.Lock() + defer s.rotation.Unlock() + + doRotate := false + if info, err := s.output.Stat(); err == nil { + if s.rotation.How == "size" { + doRotate = info.Size() >= int64(s.rotation.Period) + } else if s.rotation.How == "time" { + doRotate = info.ModTime().Unix()%int64(s.rotation.Period) == 0 + } + } + + if doRotate { + var err error + + name := fmt.Sprintf("%s-%s", s.outputName, time.Now().Format(s.rotation.Format)) + + if err := s.output.Close(); err != nil { + fmt.Printf("could not close log for rotation: %s\n", err) + return + } + + if err := os.Rename(s.outputName, name); err != nil { + fmt.Printf("could not rename %s to %s: %s\n", s.outputName, name, err) + } else if s.rotation.Compress { + zipName := fmt.Sprintf("%s.zip", name) + if err = zip.Files(zipName, []string{name}); err != nil { + fmt.Printf("error creating %s: %s", zipName, err) + } else if err = os.Remove(name); err != nil { + fmt.Printf("error deleting %s: %s", name, err) + } + } + + s.output, err = os.OpenFile(s.outputName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + fmt.Printf("could not open %s: %s", s.outputName, err) + } + } +} + func (s *EventsStream) View(e session.Event, refresh bool) { if e.Tag == "sys.log" { s.viewLogEvent(e) @@ -176,4 +225,6 @@ func (s *EventsStream) View(e session.Event, refresh bool) { if refresh && s.output == os.Stdout { s.Session.Refresh() } + + s.doRotation() } diff --git a/vendor/github.com/evilsocket/islazy/zip/files.go b/vendor/github.com/evilsocket/islazy/zip/files.go new file mode 100644 index 00000000..2ad2f4f9 --- /dev/null +++ b/vendor/github.com/evilsocket/islazy/zip/files.go @@ -0,0 +1,55 @@ +package zip + +import ( + "archive/zip" + "io" + "os" +) + +// Files compresses one or many files into a single zip archive file. +// Credits: https://golangcode.com/create-zip-files-in-go/ +func Files(filename string, files []string) error { + arc, err := os.Create(filename) + if err != nil { + return err + } + defer arc.Close() + + writer := zip.NewWriter(arc) + defer writer.Close() + + for _, file := range files { + in, err := os.Open(file) + if err != nil { + return err + } + defer in.Close() + + info, err := in.Stat() + if err != nil { + return err + } + + header, err := zip.FileInfoHeader(info) + if err != nil { + return err + } + + // Using FileInfoHeader() above only uses the basename of the file. If we want + // to preserve the folder structure we can overwrite this with the full path. + header.Name = file + // Change to deflate to gain better compression + // see http://golang.org/pkg/archive/zip/#pkg-constants + header.Method = zip.Deflate + + w, err := writer.CreateHeader(header) + if err != nil { + return err + } + if _, err = io.Copy(w, in); err != nil { + return err + } + } + + return nil +}