mirror of
https://github.com/xjasonlyu/tun2socks.git
synced 2025-10-08 18:20:41 +08:00
Refactor(log): use go.uber.org/zap
(#389)
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
package observable
|
||||
|
||||
type Iterable <-chan any
|
@@ -1,65 +0,0 @@
|
||||
package observable
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Observable struct {
|
||||
iterable Iterable
|
||||
listener map[Subscription]*Subscriber
|
||||
mux sync.Mutex
|
||||
done bool
|
||||
}
|
||||
|
||||
func (o *Observable) process() {
|
||||
for item := range o.iterable {
|
||||
o.mux.Lock()
|
||||
for _, sub := range o.listener {
|
||||
sub.Emit(item)
|
||||
}
|
||||
o.mux.Unlock()
|
||||
}
|
||||
o.close()
|
||||
}
|
||||
|
||||
func (o *Observable) close() {
|
||||
o.mux.Lock()
|
||||
defer o.mux.Unlock()
|
||||
|
||||
o.done = true
|
||||
for _, sub := range o.listener {
|
||||
sub.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Observable) Subscribe() (Subscription, error) {
|
||||
o.mux.Lock()
|
||||
defer o.mux.Unlock()
|
||||
if o.done {
|
||||
return nil, errors.New("observable is closed")
|
||||
}
|
||||
subscriber := newSubscriber()
|
||||
o.listener[subscriber.Out()] = subscriber
|
||||
return subscriber.Out(), nil
|
||||
}
|
||||
|
||||
func (o *Observable) UnSubscribe(sub Subscription) {
|
||||
o.mux.Lock()
|
||||
defer o.mux.Unlock()
|
||||
subscriber, exist := o.listener[sub]
|
||||
if !exist {
|
||||
return
|
||||
}
|
||||
delete(o.listener, sub)
|
||||
subscriber.Close()
|
||||
}
|
||||
|
||||
func NewObservable(any Iterable) *Observable {
|
||||
observable := &Observable{
|
||||
iterable: any,
|
||||
listener: map[Subscription]*Subscriber{},
|
||||
}
|
||||
go observable.process()
|
||||
return observable
|
||||
}
|
@@ -1,148 +0,0 @@
|
||||
package observable
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
func iterator(item []any) chan any {
|
||||
ch := make(chan any)
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
for _, elm := range item {
|
||||
ch <- elm
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
func TestObservable(t *testing.T) {
|
||||
iter := iterator([]any{1, 2, 3, 4, 5})
|
||||
src := NewObservable(iter)
|
||||
data, err := src.Subscribe()
|
||||
assert.Nil(t, err)
|
||||
count := 0
|
||||
for range data {
|
||||
count++
|
||||
}
|
||||
assert.Equal(t, count, 5)
|
||||
}
|
||||
|
||||
func TestObservable_MultiSubscribe(t *testing.T) {
|
||||
iter := iterator([]any{1, 2, 3, 4, 5})
|
||||
src := NewObservable(iter)
|
||||
ch1, _ := src.Subscribe()
|
||||
ch2, _ := src.Subscribe()
|
||||
count := atomic.NewInt32(0)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
waitCh := func(ch <-chan any) {
|
||||
for range ch {
|
||||
count.Inc()
|
||||
}
|
||||
wg.Done()
|
||||
}
|
||||
go waitCh(ch1)
|
||||
go waitCh(ch2)
|
||||
wg.Wait()
|
||||
assert.Equal(t, int32(10), count.Load())
|
||||
}
|
||||
|
||||
func TestObservable_UnSubscribe(t *testing.T) {
|
||||
iter := iterator([]any{1, 2, 3, 4, 5})
|
||||
src := NewObservable(iter)
|
||||
data, err := src.Subscribe()
|
||||
assert.Nil(t, err)
|
||||
src.UnSubscribe(data)
|
||||
_, open := <-data
|
||||
assert.False(t, open)
|
||||
}
|
||||
|
||||
func TestObservable_SubscribeClosedSource(t *testing.T) {
|
||||
iter := iterator([]any{1})
|
||||
src := NewObservable(iter)
|
||||
data, _ := src.Subscribe()
|
||||
<-data
|
||||
|
||||
_, closed := src.Subscribe()
|
||||
assert.NotNil(t, closed)
|
||||
}
|
||||
|
||||
func TestObservable_UnSubscribeWithNotExistSubscription(t *testing.T) {
|
||||
sub := Subscription(make(chan any))
|
||||
iter := iterator([]any{1})
|
||||
src := NewObservable(iter)
|
||||
src.UnSubscribe(sub)
|
||||
}
|
||||
|
||||
func TestObservable_SubscribeGoroutineLeak(t *testing.T) {
|
||||
iter := iterator([]any{1, 2, 3, 4, 5})
|
||||
src := NewObservable(iter)
|
||||
max := 100
|
||||
|
||||
var list []Subscription
|
||||
for i := 0; i < max; i++ {
|
||||
ch, _ := src.Subscribe()
|
||||
list = append(list, ch)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(max)
|
||||
waitCh := func(ch <-chan any) {
|
||||
for range ch {
|
||||
}
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
for _, ch := range list {
|
||||
go waitCh(ch)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
for _, sub := range list {
|
||||
_, more := <-sub
|
||||
assert.False(t, more)
|
||||
}
|
||||
|
||||
if len(list) > 0 {
|
||||
_, more := <-list[0]
|
||||
assert.False(t, more)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Observable_1000(b *testing.B) {
|
||||
ch := make(chan any)
|
||||
o := NewObservable(ch)
|
||||
num := 1000
|
||||
|
||||
var subs []Subscription
|
||||
for i := 0; i < num; i++ {
|
||||
sub, _ := o.Subscribe()
|
||||
subs = append(subs, sub)
|
||||
}
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(num)
|
||||
|
||||
b.ResetTimer()
|
||||
for _, sub := range subs {
|
||||
go func(s Subscription) {
|
||||
for range s {
|
||||
}
|
||||
wg.Done()
|
||||
}(sub)
|
||||
}
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
ch <- i
|
||||
}
|
||||
|
||||
close(ch)
|
||||
wg.Wait()
|
||||
}
|
@@ -1,33 +0,0 @@
|
||||
package observable
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Subscription <-chan any
|
||||
|
||||
type Subscriber struct {
|
||||
buffer chan any
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func (s *Subscriber) Emit(item any) {
|
||||
s.buffer <- item
|
||||
}
|
||||
|
||||
func (s *Subscriber) Out() Subscription {
|
||||
return s.buffer
|
||||
}
|
||||
|
||||
func (s *Subscriber) Close() {
|
||||
s.once.Do(func() {
|
||||
close(s.buffer)
|
||||
})
|
||||
}
|
||||
|
||||
func newSubscriber() *Subscriber {
|
||||
sub := &Subscriber{
|
||||
buffer: make(chan any, 200),
|
||||
}
|
||||
return sub
|
||||
}
|
@@ -109,7 +109,7 @@ func general(k *Key) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.SetLevel(level)
|
||||
log.SetLogger(log.Must(log.NewLeveled(level)))
|
||||
|
||||
if k.Interface != "" {
|
||||
iface, err := net.InterfaceByName(k.Interface)
|
||||
@@ -156,7 +156,7 @@ func restAPI(k *Key) error {
|
||||
|
||||
go func() {
|
||||
if err := restapi.Start(host, token); err != nil {
|
||||
log.Warnf("[RESTAPI] failed to start: %v", err)
|
||||
log.Errorf("[RESTAPI] failed to start: %v", err)
|
||||
}
|
||||
}()
|
||||
log.Infof("[RESTAPI] serve at: %s", u)
|
||||
@@ -175,7 +175,7 @@ func netstack(k *Key) (err error) {
|
||||
if k.TUNPreUp != "" {
|
||||
log.Infof("[TUN] pre-execute command: `%s`", k.TUNPreUp)
|
||||
if preUpErr := execCommand(k.TUNPreUp); preUpErr != nil {
|
||||
log.Warnf("[TUN] failed to pre-execute: %s: %v", k.TUNPreUp, preUpErr)
|
||||
log.Errorf("[TUN] failed to pre-execute: %s: %v", k.TUNPreUp, preUpErr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ func netstack(k *Key) (err error) {
|
||||
}
|
||||
log.Infof("[TUN] post-execute command: `%s`", k.TUNPostUp)
|
||||
if postUpErr := execCommand(k.TUNPostUp); postUpErr != nil {
|
||||
log.Warnf("[TUN] failed to post-execute: %s: %v", k.TUNPostUp, postUpErr)
|
||||
log.Errorf("[TUN] failed to post-execute: %s: %v", k.TUNPostUp, postUpErr)
|
||||
}
|
||||
}()
|
||||
|
||||
|
4
go.mod
4
go.mod
@@ -14,10 +14,10 @@ require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/schema v1.4.1
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.9.0
|
||||
go.uber.org/atomic v1.11.0
|
||||
go.uber.org/automaxprocs v1.5.3
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/crypto v0.25.0
|
||||
golang.org/x/sys v0.22.0
|
||||
golang.org/x/time v0.5.0
|
||||
@@ -30,8 +30,8 @@ require (
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/net v0.27.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
)
|
||||
|
14
go.sum
14
go.sum
@@ -1,7 +1,5 @@
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
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/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
@@ -32,21 +30,22 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
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=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
@@ -58,7 +57,6 @@ golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQX
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gvisor.dev/gvisor v0.0.0-20240713103206-39d6c232e61d h1:dFTIljP/5ReqgM7nMR4DauApFatUaSP8r9btX0sd8a8=
|
||||
|
2
log/doc.go
Normal file
2
log/doc.go
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package log is a thin wrapper based on "go.uber.org/zap".
|
||||
package log
|
@@ -1,7 +1,6 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"io"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -9,26 +8,30 @@ import (
|
||||
glog "gvisor.dev/gvisor/pkg/log"
|
||||
)
|
||||
|
||||
var _globalE = &emitter{}
|
||||
|
||||
func init() {
|
||||
EnableStackLog(true)
|
||||
glog.SetTarget(_globalE)
|
||||
}
|
||||
|
||||
func EnableStackLog(v bool) {
|
||||
if v {
|
||||
glog.SetTarget(&emitter{}) // built-in logger
|
||||
} else {
|
||||
glog.SetTarget(&glog.Writer{Next: io.Discard})
|
||||
}
|
||||
type emitter struct {
|
||||
logger *SugaredLogger
|
||||
}
|
||||
|
||||
type emitter struct{}
|
||||
func (e *emitter) setLogger(logger *SugaredLogger) {
|
||||
e.logger = logger.WithOptions(pkgCallerSkip)
|
||||
}
|
||||
|
||||
func (emitter) Emit(depth int, level glog.Level, _ time.Time, format string, args ...any) {
|
||||
func (e *emitter) logf(level glog.Level, format string, args ...any) {
|
||||
e.logger.Logf(1-Level(level), "[STACK] "+format, args...)
|
||||
}
|
||||
|
||||
func (e *emitter) Emit(depth int, level glog.Level, _ time.Time, format string, args ...any) {
|
||||
if _, file, line, ok := runtime.Caller(depth + 1); ok {
|
||||
// Ignore (*gonet.TCPConn).RemoteAddr() warning: `ep.GetRemoteAddress() failed`.
|
||||
if line == 457 && strings.HasSuffix(file, "/pkg/tcpip/adapters/gonet/gonet.go") {
|
||||
// Ignore: gvisor.dev/gvisor/pkg/tcpip/adapters/gonet/gonet.go:457
|
||||
if line == 457 && strings.HasSuffix(file, "gonet/gonet.go") {
|
||||
return
|
||||
}
|
||||
}
|
||||
logf(Level(level)+2, "[STACK] "+format, args...)
|
||||
e.logf(level, format, args...)
|
||||
}
|
||||
|
39
log/event.go
39
log/event.go
@@ -1,39 +0,0 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/xjasonlyu/tun2socks/v2/common/observable"
|
||||
)
|
||||
|
||||
var (
|
||||
_logCh = make(chan any)
|
||||
_source = observable.NewObservable(_logCh)
|
||||
)
|
||||
|
||||
type Event struct {
|
||||
Level Level `json:"level"`
|
||||
Message string `json:"msg"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
func newEvent(level Level, format string, args ...any) *Event {
|
||||
event := &Event{
|
||||
Level: level,
|
||||
Time: time.Now(),
|
||||
Message: fmt.Sprintf(format, args...),
|
||||
}
|
||||
_logCh <- event /* send all events to logCh */
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
func Subscribe() observable.Subscription {
|
||||
sub, _ := _source.Subscribe()
|
||||
return sub
|
||||
}
|
||||
|
||||
func UnSubscribe(sub observable.Subscription) {
|
||||
_source.UnSubscribe(sub)
|
||||
}
|
77
log/level.go
77
log/level.go
@@ -1,72 +1,31 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
type Level uint32
|
||||
// Level is an alias for zapcore.Level.
|
||||
type Level = zapcore.Level
|
||||
|
||||
// Levels are aliases for Level.
|
||||
const (
|
||||
SilentLevel Level = iota
|
||||
ErrorLevel
|
||||
WarnLevel
|
||||
InfoLevel
|
||||
DebugLevel
|
||||
DebugLevel = zapcore.DebugLevel
|
||||
InfoLevel = zapcore.InfoLevel
|
||||
WarnLevel = zapcore.WarnLevel
|
||||
ErrorLevel = zapcore.ErrorLevel
|
||||
DPanicLevel = zapcore.DPanicLevel
|
||||
PanicLevel = zapcore.PanicLevel
|
||||
FatalLevel = zapcore.FatalLevel
|
||||
InvalidLevel = zapcore.InvalidLevel
|
||||
SilentLevel = InvalidLevel + 1
|
||||
)
|
||||
|
||||
// UnmarshalJSON deserialize Level with json
|
||||
func (level *Level) UnmarshalJSON(data []byte) error {
|
||||
var lvl string
|
||||
if err := json.Unmarshal(data, &lvl); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l, err := ParseLevel(lvl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*level = l
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON serialize Level with json
|
||||
func (level Level) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(level.String())
|
||||
}
|
||||
|
||||
func (level Level) String() string {
|
||||
switch level {
|
||||
case DebugLevel:
|
||||
return "debug"
|
||||
case InfoLevel:
|
||||
return "info"
|
||||
case WarnLevel:
|
||||
return "warning"
|
||||
case ErrorLevel:
|
||||
return "error"
|
||||
case SilentLevel:
|
||||
return "silent"
|
||||
default:
|
||||
return fmt.Sprintf("not a valid level %d", level)
|
||||
}
|
||||
}
|
||||
|
||||
func ParseLevel(lvl string) (Level, error) {
|
||||
switch strings.ToLower(lvl) {
|
||||
case "silent":
|
||||
// ParseLevel is a thin wrapper for zapcore.ParseLevel.
|
||||
func ParseLevel(text string) (Level, error) {
|
||||
switch text {
|
||||
case "silent", "SILENT":
|
||||
return SilentLevel, nil
|
||||
case "error":
|
||||
return ErrorLevel, nil
|
||||
case "warning":
|
||||
return WarnLevel, nil
|
||||
case "info":
|
||||
return InfoLevel, nil
|
||||
case "debug":
|
||||
return DebugLevel, nil
|
||||
default:
|
||||
return Level(0), fmt.Errorf("not a valid logrus Level: %q", lvl)
|
||||
return zapcore.ParseLevel(text)
|
||||
}
|
||||
}
|
||||
|
108
log/log.go
108
log/log.go
@@ -1,63 +1,71 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.uber.org/atomic"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// _defaultLevel is package default logging level.
|
||||
var _defaultLevel = atomic.NewUint32(uint32(InfoLevel))
|
||||
// global Logger and SugaredLogger.
|
||||
var (
|
||||
_globalMu sync.RWMutex
|
||||
_globalL *Logger
|
||||
_globalS *SugaredLogger
|
||||
)
|
||||
|
||||
func init() {
|
||||
logrus.SetOutput(os.Stdout)
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
SetLogger(zap.Must(zap.NewProduction()))
|
||||
}
|
||||
|
||||
func SetOutput(out io.Writer) {
|
||||
logrus.SetOutput(out)
|
||||
}
|
||||
|
||||
func SetLevel(level Level) {
|
||||
_defaultLevel.Store(uint32(level))
|
||||
}
|
||||
|
||||
func Debugf(format string, args ...any) {
|
||||
logf(DebugLevel, format, args...)
|
||||
}
|
||||
|
||||
func Infof(format string, args ...any) {
|
||||
logf(InfoLevel, format, args...)
|
||||
}
|
||||
|
||||
func Warnf(format string, args ...any) {
|
||||
logf(WarnLevel, format, args...)
|
||||
}
|
||||
|
||||
func Errorf(format string, args ...any) {
|
||||
logf(ErrorLevel, format, args...)
|
||||
}
|
||||
|
||||
func Fatalf(format string, args ...any) {
|
||||
logrus.Fatalf(format, args...)
|
||||
}
|
||||
|
||||
func logf(level Level, format string, args ...any) {
|
||||
event := newEvent(level, format, args...)
|
||||
if uint32(event.Level) > _defaultLevel.Load() {
|
||||
return
|
||||
}
|
||||
|
||||
switch level {
|
||||
func NewLeveled(l Level, options ...Option) (*Logger, error) {
|
||||
switch l {
|
||||
case SilentLevel:
|
||||
return zap.NewNop(), nil
|
||||
case DebugLevel:
|
||||
logrus.WithTime(event.Time).Debugln(event.Message)
|
||||
case InfoLevel:
|
||||
logrus.WithTime(event.Time).Infoln(event.Message)
|
||||
case WarnLevel:
|
||||
logrus.WithTime(event.Time).Warnln(event.Message)
|
||||
case ErrorLevel:
|
||||
logrus.WithTime(event.Time).Errorln(event.Message)
|
||||
return zap.NewDevelopment(options...)
|
||||
case InfoLevel, WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, FatalLevel:
|
||||
cfg := zap.NewProductionConfig()
|
||||
cfg.Level.SetLevel(l)
|
||||
return cfg.Build(options...)
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid level: %s", l)
|
||||
}
|
||||
}
|
||||
|
||||
// SetLogger sets the global Logger and SugaredLogger.
|
||||
func SetLogger(logger *Logger) {
|
||||
_globalMu.Lock()
|
||||
defer _globalMu.Unlock()
|
||||
// apply pkgCallerSkip to global loggers.
|
||||
_globalL = logger.WithOptions(pkgCallerSkip)
|
||||
_globalS = _globalL.Sugar()
|
||||
_globalE.setLogger(_globalS)
|
||||
}
|
||||
|
||||
func logf(lvl Level, template string, args ...any) {
|
||||
_globalMu.RLock()
|
||||
s := _globalS
|
||||
_globalMu.RUnlock()
|
||||
s.Logf(lvl, template, args...)
|
||||
}
|
||||
|
||||
func Debugf(template string, args ...any) {
|
||||
logf(DebugLevel, template, args...)
|
||||
}
|
||||
|
||||
func Infof(template string, args ...any) {
|
||||
logf(InfoLevel, template, args...)
|
||||
}
|
||||
|
||||
func Warnf(template string, args ...any) {
|
||||
logf(WarnLevel, template, args...)
|
||||
}
|
||||
|
||||
func Errorf(template string, args ...any) {
|
||||
logf(ErrorLevel, template, args...)
|
||||
}
|
||||
|
||||
func Fatalf(template string, args ...any) {
|
||||
logf(FatalLevel, template, args...)
|
||||
}
|
||||
|
22
log/zap.go
Normal file
22
log/zap.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Must is an alias for zap.Must.
|
||||
var Must = zap.Must
|
||||
|
||||
// logger aliases for zap.Logger and zap.SugaredLogger.
|
||||
type (
|
||||
Logger = zap.Logger
|
||||
SugaredLogger = zap.SugaredLogger
|
||||
)
|
||||
|
||||
type (
|
||||
// Option is an alias for zap.Option.
|
||||
Option = zap.Option
|
||||
)
|
||||
|
||||
// pkgCallerSkip skips the pkg wrapper code as the caller.
|
||||
var pkgCallerSkip = zap.AddCallerSkip(2)
|
2
main.go
2
main.go
@@ -30,7 +30,7 @@ func init() {
|
||||
flag.StringVar(&configFile, "config", "", "YAML format configuration file")
|
||||
flag.StringVar(&key.Device, "device", "", "Use this device [driver://]name")
|
||||
flag.StringVar(&key.Interface, "interface", "", "Use network INTERFACE (Linux/MacOS only)")
|
||||
flag.StringVar(&key.LogLevel, "loglevel", "info", "Log level [debug|info|warning|error|silent]")
|
||||
flag.StringVar(&key.LogLevel, "loglevel", "info", "Log level [debug|info|warn|error|silent]")
|
||||
flag.StringVar(&key.Proxy, "proxy", "", "Use this proxy [protocol://]host[:port]")
|
||||
flag.StringVar(&key.RestAPI, "restapi", "", "HTTP statistic server listen address")
|
||||
flag.StringVar(&key.TCPSendBufferSize, "tcp-sndbuf", "", "Set TCP send buffer size for netstack")
|
||||
|
@@ -17,7 +17,7 @@ import (
|
||||
const defaultInterval = 1000
|
||||
|
||||
func init() {
|
||||
registerMountPoint("/connections", connectionRouter())
|
||||
registerEndpoint("/connections", connectionRouter())
|
||||
}
|
||||
|
||||
func connectionRouter() http.Handler {
|
||||
|
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerMountPoint("/debug/pprof/", pprofRouter())
|
||||
registerEndpoint("/debug/pprof/", pprofRouter())
|
||||
}
|
||||
|
||||
func pprofRouter() http.Handler {
|
||||
|
@@ -19,7 +19,7 @@ func SetStatsFunc(s func() tcpip.Stats) {
|
||||
}
|
||||
|
||||
func init() {
|
||||
registerMountPoint("/netstats", http.HandlerFunc(getNetStats))
|
||||
registerEndpoint("/netstats", http.HandlerFunc(getNetStats))
|
||||
}
|
||||
|
||||
func getNetStats(w http.ResponseWriter, r *http.Request) {
|
||||
|
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/gorilla/websocket"
|
||||
|
||||
V "github.com/xjasonlyu/tun2socks/v2/internal/version"
|
||||
"github.com/xjasonlyu/tun2socks/v2/log"
|
||||
"github.com/xjasonlyu/tun2socks/v2/tunnel/statistic"
|
||||
)
|
||||
|
||||
@@ -25,11 +24,11 @@ var (
|
||||
},
|
||||
}
|
||||
|
||||
_mountPoints = make(map[string]http.Handler)
|
||||
_endpoints = make(map[string]http.Handler)
|
||||
)
|
||||
|
||||
func registerMountPoint(pattern string, handler http.Handler) {
|
||||
_mountPoints[pattern] = handler
|
||||
func registerEndpoint(pattern string, handler http.Handler) {
|
||||
_endpoints[pattern] = handler
|
||||
}
|
||||
|
||||
func Start(addr, token string) error {
|
||||
@@ -46,11 +45,10 @@ func Start(addr, token string) error {
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(authenticator(token))
|
||||
r.Get("/", hello)
|
||||
r.Get("/logs", getLogs)
|
||||
r.Get("/traffic", traffic)
|
||||
r.Get("/version", version)
|
||||
// attach HTTP handlers
|
||||
for pattern, handler := range _mountPoints {
|
||||
for pattern, handler := range _endpoints {
|
||||
r.Mount(pattern, handler)
|
||||
}
|
||||
})
|
||||
@@ -103,61 +101,6 @@ func authenticator(token string) func(http.Handler) http.Handler {
|
||||
}
|
||||
}
|
||||
|
||||
func getLogs(w http.ResponseWriter, r *http.Request) {
|
||||
lvl := r.URL.Query().Get("level")
|
||||
if lvl == "" {
|
||||
lvl = "info" /* default */
|
||||
}
|
||||
|
||||
level, err := log.ParseLevel(lvl)
|
||||
if err != nil {
|
||||
render.Status(r, http.StatusBadRequest)
|
||||
render.JSON(w, r, ErrBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var wsConn *websocket.Conn
|
||||
if websocket.IsWebSocketUpgrade(r) {
|
||||
wsConn, err = _upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if wsConn == nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
render.Status(r, http.StatusOK)
|
||||
}
|
||||
|
||||
sub := log.Subscribe()
|
||||
defer log.UnSubscribe(sub)
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
for elm := range sub {
|
||||
buf.Reset()
|
||||
|
||||
e := elm.(*log.Event)
|
||||
if e.Level > level {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = json.NewEncoder(buf).Encode(e); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if wsConn == nil {
|
||||
_, err = w.Write(buf.Bytes())
|
||||
w.(http.Flusher).Flush()
|
||||
} else {
|
||||
err = wsConn.WriteMessage(websocket.TextMessage, buf.Bytes())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func traffic(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
err error
|
||||
|
Reference in New Issue
Block a user