Browse Source

Merge branch 'dev' into feature/assets

feature/assets
Dnomd343 5 months ago
parent
commit
735b12f5b7
  1. 2
      assets/assets.go
  2. 2
      assets/extract.go
  3. 0
      assets/extract_test.go
  4. 2
      assets/remote.go
  5. 2
      assets/update.go
  6. 20
      go.mod
  7. 30
      go.sum
  8. 52
      logger/encoder.go
  9. 99
      logger/encoder_test.go
  10. 65
      logger/interface.go
  11. 101
      logger/interface_test.go
  12. 95
      logger/logger.go
  13. 47
      logger/logger_test.go
  14. 2
      main.go
  15. 114
      mocks/PrimitiveArrayEncoder.go
  16. 59
      next/logger/interface.go
  17. 78
      next/logger/logger.go

2
next/assets/assets.go → assets/assets.go

@ -1,7 +1,7 @@
package assets
import (
"XProxy/next/logger"
"XProxy/logger"
"github.com/robfig/cron"
urlpkg "net/url"
"os"

2
next/assets/extract.go → assets/extract.go

@ -1,7 +1,7 @@
package assets
import (
"XProxy/next/logger"
"XProxy/logger"
"bytes"
"compress/bzip2"
"github.com/gabriel-vasile/mimetype"

0
next/assets/extract_test.go → assets/extract_test.go

2
next/assets/remote.go → assets/remote.go

@ -1,7 +1,7 @@
package assets
import (
"XProxy/next/logger"
"XProxy/logger"
"bytes"
"errors"
"github.com/andybalholm/brotli"

2
next/assets/update.go → assets/update.go

@ -1,7 +1,7 @@
package assets
import (
"XProxy/next/logger"
"XProxy/logger"
urlpkg "net/url"
"os"
"path"

20
go.mod

@ -4,27 +4,29 @@ go 1.21.0
require (
github.com/BurntSushi/toml v1.3.2
github.com/andybalholm/brotli v1.0.5
github.com/gabriel-vasile/mimetype v1.4.2
github.com/andybalholm/brotli v1.1.0
github.com/avast/retry-go v3.0.0+incompatible
github.com/dsnet/compress v0.0.1
github.com/gabriel-vasile/mimetype v1.4.3
github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a
github.com/gookit/color v1.5.4
github.com/klauspost/compress v1.16.7
github.com/klauspost/compress v1.17.6
github.com/magiconair/properties v1.8.7
github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67
github.com/robfig/cron v1.2.0
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.8.4
github.com/ulikunitz/xz v0.5.11
go.uber.org/zap v1.25.0
go.uber.org/zap v1.26.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/avast/retry-go v3.0.0+incompatible // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dsnet/compress v0.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.13.0 // indirect
)

30
go.sum

@ -1,27 +1,29 @@
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
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/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a h1:v6zMvHuY9yue4+QkG/HQ/W67wvtQmWJ4SDo9aK/GIno=
github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a/go.mod h1:I79BieaU4fxrw4LMXby6q5OS9XnoR9UIKLOzDFjUmuw=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 h1:jik8PHtAIsPlCRJjJzl4udgEf7hawInF9texMeO2jrU=
github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
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/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
@ -46,13 +48,13 @@ go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

52
next/logger/encoder.go → logger/encoder.go

@ -1,33 +1,25 @@
package logger
import (
"bytes"
"fmt"
"github.com/gookit/color"
"github.com/petermattis/goid"
"go.uber.org/zap/zapcore"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
)
// getGid get goroutine ID only for debugging.
// -> https://blog.sgmansfield.com/2015/12/goroutine-ids/
func getGid() uint64 {
b := make([]byte, 64)
b = b[:runtime.Stack(b, false)]
b = bytes.TrimPrefix(b, []byte("goroutine "))
b = b[:bytes.IndexByte(b, ' ')]
n, _ := strconv.ParseUint(string(b), 10, 64)
return n
// / getGid return goroutine id with string.
func getGid() string {
return strconv.FormatInt(goid.Get(), 10)
}
// getCaller calculate relative source path of caller.
func getCaller(ec zapcore.EntryCaller, verbose bool) string {
file, err := filepath.Rel(logHandle.path, ec.File)
file, err := filepath.Rel(project, ec.File)
if err != nil {
return "undefined"
return "unknown"
}
if verbose {
return file + ":" + strconv.Itoa(ec.Line)
@ -44,34 +36,28 @@ func timeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
// timeColoredEncoder formats the time as a colored string
// with custom prefix.
func timeColoredEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(fmt.Sprintf(
"%s %s",
color.Cyan.Render(logHandle.prefix), // colored prefix
color.Gray.Render(t.Format("2006-01-02 15:04:05.000")),
))
enc.AppendString(color.Cyan.Render(logger.prefix))
enc.AppendString(color.Gray.Render(t.Format("2006-01-02 15:04:05.000")))
}
// callerEncoder formats caller in square brackets.
func callerEncoder(ec zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) {
if !logHandle.verbose {
if logger.verbose {
enc.AppendString("[" + getGid() + "]")
enc.AppendString("[" + getCaller(ec, true) + "]")
} else {
enc.AppendString("[" + getCaller(ec, false) + "]")
return
}
enc.AppendString(fmt.Sprintf("[%d] [%s]", getGid(), getCaller(ec, true)))
}
// callerColoredEncoder formats caller in square brackets with
// magenta color.
// callerColoredEncoder formats caller in square brackets with magenta color.
func callerColoredEncoder(ec zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) {
if !logHandle.verbose {
if logger.verbose {
enc.AppendString(color.Blue.Render("[" + getGid() + "]"))
enc.AppendString(color.Magenta.Render("[" + getCaller(ec, true) + "]"))
} else {
enc.AppendString(color.Magenta.Render("[" + getCaller(ec, false) + "]"))
return
}
enc.AppendString(fmt.Sprintf(
"%s %s",
color.Blue.Render(fmt.Sprintf("[%d]", getGid())),
color.Magenta.Render("["+getCaller(ec, true)+"]"),
))
}
// levelEncoder formats log level using square brackets.
@ -79,8 +65,8 @@ func levelEncoder(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString("[" + level.CapitalString() + "]")
}
// levelColoredEncoder formats log level using square brackets
// and uses different colors.
// levelColoredEncoder formats log level using square brackets and uses
// different colors.
func levelColoredEncoder(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
levelStr := "[" + level.CapitalString() + "]"
switch level {

99
logger/encoder_test.go

@ -0,0 +1,99 @@
package logger
import (
"XProxy/mocks"
"fmt"
"github.com/magiconair/properties/assert"
"github.com/petermattis/goid"
"github.com/stretchr/testify/mock"
"go.uber.org/zap/zapcore"
"runtime"
"strings"
"testing"
"time"
)
var testTime = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)
// zapCaller build fake caller with the absolute path of current code.
func zapCaller() zapcore.EntryCaller {
_, srcPath, _, _ := runtime.Caller(0)
return zapcore.EntryCaller{
File: srcPath,
Line: 0,
}
}
// encoderTest is a helper function to test buffer output of mock encoder.
func encoderTest(t *testing.T, exec func(*mocks.PrimitiveArrayEncoder), expect string) {
encoder := mocks.NewPrimitiveArrayEncoder(t)
encoder.On("AppendInt64", mock.Anything).Maybe() // only enroll used method
encoder.On("AppendString", mock.Anything).Maybe()
exec(encoder)
var values []string
for _, call := range encoder.Calls {
values = append(values, fmt.Sprintf("%v", call.Arguments.Get(0)))
}
assert.Equal(t, strings.Join(values, " "), expect)
}
func Test_getCaller(t *testing.T) {
caller := zapCaller()
caller.File = "Invalid Path"
assert.Equal(t, getCaller(caller, true), "unknown")
assert.Equal(t, getCaller(caller, false), "unknown")
assert.Equal(t, getCaller(zapCaller(), false), "logger/encoder_test")
assert.Equal(t, getCaller(zapCaller(), true), "logger/encoder_test.go:0")
}
func Test_timeEncoder(t *testing.T) {
encoderTest(t, func(encoder *mocks.PrimitiveArrayEncoder) {
timeEncoder(testTime, encoder)
}, "2000-01-01 00:00:00.000")
encoderTest(t, func(encoder *mocks.PrimitiveArrayEncoder) {
timeColoredEncoder(testTime, encoder)
}, "\x1b[36m"+logger.prefix+"\x1b[0m \x1b[90m2000-01-01 00:00:00.000\x1b[0m")
}
func Test_callerEncoder(t *testing.T) {
verboseVal := logger.verbose
callerTest := func(entry func(zapcore.EntryCaller, zapcore.PrimitiveArrayEncoder), expect string) {
encoderTest(t, func(encoder *mocks.PrimitiveArrayEncoder) {
entry(zapCaller(), encoder)
}, expect)
}
logger.verbose = false
callerTest(callerEncoder, "[logger/encoder_test]")
callerTest(callerColoredEncoder, "\x1b[35m[logger/encoder_test]\x1b[0m")
logger.verbose = true
gid := fmt.Sprintf("[%d]", goid.Get())
callerTest(callerEncoder, gid+" [logger/encoder_test.go:0]")
callerTest(callerColoredEncoder, "\x1b[34m"+gid+"\x1b[0m \x1b[35m[logger/encoder_test.go:0]\x1b[0m")
logger.verbose = verboseVal
}
func Test_levelEncoder(t *testing.T) {
levelTest := func(entry func(zapcore.Level, zapcore.PrimitiveArrayEncoder), level zapcore.Level, expect string) {
encoderTest(t, func(encoder *mocks.PrimitiveArrayEncoder) {
entry(level, encoder)
}, expect)
}
levelTest(levelEncoder, zapcore.DebugLevel, "[DEBUG]")
levelTest(levelEncoder, zapcore.InfoLevel, "[INFO]")
levelTest(levelEncoder, zapcore.WarnLevel, "[WARN]")
levelTest(levelEncoder, zapcore.ErrorLevel, "[ERROR]")
levelTest(levelEncoder, zapcore.PanicLevel, "[PANIC]")
levelTest(levelColoredEncoder, zapcore.DebugLevel, "\x1b[39m[DEBUG]\x1b[0m")
levelTest(levelColoredEncoder, zapcore.InfoLevel, "\x1b[32m[INFO]\x1b[0m")
levelTest(levelColoredEncoder, zapcore.WarnLevel, "\x1b[33m[WARN]\x1b[0m")
levelTest(levelColoredEncoder, zapcore.ErrorLevel, "\x1b[31m[ERROR]\x1b[0m")
levelTest(levelColoredEncoder, zapcore.PanicLevel, "\x1b[91m[PANIC]\x1b[0m")
}

65
logger/interface.go

@ -0,0 +1,65 @@
package logger
import (
"go.uber.org/zap/zapcore"
"io"
)
type Level = zapcore.Level
const (
DebugLevel = zapcore.DebugLevel
InfoLevel = zapcore.InfoLevel
WarnLevel = zapcore.WarnLevel
ErrorLevel = zapcore.ErrorLevel
PanicLevel = zapcore.PanicLevel
)
func Debugf(template string, args ...interface{}) {
logger.entry.Debugf(template, args...)
}
func Infof(template string, args ...interface{}) {
logger.entry.Infof(template, args...)
}
func Warnf(template string, args ...interface{}) {
logger.entry.Warnf(template, args...)
}
func Errorf(template string, args ...interface{}) {
logger.entry.Errorf(template, args...)
}
func Panicf(template string, args ...interface{}) {
logger.entry.Panicf(template, args...)
}
// GetLevel return the current logger level.
func GetLevel() Level {
return logger.level.Level()
}
// SetLevel configure logger output level. Note that debug level will output
// more information and reduce performance.
func SetLevel(level Level) {
logger.level.SetLevel(level)
if level == DebugLevel {
logger.verbose = true
} else {
logger.verbose = false
}
}
// AddWriters add more writers to target log channel.
func AddWriters(colored bool, writers ...io.Writer) {
var syncWriters []zapcore.WriteSyncer
for _, writer := range writers {
syncWriters = append(syncWriters, zapcore.AddSync(writer))
}
if !colored {
logger.addPlainWrites(syncWriters...)
} else {
logger.addColoredWrites(syncWriters...)
}
}

101
logger/interface_test.go

@ -0,0 +1,101 @@
package logger
import (
"bytes"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
"regexp"
"strings"
"testing"
)
var Levels = []Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, PanicLevel}
func Test_level(t *testing.T) {
assert.Equal(t, DebugLevel, zap.DebugLevel)
assert.Equal(t, InfoLevel, zap.InfoLevel)
assert.Equal(t, WarnLevel, zap.WarnLevel)
assert.Equal(t, ErrorLevel, zap.ErrorLevel)
assert.Equal(t, PanicLevel, zap.PanicLevel)
for _, level := range Levels {
SetLevel(level)
assert.Equal(t, GetLevel(), level)
assert.Equal(t, logger.verbose, level == DebugLevel) // verbose only for DEBUG level
}
SetLevel(InfoLevel) // revert to INFO level
}
func Test_logger(t *testing.T) {
var plainBuf, plainTeeBuf bytes.Buffer
var coloredBuf, coloredTeeBuf bytes.Buffer
logger.plain.writers = []zapcore.WriteSyncer{} // clear slice
logger.colored.writers = []zapcore.WriteSyncer{}
AddWriters(false, &plainBuf, &plainTeeBuf)
AddWriters(true, &coloredBuf, &coloredTeeBuf)
logger.update() // apply test writers
printLogs := func(usingLevel Level) {
SetLevel(usingLevel)
Debugf("Here is %s level", "DEBUG")
Infof("Here is %s level", "INFO")
Warnf("Here is %s level", "WARN")
Errorf("Here is %s level", "ERROR")
assert.Panics(t, func() {
Panicf("Here is %s level", "PANIC")
})
}
printLogs(DebugLevel) // output into buffer
printLogs(InfoLevel)
printLogs(WarnLevel)
printLogs(ErrorLevel)
printLogs(PanicLevel)
assertLine := func(log string, level Level, colored bool, verbose bool) {
regex := `^([\d.:\- ]+) \[(\S+)] (\[\d+] )?\[(\S+)] Here is (\S+) level$`
if colored {
regex = `^\x1b\[36m\[XProxy]\x1b\[0m \x1b\[90m([\d.:\- ]+)\x1b\[0m ` +
`\x1b\[\d\dm\[(\S+)]\x1b\[0m (\x1b\[34m\[\d+]\x1b\[0m )?` +
`\x1b\[35m\[(\S+)]\x1b\[0m Here is (\S+) level$`
}
matches := regexp.MustCompile(regex).FindStringSubmatch(log)
timeRegex := regexp.MustCompile(`^\d{4}(-\d\d){2} \d{2}(:\d\d){2}\.\d{3}$`)
assert.NotEmpty(t, matches) // valid log line
assert.Regexp(t, timeRegex, matches[1]) // valid time format
assert.Equal(t, level.CapitalString(), matches[2]) // valid level string
assert.Equal(t, level.CapitalString(), matches[5])
if !verbose {
assert.Equal(t, "logger/interface_test", matches[4])
} else {
assert.Regexp(t, regexp.MustCompile(`^logger/interface_test.go:\d+$`), matches[4])
}
}
assertLogs := func(buffer string, colored bool) {
var line string
logs := strings.Split(buffer, "\n")
for _, limit := range Levels {
for _, level := range Levels {
if level >= limit {
line, logs = logs[0], logs[1:]
assertLine(line, level, colored, limit == DebugLevel)
}
}
}
assert.Equal(t, len(logs), 1)
assert.Equal(t, logs[0], "") // last line is empty
}
assertLogs(plainBuf.String(), false)
assertLogs(coloredBuf.String(), true)
assert.Equal(t, plainBuf.String(), plainTeeBuf.String())
assert.Equal(t, coloredBuf.String(), coloredTeeBuf.String())
logger.plain.writers = []zapcore.WriteSyncer{} // revert logger configure
logger.colored.writers = []zapcore.WriteSyncer{os.Stdout}
logger.level.SetLevel(InfoLevel)
logger.update()
}

95
logger/logger.go

@ -0,0 +1,95 @@
package logger
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
"path"
"runtime"
)
var project string // project absolute path
var logger *logCore // singleton logger handle
// logChannel handle multiple writers with unified format.
type logChannel struct {
encoder zapcore.Encoder
writers []zapcore.WriteSyncer
}
// logCore manage log level, channels and other interfaces.
type logCore struct {
prefix string // custom output prefix
verbose bool // show verbose information
plain logChannel // log channel with plain text
colored logChannel // log channel with colored text
level *zap.AtomicLevel // zap log level pointer
entry *zap.SugaredLogger // zap sugared logger entry
}
func init() {
_, src, _, _ := runtime.Caller(0) // absolute path of current code
project = path.Join(path.Dir(src), "../")
zapLevel := zap.NewAtomicLevelAt(InfoLevel) // using info level in default
logger = &logCore{
verbose: false,
level: &zapLevel,
prefix: "[XProxy]",
plain: buildChannel(false),
colored: buildChannel(true),
}
logger.addColoredWrites(os.Stderr) // output into stderr in default
}
// buildChannel generate logChannel with `colored` option.
func buildChannel(colored bool) logChannel {
config := zapcore.EncoderConfig{
ConsoleSeparator: " ",
MessageKey: "msg",
LevelKey: "level",
TimeKey: "time",
CallerKey: "caller",
EncodeTime: timeEncoder,
EncodeLevel: levelEncoder,
EncodeCaller: callerEncoder,
}
if colored { // using colored version
config.EncodeTime = timeColoredEncoder
config.EncodeLevel = levelColoredEncoder
config.EncodeCaller = callerColoredEncoder
}
return logChannel{
encoder: zapcore.NewConsoleEncoder(config),
writers: []zapcore.WriteSyncer{}, // without any writer
}
}
// update refreshes the binding of the log core to the writers.
func (handle *logCore) update() {
buildCore := func(channel *logChannel) zapcore.Core { // build zap core from logChannel
return zapcore.NewCore(
channel.encoder,
zap.CombineWriteSyncers(channel.writers...),
handle.level,
)
}
handle.entry = zap.New(
zapcore.NewTee(buildCore(&handle.plain), buildCore(&handle.colored)),
zap.AddCaller(), zap.AddCallerSkip(1),
).Named("xproxy").Sugar()
}
// addPlainWrites adds plain text writers to the logCore.
func (handle *logCore) addPlainWrites(writers ...zapcore.WriteSyncer) {
handle.plain.writers = append(handle.plain.writers, writers...)
handle.update()
}
// addColoredWrites adds colored text writers to the logCore.
func (handle *logCore) addColoredWrites(writers ...zapcore.WriteSyncer) {
handle.colored.writers = append(handle.colored.writers, writers...)
handle.update()
}

47
logger/logger_test.go

@ -0,0 +1,47 @@
package logger
import (
"github.com/stretchr/testify/assert"
"go.uber.org/zap/zapcore"
"os"
"path"
"testing"
)
func Test_init(t *testing.T) {
pwd := path.Dir(zapCaller().File)
assert.Equal(t, path.Join(pwd, "../"), project)
assert.NotNil(t, logger)
assert.NotNil(t, logger.entry)
assert.NotNil(t, logger.level)
assert.Equal(t, logger.verbose, false)
assert.Equal(t, logger.prefix, "[XProxy]")
assert.Equal(t, logger.level.Level(), InfoLevel)
assert.Equal(t, len(logger.plain.writers), 0)
assert.Equal(t, len(logger.colored.writers), 1)
for _, level := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, PanicLevel} {
logger.level.SetLevel(level)
assert.Equal(t, logger.entry.Level(), level)
}
logger.level.SetLevel(InfoLevel) // revert to INFO level
}
func Test_addWrites(t *testing.T) {
core := new(logCore)
assert.Equal(t, len(core.plain.writers), 0)
assert.Equal(t, len(core.colored.writers), 0)
core.entry = nil
core.addPlainWrites(os.Stdout, os.Stdout, os.Stdout)
assert.NotNil(t, core.entry)
assert.Equal(t, len(core.plain.writers), 3)
core.entry = nil
core.addColoredWrites(os.Stderr, os.Stderr, os.Stderr)
assert.NotNil(t, core.entry)
assert.Equal(t, len(core.plain.writers), 3)
}

2
next/main.go → main.go

@ -1,6 +1,6 @@
package main
import "XProxy/next/assets"
import "XProxy/assets"
func main() {
remoteAssets := map[string]string{

114
mocks/PrimitiveArrayEncoder.go

@ -0,0 +1,114 @@
// Code generated by mockery v2.40.1. DO NOT EDIT.
package mocks
import mock "github.com/stretchr/testify/mock"
// PrimitiveArrayEncoder is an autogenerated mock type for the PrimitiveArrayEncoder type
type PrimitiveArrayEncoder struct {
mock.Mock
}
// AppendBool provides a mock function with given fields: _a0
func (_m *PrimitiveArrayEncoder) AppendBool(_a0 bool) {
_m.Called(_a0)
}
// AppendByteString provides a mock function with given fields: _a0
func (_m *PrimitiveArrayEncoder) AppendByteString(_a0 []byte) {
_m.Called(_a0)
}
// AppendComplex128 provides a mock function with given fields: _a0
func (_m *PrimitiveArrayEncoder) AppendComplex128(_a0 complex128) {
_m.Called(_a0)
}
// AppendComplex64 provides a mock function with given fields: _a0
func (_m *PrimitiveArrayEncoder) AppendComplex64(_a0 complex64) {
_m.Called(_a0)
}
// AppendFloat32 provides a mock function with given fields: _a0
func (_m *PrimitiveArrayEncoder) AppendFloat32(_a0 float32) {
_m.Called(_a0)
}
// AppendFloat64 provides a mock function with given fields: _a0
func (_m *PrimitiveArrayEncoder) AppendFloat64(_a0 float64) {
_m.Called(_a0)
}
// AppendInt provides a mock function with given fields: _a0
func (_m *PrimitiveArrayEncoder) AppendInt(_a0 int) {
_m.Called(_a0)
}
// AppendInt16 provides a mock function with given fields: _a0
func (_m *PrimitiveArrayEncoder) AppendInt16(_a0 int16) {
_m.Called(_a0)
}
// AppendInt32 provides a mock function with given fields: _a0
func (_m *PrimitiveArrayEncoder) AppendInt32(_a0 int32) {
_m.Called(_a0)
}
// AppendInt64 provides a mock function with given fields: _a0
func (_m *PrimitiveArrayEncoder) AppendInt64(_a0 int64) {
_m.Called(_a0)
}
// AppendInt8 provides a mock function with given fields: _a0
func (_m *PrimitiveArrayEncoder) AppendInt8(_a0 int8) {
_m.Called(_a0)
}
// AppendString provides a mock function with given fields: _a0
func (_m *PrimitiveArrayEncoder) AppendString(_a0 string) {
_m.Called(_a0)
}
// AppendUint provides a mock function with given fields: _a0
func (_m *PrimitiveArrayEncoder) AppendUint(_a0 uint) {
_m.Called(_a0)
}
// AppendUint16 provides a mock function with given fields: _a0
func (_m *PrimitiveArrayEncoder) AppendUint16(_a0 uint16) {
_m.Called(_a0)
}
// AppendUint32 provides a mock function with given fields: _a0
func (_m *PrimitiveArrayEncoder) AppendUint32(_a0 uint32) {
_m.Called(_a0)
}
// AppendUint64 provides a mock function with given fields: _a0
func (_m *PrimitiveArrayEncoder) AppendUint64(_a0 uint64) {
_m.Called(_a0)
}
// AppendUint8 provides a mock function with given fields: _a0
func (_m *PrimitiveArrayEncoder) AppendUint8(_a0 uint8) {
_m.Called(_a0)
}
// AppendUintptr provides a mock function with given fields: _a0
func (_m *PrimitiveArrayEncoder) AppendUintptr(_a0 uintptr) {
_m.Called(_a0)
}
// NewPrimitiveArrayEncoder creates a new instance of PrimitiveArrayEncoder. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewPrimitiveArrayEncoder(t interface {
mock.TestingT
Cleanup(func())
}) *PrimitiveArrayEncoder {
mock := &PrimitiveArrayEncoder{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

59
next/logger/interface.go

@ -1,59 +0,0 @@
package logger
import (
"go.uber.org/zap/zapcore"
"io"
)
const (
DebugLevel = zapcore.DebugLevel
InfoLevel = zapcore.InfoLevel
WarnLevel = zapcore.WarnLevel
ErrorLevel = zapcore.ErrorLevel
PanicLevel = zapcore.PanicLevel
)
func Debugf(template string, args ...interface{}) {
logHandle.sugar.Debugf(template, args...)
}
func Infof(template string, args ...interface{}) {
logHandle.sugar.Infof(template, args...)
}
func Warnf(template string, args ...interface{}) {
logHandle.sugar.Warnf(template, args...)
}
func Errorf(template string, args ...interface{}) {
logHandle.sugar.Errorf(template, args...)
}
func Panicf(template string, args ...interface{}) {
logHandle.sugar.Panicf(template, args...)
}
// GetLevel return the current logger level.
func GetLevel() zapcore.Level {
return logHandle.level.Level()
}
// SetLevel configure logger output level. Note that debug level
// will output more information and reduce performance.
func SetLevel(level zapcore.Level) {
logHandle.level.SetLevel(level)
if level == DebugLevel {
logHandle.verbose = true
} else {
logHandle.verbose = false
}
}
// AddOutputs adds more plain output channel to the logger module.
func AddOutputs(outputs ...io.Writer) {
var writers []zapcore.WriteSyncer
for _, output := range outputs {
writers = append(writers, zapcore.AddSync(output))
}
addWrites(writers...)
}

78
next/logger/logger.go

@ -1,78 +0,0 @@
package logger
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
"path"
"runtime"
)
type logger struct {
logger *zap.Logger
level *zap.AtomicLevel
sugar *zap.SugaredLogger
writers []zapcore.WriteSyncer
stderr zapcore.Core // fixed stderr output
prefix string // custom output prefix
path string // project absolute path
verbose bool // show goroutine id and caller line
}
var logHandle *logger // singleton logger handle
// logConfig generates log config for XProxy.
func logConfig(colored bool) zapcore.EncoderConfig {
config := zapcore.EncoderConfig{
ConsoleSeparator: " ",
MessageKey: "msg",
LevelKey: "level",
TimeKey: "time",
CallerKey: "caller",
EncodeTime: timeEncoder,
EncodeLevel: levelEncoder,
EncodeCaller: callerEncoder,
}
if colored {
config.EncodeTime = timeColoredEncoder
config.EncodeLevel = levelColoredEncoder
config.EncodeCaller = callerColoredEncoder
}
return config
}
func init() {
zapLevel := zap.NewAtomicLevelAt(InfoLevel) // using info level in default
zapCore := zapcore.NewCore(
zapcore.NewConsoleEncoder(logConfig(true)), // colorful output
zapcore.Lock(os.Stderr),
zapLevel,
)
zapLogger := zap.New(zapCore, zap.AddCaller(), zap.AddCallerSkip(1))
_, src, _, _ := runtime.Caller(0) // absolute path of current code
logHandle = &logger{
logger: zapLogger,
level: &zapLevel,
stderr: zapCore,
sugar: zapLogger.Sugar(),
writers: []zapcore.WriteSyncer{},
path: path.Join(path.Dir(src), "../"),
prefix: "[XProxy]",
verbose: false,
}
}
// addWrites adds more plain log writers.
func addWrites(writers ...zapcore.WriteSyncer) {
logHandle.writers = append(logHandle.writers, writers...)
plainCore := zapcore.NewCore(
zapcore.NewConsoleEncoder(logConfig(false)), // without colored
zap.CombineWriteSyncers(logHandle.writers...),
logHandle.level,
)
logHandle.logger = zap.New(
zapcore.NewTee(logHandle.stderr, plainCore),
zap.AddCaller(), zap.AddCallerSkip(1),
)
logHandle.sugar = logHandle.logger.Sugar()
}
Loading…
Cancel
Save