chore: downgrade golang version to 1.20, support Windows 7. (#435)

* chore: downgrade golang version to 1.20, support windows 7

* chore: Update dependencies for Go project.

- Update dependencies in go.sum
- Improvements and optimizations in various files
- Bug fixes and error handling enhancements

* chore: Update modernc.org/sqlite library versions in go.mod and go.sum files

- Update version of `modernc.org/sqlite` to `v1.31.1` in `go.mod` and `go.sum` files
- Update module hash in `go.sum` file for `modernc.org/sqlite`
- Ensure consistency between `go.mod` and `go.sum` files in relation to `modernc.org/sqlite` version

* chore: replace log/slog with standard logger (#436)

* chore: replace log/slog with standard logger

* chore: Update Go dependencies and versions

- Update Go version from `1.22.5` to `1.20` and other dependencies
- Update critical dependencies to latest versions
- Ensure compatibility with new versions of dependencies

* chore: Optimize dependency management in workflows

- Update build and lint workflows to use `go mod tidy` for getting dependencies
- Change modules download mode to `'mod'` in linters configuration
- Add step to get dependencies in lint workflow

* refactor: Update dependencies and refactor Chromium key deletion logic

- Update `modernc.org/sqlite` to `v1.31.1` in `go.mod` and `go.sum`
- Increase version number to `0.5.0` in `cmd/hack-browser-data/main.go`
- Refactor and update logic for filtering and copying items in `browser/chromium/chromium.go`

* Improve logging functionality and data type conversion

- Add `String()` method to `DataType` enum in types.go
- Update log level to Debug in logger_test.go
- Set log level to Debug in `TestLoggerDebug` and `TestLoggerDebugf` functions
This commit is contained in:
ᴍᴏᴏɴD4ʀᴋ
2024-10-22 11:13:06 +08:00
parent 31591411e4
commit ad020cf135
33 changed files with 695 additions and 364 deletions
+37
View File
@@ -0,0 +1,37 @@
package level
// Level defines all the available levels we can log at
type Level int32
const (
// DebugLevel is the lowest level of logging.
// Debug logs are intended for debugging and development purposes.
DebugLevel Level = iota + 1
// WarnLevel is used for undesired but relatively expected events,
// which may indicate a problem.
WarnLevel
// ErrorLevel is used for undesired and unexpected events that
// the program can recover from.
ErrorLevel
// FatalLevel is used for undesired and unexpected events that
// the program cannot recover from.
FatalLevel
)
func (l Level) String() string {
switch l {
case DebugLevel:
return "DEBUG"
case WarnLevel:
return "WARN"
case ErrorLevel:
return "ERROR"
case FatalLevel:
return "FATAL"
default:
return "UNKNOWN"
}
}
+46
View File
@@ -0,0 +1,46 @@
package log
import (
"github.com/moond4rk/hackbrowserdata/log/level"
)
var (
// defaultLogger is the default logger used by the package-level functions.
defaultLogger = NewLogger(nil)
)
func SetVerbose() {
defaultLogger.SetLevel(level.DebugLevel)
}
func Debug(args ...any) {
defaultLogger.Debug(args...)
}
func Debugf(format string, args ...any) {
defaultLogger.Debugf(format, args...)
}
func Warn(args ...any) {
defaultLogger.Warn(args...)
}
func Warnf(format string, args ...any) {
defaultLogger.Warnf(format, args...)
}
func Error(args ...any) {
defaultLogger.Error(args...)
}
func Errorf(format string, args ...any) {
defaultLogger.Errorf(format, args...)
}
func Fatal(args ...any) {
defaultLogger.Fatal(args...)
}
func Fatalf(format string, args ...any) {
defaultLogger.Fatalf(format, args...)
}
+182
View File
@@ -0,0 +1,182 @@
package log
import (
"fmt"
"io"
stdlog "log"
"os"
"runtime"
"strings"
"sync/atomic"
"github.com/moond4rk/hackbrowserdata/log/level"
)
// NewLogger creates and returns a new instance of Logger.
// Log level is set to DebugLevel by default.
func NewLogger(base Base) *Logger {
if base == nil {
base = newBase(os.Stderr)
}
return &Logger{base: base, minLevel: level.WarnLevel}
}
// Logger logs message to io.Writer at various log levels.
type Logger struct {
base Base
// Minimum log level for this logger.
// Message with level lower than this level won't be outputted.
minLevel level.Level
}
// canLogAt reports whether logger can log at level v.
func (l *Logger) canLogAt(v level.Level) bool {
return v >= level.Level(atomic.LoadInt32((*int32)(&l.minLevel)))
}
// SetLevel sets the logger level.
// It panics if v is less than DebugLevel or greater than FatalLevel.
func (l *Logger) SetLevel(v level.Level) {
if v < level.DebugLevel || v > level.FatalLevel {
panic("log: invalid log level")
}
atomic.StoreInt32((*int32)(&l.minLevel), int32(v))
}
func (l *Logger) Debug(args ...any) {
if !l.canLogAt(level.DebugLevel) {
return
}
l.base.Debug(args...)
}
func (l *Logger) Warn(args ...any) {
if !l.canLogAt(level.WarnLevel) {
return
}
l.base.Warn(args...)
}
func (l *Logger) Error(args ...any) {
if !l.canLogAt(level.ErrorLevel) {
return
}
l.base.Error(args...)
}
func (l *Logger) Fatal(args ...any) {
if !l.canLogAt(level.FatalLevel) {
return
}
l.base.Fatal(args...)
}
func (l *Logger) Debugf(format string, args ...any) {
if !l.canLogAt(level.DebugLevel) {
return
}
l.base.Debug(fmt.Sprintf(format, args...))
}
func (l *Logger) Warnf(format string, args ...any) {
if !l.canLogAt(level.WarnLevel) {
return
}
l.base.Warn(fmt.Sprintf(format, args...))
}
func (l *Logger) Errorf(format string, args ...any) {
if !l.canLogAt(level.ErrorLevel) {
return
}
l.base.Error(fmt.Sprintf(format, args...))
}
func (l *Logger) Fatalf(format string, args ...any) {
if !l.canLogAt(level.FatalLevel) {
return
}
l.base.Fatal(fmt.Sprintf(format, args...))
}
type Base interface {
Debug(args ...any)
Warn(args ...any)
Error(args ...any)
Fatal(args ...any)
}
// baseLogger is a wrapper object around log.Logger from the standard library.
// It supports logging at various log levels.
type baseLogger struct {
*stdlog.Logger
callDepth int
}
func newBase(out io.Writer) *baseLogger {
prefix := "[hack-browser-data] "
base := &baseLogger{
Logger: stdlog.New(out, prefix, stdlog.Lshortfile),
}
base.callDepth = base.calculateCallDepth()
return base
}
// calculateCallDepth returns the call depth for the logger.
func (l *baseLogger) calculateCallDepth() int {
return l.getCallDepth()
}
func (l *baseLogger) prefixPrint(prefix string, args ...any) {
args = append([]any{prefix}, args...)
if err := l.Output(l.callDepth, fmt.Sprint(args...)); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "log output error: %v\n", err)
}
}
func (l *baseLogger) getCallDepth() int {
var defaultCallDepth = 2
pcs := make([]uintptr, 10)
n := runtime.Callers(defaultCallDepth, pcs)
frames := runtime.CallersFrames(pcs[:n])
for i := 0; i < n; i++ {
frame, more := frames.Next()
if !l.isLoggerPackage(frame.Function) {
return i + 1
}
if !more {
break
}
}
return defaultCallDepth
}
func (l *baseLogger) isLoggerPackage(funcName string) bool {
const loggerFuncName = "hackbrowserdata/log"
return strings.Contains(funcName, loggerFuncName)
}
// Debug logs a message at Debug level.
func (l *baseLogger) Debug(args ...any) {
l.prefixPrint("DEBUG: ", args...)
}
// Warn logs a message at Warning level.
func (l *baseLogger) Warn(args ...any) {
l.prefixPrint("WARN: ", args...)
}
// Error logs a message at Error level.
func (l *baseLogger) Error(args ...any) {
l.prefixPrint("ERROR: ", args...)
}
var osExit = os.Exit
// Fatal logs a message at Fatal level
// and process will exit with status set to 1.
func (l *baseLogger) Fatal(args ...any) {
l.prefixPrint("FATAL: ", args...)
osExit(1)
}
+262
View File
@@ -0,0 +1,262 @@
package log
import (
"bytes"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
level2 "github.com/moond4rk/hackbrowserdata/log/level"
)
const (
pattern = `^\[hack\-browser\-data] \w+\.go:\d+:`
)
type baseTestCase struct {
description string
message string
suffix string
level level2.Level
wantedPattern string
}
var (
baseTestCases = []baseTestCase{
{
description: "without trailing newline, logger adds newline",
message: "hello, hacker!",
suffix: "",
},
{
description: "with trailing newline, logger preserves newline",
message: "hello, hacker!",
suffix: "\n",
},
}
)
func TestLoggerDebug(t *testing.T) {
for _, tc := range baseTestCases {
tc := tc
tc.level = level2.DebugLevel
message := tc.message + tc.suffix
tc.wantedPattern = fmt.Sprintf("%s %s: %s\n$", pattern, tc.level, tc.message)
t.Run(tc.description, func(t *testing.T) {
var buf bytes.Buffer
logger := NewLogger(newBase(&buf))
logger.SetLevel(level2.DebugLevel)
logger.Debug(message)
got := buf.String()
assert.Regexp(t, tc.wantedPattern, got)
})
}
}
func TestLoggerWarn(t *testing.T) {
for _, tc := range baseTestCases {
tc := tc
tc.level = level2.WarnLevel
message := tc.message + tc.suffix
tc.wantedPattern = fmt.Sprintf("%s %s: %s\n$", pattern, tc.level, tc.message)
t.Run(tc.description, func(t *testing.T) {
var buf bytes.Buffer
logger := NewLogger(newBase(&buf))
logger.Warn(message)
got := buf.String()
assert.Regexp(t, tc.wantedPattern, got)
})
}
}
func TestLoggerError(t *testing.T) {
for _, tc := range baseTestCases {
tc := tc
tc.level = level2.ErrorLevel
message := tc.message + tc.suffix
tc.wantedPattern = fmt.Sprintf("%s %s: %s\n$", pattern, tc.level, tc.message)
t.Run(tc.description, func(t *testing.T) {
var buf bytes.Buffer
logger := NewLogger(newBase(&buf))
logger.Error(message)
got := buf.String()
assert.Regexp(t, tc.wantedPattern, got)
})
}
}
func TestLoggerFatal(t *testing.T) {
originalOsExit := osExit
defer func() { osExit = originalOsExit }()
for _, tc := range baseTestCases {
tc := tc
tc.level = level2.FatalLevel
message := tc.message + tc.suffix
tc.wantedPattern = fmt.Sprintf("%s %s: %s\n$", pattern, tc.level, tc.message)
t.Run(tc.description, func(t *testing.T) {
var buf bytes.Buffer
exitCalled := false
exitCode := 0
osExit = func(code int) {
exitCalled = true
exitCode = code
}
logger := NewLogger(newBase(&buf))
logger.Fatal(message)
got := buf.String()
assert.Regexp(t, tc.wantedPattern, got)
assert.True(t, exitCalled)
assert.Equal(t, 1, exitCode)
})
}
}
type formatTestCase struct {
description string
format string
args []interface{}
level level2.Level
wantedPattern string
}
var (
formatTestCases = []formatTestCase{
{
description: "message with format prefix",
format: "hello, %s!",
args: []any{"Hacker"},
},
{
description: "message with format prefix",
format: "hello, %d,%d,%d!",
args: []any{1, 2, 3},
},
{
description: "message with format prefix",
format: "hello, %s,%d,%d!",
args: []any{"Hacker", 2, 3},
},
}
)
func TestLoggerDebugf(t *testing.T) {
for _, tc := range formatTestCases {
tc := tc
tc.level = level2.DebugLevel
message := fmt.Sprintf(tc.format, tc.args...)
tc.wantedPattern = fmt.Sprintf("%s %s: %s\n$", pattern, tc.level, message)
t.Run(tc.description, func(t *testing.T) {
var buf bytes.Buffer
logger := NewLogger(newBase(&buf))
logger.SetLevel(level2.DebugLevel)
logger.Debugf(tc.format, tc.args...)
got := buf.String()
assert.Regexp(t, tc.wantedPattern, got)
})
}
}
func TestLoggerWarnf(t *testing.T) {
for _, tc := range formatTestCases {
tc := tc
tc.level = level2.WarnLevel
message := fmt.Sprintf(tc.format, tc.args...)
tc.wantedPattern = fmt.Sprintf("%s %s: %s\n$", pattern, tc.level, message)
t.Run(tc.description, func(t *testing.T) {
var buf bytes.Buffer
logger := NewLogger(newBase(&buf))
logger.Warnf(tc.format, tc.args...)
got := buf.String()
assert.Regexp(t, tc.wantedPattern, got)
})
}
}
func TestLoggerErrorf(t *testing.T) {
for _, tc := range formatTestCases {
tc := tc
tc.level = level2.ErrorLevel
message := fmt.Sprintf(tc.format, tc.args...)
tc.wantedPattern = fmt.Sprintf("%s %s: %s\n$", pattern, tc.level, message)
t.Run(tc.description, func(t *testing.T) {
var buf bytes.Buffer
logger := NewLogger(newBase(&buf))
logger.Errorf(tc.format, tc.args...)
got := buf.String()
assert.Regexp(t, tc.wantedPattern, got)
})
}
}
func TestLoggerFatalf(t *testing.T) {
originalOsExit := osExit
defer func() { osExit = originalOsExit }()
for _, tc := range formatTestCases {
tc := tc
tc.level = level2.FatalLevel
message := fmt.Sprintf(tc.format, tc.args...)
tc.wantedPattern = fmt.Sprintf("%s %s: %s\n$", pattern, tc.level, message)
t.Run(tc.description, func(t *testing.T) {
var buf bytes.Buffer
exitCalled := false
exitCode := 0
osExit = func(code int) {
exitCalled = true
exitCode = code
}
logger := NewLogger(newBase(&buf))
logger.Fatalf(tc.format, tc.args...)
got := buf.String()
assert.Regexp(t, tc.wantedPattern, got)
assert.True(t, exitCalled)
assert.Equal(t, 1, exitCode)
})
}
}
func TestLoggerWithLowerLevels(t *testing.T) {
// Logger should not log messages at a level
// lower than the specified level.
levels := []level2.Level{level2.DebugLevel, level2.WarnLevel, level2.ErrorLevel, level2.FatalLevel}
ops := []struct {
op string
level level2.Level
logFunc func(*Logger)
expected bool
}{
{"Debug", level2.DebugLevel, func(l *Logger) { l.Debug("hello") }, false},
{"Warn", level2.WarnLevel, func(l *Logger) { l.Warn("hello") }, false},
{"Error", level2.ErrorLevel, func(l *Logger) { l.Error("hello") }, false},
{"Fatal", level2.FatalLevel, func(l *Logger) { l.Fatal("hello") }, false},
}
for _, setLevel := range levels {
for _, op := range ops {
var buf bytes.Buffer
logger := NewLogger(newBase(&buf))
logger.SetLevel(setLevel)
expectedOutput := op.level >= setLevel
exitCalled := false
exitCode := 0
osExit = func(code int) {
exitCalled = true
exitCode = code
}
op.logFunc(logger)
output := buf.String()
if expectedOutput {
assert.NotEmpty(t, output)
} else {
assert.Empty(t, output)
}
if op.op == "Fatal" {
assert.True(t, exitCalled)
assert.Equal(t, 1, exitCode)
}
}
}
}