diff --git a/README.md b/README.md index 4347d64..f60789e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ # HackBrowserData -[![Lint](https://github.com/moonD4rk/HackBrowserData/actions/workflows/lint.yml/badge.svg)](https://github.com/moonD4rk/HackBrowserData/actions/workflows/lint.yml) [![build](https://github.com/moonD4rk/HackBrowserData/actions/workflows/build.yml/badge.svg)](https://github.com/moonD4rk/HackBrowserData/actions/workflows/build.yml) [![Release](https://github.com/moonD4rk/HackBrowserData/actions/workflows/release.yml/badge.svg)](https://github.com/moonD4rk/HackBrowserData/actions/workflows/release.yml) [![run tests](https://github.com/moonD4rk/HackBrowserData/actions/workflows/unittest.yml/badge.svg)](https://github.com/moonD4rk/HackBrowserData/actions/workflows/unittest.yml) [![Coverage Status](https://coveralls.io/repos/github/moonD4rk/HackBrowserData/badge.svg)](https://coveralls.io/github/moonD4rk/HackBrowserData) +[![Lint](https://github.com/moonD4rk/HackBrowserData/actions/workflows/lint.yml/badge.svg)](https://github.com/moonD4rk/HackBrowserData/actions/workflows/lint.yml) [![build](https://github.com/moonD4rk/HackBrowserData/actions/workflows/build.yml/badge.svg)](https://github.com/moonD4rk/HackBrowserData/actions/workflows/build.yml) [![Release](https://github.com/moonD4rk/HackBrowserData/actions/workflows/release.yml/badge.svg)](https://github.com/moonD4rk/HackBrowserData/actions/workflows/release.yml) [![unit tests](https://github.com/moonD4rk/HackBrowserData/actions/workflows/unittest.yml/badge.svg)](https://github.com/moonD4rk/HackBrowserData/actions/workflows/unittest.yml) [![Coverage Status](https://coveralls.io/repos/github/moonD4rk/HackBrowserData/badge.svg?branch=main)](https://coveralls.io/github/moonD4rk/HackBrowserData?branch=main) [中文说明](https://github.com/moonD4rk/HackBrowserData/blob/master/README_ZH.md) @@ -90,7 +90,7 @@ Installation of `HackBrowserData` is dead-simple, just download [the release for ### Building from source -only support `go 1.18+` with go generics +only support `go 1.21+` with go generics and log/slog standard library. ```bash $ git clone https://github.com/moonD4rk/HackBrowserData diff --git a/browser/browser.go b/browser/browser.go index 25c3df6..80e0133 100644 --- a/browser/browser.go +++ b/browser/browser.go @@ -1,6 +1,7 @@ package browser import ( + "log/slog" "path/filepath" "sort" "strings" @@ -8,7 +9,6 @@ import ( "github.com/moond4rk/hackbrowserdata/browser/chromium" "github.com/moond4rk/hackbrowserdata/browser/firefox" "github.com/moond4rk/hackbrowserdata/browsingdata" - "github.com/moond4rk/hackbrowserdata/log" "github.com/moond4rk/hackbrowserdata/utils/fileutil" "github.com/moond4rk/hackbrowserdata/utils/typeutil" ) @@ -44,16 +44,16 @@ func pickChromium(name, profile string) []Browser { if name == "all" { for _, v := range chromiumList { if !fileutil.IsDirExists(filepath.Clean(v.profilePath)) { - log.Noticef("find browser %s failed, profile folder does not exist", v.name) + slog.Warn("find browser failed, profile folder does not exist", "browser", v.name) continue } multiChromium, err := chromium.New(v.name, v.storage, v.profilePath, v.items) if err != nil { - log.Errorf("new chromium error: %v", err) + slog.Error("new chromium error", "err", err) continue } for _, b := range multiChromium { - log.Noticef("find browser %s success", b.Name()) + slog.Warn("find browser success", "browser", b.Name()) browsers = append(browsers, b) } } @@ -63,14 +63,14 @@ func pickChromium(name, profile string) []Browser { profile = c.profilePath } if !fileutil.IsDirExists(filepath.Clean(profile)) { - log.Fatalf("find browser %s failed, profile folder does not exist", c.name) + slog.Error("find browser failed, profile folder does not exist", "browser", c.name) } chromiumList, err := chromium.New(c.name, c.storage, profile, c.items) if err != nil { - log.Fatalf("new chromium error: %s", err) + slog.Error("new chromium error", "err", err) } for _, b := range chromiumList { - log.Noticef("find browser %s success", b.Name()) + slog.Warn("find browser success", "browser", b.Name()) browsers = append(browsers, b) } } @@ -89,17 +89,17 @@ func pickFirefox(name, profile string) []Browser { } if !fileutil.IsDirExists(filepath.Clean(profile)) { - log.Noticef("find browser firefox %s failed, profile folder does not exist", v.name) + slog.Warn("find browser failed, profile folder does not exist", "browser", v.name) continue } if multiFirefox, err := firefox.New(profile, v.items); err == nil { for _, b := range multiFirefox { - log.Noticef("find browser firefox %s success", b.Name()) + slog.Warn("find browser success", "browser", b.Name()) browsers = append(browsers, b) } } else { - log.Error(err) + slog.Error("new firefox error", "err", err) } } diff --git a/browser/chromium/chromium.go b/browser/chromium/chromium.go index 4dd498b..4ce4230 100644 --- a/browser/chromium/chromium.go +++ b/browser/chromium/chromium.go @@ -2,12 +2,12 @@ package chromium import ( "io/fs" + "log/slog" "path/filepath" "strings" "github.com/moond4rk/hackbrowserdata/browsingdata" "github.com/moond4rk/hackbrowserdata/item" - "github.com/moond4rk/hackbrowserdata/log" "github.com/moond4rk/hackbrowserdata/utils/fileutil" "github.com/moond4rk/hackbrowserdata/utils/typeutil" ) @@ -90,7 +90,7 @@ func (c *Chromium) copyItemToLocal() error { err = fileutil.CopyFile(path, filename) } if err != nil { - log.Errorf("copy %s to %s error: %v", path, filename, err) + slog.Error("copy item to local error", "path", path, "filename", filename, "err", err) continue } } diff --git a/browser/chromium/chromium_darwin.go b/browser/chromium/chromium_darwin.go index b69b599..eef9b43 100644 --- a/browser/chromium/chromium_darwin.go +++ b/browser/chromium/chromium_darwin.go @@ -7,6 +7,7 @@ import ( "crypto/sha1" "errors" "fmt" + "log/slog" "os" "os/exec" "strings" @@ -14,7 +15,6 @@ import ( "golang.org/x/crypto/pbkdf2" "github.com/moond4rk/hackbrowserdata/item" - "github.com/moond4rk/hackbrowserdata/log" ) var ( @@ -55,6 +55,6 @@ func (c *Chromium) GetMasterKey() ([]byte, error) { return nil, errWrongSecurityCommand } c.masterKey = key - log.Infof("%s initialized master key success", c.name) + slog.Info("get master key success", "browser", c.name) return key, nil } diff --git a/browser/chromium/chromium_linux.go b/browser/chromium/chromium_linux.go index e950c22..ae01d67 100644 --- a/browser/chromium/chromium_linux.go +++ b/browser/chromium/chromium_linux.go @@ -5,6 +5,7 @@ package chromium import ( "crypto/sha1" "fmt" + "log/slog" "os" "github.com/godbus/dbus/v5" @@ -12,7 +13,6 @@ import ( "golang.org/x/crypto/pbkdf2" "github.com/moond4rk/hackbrowserdata/item" - "github.com/moond4rk/hackbrowserdata/log" ) func (c *Chromium) GetMasterKey() ([]byte, error) { @@ -34,7 +34,7 @@ func (c *Chromium) GetMasterKey() ([]byte, error) { } defer func() { if err := session.Close(); err != nil { - log.Errorf("close session failed: %v", err) + slog.Error("close dbus session error", "err", err.Error()) } }() collections, err := svc.GetAllCollections() @@ -50,7 +50,7 @@ func (c *Chromium) GetMasterKey() ([]byte, error) { for _, i := range items { label, err := i.GetLabel() if err != nil { - log.Error(err) + slog.Warn("get label from dbus", "err", err.Error()) continue } if label == c.storage { @@ -71,6 +71,6 @@ func (c *Chromium) GetMasterKey() ([]byte, error) { // @https://source.chromium.org/chromium/chromium/src/+/master:components/os_crypt/os_crypt_linux.cc key := pbkdf2.Key(secret, salt, 1, 16, sha1.New) c.masterKey = key - log.Infof("%s initialized master key success", c.name) + slog.Info("get master key success", "browser", c.name) return key, nil } diff --git a/browser/chromium/chromium_windows.go b/browser/chromium/chromium_windows.go index ca41780..d0e6841 100644 --- a/browser/chromium/chromium_windows.go +++ b/browser/chromium/chromium_windows.go @@ -5,13 +5,13 @@ package chromium import ( "encoding/base64" "errors" + "log/slog" "os" "github.com/tidwall/gjson" "github.com/moond4rk/hackbrowserdata/crypto" "github.com/moond4rk/hackbrowserdata/item" - "github.com/moond4rk/hackbrowserdata/log" "github.com/moond4rk/hackbrowserdata/utils/fileutil" ) @@ -35,9 +35,9 @@ func (c *Chromium) GetMasterKey() ([]byte, error) { } c.masterKey, err = crypto.DPAPI(key[5:]) if err != nil { - log.Errorf("%s failed to decrypt master key, maybe this profile was created on a different OS installation", c.name) + slog.Error("decrypt master key failed", "err", err) return nil, err } - log.Infof("%s initialized master key success", c.name) + slog.Info("get master key success", "browser", c.name) return c.masterKey, nil } diff --git a/browsingdata/bookmark/bookmark.go b/browsingdata/bookmark/bookmark.go index 90252b6..9f6a6bc 100644 --- a/browsingdata/bookmark/bookmark.go +++ b/browsingdata/bookmark/bookmark.go @@ -2,6 +2,7 @@ package bookmark import ( "database/sql" + "log/slog" "os" "sort" "time" @@ -11,7 +12,6 @@ import ( "github.com/tidwall/gjson" "github.com/moond4rk/hackbrowserdata/item" - "github.com/moond4rk/hackbrowserdata/log" "github.com/moond4rk/hackbrowserdata/utils/fileutil" "github.com/moond4rk/hackbrowserdata/utils/typeutil" ) @@ -102,7 +102,7 @@ func (f *FirefoxBookmark) Parse(_ []byte) error { defer db.Close() _, err = db.Exec(closeJournalMode) if err != nil { - log.Error(err) + slog.Error("close journal mode error", "err", err) } rows, err := db.Query(queryFirefoxBookMark) if err != nil { @@ -115,7 +115,7 @@ func (f *FirefoxBookmark) Parse(_ []byte) error { title, url string ) if err = rows.Scan(&id, &url, &bt, &dateAdded, &title); err != nil { - log.Warn(err) + slog.Error("scan bookmark error", "err", err) } *f = append(*f, bookmark{ ID: id, diff --git a/browsingdata/browsingdata.go b/browsingdata/browsingdata.go index a0ff5ac..f38db59 100644 --- a/browsingdata/browsingdata.go +++ b/browsingdata/browsingdata.go @@ -1,7 +1,7 @@ package browsingdata import ( - "path" + "log/slog" "github.com/moond4rk/hackbrowserdata/browsingdata/bookmark" "github.com/moond4rk/hackbrowserdata/browsingdata/cookie" @@ -13,7 +13,6 @@ import ( "github.com/moond4rk/hackbrowserdata/browsingdata/password" "github.com/moond4rk/hackbrowserdata/browsingdata/sessionstorage" "github.com/moond4rk/hackbrowserdata/item" - "github.com/moond4rk/hackbrowserdata/log" "github.com/moond4rk/hackbrowserdata/utils/fileutil" ) @@ -40,7 +39,7 @@ func New(items []item.Item) *Data { func (d *Data) Recovery(masterKey []byte) error { for _, source := range d.sources { if err := source.Parse(masterKey); err != nil { - log.Errorf("parse %s error %s", source.Name(), err.Error()) + slog.Error("parse error", "source_data", source.Name(), "err", err.Error()) continue } } @@ -59,18 +58,18 @@ func (d *Data) Output(dir, browserName, flag string) { f, err := output.CreateFile(dir, filename) if err != nil { - log.Errorf("create file %s error %s", filename, err.Error()) + slog.Error("create file error", "filename", filename, "err", err.Error()) continue } if err := output.Write(source, f); err != nil { - log.Errorf("write to file %s error %s", filename, err.Error()) + slog.Error("write to file error", "filename", filename, "err", err.Error()) continue } if err := f.Close(); err != nil { - log.Errorf("close file %s error %s", filename, err.Error()) + slog.Error("close file error", "filename", filename, "err", err.Error()) continue } - log.Noticef("output to file %s success", path.Join(dir, filename)) + slog.Warn("export success", "filename", filename) } } diff --git a/browsingdata/cookie/cookie.go b/browsingdata/cookie/cookie.go index 8cf6a92..a9f3c8a 100644 --- a/browsingdata/cookie/cookie.go +++ b/browsingdata/cookie/cookie.go @@ -2,6 +2,7 @@ package cookie import ( "database/sql" + "log/slog" "os" "sort" "time" @@ -11,7 +12,6 @@ import ( "github.com/moond4rk/hackbrowserdata/crypto" "github.com/moond4rk/hackbrowserdata/item" - "github.com/moond4rk/hackbrowserdata/log" "github.com/moond4rk/hackbrowserdata/utils/typeutil" ) @@ -55,7 +55,7 @@ func (c *ChromiumCookie) Parse(masterKey []byte) error { value, encryptValue []byte ) if err = rows.Scan(&key, &encryptValue, &host, &path, &createDate, &expireDate, &isSecure, &isHTTPOnly, &hasExpire, &isPersistent); err != nil { - log.Warn(err) + slog.Error("scan chromium cookie error", "err", err) } cookie := cookie{ @@ -77,7 +77,7 @@ func (c *ChromiumCookie) Parse(masterKey []byte) error { value, err = crypto.DecryptPass(masterKey, encryptValue) } if err != nil { - log.Error(err) + slog.Error("decrypt chromium cookie error", "err", err) } } cookie.Value = string(value) @@ -123,7 +123,7 @@ func (f *FirefoxCookie) Parse(_ []byte) error { creationTime, expiry int64 ) if err = rows.Scan(&name, &value, &host, &path, &creationTime, &expiry, &isSecure, &isHTTPOnly); err != nil { - log.Warn(err) + slog.Error("scan firefox cookie error", "err", err) } *f = append(*f, cookie{ KeyName: name, diff --git a/browsingdata/creditcard/creditcard.go b/browsingdata/creditcard/creditcard.go index 4f601d1..3487b5b 100644 --- a/browsingdata/creditcard/creditcard.go +++ b/browsingdata/creditcard/creditcard.go @@ -2,6 +2,7 @@ package creditcard import ( "database/sql" + "log/slog" "os" // import sqlite3 driver @@ -9,7 +10,6 @@ import ( "github.com/moond4rk/hackbrowserdata/crypto" "github.com/moond4rk/hackbrowserdata/item" - "github.com/moond4rk/hackbrowserdata/log" ) type ChromiumCreditCard []card @@ -47,7 +47,7 @@ func (c *ChromiumCreditCard) Parse(masterKey []byte) error { value, encryptValue []byte ) if err := rows.Scan(&guid, &name, &month, &year, &encryptValue, &address, &nickname); err != nil { - log.Warn(err) + slog.Error("scan chromium credit card error", "err", err) } ccInfo := card{ GUID: guid, @@ -64,7 +64,7 @@ func (c *ChromiumCreditCard) Parse(masterKey []byte) error { value, err = crypto.DecryptPass(masterKey, encryptValue) } if err != nil { - log.Error(err) + slog.Error("decrypt chromium credit card error", "err", err) } } @@ -102,7 +102,7 @@ func (c *YandexCreditCard) Parse(masterKey []byte) error { value, encryptValue []byte ) if err := rows.Scan(&guid, &name, &month, &year, &encryptValue, &address, &nickname); err != nil { - log.Warn(err) + slog.Error("scan chromium credit card error", "err", err) } ccInfo := card{ GUID: guid, @@ -119,7 +119,7 @@ func (c *YandexCreditCard) Parse(masterKey []byte) error { value, err = crypto.DecryptPass(masterKey, encryptValue) } if err != nil { - log.Error(err) + slog.Error("decrypt chromium credit card error", "err", err) } } ccInfo.CardNumber = string(value) diff --git a/browsingdata/download/download.go b/browsingdata/download/download.go index 8c6807b..2bb1c3a 100644 --- a/browsingdata/download/download.go +++ b/browsingdata/download/download.go @@ -2,6 +2,7 @@ package download import ( "database/sql" + "log/slog" "os" "sort" "strings" @@ -12,7 +13,6 @@ import ( "github.com/tidwall/gjson" "github.com/moond4rk/hackbrowserdata/item" - "github.com/moond4rk/hackbrowserdata/log" "github.com/moond4rk/hackbrowserdata/utils/typeutil" ) @@ -49,7 +49,7 @@ func (c *ChromiumDownload) Parse(_ []byte) error { totalBytes, startTime, endTime int64 ) if err := rows.Scan(&targetPath, &tabURL, &totalBytes, &startTime, &endTime, &mimeType); err != nil { - log.Warn(err) + slog.Warn("scan chromium download error", "err", err) } data := download{ TargetPath: targetPath, @@ -92,7 +92,7 @@ func (f *FirefoxDownload) Parse(_ []byte) error { _, err = db.Exec(closeJournalMode) if err != nil { - log.Error(err) + slog.Error("close journal mode error", "err", err) } rows, err := db.Query(queryFirefoxDownload) if err != nil { @@ -105,7 +105,7 @@ func (f *FirefoxDownload) Parse(_ []byte) error { placeID, dateAdded int64 ) if err = rows.Scan(&placeID, &content, &url, &dateAdded); err != nil { - log.Warn(err) + slog.Warn("scan firefox download error", "err", err) } contentList := strings.Split(content, ",{") if len(contentList) > 1 { diff --git a/browsingdata/history/history.go b/browsingdata/history/history.go index f543fbd..d881e1c 100644 --- a/browsingdata/history/history.go +++ b/browsingdata/history/history.go @@ -2,6 +2,7 @@ package history import ( "database/sql" + "log/slog" "os" "sort" "time" @@ -10,7 +11,6 @@ import ( _ "github.com/mattn/go-sqlite3" "github.com/moond4rk/hackbrowserdata/item" - "github.com/moond4rk/hackbrowserdata/log" "github.com/moond4rk/hackbrowserdata/utils/typeutil" ) @@ -47,7 +47,7 @@ func (c *ChromiumHistory) Parse(_ []byte) error { lastVisitTime int64 ) if err := rows.Scan(&url, &title, &visitCount, &lastVisitTime); err != nil { - log.Warn(err) + slog.Warn("scan chromium history error", "err", err) } data := history{ URL: url, @@ -103,7 +103,7 @@ func (f *FirefoxHistory) Parse(_ []byte) error { visitCount int ) if err = rows.Scan(&id, &url, &visitDate, &title, &visitCount); err != nil { - log.Warn(err) + slog.Error("scan firefox history error", "err", err) } *f = append(*f, history{ Title: title, diff --git a/browsingdata/localstorage/localstorage.go b/browsingdata/localstorage/localstorage.go index 1fe3a6b..2652ebc 100644 --- a/browsingdata/localstorage/localstorage.go +++ b/browsingdata/localstorage/localstorage.go @@ -4,6 +4,7 @@ import ( "bytes" "database/sql" "fmt" + "log/slog" "os" "strings" @@ -12,7 +13,6 @@ import ( "golang.org/x/text/transform" "github.com/moond4rk/hackbrowserdata/item" - "github.com/moond4rk/hackbrowserdata/log" "github.com/moond4rk/hackbrowserdata/utils/byteutil" "github.com/moond4rk/hackbrowserdata/utils/typeutil" ) @@ -115,7 +115,7 @@ func (f *FirefoxLocalStorage) Parse(_ []byte) error { _, err = db.Exec(closeJournalMode) if err != nil { - log.Error(err) + slog.Error("close journal mode error", "err", err) } rows, err := db.Query(queryLocalStorage) if err != nil { @@ -125,7 +125,7 @@ func (f *FirefoxLocalStorage) Parse(_ []byte) error { for rows.Next() { var originKey, key, value string if err = rows.Scan(&originKey, &key, &value); err != nil { - log.Warn(err) + slog.Error("scan firefox local storage error", "err", err) } s := new(storage) s.fillFirefox(originKey, key, value) diff --git a/browsingdata/password/password.go b/browsingdata/password/password.go index 47dffee..e6277d8 100644 --- a/browsingdata/password/password.go +++ b/browsingdata/password/password.go @@ -4,6 +4,7 @@ import ( "bytes" "database/sql" "encoding/base64" + "log/slog" "os" "sort" "time" @@ -14,7 +15,6 @@ import ( "github.com/moond4rk/hackbrowserdata/crypto" "github.com/moond4rk/hackbrowserdata/item" - "github.com/moond4rk/hackbrowserdata/log" "github.com/moond4rk/hackbrowserdata/utils/typeutil" ) @@ -54,7 +54,7 @@ func (c *ChromiumPassword) Parse(masterKey []byte) error { create int64 ) if err := rows.Scan(&url, &username, &pwd, &create); err != nil { - log.Warn(err) + slog.Error("scan chromium password error", "err", err) } login := loginData{ UserName: username, @@ -68,7 +68,7 @@ func (c *ChromiumPassword) Parse(masterKey []byte) error { password, err = crypto.DecryptPass(masterKey, pwd) } if err != nil { - log.Error(err) + slog.Error("decrypt chromium password error", "err", err) } } if create > time.Now().Unix() { @@ -121,7 +121,7 @@ func (c *YandexPassword) Parse(masterKey []byte) error { create int64 ) if err := rows.Scan(&url, &username, &pwd, &create); err != nil { - log.Warn(err) + slog.Error("scan yandex password error", "err", err) } login := loginData{ UserName: username, @@ -136,7 +136,7 @@ func (c *YandexPassword) Parse(masterKey []byte) error { password, err = crypto.DecryptPass(masterKey, pwd) } if err != nil { - log.Errorf("decrypt yandex password error %s", err) + slog.Error("decrypt yandex password error", "err", err) } } if create > time.Now().Unix() { diff --git a/browsingdata/sessionstorage/sessionstorage.go b/browsingdata/sessionstorage/sessionstorage.go index 79dd356..ad694e2 100644 --- a/browsingdata/sessionstorage/sessionstorage.go +++ b/browsingdata/sessionstorage/sessionstorage.go @@ -4,6 +4,7 @@ import ( "bytes" "database/sql" "fmt" + "log/slog" "os" "strings" @@ -12,7 +13,6 @@ import ( "golang.org/x/text/transform" "github.com/moond4rk/hackbrowserdata/item" - "github.com/moond4rk/hackbrowserdata/log" "github.com/moond4rk/hackbrowserdata/utils/byteutil" "github.com/moond4rk/hackbrowserdata/utils/typeutil" ) @@ -123,7 +123,7 @@ func (f *FirefoxSessionStorage) Parse(_ []byte) error { _, err = db.Exec(closeJournalMode) if err != nil { - log.Error(err) + slog.Error("close journal mode error", "err", err) } rows, err := db.Query(querySessionStorage) if err != nil { @@ -133,7 +133,7 @@ func (f *FirefoxSessionStorage) Parse(_ []byte) error { for rows.Next() { var originKey, key, value string if err = rows.Scan(&originKey, &key, &value); err != nil { - log.Warn(err) + slog.Error("scan session storage error", "err", err) } s := new(session) s.fillFirefox(originKey, key, value) diff --git a/cmd/hack-browser-data/main.go b/cmd/hack-browser-data/main.go index fad33de..52e435a 100644 --- a/cmd/hack-browser-data/main.go +++ b/cmd/hack-browser-data/main.go @@ -1,12 +1,13 @@ package main import ( + "log/slog" "os" "github.com/urfave/cli/v2" "github.com/moond4rk/hackbrowserdata/browser" - "github.com/moond4rk/hackbrowserdata/log" + "github.com/moond4rk/hackbrowserdata/logger" "github.com/moond4rk/hackbrowserdata/utils/fileutil" ) @@ -42,17 +43,18 @@ func Execute() { HideHelpCommand: true, Action: func(c *cli.Context) error { if verbose { - log.SetVerbose() + logger.Default.SetVerbose() + logger.Configure(logger.Default) } browsers, err := browser.PickBrowsers(browserName, profilePath) if err != nil { - log.Error(err) + slog.Error("pick browsers error", "err", err) } for _, b := range browsers { data, err := b.BrowsingData(isFullExport) if err != nil { - log.Error(err) + slog.Error("get browsing data error", "err", err) continue } data.Output(outputDir, b.Name(), outputFormat) @@ -60,9 +62,9 @@ func Execute() { if compress { if err = fileutil.CompressDir(outputDir); err != nil { - log.Error(err) + slog.Error("compress error: ", "err", err) } - log.Noticef("compress success") + slog.Info("compress success") } return nil }, diff --git a/go.mod b/go.mod index a344341..cf65553 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.21 require ( github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a github.com/godbus/dbus/v5 v5.1.0 - github.com/gookit/slog v0.5.4 github.com/mattn/go-sqlite3 v1.14.19 github.com/otiai10/copy v1.14.0 github.com/ppacher/go-dbus-keyring v1.0.1 @@ -14,7 +13,6 @@ require ( github.com/tidwall/gjson v1.17.0 github.com/urfave/cli/v2 v2.27.1 golang.org/x/crypto v0.18.0 - golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc golang.org/x/text v0.14.0 ) @@ -22,15 +20,10 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect - github.com/gookit/color v1.5.4 // indirect - github.com/gookit/goutil v0.6.12 // indirect - github.com/gookit/gsr v0.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.16.0 // indirect diff --git a/go.sum b/go.sum index efefcac..195d518 100644 --- a/go.sum +++ b/go.sum @@ -11,14 +11,6 @@ github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= -github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= -github.com/gookit/goutil v0.6.12 h1:73vPUcTtVGXbhSzBOFcnSB1aJl7Jq9np3RAE50yIDZc= -github.com/gookit/goutil v0.6.12/go.mod h1:g6krlFib8xSe3G1h02IETowOtrUGpAmetT8IevDpvpM= -github.com/gookit/gsr v0.1.0 h1:0gadWaYGU4phMs0bma38t+Do5OZowRMEVlHv31p0Zig= -github.com/gookit/gsr v0.1.0/go.mod h1:7wv4Y4WCnil8+DlDYHBjidzrEzfHhXEoFjEA0pPPWpI= -github.com/gookit/slog v0.5.4 h1:EMctf/kap/SR8cnhkUucL0D3YZwUAJJ+WKQ/DN6kS5s= -github.com/gookit/slog v0.5.4/go.mod h1:awroa12zroMvjFpS7tdpTX12AqIzVewUlC10tsj4TYY= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= @@ -50,16 +42,10 @@ github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= -github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM= -golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= diff --git a/log/log.go b/log/log.go deleted file mode 100644 index 4bd48fe..0000000 --- a/log/log.go +++ /dev/null @@ -1,119 +0,0 @@ -package log - -import ( - "os" - - "github.com/gookit/slog" -) - -var std = &slog.SugaredLogger{} - -func init() { - std = newStdLogger(slog.NoticeLevel) -} - -// SetVerbose set log level to debug -func SetVerbose() { - std = newStdLogger(slog.DebugLevel) -} - -const template = "[{{level}}] [{{caller}}] {{message}} {{data}} {{extra}}\n" - -// newStdLogger is a new std logger -func newStdLogger(level slog.Level) *slog.SugaredLogger { - return slog.NewSugaredLogger(os.Stdout, level).Config(func(sl *slog.SugaredLogger) { - sl.SetName("stdLogger") - sl.ReportCaller = true - sl.CallerSkip = 7 - // auto enable console color - sl.Formatter.(*slog.TextFormatter).EnableColor = false - sl.Formatter.(*slog.TextFormatter).SetTemplate(template) - }) -} - -// Trace logs a message at level Trace -func Trace(args ...interface{}) { - std.Log(slog.TraceLevel, args...) -} - -// Tracef logs a message at level Trace -func Tracef(format string, args ...interface{}) { - std.Logf(slog.TraceLevel, format, args...) -} - -// Info logs a message at level Info -func Info(args ...interface{}) { - std.Log(slog.InfoLevel, args...) -} - -// Infof logs a message at level Info -func Infof(format string, args ...interface{}) { - std.Logf(slog.InfoLevel, format, args...) -} - -// Notice logs a message at level Notice -func Notice(args ...interface{}) { - std.Log(slog.NoticeLevel, args...) -} - -// Noticef logs a message at level Notice -func Noticef(format string, args ...interface{}) { - std.Logf(slog.NoticeLevel, format, args...) -} - -// Warn logs a message at level Warn -func Warn(args ...interface{}) { - std.Log(slog.WarnLevel, args...) -} - -// Warnf logs a message at level Warn -func Warnf(format string, args ...interface{}) { - std.Logf(slog.WarnLevel, format, args...) -} - -// Error logs a message at level Error -func Error(args ...interface{}) { - std.Log(slog.ErrorLevel, args...) -} - -// ErrorT logs a error type at level Error -func ErrorT(err error) { - if err != nil { - std.Log(slog.ErrorLevel, err) - } -} - -// Errorf logs a message at level Error -func Errorf(format string, args ...interface{}) { - std.Logf(slog.ErrorLevel, format, args...) -} - -// Debug logs a message at level Debug -func Debug(args ...interface{}) { - std.Log(slog.DebugLevel, args...) -} - -// Debugf logs a message at level Debug -func Debugf(format string, args ...interface{}) { - std.Logf(slog.DebugLevel, format, args...) -} - -// Fatal logs a message at level Fatal -func Fatal(args ...interface{}) { - std.Log(slog.FatalLevel, args...) -} - -// Fatalf logs a message at level Fatal -func Fatalf(format string, args ...interface{}) { - std.Logf(slog.FatalLevel, format, args...) -} - -// Panic logs a message at level Panic -func Panic(args ...interface{}) { - std.Log(slog.PanicLevel, args...) -} - -// Panicf logs a message at level Panic -func Panicf(format string, args ...interface{}) { - std.Logf(slog.PanicLevel, format, args...) -} diff --git a/logger/handler.go b/logger/handler.go new file mode 100644 index 0000000..ee76e97 --- /dev/null +++ b/logger/handler.go @@ -0,0 +1,63 @@ +package logger + +import ( + "context" + "log/slog" + "os" +) + +var _ slog.Handler = (*LogHandler)(nil) + +// LogHandler is a slog.Handler implementation that can be used to log to a file. +type LogHandler struct { + handler slog.Handler +} + +func NewHandler(logger *Logger) LogHandler { + if logger == nil { + logger = Default + } + + level := logger.Level + if logger.IsVerbose { + level = slog.LevelDebug + } + + output := logger.Output + if output == nil { + output = os.Stderr + } + + handlerOptions := &slog.HandlerOptions{ + AddSource: logger.AddSource, + Level: level, + ReplaceAttr: logger.ReplaceAttr, + } + + if logger.IsJSONHandler { + return LogHandler{ + handler: slog.NewJSONHandler(output, handlerOptions), + } + } + return LogHandler{ + handler: slog.NewTextHandler(output, handlerOptions), + } +} + +var _ slog.Handler = (*LogHandler)(nil) + +func (t LogHandler) Handle(ctx context.Context, r slog.Record) error { + return t.handler.Handle(ctx, r) +} + +func (t LogHandler) Enabled(ctx context.Context, l slog.Level) bool { + return t.handler.Enabled(ctx, l) +} + +func (t LogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + return LogHandler{handler: t.handler.WithAttrs(attrs)} +} + +func (t LogHandler) WithGroup(name string) slog.Handler { + return LogHandler{handler: t.handler.WithGroup(name)} +} diff --git a/logger/logger.go b/logger/logger.go new file mode 100644 index 0000000..c403272 --- /dev/null +++ b/logger/logger.go @@ -0,0 +1,106 @@ +package logger + +import ( + "io" + "log/slog" + "os" + "path/filepath" +) + +// Default is the default *Logger for the default handler. +var Default = &Logger{ + AddSource: true, + IsVerbose: false, + IsJSONHandler: true, + Level: slog.LevelWarn, + ReplaceAttr: defaultReplaceAttrFunc, + Output: os.Stderr, +} + +func init() { + Configure(Default) +} + +// Configure configures the logger by the given options. +func Configure(opts *Logger) { + customHandler := NewHandler(opts) + slog.SetDefault(slog.New(customHandler)) +} + +type Logger struct { + // AddSource indicates whether to add source code location to the log. + AddSource bool + + // IsVerbose indicates whether to enable verbose mode. If true, debug level will be enabled. + // If false, only warn and error level will be enabled. + IsVerbose bool + + // Level indicates the log level of the handler. If IsVerbose is true, Level will be slog.LevelDebug. + Level slog.Level + + // IsJSONHandler indicates whether to use JSON format for log output. + IsJSONHandler bool + + // ReplaceAttr is a function that can be used to replace the value of an attribute. + ReplaceAttr func(groups []string, a slog.Attr) slog.Attr + + // Output is the writer to write the log to. If nil, os.Stderr will be used. + Output io.Writer +} + +func (o *Logger) clone() *Logger { + return &Logger{ + AddSource: o.AddSource, + IsVerbose: o.IsVerbose, + IsJSONHandler: o.IsJSONHandler, + ReplaceAttr: o.ReplaceAttr, + Output: o.Output, + } +} + +// SetMaxLevel sets the max logging level for logger, default is slog.LevelWarn. +// if IsVerbose is true, level will be slog.LevelDebug. +func (o *Logger) SetMaxLevel(level slog.Level) { + o.Level = level +} + +func (o *Logger) SetJSONHandler() { + o.IsJSONHandler = true +} + +func (o *Logger) SetTextHandler() { + o.IsJSONHandler = false +} + +func (o *Logger) SetOutput(output io.Writer) { + o.Output = output +} + +func (o *Logger) SetVerbose() { + o.IsVerbose = true + o.Level = slog.LevelDebug +} + +func (o *Logger) SetReplaceAttrFunc(replaceAttrFunc func(groups []string, a slog.Attr) slog.Attr) { + o.ReplaceAttr = replaceAttrFunc +} + +// defaultReplaceAttrFunc is a function that can be used to replace the value of an attribute. +// remove time key and source prefix +var defaultReplaceAttrFunc = func(groups []string, a slog.Attr) slog.Attr { + // Remove time attributes from the log. + if a.Key == slog.TimeKey { + return slog.Attr{} + } + // Remove source filepath prefix attributes from the log. + if a.Key == slog.SourceKey { + source, ok := a.Value.Any().(*slog.Source) + if !ok { + return slog.Attr{} + } + if source != nil { + source.File = filepath.Base(source.File) + } + } + return a +} diff --git a/logger/logger_test.go b/logger/logger_test.go new file mode 100644 index 0000000..7409d07 --- /dev/null +++ b/logger/logger_test.go @@ -0,0 +1,68 @@ +package logger + +import ( + "bytes" + "log/slog" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConfigure(t *testing.T) { + asserts := assert.New(t) + buf := new(bytes.Buffer) + + opts := &Logger{ + AddSource: true, + IsVerbose: false, + IsJSONHandler: true, + Output: buf, + } + Configure(opts) + + slog.Warn("test message") + + output := buf.String() + asserts.Contains(output, "test message", "Log output should contain the test message") +} + +func TestSetVerbose(t *testing.T) { + asserts := assert.New(t) + buf := new(bytes.Buffer) + + logger := Default.clone() + logger.SetVerbose() + logger.SetOutput(buf) + Configure(logger) + + slog.Debug("verbose test") + + output := buf.String() + asserts.Contains(output, "verbose test", "Verbose mode should enable debug level logs") +} + +func TestLogger_SetJSONHandler(t *testing.T) { + asserts := assert.New(t) + + logger := Default.clone() + logger.SetJSONHandler() + asserts.True(logger.IsJSONHandler, "IsJSONHandler should be true") +} + +func TestOptionsClone(t *testing.T) { + asserts := assert.New(t) + + original := &Logger{ + AddSource: true, + IsVerbose: true, + IsJSONHandler: true, + Output: os.Stdout, + } + cloned := original.clone() + + asserts.Equal(original.AddSource, cloned.AddSource, "AddSource should be equal") + asserts.Equal(original.IsVerbose, cloned.IsVerbose, "IsVerbose should be equal") + asserts.Equal(original.IsJSONHandler, cloned.IsJSONHandler, "IsJSONHandler should be equal") + asserts.Equal(original.Output, cloned.Output, "Output should be equal") +} diff --git a/utils/typeutil/typeutil.go b/utils/typeutil/typeutil.go index 8ac96a7..58c9c0b 100644 --- a/utils/typeutil/typeutil.go +++ b/utils/typeutil/typeutil.go @@ -2,8 +2,6 @@ package typeutil import ( "time" - - "golang.org/x/exp/constraints" ) // Keys returns a slice of the keys of the map. based with go 1.18 generics @@ -15,7 +13,14 @@ func Keys[K comparable, V any](m map[K]V) []K { return r } -func IntToBool[T constraints.Signed](a T) bool { +// Signed is a constraint that permits any signed integer type. +// If future releases of Go add new predeclared signed integer types, +// this constraint will be modified to include them. +type Signed interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 +} + +func IntToBool[T Signed](a T) bool { switch a { case 0, -1: return false