diff --git a/README.md b/README.md index 65f56df..c92056f 100644 --- a/README.md +++ b/README.md @@ -102,9 +102,9 @@ go-watch-logs --file-path=my.log --match='HTTP/1.1" 50' --every=60 ```sh $ go test -bench=. ./... -benchmem -BenchmarkReadFileAndMatchErrors-10 969 1173870 ns/op 12920 B/op 146 allocs/op -BenchmarkLoadAndSaveState-10 5296 230536 ns/op 9179 B/op 180 allocs/op -BenchmarkLogRotation-10 1036 1175464 ns/op 12930 B/op 146 allocs/op +BenchmarkReadFileAndMatchErrors-10 13588 91900 ns/op 8243 B/op 43 allocs/op +BenchmarkLoadAndSaveState-10 3135621 375.3 ns/op 352 B/op 8 allocs/op +BenchmarkLogRotation-10 13807 101088 ns/op 8243 B/op 43 allocs/op ``` ## Development Notes diff --git a/go.mod b/go.mod index b67eb15..30d1e51 100644 --- a/go.mod +++ b/go.mod @@ -4,32 +4,26 @@ go 1.22.3 require ( github.com/MatusOllah/slogcolor v1.4.0 - github.com/glebarez/go-sqlite v1.22.0 github.com/gravwell/gravwell/v3 v3.8.34 github.com/jasonlvhit/gocron v0.0.1 + github.com/k0kubun/pp v3.0.1+incompatible github.com/kevincobain2000/go-msteams v1.1.1 github.com/mattn/go-isatty v0.0.20 github.com/natefinch/lumberjack v2.0.0+incompatible + github.com/patrickmn/go-cache v2.1.0+incompatible github.com/stretchr/testify v1.10.0 ) require ( github.com/BurntSushi/toml v1.4.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/color v1.16.0 // indirect - github.com/google/uuid v1.6.0 // indirect + github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/ncruces/go-strftime v0.1.9 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect golang.org/x/sys v0.23.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - modernc.org/libc v1.57.0 // indirect - modernc.org/mathutil v1.6.0 // indirect - modernc.org/memory v1.8.0 // indirect - modernc.org/sqlite v1.32.0 // indirect ) diff --git a/go.sum b/go.sum index 7ea5636..3db2257 100644 --- a/go.sum +++ b/go.sum @@ -6,24 +6,20 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= -github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc= github.com/go-redis/redis v6.15.5+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= -github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gravwell/gravwell/v3 v3.8.34 h1:3Cctgw3RAjZBxvm1rUlZybzKPxrVdmC6nt6vlozOMbI= github.com/gravwell/gravwell/v3 v3.8.34/go.mod h1:FsIn6mNCcY7wEswbhxRpLchB9cF5jjaQIb/V3jh1YOg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jasonlvhit/gocron v0.0.1 h1:qTt5qF3b3srDjeOIR4Le1LfeyvoYzJlYpqvG7tJX5YU= github.com/jasonlvhit/gocron v0.0.1/go.mod h1:k9a3TV8VcU73XZxfVHCHWMWF9SOqgoku0/QlY2yvlA4= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40= +github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= github.com/kevincobain2000/go-msteams v1.1.1 h1:vZ8AYvVmiCdC+VZwsw7RFhb89RG/GasX9kvbdKheFN4= github.com/kevincobain2000/go-msteams v1.1.1/go.mod h1:+HowoQQHg9HLfx3CYQGImGGYw20+kN9rFmUXgxrqBzo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -40,16 +36,14 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= -github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= -github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 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/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -57,14 +51,10 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= -golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -73,8 +63,6 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= -golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -87,27 +75,3 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= -modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= -modernc.org/ccgo/v4 v4.20.7 h1:skrinQsjxWfvj6nbC3ztZPJy+NuwmB3hV9zX/pthNYQ= -modernc.org/ccgo/v4 v4.20.7/go.mod h1:UOkI3JSG2zT4E2ioHlncSOZsXbuDCZLvPi3uMlZT5GY= -modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= -modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= -modernc.org/gc/v2 v2.5.0 h1:bJ9ChznK1L1mUtAQtxi0wi5AtAs5jQuw4PrPHO5pb6M= -modernc.org/gc/v2 v2.5.0/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= -modernc.org/libc v1.57.0 h1:sHiZeKNUCl2kKPcx91eECBLhDj+OB8V3fF0CkVbEmaQ= -modernc.org/libc v1.57.0/go.mod h1:EY/egGEU7Ju66eU6SBqCNYaFUDuc4npICkMWnU5EE3A= -modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= -modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= -modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= -modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= -modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= -modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= -modernc.org/sqlite v1.32.0 h1:6BM4uGza7bWypsw4fdLRsLxut6bHe4c58VeqjRgST8s= -modernc.org/sqlite v1.32.0/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA= -modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= -modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= -modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= -modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/main.go b/main.go index c726f6e..cec0a7f 100644 --- a/main.go +++ b/main.go @@ -1,14 +1,13 @@ package main import ( - "flag" "fmt" "log/slog" "os" "sync" "github.com/jasonlvhit/gocron" - gmt "github.com/kevincobain2000/go-msteams/src" + "github.com/patrickmn/go-cache" "github.com/rakutentech/go-watch-logs/pkg" ) @@ -20,12 +19,12 @@ var version = "dev" var filePaths []string var filePathsMutex sync.Mutex +var cacheMutex sync.Mutex +var caches = make(map[string]*cache.Cache) + func main() { pkg.Parseflags(&f) pkg.SetupLoggingStdout(f) // nolint: errcheck - flag.VisitAll(func(f *flag.Flag) { - slog.Info(f.Name, slog.String("value", f.Value.String())) - }) parseProxy() wantsVersion() validate() @@ -35,34 +34,7 @@ func main() { return } - var err error - newFilePaths, err := pkg.FilesByPattern(f.FilePath, f.NotifyOnlyRecent) - if err != nil { - slog.Error("Error finding files", "error", err.Error()) - return - } - if len(newFilePaths) == 0 { - slog.Warn("No files found", "filePath", f.FilePath) - slog.Warn("Keep watching for new files") - } - if len(newFilePaths) > f.FilePathsCap { - slog.Warn("Too many files found", "count", len(newFilePaths), "cap", f.FilePathsCap) - slog.Info("Capping to", "count", f.FilePathsCap) - } - - filePaths = pkg.Capped(f.FilePathsCap, newFilePaths) - - for _, filePath := range filePaths { - isText, err := pkg.IsTextFile(filePath) - if err != nil { - slog.Error("Error checking if file is text", "error", err.Error(), "filePath", filePath) - return - } - if !isText { - slog.Error("File is not a text file", "filePath", filePath) - return - } - } + syncFilePaths() for _, filePath := range filePaths { watch(filePath) @@ -72,84 +44,91 @@ func main() { } } -func startCron() { - if err := gocron.Every(1).Second().Do(pkg.PrintMemUsage, &f); err != nil { - slog.Error("Error scheduling memory usage", "error", err.Error()) - return +func syncCaches() { + cacheMutex.Lock() + defer cacheMutex.Unlock() + for filePath := range caches { + found := false + for _, f := range filePaths { + if f == filePath { + found = true + break + } + } + if !found { + slog.Info("Deleting cache obj", "filePath", filePath) + delete(caches, filePath) + } } - if err := gocron.Every(f.Every).Second().Do(syncFilePaths); err != nil { - slog.Error("Error scheduling syncFilePaths", "error", err.Error()) - return + for _, filePath := range filePaths { + if _, ok := caches[filePath]; ok { + continue + } + slog.Info("Creating cache obj", "filePath", filePath) + caches[filePath] = cache.New(cache.NoExpiration, cache.NoExpiration) } - if f.HealthCheckEvery > 0 { - if err := gocron.Every(f.HealthCheckEvery).Second().Do(sendHealthCheck); err != nil { - slog.Error("Error scheduling health check", "error", err.Error()) +} + +func startCron() { + if f.LogLevel == pkg.AppLogLevelDebug { + if err := gocron.Every(1).Second().Do(pkg.PrintMemUsage, &f); err != nil { + slog.Error("Error scheduling memory usage", "error", err.Error()) return } } - if err := gocron.Every(f.Every).Second().Do(cron); err != nil { + if err := gocron.Every(f.Every).Second().Do(cronWatch); err != nil { slog.Error("Error scheduling cron", "error", err.Error()) return } <-gocron.Start() } -func cron() { +func cronWatch() { + syncFilePaths() filePathsMutex.Lock() defer filePathsMutex.Unlock() + cacheMutex.Lock() + defer cacheMutex.Unlock() + for _, filePath := range filePaths { watch(filePath) } } func syncFilePaths() { + slog.Info("Syncing files") var err error - newFilePaths, err := pkg.FilesByPattern(f.FilePath, f.NotifyOnlyRecent) + fpCrawled, err := pkg.FilesByPattern(f.FilePath, f.FileRecentSecs) if err != nil { slog.Error("Error finding files", "error", err.Error()) return } - if len(newFilePaths) == 0 { + if len(fpCrawled) == 0 { slog.Warn("No files found", "filePath", f.FilePath) slog.Warn("Keep watching for new files") return } filePathsMutex.Lock() - filePaths = pkg.Capped(f.FilePathsCap, newFilePaths) + fpCrawled = pkg.Capped(f.FilePathsCap, fpCrawled) - filePathsMutex.Unlock() -} + fpFiltered := make([]string, 0, len(fpCrawled)) -func sendHealthCheck() { - details := pkg.GetHealthCheckDetails(&f, version) - for idx, filePath := range filePaths { - details = append(details, gmt.Details{ - Label: fmt.Sprintf("File Path %d", idx+1), - Message: filePath, - }) - } - - var logDetails []interface{} // nolint: prealloc - for _, detail := range details { - logDetails = append(logDetails, detail.Label, detail.Message) - } - slog.Info("Sending Health Check Notify", logDetails...) - if f.MSTeamsHook == "" { - slog.Warn("MS Teams hook not set") - return + for _, filePath := range fpCrawled { + isText, err := pkg.IsTextFile(filePath) + if err != nil || !isText { + continue + } + fpFiltered = append(fpFiltered, filePath) } + filePaths = fpFiltered - hostname, _ := os.Hostname() - - err := gmt.Send(hostname, details, f.MSTeamsHook, f.Proxy) - if err != nil { - slog.Error("Error sending to Teams", "error", err.Error()) - } else { - slog.Info("Successfully sent to MS Teams") - } + filePathsMutex.Unlock() + syncCaches() + slog.Info("Files found", "count", len(filePaths)) + slog.Info("Caches set", "count", len(caches)) } func validate() { @@ -163,7 +142,7 @@ func validate() { } func watch(filePath string) { - watcher, err := pkg.NewWatcher(filePath, f) + watcher, err := pkg.NewWatcher(filePath, f, caches[filePath]) if err != nil { slog.Error("Error creating watcher", "error", err.Error(), "filePath", filePath) @@ -178,31 +157,36 @@ func watch(filePath string) { slog.Error("Error scanning file", "error", err.Error(), "filePath", filePath) return } + reportResult(result) + if _, err := pkg.ExecShell(f.PostCommand); err != nil { + slog.Error("Error running post command", "error", err.Error()) + } +} + +func reportResult(result *pkg.ScanResult) { + slog.Info("File info", "filePath", result.FilePath, "size", result.FileInfo.Size(), "modTime", result.FileInfo.ModTime()) slog.Info("Lines read", "count", result.LinesRead) slog.Info("Scanning complete", "filePath", result.FilePath) - slog.Info("1st line (truncated to 200 chars)", "date", result.FirstDate, "line", pkg.Truncate(result.FirstLine, pkg.TruncateMax)) - slog.Info("Preview line (truncated to 200 chars)", "line", pkg.Truncate(result.PreviewLine, pkg.TruncateMax)) - slog.Info("Last line (truncated to 200 chars)", "date", result.LastDate, "line", pkg.Truncate(result.LastLine, pkg.TruncateMax)) + slog.Info("1st line", "date", result.FirstDate, "line", pkg.Truncate(result.FirstLine, pkg.TruncateMax)) + slog.Info("Preview line", "line", pkg.Truncate(result.PreviewLine, pkg.TruncateMax)) + slog.Info("Last line", "date", result.LastDate, "line", pkg.Truncate(result.LastLine, pkg.TruncateMax)) slog.Info("Error count", "percent", fmt.Sprintf("%d (%.2f)", result.ErrorCount, result.ErrorPercent)+"%") + slog.Info("History", "max streak", f.Streak, "current streaks", result.Streak, "symbols", pkg.StreakSymbols(result.Streak, f.Streak, f.Min)) + slog.Info("Scan", "count", result.ScanCount) - if result.ErrorCount < 0 { + // is first scan, cache isn't ready, so skip the notification + if result.ScanCount == 1 { return } - if result.ErrorCount < f.Min { + + if !pkg.NonStreakZero(result.Streak, f.Streak, f.Min) { + slog.Info("Streak not met", "streak", f.Streak, "streaks", result.Streak) return } - if !f.NotifyOnlyRecent { - pkg.Notify(result, f, version) - } - if f.NotifyOnlyRecent && pkg.IsRecentlyModified(result.FileInfo, f.Every) { + if pkg.IsRecentlyModified(result.FileInfo, f.Every) { pkg.Notify(result, f, version) } - if f.PostCommand != "" { - if _, err := pkg.ExecShell(f.PostCommand); err != nil { - slog.Error("Error running post command", "error", err.Error()) - } - } } func parseProxy() string { diff --git a/pkg/database.go b/pkg/database.go deleted file mode 100644 index 19d1cb3..0000000 --- a/pkg/database.go +++ /dev/null @@ -1,63 +0,0 @@ -package pkg - -import ( - "database/sql" - "fmt" - "log/slog" - "os" - "time" - - _ "github.com/glebarez/go-sqlite" // nolint: revive -) - -func InitDB(dbName string) (*sql.DB, error) { - slog.Info("Initializing database", "dbName", dbName) - var err error - db, err := sql.Open("sqlite", dbName) - if err != nil { - slog.Error("Error opening database", "error", err.Error()) - return nil, err - } - - db.SetMaxOpenConns(5) - db.SetMaxIdleConns(5) - db.SetConnMaxLifetime(time.Hour) - - if err := createTables(db); err != nil { - return nil, err - } - - return db, nil -} - -func GetUniqDBName(f Flags) string { - suffix := Hash(fmt.Sprintf("%s-%s-%s-%d", f.FilePath, f.Match, f.Ignore, f.Every)) + ".sqlite" - dbName := f.DBPath + "." + suffix - return dbName -} - -func DeleteDB(dbName string) error { - if _, err := os.Stat(dbName); err == nil { - if err := os.Remove(dbName); err != nil { - return err - } - } - return nil -} - -func createTables(db *sql.DB) error { - slog.Info("Creating tables if not exist") - _, err := db.Exec(` - CREATE TABLE IF NOT EXISTS state ( - key TEXT PRIMARY KEY, - value INTEGER, - updated_at DATETIME - ) - `) - if err != nil { - slog.Error("Error creating state table", "error", err.Error()) - return err - } - - return nil -} diff --git a/pkg/files.go b/pkg/files.go index ee4a664..52c916a 100644 --- a/pkg/files.go +++ b/pkg/files.go @@ -33,7 +33,7 @@ func IsTextFile(filename string) (bool, error) { return utf8.Valid(buffer[:n]), nil } -func FilesByPattern(pattern string, onlyRecent bool) ([]string, error) { +func FilesByPattern(pattern string, withinSeconds uint64) ([]string, error) { // Check if the pattern is a directory info, err := os.Stat(pattern) if err == nil && info.IsDir() { @@ -61,14 +61,14 @@ func FilesByPattern(pattern string, onlyRecent bool) ([]string, error) { } // only return files that are recently modified - if onlyRecent { + if withinSeconds > 0 { var recentFiles []string for _, file := range files { info, err := os.Stat(file) if err != nil { continue } - if IsRecentlyModified(info, 86400) { + if IsRecentlyModified(info, withinSeconds) { recentFiles = append(recentFiles, file) } } @@ -85,7 +85,7 @@ func GetHomedir() string { return home } -func IsRecentlyModified(fileInfo os.FileInfo, within uint64) bool { +func IsRecentlyModified(fileInfo os.FileInfo, withinSeconds uint64) bool { // Get the current time now := time.Now() @@ -93,7 +93,7 @@ func IsRecentlyModified(fileInfo os.FileInfo, within uint64) bool { modTime := fileInfo.ModTime() // Add a 1-hour buffer (3600 seconds) to the "within" duration - adjustedWithin := within + 3600 + adjustedWithin := withinSeconds + 3600 // Ensure that adjustedWithin is within the bounds of int64 if adjustedWithin > math.MaxInt64 { diff --git a/pkg/flags.go b/pkg/flags.go index 26d89fb..e01f1d0 100644 --- a/pkg/flags.go +++ b/pkg/flags.go @@ -5,45 +5,41 @@ import ( ) type Flags struct { - FilePath string - FilePathsCap int - Match string - Ignore string - DBPath string - PostCommand string - LogFile string + FilePath string + FilePathsCap int + FileRecentSecs uint64 + Match string + Ignore string + PostCommand string + LogFile string - Min int - Every uint64 - HealthCheckEvery uint64 - Proxy string - LogLevel int - MemLimit int - MSTeamsHook string - NotifyOnlyRecent bool - MaxBufferSizeMB int - Test bool - Version bool + Min int + Streak int + Every uint64 + Proxy string + LogLevel int + MemLimit int + MSTeamsHook string + MaxBufferMB int + Test bool + Version bool } func Parseflags(f *Flags) { flag.StringVar(&f.FilePath, "file-path", "", "full path to the file to watch") flag.StringVar(&f.FilePath, "f", "", "(short for --file-path) full path to the file to watch") flag.StringVar(&f.LogFile, "log-file", "", "full path to output log file. Empty will log to stdout") - flag.StringVar(&f.DBPath, "db-path", GetHomedir()+"/.go-watch-logs.db", "path to store db file.") flag.StringVar(&f.Match, "match", "", "regex for matching errors (empty to match all lines)") flag.StringVar(&f.Ignore, "ignore", "", "regex for ignoring errors (empty to ignore none)") flag.StringVar(&f.PostCommand, "post-cmd", "", "run this shell command after every scan when min errors are found") flag.Uint64Var(&f.Every, "every", 0, "run every n seconds (0 to run once)") - flag.Uint64Var(&f.HealthCheckEvery, "health-check-every", 0, `run health check every n seconds (0 to disable) -sends health check ping to ms teams webhook -`) flag.IntVar(&f.LogLevel, "log-level", 0, "log level (0=info, -4=debug, 4=warn, 8=error)") - flag.IntVar(&f.MemLimit, "mem-limit", 256, "memory limit in MB (0 to disable)") + flag.IntVar(&f.MemLimit, "mem-limit", 128, "memory limit in MB (0 to disable)") flag.IntVar(&f.FilePathsCap, "file-paths-cap", 100, "max number of file paths to watch") + flag.Uint64Var(&f.FileRecentSecs, "file-recent-secs", 86400, "only files modified in the last n seconds, 0 to disable") flag.IntVar(&f.Min, "min", 1, "on minimum num of matches, it should notify") - flag.IntVar(&f.MaxBufferSizeMB, "max-buffer-size-mb", 0, "max buffer size in MB, default is 0 (not provided) for go's default 64KB") - flag.BoolVar(&f.NotifyOnlyRecent, "notify-only-recent", true, "Notify on latest file only by timestamp based on --every") + flag.IntVar(&f.Streak, "streak", 1, "on minimum num of streak matches, it should notify") + flag.IntVar(&f.MaxBufferMB, "mbf", 0, "max buffer in MB, default is 0 (not provided) for go's default 64KB") flag.BoolVar(&f.Version, "version", false, "") flag.BoolVar(&f.Test, "test", false, `Quickly test paths or regex # will test if the input matches the regex @@ -65,10 +61,4 @@ func ParsePostFlags(f *Flags) { panic("Failed to ensure directory for log file: " + err.Error()) } } - - if f.DBPath != "" { - if err := MkdirP(f.DBPath); err != nil { - panic("Failed to ensure directory for DB path: " + err.Error()) - } - } } diff --git a/pkg/log.go b/pkg/log.go index 0d02fb3..e278af1 100644 --- a/pkg/log.go +++ b/pkg/log.go @@ -11,6 +11,10 @@ import ( "github.com/natefinch/lumberjack" ) +const ( + AppLogLevelDebug = -4 +) + // GlobalHandler is a custom handler that catches all logs type GlobalHandler struct { next slog.Handler diff --git a/pkg/notify.go b/pkg/notify.go index abc8df4..552e974 100644 --- a/pkg/notify.go +++ b/pkg/notify.go @@ -4,6 +4,7 @@ import ( "fmt" "log/slog" "os" + "time" gmt "github.com/kevincobain2000/go-msteams/src" ) @@ -41,71 +42,6 @@ func NotifyOwnError(e error, r slog.Record, msTeamsHook, proxy string) { func Notify(result *ScanResult, f Flags, version string) { slog.Info("Sending to MS Teams") - details := GetAlertDetails(&f, version, result) - - var logDetails []interface{} // nolint: prealloc - for _, detail := range details { - logDetails = append(logDetails, detail.Label, detail.Message) - } - - if f.MSTeamsHook == "" { - slog.Warn("MS Teams hook not set") - return - } - slog.Info("Sending Alert Notify", logDetails...) - - hostname, _ := os.Hostname() - - err := gmt.Send(hostname, details, f.MSTeamsHook, f.Proxy) - if err != nil { - slog.Error("Error sending to Teams", "error", err.Error()) - } else { - slog.Info("Successfully sent to MS Teams") - } -} - -func GetHealthCheckDetails(f *Flags, version string) []gmt.Details { - return []gmt.Details{ - { - Label: "Health Check", - Message: "All OK, go-watch-logs is running actively.", - }, - { - Label: "Next Ping", - Message: fmt.Sprintf("%d secs", f.HealthCheckEvery), - }, - { - Label: "Version", - Message: version, - }, - { - Label: "File Path Pattern", - Message: f.FilePath, - }, - { - Label: "File Path Cap", - Message: fmt.Sprintf("%d", f.FilePathsCap), - }, - { - Label: "Match Pattern", - Message: f.Match, - }, - { - Label: "Ignore Pattern", - Message: f.Ignore, - }, - { - Label: "Min Errors Threshold", - Message: fmt.Sprintf("%d", f.Min), - }, - { - Label: "Monitoring Every", - Message: fmt.Sprintf("%d secs", f.Every), - }, - } -} - -func GetAlertDetails(f *Flags, version string, result *ScanResult) []gmt.Details { details := []gmt.Details{ { Label: "go-watch-log version", @@ -127,18 +63,6 @@ func GetAlertDetails(f *Flags, version string, result *ScanResult) []gmt.Details Label: "Ignore Pattern", Message: f.Ignore, }, - { - Label: "Min Errors Threshold", - Message: fmt.Sprintf("%d", f.Min), - }, - { - Label: "Lines Read", - Message: fmt.Sprintf("%d", result.LinesRead), - }, - { - Label: "Total Errors Found", - Message: fmt.Sprintf("%d (%.2f)", result.ErrorCount, result.ErrorPercent) + "%", - }, { Label: "First Line", Message: Truncate(result.FirstLine, TruncateMax), @@ -151,12 +75,61 @@ func GetAlertDetails(f *Flags, version string, result *ScanResult) []gmt.Details Label: "Last Line", Message: Truncate(result.LastLine, TruncateMax), }, + { + Label: "Details", + Message: fmt.Sprintf( + "Min Threshold: %d, Lines Read: %d\n\rMatches Found: %d, Ratio %.2f%%", + f.Min, + result.LinesRead, + result.ErrorCount, + result.ErrorPercent, + ), + }, + { + Label: fmt.Sprintf("Streaks (Max %d)", f.Streak), + Message: StreakSymbols(result.Streak, f.Streak, f.Min), + }, } if result.FirstDate != "" || result.LastDate != "" { + var duration string + if result.FirstDate != "" && result.LastDate != "" { + firstDate, err := time.Parse("2006-01-02 15:04:05", result.FirstDate) + if err != nil { + duration = "X" + } else { + lastDate, err := time.Parse("2006-01-02 15:04:05", result.LastDate) + if err == nil { + duration = lastDate.Sub(firstDate).String() + } else { + duration = "X" + } + } + } + details = append(details, gmt.Details{ - Label: "Time Range", - Message: fmt.Sprintf("%s to %s", result.FirstDate, result.LastDate), + Label: "Range", + Message: fmt.Sprintf("%s to %s (Duration: %s)", result.FirstDate, result.LastDate, duration), }) } - return details + + var logDetails []interface{} // nolint: prealloc + for _, detail := range details { + logDetails = append(logDetails, detail.Label, detail.Message) + } + + slog.Info("Sending Alert Notify", logDetails...) + + hostname, _ := os.Hostname() + + if f.MSTeamsHook == "" { + slog.Warn("MS Teams hook not set") + return + } + + err := gmt.Send(hostname, details, f.MSTeamsHook, f.Proxy) + if err != nil { + slog.Error("Error sending to Teams", "error", err.Error()) + } else { + slog.Info("Successfully sent to MS Teams") + } } diff --git a/pkg/slices.go b/pkg/slices.go index ed82b77..11272e7 100644 --- a/pkg/slices.go +++ b/pkg/slices.go @@ -8,3 +8,16 @@ func Capped[T any](cap int, slice []T) []T { } return slice[:capped] } + +func NonStreakZero(streaks []int, streak int, minimum int) bool { + // check if last three elements are over a minimum + if len(streaks) < streak { + return false + } + for i := 0; i < streak; i++ { + if streaks[len(streaks)-1-i] < minimum { + return false + } + } + return true +} diff --git a/pkg/strings.go b/pkg/strings.go index 9ff1cb0..af49f4b 100644 --- a/pkg/strings.go +++ b/pkg/strings.go @@ -4,6 +4,7 @@ import ( "crypto/sha256" "encoding/hex" "log/slog" + "strings" "sync" "github.com/gravwell/gravwell/v3/timegrinder" @@ -65,3 +66,20 @@ func SearchDate(input string) string { } return ts.Format("2006-01-02 15:04:05") } + +func StreakSymbols(arr []int, length int, minimum int) string { + var symbols []string + for _, v := range arr { + if v >= minimum { + symbols = append(symbols, "✕") + } else { + symbols = append(symbols, "✓") + } + } + // Fill the rest with grey symbols based on streak length + for i := len(symbols); i < length*StreakMultiplier; i++ { + symbols = append([]string{"□"}, symbols...) + } + + return strings.Join(symbols, " ") +} diff --git a/pkg/system.go b/pkg/system.go index cdc2d00..4e54231 100644 --- a/pkg/system.go +++ b/pkg/system.go @@ -41,6 +41,9 @@ func BToMb(b uint64) uint64 { } func ExecShell(command string) (string, error) { + if command == "" { + return "", nil + } cmd := exec.Command("sh", "-c", command) out, err := cmd.CombinedOutput() return string(out), err diff --git a/pkg/testit.go b/pkg/testit.go index 43875f5..bc3e131 100644 --- a/pkg/testit.go +++ b/pkg/testit.go @@ -6,7 +6,7 @@ import ( ) func TestIt(filepath string, match string) { - fps, err := FilesByPattern(filepath, false) + fps, err := FilesByPattern(filepath, 0) if err != nil { slog.Error("Error finding files", "error", err.Error()) } diff --git a/pkg/watcher.go b/pkg/watcher.go index 6e7d725..93a4ff0 100644 --- a/pkg/watcher.go +++ b/pkg/watcher.go @@ -2,49 +2,53 @@ package pkg import ( "bufio" - "database/sql" "io" "os" "regexp" - "strings" "time" + + "github.com/patrickmn/go-cache" ) type Watcher struct { - db *sql.DB - dbName string // full path + cache *cache.Cache filePath string lastLineKey string lastFileSizeKey string + errorHistoryKey string + scanCountKey string matchPattern string ignorePattern string - maxBufferSizeMB int + maxBufferMB int lastLineNum int lastFileSize int64 timestampNow string + streak int // Number of error counts to maintain } +const ( + StreakMultiplier = 2 +) + func NewWatcher( filePath string, f Flags, + c *cache.Cache, ) (*Watcher, error) { - dbName := GetUniqDBName(f) - db, err := InitDB(dbName) - if err != nil { - return nil, err - } now := time.Now() watcher := &Watcher{ - db: db, - dbName: dbName, + cache: c, filePath: filePath, matchPattern: f.Match, ignorePattern: f.Ignore, - lastLineKey: "llk-" + filePath, - lastFileSizeKey: "llks-" + filePath, + lastLineKey: "lk-" + filePath, + lastFileSizeKey: "sk-" + filePath, + errorHistoryKey: "eh-" + filePath, + scanCountKey: "sc-" + filePath, timestampNow: now.Format("2006-01-02 15:04:05"), - maxBufferSizeMB: f.MaxBufferSizeMB, + maxBufferMB: f.MaxBufferMB, + streak: f.Streak * StreakMultiplier, } if err := watcher.loadState(); err != nil { return nil, err @@ -64,6 +68,8 @@ type ScanResult struct { PreviewLine string LastLine string LastDate string + Streak []int // History of error counts for this file path + ScanCount int // Total number of scans performed } func (w *Watcher) Scan() (*ScanResult, error) { @@ -104,9 +110,9 @@ func (w *Watcher) Scan() (*ScanResult, error) { } scanner := bufio.NewScanner(file) - if w.maxBufferSizeMB > 0 { - // for large lines - scanner.Buffer(make([]byte, 0, 64*1024), w.maxBufferSizeMB*1024*1024) + if w.maxBufferMB > 0 { + // For large lines + scanner.Buffer(make([]byte, 0, 64*1024), w.maxBufferMB*1024*1024) } currentLineNum := 1 linesRead := 0 @@ -117,7 +123,7 @@ func (w *Watcher) Scan() (*ScanResult, error) { bytesRead += int64(len(line)) + 1 // Adding 1 for the newline character currentLineNum++ linesRead = currentLineNum - w.lastLineNum - // convert to positive number + // Convert to positive number if linesRead < 0 { linesRead = -linesRead } @@ -130,8 +136,8 @@ func (w *Watcher) Scan() (*ScanResult, error) { if firstLine == "" { firstLine = lineStr } - if len(previewLine) < 1000 { - previewLine += lineStr + "\n" + if len(previewLine) < 500 { + previewLine += lineStr + "\n\r" } lastLine = lineStr matchCounts++ @@ -154,14 +160,24 @@ func (w *Watcher) Scan() (*ScanResult, error) { matchPercentage = float64(int(matchPercentage*100)) / 100 w.lastLineNum = currentLineNum w.lastFileSize = bytesRead + + // Update scan count + w.incrementScanCount() + + // Update error history + w.updateErrorHistory(matchCounts) + + // Save state if err := w.saveState(); err != nil { - if strings.HasPrefix(err.Error(), "database is locked") { - if err := DeleteDB(w.dbName); err != nil { - return nil, err - } - } return nil, err } + + // Get the error history + errorHistory := w.getErrorHistory() + + // Get the scan count + scanCount := w.getScanCount() + return &ScanResult{ ErrorCount: matchCounts, FirstDate: SearchDate(firstLine), @@ -173,37 +189,68 @@ func (w *Watcher) Scan() (*ScanResult, error) { FileInfo: fileInfo, ErrorPercent: matchPercentage, LinesRead: linesRead, + Streak: errorHistory, + ScanCount: scanCount, }, nil } func (w *Watcher) loadState() error { - row := w.db.QueryRow(`SELECT value FROM state WHERE key = ?`, w.lastLineKey) - var lastLineNum int - err := row.Scan(&lastLineNum) - if err != nil && err != sql.ErrNoRows { - return err + if value, found := w.cache.Get(w.lastLineKey); found { + w.lastLineNum = value.(int) } - w.lastLineNum = lastLineNum - - row = w.db.QueryRow(`SELECT value FROM state WHERE key = ?`, w.lastFileSizeKey) - var lastFileSize int64 - err = row.Scan(&lastFileSize) - if err != nil && err != sql.ErrNoRows { - return err + if value, found := w.cache.Get(w.lastFileSizeKey); found { + w.lastFileSize = value.(int64) } - w.lastFileSize = lastFileSize return nil } func (w *Watcher) saveState() error { - _, err := w.db.Exec(`REPLACE INTO state (key, value, updated_at) VALUES (?, ?, ?)`, w.lastLineKey, w.lastLineNum, w.timestampNow) - if err != nil { - return err + w.cache.Set(w.lastLineKey, w.lastLineNum, cache.DefaultExpiration) + w.cache.Set(w.lastFileSizeKey, w.lastFileSize, cache.DefaultExpiration) + return nil +} + +func (w *Watcher) updateErrorHistory(newErrorCount int) { + if w.getScanCount() == 1 { + return + } + var history []int + if value, found := w.cache.Get(w.errorHistoryKey); found { + history = value.([]int) + } + + // Add the new error count and limit the history size + history = append(history, newErrorCount) + if len(history) > w.streak { + history = history[len(history)-w.streak:] + } + + w.cache.Set(w.errorHistoryKey, history, cache.DefaultExpiration) +} + +func (w *Watcher) getErrorHistory() []int { + if value, found := w.cache.Get(w.errorHistoryKey); found { + return value.([]int) + } + return []int{} +} + +func (w *Watcher) incrementScanCount() { + count := 0 + if value, found := w.cache.Get(w.scanCountKey); found { + count = value.(int) } - _, err = w.db.Exec(`REPLACE INTO state (key, value, updated_at) VALUES (?, ?, ?)`, w.lastFileSizeKey, w.lastFileSize, w.timestampNow) - return err + count++ + w.cache.Set(w.scanCountKey, count, cache.DefaultExpiration) +} + +func (w *Watcher) getScanCount() int { + if value, found := w.cache.Get(w.scanCountKey); found { + return value.(int) + } + return 0 } func (w *Watcher) Close() error { - return w.db.Close() + return nil } diff --git a/pkg/watcher_test.go b/pkg/watcher_test.go index 2c06dd3..598fe06 100644 --- a/pkg/watcher_test.go +++ b/pkg/watcher_test.go @@ -4,6 +4,7 @@ import ( "os" "testing" + "github.com/patrickmn/go-cache" "github.com/stretchr/testify/assert" ) @@ -27,12 +28,13 @@ func TestNewWatcher(t *testing.T) { ignorePattern := "ignore" // nolint: goconst f := Flags{ - DBPath: "test", Match: matchPattern, Ignore: ignorePattern, } - watcher, err := NewWatcher(filePath, f) + caches := make(map[string]*cache.Cache) + caches[filePath] = cache.New(cache.NoExpiration, cache.NoExpiration) + watcher, err := NewWatcher(filePath, f, caches[filePath]) assert.NoError(t, err) assert.NotNil(t, watcher) @@ -53,12 +55,13 @@ error:1` ignorePattern := `ignore` // nolint: goconst f := Flags{ - DBPath: "test", Match: matchPattern, Ignore: ignorePattern, } - watcher, err := NewWatcher(filePath, f) + caches := make(map[string]*cache.Cache) + caches[filePath] = cache.New(cache.NoExpiration, cache.NoExpiration) + watcher, err := NewWatcher(filePath, f, caches[filePath]) assert.NoError(t, err) defer watcher.Close() @@ -81,12 +84,13 @@ line2` ignorePattern := `ignore` // nolint: goconst f := Flags{ - DBPath: "test", Match: matchPattern, Ignore: ignorePattern, } - watcher, err := NewWatcher(filePath, f) + caches := make(map[string]*cache.Cache) + caches[filePath] = cache.New(cache.NoExpiration, cache.NoExpiration) + watcher, err := NewWatcher(filePath, f, caches[filePath]) assert.NoError(t, err) defer watcher.Close() @@ -123,12 +127,13 @@ error:1` ignorePattern := `ignore` f := Flags{ - DBPath: "test", Match: matchPattern, Ignore: ignorePattern, } - watcher, err := NewWatcher(filePath, f) + caches := make(map[string]*cache.Cache) + caches[filePath] = cache.New(cache.NoExpiration, cache.NoExpiration) + watcher, err := NewWatcher(filePath, f, caches[filePath]) if err != nil { b.Fatal(err) } @@ -143,18 +148,18 @@ error:1` } func BenchmarkLoadAndSaveState(b *testing.B) { - dbName := "test.db" filePath := "test.log" matchPattern := "error:1" ignorePattern := "ignore" f := Flags{ - DBPath: dbName, Match: matchPattern, Ignore: ignorePattern, } - watcher, err := NewWatcher(filePath, f) + caches := make(map[string]*cache.Cache) + caches[filePath] = cache.New(cache.NoExpiration, cache.NoExpiration) + watcher, err := NewWatcher(filePath, f, caches[filePath]) if err != nil { b.Fatal(err) } @@ -163,7 +168,9 @@ func BenchmarkLoadAndSaveState(b *testing.B) { watcher.lastLineNum = 10 for i := 0; i < b.N; i++ { - _, err := NewWatcher(filePath, f) + caches := make(map[string]*cache.Cache) + caches[filePath] = cache.New(cache.NoExpiration, cache.NoExpiration) + _, err := NewWatcher(filePath, f, caches[filePath]) if err != nil { b.Fatal(err) } @@ -184,12 +191,13 @@ line2` ignorePattern := `ignore` f := Flags{ - DBPath: "test", Match: matchPattern, Ignore: ignorePattern, } - watcher, err := NewWatcher(filePath, f) + caches := make(map[string]*cache.Cache) + caches[filePath] = cache.New(cache.NoExpiration, cache.NoExpiration) + watcher, err := NewWatcher(filePath, f, caches[filePath]) if err != nil { b.Fatal(err) }