diff --git a/.gitignore b/.gitignore index 9b99725..29d89c7 100755 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ tmp/ *.buntdb .DS_Store go-watch-logs +*.sqlite +*.sqlite3 \ No newline at end of file diff --git a/README.md b/README.md index 14047ea..98c2603 100644 --- a/README.md +++ b/README.md @@ -70,8 +70,6 @@ go-watch-logs --file-path=my.log --match='HTTP/1.1" 50' --every=60 on minimum num of matches, it should notify (default 1) -ms-teams-hook string ms teams webhook - -no-cache - read back from the start of the file (default false) -proxy string http proxy for webhooks -version diff --git a/go.mod b/go.mod index e393300..bfd5145 100644 --- a/go.mod +++ b/go.mod @@ -5,26 +5,29 @@ go 1.21 toolchain go1.22.3 require ( + github.com/glebarez/go-sqlite v1.22.0 github.com/jasonlvhit/gocron v0.0.1 + github.com/k0kubun/pp v3.0.1+incompatible github.com/kevincobain2000/go-msteams v0.0.0-20231124044510-4369c04dd224 github.com/lmittmann/tint v1.0.4 - github.com/mattn/go-isatty v0.0.16 + github.com/mattn/go-isatty v0.0.20 github.com/stretchr/testify v1.9.0 - github.com/tidwall/buntdb v1.3.1 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/google/uuid v1.5.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/pmezard/go-difflib v1.0.0 // indirect - github.com/tidwall/btree v1.4.2 // indirect - github.com/tidwall/gjson v1.14.3 // indirect - github.com/tidwall/grect v0.1.4 // indirect - github.com/tidwall/match v1.1.1 // indirect - github.com/tidwall/pretty v1.2.0 // indirect - github.com/tidwall/rtred v0.1.2 // indirect - github.com/tidwall/tinyqueue v0.1.1 // indirect - golang.org/x/sys v0.10.0 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + golang.org/x/sys v0.15.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + modernc.org/libc v1.37.6 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.7.2 // indirect + modernc.org/sqlite v1.28.0 // indirect ) diff --git a/go.sum b/go.sum index 9f61b2a..ad208da 100644 --- a/go.sum +++ b/go.sum @@ -2,12 +2,24 @@ 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/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-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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 v0.0.0-20231124044510-4369c04dd224 h1:YLj2tqX+dD34tRTW7WUuOW00G+RPBQAKGRYGNmkYuBs= github.com/kevincobain2000/go-msteams v0.0.0-20231124044510-4369c04dd224/go.mod h1:+HowoQQHg9HLfx3CYQGImGGYw20+kN9rFmUXgxrqBzo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -19,41 +31,25 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lmittmann/tint v1.0.4 h1:LeYihpJ9hyGvE0w+K2okPTGUdVLfng1+nDNVR4vWISc= github.com/lmittmann/tint v1.0.4/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 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/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= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 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= -github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI= -github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxcktb+Kj8= -github.com/tidwall/btree v1.4.2 h1:PpkaieETJMUxYNADsjgtNRcERX7mGc/GP2zp/r5FM3g= -github.com/tidwall/btree v1.4.2/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE= -github.com/tidwall/buntdb v1.3.1 h1:HKoDF01/aBhl9RjYtbaLnvX9/OuenwvQiC3OP1CcL4o= -github.com/tidwall/buntdb v1.3.1/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU= -github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw= -github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg= -github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q= -github.com/tidwall/lotsa v1.0.2 h1:dNVBH5MErdaQ/xd9s769R31/n2dXavsQ0Yf4TMEHHw8= -github.com/tidwall/lotsa v1.0.2/go.mod h1:X6NiU+4yHA3fE3Puvpnn1XMDrFZrE9JO2/w+UMuqgR8= -github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= -github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= -github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8= -github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ= -github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE= -github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 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= @@ -63,8 +59,9 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h 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= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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= @@ -75,3 +72,11 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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/libc v1.37.6 h1:orZH3c5wmhIQFTXF+Nt+eeauyd+ZIt2BX6ARe+kD+aw= +modernc.org/libc v1.37.6/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= +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.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= +modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= +modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ= +modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= diff --git a/main.go b/main.go index 092e65d..7fb4a71 100644 --- a/main.go +++ b/main.go @@ -23,7 +23,6 @@ type Flags struct { proxy string logLevel int msTeamsHook string - noCache bool version bool } @@ -44,7 +43,6 @@ func main() { "dbPath", f.dbPath, "min", f.min, "every", f.every, - "noCache", f.noCache, "version", f.version, "loglevel", f.logLevel, "proxy", f.proxy, @@ -102,7 +100,7 @@ func validate() { } func watch(filePath string) { - watcher, err := pkg.NewWatcher(f.dbPath, filePath, f.match, f.ignore, f.noCache) + watcher, err := pkg.NewWatcher(f.dbPath, filePath, f.match, f.ignore) if err != nil { slog.Error("Error creating watcher", "error", err.Error(), "filePath", filePath) return @@ -163,7 +161,6 @@ func flags() { flag.Uint64Var(&f.every, "every", 0, "run every n seconds (0 to run once)") flag.IntVar(&f.logLevel, "log-level", 0, "log level (0=info, 1=debug)") flag.IntVar(&f.min, "min", 1, "on minimum num of matches, it should notify") - flag.BoolVar(&f.noCache, "no-cache", false, "read back from the start of the file (default false)") flag.BoolVar(&f.version, "version", false, "") flag.StringVar(&f.proxy, "proxy", "", "http proxy for webhooks") diff --git a/pkg/database.go b/pkg/database.go new file mode 100644 index 0000000..46fe2d2 --- /dev/null +++ b/pkg/database.go @@ -0,0 +1,39 @@ +package pkg + +import ( + "database/sql" + "os" + "time" + + _ "github.com/glebarez/go-sqlite" // nolint: revive +) + +func InitDB(dbName string) (*sql.DB, error) { + db, err := sql.Open("sqlite", dbName) + if err != nil { + return nil, err + } + + _, err = db.Exec(` + CREATE TABLE IF NOT EXISTS state ( + key TEXT PRIMARY KEY, + value INTEGER + ) + `) + if err != nil { + return nil, err + } + db.SetMaxOpenConns(5) + db.SetMaxIdleConns(5) + db.SetConnMaxLifetime(time.Hour) + + return db, nil +} +func Vacuum(dbName string) error { + if _, err := os.Stat(dbName); err == nil { + if err := os.Remove(dbName); err != nil { + return err + } + } + return nil +} diff --git a/pkg/watcher.go b/pkg/watcher.go index 7a4e47b..98c798f 100644 --- a/pkg/watcher.go +++ b/pkg/watcher.go @@ -2,28 +2,23 @@ package pkg import ( "bufio" - "errors" - "fmt" + "database/sql" "io" - "log/slog" "os" "regexp" - "sync" - - "github.com/tidwall/buntdb" + "strings" ) type Watcher struct { - db *buntdb.DB + db *sql.DB + dbName string // full path filePath string lastLineKey string lastFileSizeKey string matchPattern string ignorePattern string - noCache bool lastLineNum int lastFileSize int64 - mutex sync.Mutex } func NewWatcher( @@ -31,64 +26,28 @@ func NewWatcher( filePath string, matchPattern string, ignorePattern string, - noCache bool, ) (*Watcher, error) { - if dbName == "" { - return nil, errors.New("dbName is required") - } - // add a suffix to the database name, is just in case some, cuz we are doing os remove - // and don't want to remove any other file on mis configuration - if dbName != ":memory:" { - dbName += ".buntdb" - } - db, err := buntdb.Open(dbName) + dbName += ".sqlite" + db, err := InitDB(dbName) if err != nil { - if err.Error() == "invalid database" { - slog.Warn("Invalid database, removing and creating a new one", "dbName", dbName) - err = os.Remove(dbName) - if err != nil { - return nil, err - } - db, err = buntdb.Open(dbName) - if err != nil { - return nil, err - } - } else { - return nil, err - } + return nil, err } watcher := &Watcher{ db: db, + dbName: dbName, filePath: filePath, matchPattern: matchPattern, ignorePattern: ignorePattern, - noCache: noCache, - lastLineKey: Hash(filePath + "llk"), - lastFileSizeKey: Hash(filePath + "llks"), - } - if watcher.noCache { - if err := watcher.NoCache(); err != nil { - return nil, err - } + lastLineKey: "llk-" + filePath, + lastFileSizeKey: "llks-" + filePath, } - if err := watcher.loadState(); err != nil { return nil, err } return watcher, nil } -func (w *Watcher) NoCache() error { - return w.db.Update(func(tx *buntdb.Tx) error { - _, _, err := tx.Set(w.lastLineKey, "0", nil) - if err != nil { - return err - } - _, _, err = tx.Set(w.lastFileSizeKey, "0", nil) - return err - }) -} type ScanResult struct { ErrorCount int @@ -97,8 +56,6 @@ type ScanResult struct { } func (w *Watcher) Scan() (*ScanResult, error) { - w.mutex.Lock() - defer w.mutex.Unlock() errorCounts := 0 firstLine := "" lastLine := "" @@ -111,7 +68,7 @@ func (w *Watcher) Scan() (*ScanResult, error) { currentFileSize := fileInfo.Size() // Detect log rotation - if currentFileSize < w.lastFileSize { + if currentFileSize+2 < w.lastFileSize { w.lastFileSize = 0 } @@ -161,6 +118,11 @@ func (w *Watcher) Scan() (*ScanResult, error) { w.lastLineNum = currentLineNum w.lastFileSize = bytesRead if err := w.saveState(); err != nil { + if strings.HasPrefix(err.Error(), "database is locked") { + if err := Vacuum(w.dbName); err != nil { + return nil, err + } + } return nil, err } return &ScanResult{ @@ -171,37 +133,31 @@ func (w *Watcher) Scan() (*ScanResult, error) { } func (w *Watcher) loadState() error { - return w.db.View(func(tx *buntdb.Tx) error { - lastLineStr, err := tx.Get(w.lastLineKey) - if errors.Is(err, buntdb.ErrNotFound) { - return nil - } - if err != nil { - return err - } - fmt.Sscanf(lastLineStr, "%d", &w.lastLineNum) // nolint: errcheck + 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 + } + w.lastLineNum = lastLineNum - lastFileSizeStr, err := tx.Get(w.lastFileSizeKey) - if errors.Is(err, buntdb.ErrNotFound) { - return nil - } - if err != nil { - return err - } - fmt.Sscanf(lastFileSizeStr, "%d", &w.lastFileSize) // nolint: errcheck - return nil - }) + 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 + } + w.lastFileSize = lastFileSize + return nil } func (w *Watcher) saveState() error { - return w.db.Update(func(tx *buntdb.Tx) error { - _, _, err := tx.Set(w.lastLineKey, fmt.Sprintf("%d", w.lastLineNum), nil) - if err != nil { - return err - } - _, _, err = tx.Set(w.lastFileSizeKey, fmt.Sprintf("%d", w.lastFileSize), nil) + _, err := w.db.Exec(`REPLACE INTO state (key, value) VALUES (?, ?)`, w.lastLineKey, w.lastLineNum) + if err != nil { return err - }) + } + _, err = w.db.Exec(`REPLACE INTO state (key, value) VALUES (?, ?)`, w.lastFileSizeKey, w.lastFileSize) + return err } func (w *Watcher) Close() error { diff --git a/pkg/watcher_test.go b/pkg/watcher_test.go index be77c51..1a9fc92 100644 --- a/pkg/watcher_test.go +++ b/pkg/watcher_test.go @@ -8,7 +8,7 @@ import ( ) const ( - inMemory = ":memory:" + inMemory = "testdb" ) func setupTempFile(content string) (string, error) { @@ -31,7 +31,7 @@ func TestNewWatcher(t *testing.T) { matchPattern := "error:1" // nolint: goconst ignorePattern := "ignore" // nolint: goconst - watcher, err := NewWatcher(dbName, filePath, matchPattern, ignorePattern, false) + watcher, err := NewWatcher(dbName, filePath, matchPattern, ignorePattern) assert.NoError(t, err) assert.NotNil(t, watcher) @@ -52,7 +52,7 @@ error:1` matchPattern := `error:1` // nolint: goconst ignorePattern := `ignore` // nolint: goconst - watcher, err := NewWatcher(dbName, filePath, matchPattern, ignorePattern, false) + watcher, err := NewWatcher(dbName, filePath, matchPattern, ignorePattern) assert.NoError(t, err) defer watcher.Close() @@ -75,7 +75,7 @@ line2` matchPattern := `error:1` // nolint: goconst ignorePattern := `ignore` // nolint: goconst - watcher, err := NewWatcher(dbName, filePath, matchPattern, ignorePattern, false) + watcher, err := NewWatcher(dbName, filePath, matchPattern, ignorePattern) assert.NoError(t, err) defer watcher.Close() @@ -112,7 +112,7 @@ error:1` matchPattern := `error:1` ignorePattern := `ignore` - watcher, err := NewWatcher(dbName, filePath, matchPattern, ignorePattern, false) + watcher, err := NewWatcher(dbName, filePath, matchPattern, ignorePattern) if err != nil { b.Fatal(err) } @@ -132,7 +132,7 @@ func BenchmarkLoadAndSaveState(b *testing.B) { matchPattern := "error:1" ignorePattern := "ignore" - watcher, err := NewWatcher(dbName, filePath, matchPattern, ignorePattern, false) + watcher, err := NewWatcher(dbName, filePath, matchPattern, ignorePattern) if err != nil { b.Fatal(err) } @@ -141,7 +141,7 @@ func BenchmarkLoadAndSaveState(b *testing.B) { watcher.lastLineNum = 10 for i := 0; i < b.N; i++ { - _, err := NewWatcher(dbName, filePath, matchPattern, ignorePattern, false) + _, err := NewWatcher(dbName, filePath, matchPattern, ignorePattern) if err != nil { b.Fatal(err) } @@ -162,7 +162,7 @@ line2` matchPattern := `error:1` ignorePattern := `ignore` - watcher, err := NewWatcher(dbName, filePath, matchPattern, ignorePattern, false) + watcher, err := NewWatcher(dbName, filePath, matchPattern, ignorePattern) if err != nil { b.Fatal(err) }