diff --git a/.github/update_dependencies.sh b/.github/update_dependencies.sh index 22edc6df..92f4f604 100755 --- a/.github/update_dependencies.sh +++ b/.github/update_dependencies.sh @@ -4,7 +4,7 @@ PROJECTS=$(dirname "$0")/../.. go get -x github.com/sagernet/sing@$(git -C $PROJECTS/sing rev-parse HEAD) go get -x github.com/sagernet/sing-dns@$(git -C $PROJECTS/sing-dns rev-parse HEAD) -go get -x github.com/sagernet/sing-tun@$(git -C $PROJECTS/sing-dns rev-parse HEAD) +go get -x github.com/sagernet/sing-tun@$(git -C $PROJECTS/sing-tun rev-parse HEAD) go get -x github.com/sagernet/sing-shadowsocks@$(git -C $PROJECTS/sing-shadowsocks rev-parse HEAD) go mod tidy pushd test diff --git a/box.go b/box.go index 3f524854..26c1346f 100644 --- a/box.go +++ b/box.go @@ -2,6 +2,8 @@ package box import ( "context" + "io" + "os" "time" "github.com/sagernet/sing-box/adapter" @@ -18,20 +20,54 @@ import ( var _ adapter.Service = (*Box)(nil) type Box struct { - router adapter.Router - logger log.Logger - inbounds []adapter.Inbound - outbounds []adapter.Outbound - createdAt time.Time + createdAt time.Time + router adapter.Router + inbounds []adapter.Inbound + outbounds []adapter.Outbound + logFactory log.Factory + logger log.ContextLogger + logFile *os.File } func New(ctx context.Context, options option.Options) (*Box, error) { createdAt := time.Now() - logger, err := log.NewLogger(common.PtrValueOrDefault(options.Log)) - if err != nil { - return nil, E.Cause(err, "parse log options") + logOptions := common.PtrValueOrDefault(options.Log) + + var logFactory log.Factory + var logFile *os.File + if logOptions.Disabled { + logFactory = log.NewNOPFactory() + } else { + var logWriter io.Writer + switch logOptions.Output { + case "", "stderr": + logWriter = os.Stderr + case "stdout": + logWriter = os.Stdout + default: + var err error + logFile, err = os.OpenFile(logOptions.Output, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) + if err != nil { + return nil, err + } + } + logFormatter := log.Formatter{ + BaseTime: createdAt, + DisableColors: logOptions.DisableColor || logFile != nil, + DisableTimestamp: !logOptions.Timestamp && logFile != nil, + FullTimestamp: logOptions.Timestamp, + TimestampFormat: "-0700 2006-01-02 15:04:05", + } + logFactory = log.NewFactory(logFormatter, logWriter) } - router, err := route.NewRouter(ctx, logger, common.PtrValueOrDefault(options.Route), common.PtrValueOrDefault(options.DNS)) + + router, err := route.NewRouter( + ctx, + logFactory.NewLogger("router"), + logFactory.NewLogger("dns"), + common.PtrValueOrDefault(options.Route), + common.PtrValueOrDefault(options.DNS), + ) if err != nil { return nil, E.Cause(err, "parse route options") } @@ -39,7 +75,18 @@ func New(ctx context.Context, options option.Options) (*Box, error) { outbounds := make([]adapter.Outbound, 0, len(options.Outbounds)) for i, inboundOptions := range options.Inbounds { var in adapter.Inbound - in, err = inbound.New(ctx, router, logger, i, inboundOptions) + var tag string + if inboundOptions.Tag != "" { + tag = inboundOptions.Tag + } else { + tag = F.ToString(i) + } + in, err = inbound.New( + ctx, + router, + logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")), + inboundOptions, + ) if err != nil { return nil, E.Cause(err, "parse inbound[", i, "]") } @@ -47,14 +94,23 @@ func New(ctx context.Context, options option.Options) (*Box, error) { } for i, outboundOptions := range options.Outbounds { var out adapter.Outbound - out, err = outbound.New(router, logger, i, outboundOptions) + var tag string + if outboundOptions.Tag != "" { + tag = outboundOptions.Tag + } else { + tag = F.ToString(i) + } + out, err = outbound.New( + router, + logFactory.NewLogger(F.ToString("outbound/", outboundOptions.Type, "[", tag, "]")), + outboundOptions) if err != nil { return nil, E.Cause(err, "parse outbound[", i, "]") } outbounds = append(outbounds, out) } err = router.Initialize(outbounds, func() adapter.Outbound { - out, oErr := outbound.New(router, logger, 0, option.Outbound{Type: "direct", Tag: "default"}) + out, oErr := outbound.New(router, logFactory.NewLogger("outbound/direct"), option.Outbound{Type: "direct", Tag: "default"}) common.Must(oErr) outbounds = append(outbounds, out) return out @@ -63,20 +119,18 @@ func New(ctx context.Context, options option.Options) (*Box, error) { return nil, err } return &Box{ - router: router, - logger: logger, - inbounds: inbounds, - outbounds: outbounds, - createdAt: createdAt, + router: router, + inbounds: inbounds, + outbounds: outbounds, + createdAt: createdAt, + logFactory: logFactory, + logger: logFactory.NewLogger(""), + logFile: logFile, }, nil } func (s *Box) Start() error { - err := s.logger.Start() - if err != nil { - return err - } - err = s.router.Start() + err := s.router.Start() if err != nil { return err } @@ -99,6 +153,6 @@ func (s *Box) Close() error { } return common.Close( s.router, - s.logger, + common.PtrOrNil(s.logFile), ) } diff --git a/cmd/sing-box/cmd_check.go b/cmd/sing-box/cmd_check.go index adca5743..e6d55d12 100644 --- a/cmd/sing-box/cmd_check.go +++ b/cmd/sing-box/cmd_check.go @@ -5,10 +5,10 @@ import ( "os" "github.com/sagernet/sing-box" + "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/goccy/go-json" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -21,17 +21,17 @@ var commandCheck = &cobra.Command{ func checkConfiguration(cmd *cobra.Command, args []string) { configContent, err := os.ReadFile(configPath) if err != nil { - logrus.Fatal("read config: ", err) + log.Fatal("read config: ", err) } var options option.Options err = json.Unmarshal(configContent, &options) if err != nil { - logrus.Fatal("decode config: ", err) + log.Fatal("decode config: ", err) } ctx, cancel := context.WithCancel(context.Background()) _, err = box.New(ctx, options) if err != nil { - logrus.Fatal("create service: ", err) + log.Fatal("create service: ", err) } cancel() } diff --git a/cmd/sing-box/cmd_format.go b/cmd/sing-box/cmd_format.go index c0408656..9e51cd9d 100644 --- a/cmd/sing-box/cmd_format.go +++ b/cmd/sing-box/cmd_format.go @@ -5,10 +5,10 @@ import ( "os" "path/filepath" + "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/goccy/go-json" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -27,19 +27,19 @@ func init() { func formatConfiguration(cmd *cobra.Command, args []string) { configContent, err := os.ReadFile(configPath) if err != nil { - logrus.Fatal("read config: ", err) + log.Fatal("read config: ", err) } var options option.Options err = json.Unmarshal(configContent, &options) if err != nil { - logrus.Fatal("decode config: ", err) + log.Fatal("decode config: ", err) } buffer := new(bytes.Buffer) encoder := json.NewEncoder(buffer) encoder.SetIndent("", " ") err = encoder.Encode(options) if err != nil { - logrus.Fatal("encode config: ", err) + log.Fatal("encode config: ", err) } if !commandFormatFlagWrite { os.Stdout.WriteString(buffer.String() + "\n") @@ -50,12 +50,12 @@ func formatConfiguration(cmd *cobra.Command, args []string) { } output, err := os.Create(configPath) if err != nil { - logrus.Fatal("open output: ", err) + log.Fatal("open output: ", err) } _, err = output.Write(buffer.Bytes()) output.Close() if err != nil { - logrus.Fatal("write output: ", err) + log.Fatal("write output: ", err) } outputPath, _ := filepath.Abs(configPath) os.Stderr.WriteString(outputPath + "\n") diff --git a/cmd/sing-box/cmd_run.go b/cmd/sing-box/cmd_run.go index b98f4000..a4c35052 100644 --- a/cmd/sing-box/cmd_run.go +++ b/cmd/sing-box/cmd_run.go @@ -7,10 +7,10 @@ import ( "syscall" "github.com/sagernet/sing-box" + "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/goccy/go-json" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -23,12 +23,12 @@ var commandRun = &cobra.Command{ func run(cmd *cobra.Command, args []string) { configContent, err := os.ReadFile(configPath) if err != nil { - logrus.Fatal("read config: ", err) + log.Fatal("read config: ", err) } var options option.Options err = json.Unmarshal(configContent, &options) if err != nil { - logrus.Fatal("decode config: ", err) + log.Fatal("decode config: ", err) } if disableColor { if options.Log == nil { @@ -39,11 +39,11 @@ func run(cmd *cobra.Command, args []string) { ctx, cancel := context.WithCancel(context.Background()) instance, err := box.New(ctx, options) if err != nil { - logrus.Fatal("create service: ", err) + log.Fatal("create service: ", err) } err = instance.Start() if err != nil { - logrus.Fatal("start service: ", err) + log.Fatal("start service: ", err) } osSignals := make(chan os.Signal, 1) signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM) diff --git a/cmd/sing-box/main.go b/cmd/sing-box/main.go index e1b31b58..3d66f4ae 100644 --- a/cmd/sing-box/main.go +++ b/cmd/sing-box/main.go @@ -5,15 +5,9 @@ import ( "github.com/sagernet/sing-box/log" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) -func init() { - logrus.StandardLogger().SetLevel(logrus.TraceLevel) - logrus.StandardLogger().SetFormatter(&log.LogrusTextFormatter{}) -} - var ( configPath string workingDir string @@ -38,17 +32,14 @@ func init() { func main() { if err := mainCommand.Execute(); err != nil { - logrus.Fatal(err) + log.Fatal(err) } } func preRun(cmd *cobra.Command, args []string) { - if disableColor { - logrus.StandardLogger().SetFormatter(&log.LogrusTextFormatter{DisableColors: true}) - } if workingDir != "" { if err := os.Chdir(workingDir); err != nil { - logrus.Fatal(err) + log.Fatal(err) } } } diff --git a/go.mod b/go.mod index a1e090ea..5e9a72c6 100644 --- a/go.mod +++ b/go.mod @@ -7,11 +7,10 @@ require ( github.com/goccy/go-json v0.9.8 github.com/logrusorgru/aurora v2.0.3+incompatible github.com/oschwald/maxminddb-golang v1.9.0 - github.com/sagernet/sing v0.0.0-20220711103842-d3fb2260ef61 + github.com/sagernet/sing v0.0.0-20220712060558-029ab1ce4f91 github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619 github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 github.com/sagernet/sing-tun v0.0.0-20220711091522-4f7247190c96 - github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.5.0 github.com/stretchr/testify v1.8.0 github.com/vishvananda/netlink v1.1.0 diff --git a/go.sum b/go.sum index b80bb26d..ea4632e5 100644 --- a/go.sum +++ b/go.sum @@ -25,23 +25,20 @@ github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm 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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagernet/sing v0.0.0-20220711103842-d3fb2260ef61 h1:sOx7t+MFssiCAY2afRHQSmkWZNpLQnjF0Hwv/TNVMvk= -github.com/sagernet/sing v0.0.0-20220711103842-d3fb2260ef61/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c= +github.com/sagernet/sing v0.0.0-20220712060558-029ab1ce4f91 h1:fYsRChEViZHDvrOLp7fbswYCH3txaVyAl1zB0cnSNlc= +github.com/sagernet/sing v0.0.0-20220712060558-029ab1ce4f91/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c= github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619 h1:oHbOmq1WS0XaZmXp6WpxzyB2xeyRIA1/L8EJKuNntfY= github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619/go.mod h1:y2fpvoxukw3G7eApIZwkcpcG/NE4AB8pCQI0Qd8rMqk= github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 h1:whNDUGOAX5GPZkSy4G3Gv9QyIgk5SXRyjkRuP7ohF8k= github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw= github.com/sagernet/sing-tun v0.0.0-20220711091522-4f7247190c96 h1:BPsCEEKmww4PCuL2qCKGpwuS/HllNz4/G7EjvSHlXXg= github.com/sagernet/sing-tun v0.0.0-20220711091522-4f7247190c96/go.mod h1:OLQnVTGk8NMVdoegQvenGHsGEv3diSMWe9Uh02cel0E= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -55,7 +52,6 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0= golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d h1:/m5NbqQelATgoSPVC2Z23sR4kVNokFwDDyWh/3rGY+I= golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/inbound/builder.go b/inbound/builder.go index 1e60ef69..571a4eb6 100644 --- a/inbound/builder.go +++ b/inbound/builder.go @@ -9,33 +9,25 @@ import ( "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" - F "github.com/sagernet/sing/common/format" ) -func New(ctx context.Context, router adapter.Router, logger log.Logger, index int, options option.Inbound) (adapter.Inbound, error) { +func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.Inbound) (adapter.Inbound, error) { if common.IsEmptyByEquals(options) { return nil, E.New("empty inbound config") } - var tag string - if options.Tag != "" { - tag = options.Tag - } else { - tag = F.ToString(index) - } - inboundLogger := logger.WithPrefix(F.ToString("inbound/", options.Type, "[", tag, "]: ")) switch options.Type { case C.TypeDirect: - return NewDirect(ctx, router, inboundLogger, options.Tag, options.DirectOptions), nil + return NewDirect(ctx, router, logger, options.Tag, options.DirectOptions), nil case C.TypeSocks: - return NewSocks(ctx, router, inboundLogger, options.Tag, options.SocksOptions), nil + return NewSocks(ctx, router, logger, options.Tag, options.SocksOptions), nil case C.TypeHTTP: - return NewHTTP(ctx, router, inboundLogger, options.Tag, options.HTTPOptions), nil + return NewHTTP(ctx, router, logger, options.Tag, options.HTTPOptions), nil case C.TypeMixed: - return NewMixed(ctx, router, inboundLogger, options.Tag, options.MixedOptions), nil + return NewMixed(ctx, router, logger, options.Tag, options.MixedOptions), nil case C.TypeShadowsocks: - return NewShadowsocks(ctx, router, inboundLogger, options.Tag, options.ShadowsocksOptions) + return NewShadowsocks(ctx, router, logger, options.Tag, options.ShadowsocksOptions) case C.TypeTun: - return NewTun(ctx, router, inboundLogger, options.Tag, options.TunOptions) + return NewTun(ctx, router, logger, options.Tag, options.TunOptions) default: return nil, E.New("unknown inbound type: ", options.Type) } diff --git a/inbound/default.go b/inbound/default.go index 8b7f50c7..1879d60e 100644 --- a/inbound/default.go +++ b/inbound/default.go @@ -29,7 +29,7 @@ type myInboundAdapter struct { network []string ctx context.Context router adapter.Router - logger log.Logger + logger log.ContextLogger tag string listenOptions option.ListenOptions connHandler adapter.ConnectionHandler @@ -107,19 +107,19 @@ func (a *myInboundAdapter) upstreamContextHandler() adapter.UpstreamHandlerAdapt } func (a *myInboundAdapter) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - a.logger.WithContext(ctx).Info("inbound connection to ", metadata.Destination) + a.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination) return a.router.RouteConnection(ctx, conn, metadata) } func (a *myInboundAdapter) streamPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - a.logger.WithContext(ctx).Info("inbound packet connection to ", metadata.Destination) + a.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination) return a.router.RoutePacketConnection(ctx, conn, metadata) } func (a *myInboundAdapter) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - ctx = log.ContextWithID(ctx) - a.logger.WithContext(ctx).Info("inbound packet connection from ", metadata.Source) - a.logger.WithContext(ctx).Info("inbound packet connection to ", metadata.Destination) + ctx = log.ContextWithNewID(ctx) + a.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source) + a.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination) return a.router.RoutePacketConnection(ctx, conn, metadata) } @@ -131,7 +131,7 @@ func (a *myInboundAdapter) loopTCPIn() { return } go func() { - ctx := log.ContextWithID(a.ctx) + ctx := log.ContextWithNewID(a.ctx) var metadata adapter.InboundContext metadata.Inbound = a.tag metadata.SniffEnabled = a.listenOptions.SniffEnabled @@ -139,7 +139,7 @@ func (a *myInboundAdapter) loopTCPIn() { metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy) metadata.Network = C.NetworkTCP metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr()) - a.logger.WithContext(ctx).Info("inbound connection from ", metadata.Source) + a.logger.InfoContext(ctx, "inbound connection from ", metadata.Source) hErr := a.connHandler.NewConnection(ctx, conn, metadata) if hErr != nil { conn.Close() @@ -235,13 +235,13 @@ func (a *myInboundAdapter) NewError(ctx context.Context, err error) { NewError(a.logger, ctx, err) } -func NewError(logger log.Logger, ctx context.Context, err error) { +func NewError(logger log.ContextLogger, ctx context.Context, err error) { common.Close(err) if E.IsClosed(err) || E.IsCanceled(err) { - logger.WithContext(ctx).Debug("connection closed") + logger.DebugContext(ctx, "connection closed") return } - logger.WithContext(ctx).Error(err) + logger.ErrorContext(ctx, err) } func (a *myInboundAdapter) writePacket(buffer *buf.Buffer, destination M.Socksaddr) error { diff --git a/inbound/direct.go b/inbound/direct.go index 55f079f2..9307a1d3 100644 --- a/inbound/direct.go +++ b/inbound/direct.go @@ -24,7 +24,7 @@ type Direct struct { overrideDestination M.Socksaddr } -func NewDirect(ctx context.Context, router adapter.Router, logger log.Logger, tag string, options option.DirectInboundOptions) *Direct { +func NewDirect(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.DirectInboundOptions) *Direct { inbound := &Direct{ myInboundAdapter: myInboundAdapter{ protocol: C.TypeDirect, @@ -64,7 +64,7 @@ func (d *Direct) NewConnection(ctx context.Context, conn net.Conn, metadata adap case 3: metadata.Destination.Port = d.overrideDestination.Port } - d.logger.WithContext(ctx).Info("inbound connection to ", metadata.Destination) + d.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination) return d.router.RouteConnection(ctx, conn, metadata) } @@ -79,6 +79,6 @@ func (d *Direct) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.B case 3: metadata.Destination.Port = d.overrideDestination.Port } - d.udpNat.NewPacketDirect(adapter.WithContext(log.ContextWithID(ctx), &metadata), metadata.Source.AddrPort(), conn, buffer, adapter.UpstreamMetadata(metadata)) + d.udpNat.NewPacketDirect(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), metadata.Source.AddrPort(), conn, buffer, adapter.UpstreamMetadata(metadata)) return nil } diff --git a/inbound/dns.go b/inbound/dns.go index 47889589..facc8ae1 100644 --- a/inbound/dns.go +++ b/inbound/dns.go @@ -15,7 +15,7 @@ import ( "golang.org/x/net/dns/dnsmessage" ) -func NewDNSConnection(ctx context.Context, router adapter.Router, logger log.Logger, conn net.Conn, metadata adapter.InboundContext) error { +func NewDNSConnection(ctx context.Context, router adapter.Router, logger log.ContextLogger, conn net.Conn, metadata adapter.InboundContext) error { ctx = adapter.WithContext(ctx, &metadata) _buffer := buf.StackNewSize(1024) defer common.KeepAlive(_buffer) @@ -43,7 +43,7 @@ func NewDNSConnection(ctx context.Context, router adapter.Router, logger log.Log if len(message.Questions) > 0 { question := message.Questions[0] metadata.Domain = string(question.Name.Data[:question.Name.Length-1]) - logger.WithContext(ctx).Debug("inbound dns query ", formatDNSQuestion(question), " from ", metadata.Source) + logger.DebugContext(ctx, "inbound dns query ", formatDNSQuestion(question), " from ", metadata.Source) } go func() error { response, err := router.Exchange(ctx, &message) @@ -67,7 +67,7 @@ func NewDNSConnection(ctx context.Context, router adapter.Router, logger log.Log } } -func NewDNSPacketConnection(ctx context.Context, router adapter.Router, logger log.Logger, conn N.PacketConn, metadata adapter.InboundContext) error { +func NewDNSPacketConnection(ctx context.Context, router adapter.Router, logger log.ContextLogger, conn N.PacketConn, metadata adapter.InboundContext) error { ctx = adapter.WithContext(ctx, &metadata) _buffer := buf.StackNewSize(1024) defer common.KeepAlive(_buffer) @@ -87,7 +87,7 @@ func NewDNSPacketConnection(ctx context.Context, router adapter.Router, logger l if len(message.Questions) > 0 { question := message.Questions[0] metadata.Domain = string(question.Name.Data[:question.Name.Length-1]) - logger.WithContext(ctx).Debug("inbound dns query ", formatDNSQuestion(question), " from ", metadata.Source) + logger.DebugContext(ctx, "inbound dns query ", formatDNSQuestion(question), " from ", metadata.Source) } go func() error { response, err := router.Exchange(ctx, &message) diff --git a/inbound/http.go b/inbound/http.go index 11708f80..dd263dd3 100644 --- a/inbound/http.go +++ b/inbound/http.go @@ -21,7 +21,7 @@ type HTTP struct { authenticator auth.Authenticator } -func NewHTTP(ctx context.Context, router adapter.Router, logger log.Logger, tag string, options option.SimpleInboundOptions) *HTTP { +func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SimpleInboundOptions) *HTTP { inbound := &HTTP{ myInboundAdapter{ protocol: C.TypeHTTP, diff --git a/inbound/mixed.go b/inbound/mixed.go index d956bc2c..27baab16 100644 --- a/inbound/mixed.go +++ b/inbound/mixed.go @@ -27,7 +27,7 @@ type Mixed struct { authenticator auth.Authenticator } -func NewMixed(ctx context.Context, router adapter.Router, logger log.Logger, tag string, options option.SimpleInboundOptions) *Mixed { +func NewMixed(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SimpleInboundOptions) *Mixed { inbound := &Mixed{ myInboundAdapter{ protocol: C.TypeMixed, diff --git a/inbound/shadowsocks.go b/inbound/shadowsocks.go index 315f15ec..eee3a268 100644 --- a/inbound/shadowsocks.go +++ b/inbound/shadowsocks.go @@ -17,7 +17,7 @@ import ( N "github.com/sagernet/sing/common/network" ) -func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Logger, tag string, options option.ShadowsocksInboundOptions) (adapter.Inbound, error) { +func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (adapter.Inbound, error) { if len(options.Users) > 0 && len(options.Destinations) > 0 { return nil, E.New("users and destinations options must not be combined") } @@ -37,7 +37,7 @@ type Shadowsocks struct { service shadowsocks.Service } -func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Logger, tag string, options option.ShadowsocksInboundOptions) (*Shadowsocks, error) { +func newShadowsocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (*Shadowsocks, error) { inbound := &Shadowsocks{ myInboundAdapter: myInboundAdapter{ protocol: C.TypeShadowsocks, @@ -73,9 +73,9 @@ func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Logge } func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return h.service.NewConnection(adapter.WithContext(log.ContextWithID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) + return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) } func (h *Shadowsocks) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error { - return h.service.NewPacket(adapter.WithContext(log.ContextWithID(ctx), &metadata), conn, buffer, adapter.UpstreamMetadata(metadata)) + return h.service.NewPacket(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, buffer, adapter.UpstreamMetadata(metadata)) } diff --git a/inbound/shadowsocks_multi.go b/inbound/shadowsocks_multi.go index 0f8dcb4f..bb521186 100644 --- a/inbound/shadowsocks_multi.go +++ b/inbound/shadowsocks_multi.go @@ -24,7 +24,7 @@ type ShadowsocksMulti struct { users []option.ShadowsocksUser } -func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.Logger, tag string, options option.ShadowsocksInboundOptions) (*ShadowsocksMulti, error) { +func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (*ShadowsocksMulti, error) { inbound := &ShadowsocksMulti{ myInboundAdapter: myInboundAdapter{ protocol: C.TypeShadowsocks, @@ -68,11 +68,11 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log. } func (h *ShadowsocksMulti) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return h.service.NewConnection(adapter.WithContext(log.ContextWithID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) + return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) } func (h *ShadowsocksMulti) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error { - return h.service.NewPacket(adapter.WithContext(log.ContextWithID(ctx), &metadata), conn, buffer, adapter.UpstreamMetadata(metadata)) + return h.service.NewPacket(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, buffer, adapter.UpstreamMetadata(metadata)) } func (h *ShadowsocksMulti) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { @@ -81,7 +81,7 @@ func (h *ShadowsocksMulti) newConnection(ctx context.Context, conn net.Conn, met if user == "" { user = F.ToString(userCtx.User) } - h.logger.WithContext(ctx).Info("[", user, "] inbound connection to ", metadata.Destination) + h.logger.InfoContext(ctx, "[", user, "] inbound connection to ", metadata.Destination) return h.router.RouteConnection(ctx, conn, metadata) } @@ -91,8 +91,8 @@ func (h *ShadowsocksMulti) newPacketConnection(ctx context.Context, conn N.Packe if user == "" { user = F.ToString(userCtx.User) } - ctx = log.ContextWithID(ctx) - h.logger.WithContext(ctx).Info("[", user, "] inbound packet connection from ", metadata.Source) - h.logger.WithContext(ctx).Info("[", user, "] inbound packet connection to ", metadata.Destination) + ctx = log.ContextWithNewID(ctx) + h.logger.InfoContext(ctx, "[", user, "] inbound packet connection from ", metadata.Source) + h.logger.InfoContext(ctx, "[", user, "] inbound packet connection to ", metadata.Destination) return h.router.RoutePacketConnection(ctx, conn, metadata) } diff --git a/inbound/shadowsocks_relay.go b/inbound/shadowsocks_relay.go index 9136caba..13f56c89 100644 --- a/inbound/shadowsocks_relay.go +++ b/inbound/shadowsocks_relay.go @@ -24,7 +24,7 @@ type ShadowsocksRelay struct { destinations []option.ShadowsocksDestination } -func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log.Logger, tag string, options option.ShadowsocksInboundOptions) (*ShadowsocksRelay, error) { +func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksInboundOptions) (*ShadowsocksRelay, error) { inbound := &ShadowsocksRelay{ myInboundAdapter: myInboundAdapter{ protocol: C.TypeShadowsocks, @@ -68,11 +68,11 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log. } func (h *ShadowsocksRelay) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return h.service.NewConnection(adapter.WithContext(log.ContextWithID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) + return h.service.NewConnection(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, adapter.UpstreamMetadata(metadata)) } func (h *ShadowsocksRelay) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error { - return h.service.NewPacket(adapter.WithContext(log.ContextWithID(ctx), &metadata), conn, buffer, adapter.UpstreamMetadata(metadata)) + return h.service.NewPacket(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), conn, buffer, adapter.UpstreamMetadata(metadata)) } func (h *ShadowsocksRelay) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { @@ -81,7 +81,7 @@ func (h *ShadowsocksRelay) newConnection(ctx context.Context, conn net.Conn, met if destination == "" { destination = F.ToString(userCtx.User) } - h.logger.WithContext(ctx).Info("[", destination, "] inbound connection to ", metadata.Destination) + h.logger.InfoContext(ctx, "[", destination, "] inbound connection to ", metadata.Destination) return h.router.RouteConnection(ctx, conn, metadata) } @@ -91,8 +91,8 @@ func (h *ShadowsocksRelay) newPacketConnection(ctx context.Context, conn N.Packe if destination == "" { destination = F.ToString(userCtx.User) } - ctx = log.ContextWithID(ctx) - h.logger.WithContext(ctx).Info("[", destination, "] inbound packet connection from ", metadata.Source) - h.logger.WithContext(ctx).Info("[", destination, "] inbound packet connection to ", metadata.Destination) + ctx = log.ContextWithNewID(ctx) + h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection from ", metadata.Source) + h.logger.InfoContext(ctx, "[", destination, "] inbound packet connection to ", metadata.Destination) return h.router.RoutePacketConnection(ctx, conn, metadata) } diff --git a/inbound/socks.go b/inbound/socks.go index 496804a7..91c12f01 100644 --- a/inbound/socks.go +++ b/inbound/socks.go @@ -20,7 +20,7 @@ type Socks struct { authenticator auth.Authenticator } -func NewSocks(ctx context.Context, router adapter.Router, logger log.Logger, tag string, options option.SimpleInboundOptions) *Socks { +func NewSocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SimpleInboundOptions) *Socks { inbound := &Socks{ myInboundAdapter{ protocol: C.TypeSocks, diff --git a/inbound/tun.go b/inbound/tun.go index ea37cddc..e6888551 100644 --- a/inbound/tun.go +++ b/inbound/tun.go @@ -31,7 +31,7 @@ type Tun struct { ctx context.Context router adapter.Router - logger log.Logger + logger log.ContextLogger inboundOptions option.InboundOptions tunName string tunMTU uint32 @@ -44,7 +44,7 @@ type Tun struct { tun *tun.GVisorTun } -func NewTun(ctx context.Context, router adapter.Router, logger log.Logger, tag string, options option.TunInboundOptions) (*Tun, error) { +func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions) (*Tun, error) { tunName := options.InterfaceName if tunName == "" { tunName = mkInterfaceName() @@ -107,7 +107,7 @@ func (t *Tun) Close() error { } func (t *Tun) NewConnection(ctx context.Context, conn net.Conn, upstreamMetadata M.Metadata) error { - ctx = log.ContextWithID(ctx) + ctx = log.ContextWithNewID(ctx) var metadata adapter.InboundContext metadata.Inbound = t.tag metadata.Network = C.NetworkTCP @@ -121,13 +121,13 @@ func (t *Tun) NewConnection(ctx context.Context, conn net.Conn, upstreamMetadata return NewDNSConnection(ctx, t.router, t.logger, conn, metadata) }) } - t.logger.WithContext(ctx).Info("inbound connection from ", metadata.Source) - t.logger.WithContext(ctx).Info("inbound connection to ", metadata.Destination) + t.logger.InfoContext(ctx, "inbound connection from ", metadata.Source) + t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination) return t.router.RouteConnection(ctx, conn, metadata) } func (t *Tun) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstreamMetadata M.Metadata) error { - ctx = log.ContextWithID(ctx) + ctx = log.ContextWithNewID(ctx) var metadata adapter.InboundContext metadata.Inbound = t.tag metadata.Network = C.NetworkUDP @@ -141,8 +141,8 @@ func (t *Tun) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstre return NewDNSPacketConnection(ctx, t.router, t.logger, conn, metadata) }) } - t.logger.WithContext(ctx).Info("inbound packet connection from ", metadata.Source) - t.logger.WithContext(ctx).Info("inbound packet connection to ", metadata.Destination) + t.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source) + t.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination) return t.router.RoutePacketConnection(ctx, conn, metadata) } diff --git a/inbound/tun_disabled.go b/inbound/tun_disabled.go index 6c21a265..2867b279 100644 --- a/inbound/tun_disabled.go +++ b/inbound/tun_disabled.go @@ -11,6 +11,6 @@ import ( E "github.com/sagernet/sing/common/exceptions" ) -func NewTun(ctx context.Context, router adapter.Router, logger log.Logger, tag string, options option.TunInboundOptions) (adapter.Inbound, error) { +func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions) (adapter.Inbound, error) { return nil, E.New("tun disabled in this build") } diff --git a/log/default.go b/log/default.go new file mode 100644 index 00000000..bff2a39d --- /dev/null +++ b/log/default.go @@ -0,0 +1,119 @@ +package log + +import ( + "context" + "io" + "os" + "time" + + F "github.com/sagernet/sing/common/format" +) + +var _ Factory = (*simpleFactory)(nil) + +type simpleFactory struct { + formatter Formatter + writer io.Writer + level Level +} + +func NewFactory(formatter Formatter, writer io.Writer) Factory { + return &simpleFactory{ + formatter: formatter, + writer: writer, + level: LevelTrace, + } +} + +func (f *simpleFactory) Level() Level { + return f.level +} + +func (f *simpleFactory) SetLevel(level Level) { + f.level = level +} + +func (f *simpleFactory) Logger() ContextLogger { + return f.NewLogger("") +} + +func (f *simpleFactory) NewLogger(tag string) ContextLogger { + return &simpleLogger{f, tag} +} + +var _ ContextLogger = (*simpleLogger)(nil) + +type simpleLogger struct { + *simpleFactory + tag string +} + +func (l *simpleLogger) Log(ctx context.Context, level Level, args []any) { + if level > l.level { + return + } + message := l.formatter.Format(ctx, level, l.tag, F.ToString(args...), time.Now()) + "\n" + if level == LevelPanic { + panic(message) + } + l.writer.Write([]byte(message)) + if level == LevelFatal { + os.Exit(1) + } +} + +func (l *simpleLogger) Trace(args ...any) { + l.Log(nil, LevelTrace, args) +} + +func (l *simpleLogger) Debug(args ...any) { + l.Log(nil, LevelDebug, args) +} + +func (l *simpleLogger) Info(args ...any) { + l.Log(nil, LevelInfo, args) +} + +func (l *simpleLogger) Warn(args ...any) { + l.Log(nil, LevelWarn, args) +} + +func (l *simpleLogger) Error(args ...any) { + l.Log(nil, LevelError, args) +} + +func (l *simpleLogger) Fatal(args ...any) { + l.Log(nil, LevelFatal, args) +} + +func (l *simpleLogger) Panic(args ...any) { + l.Log(nil, LevelPanic, args) +} + +func (l *simpleLogger) TraceContext(ctx context.Context, args ...any) { + l.Log(ctx, LevelTrace, args) +} + +func (l *simpleLogger) DebugContext(ctx context.Context, args ...any) { + l.Log(ctx, LevelDebug, args) +} + +func (l *simpleLogger) InfoContext(ctx context.Context, args ...any) { + l.Log(ctx, LevelInfo, args) +} + +func (l *simpleLogger) WarnContext(ctx context.Context, args ...any) { + l.Log(ctx, LevelWarn, args) +} + +func (l *simpleLogger) ErrorContext(ctx context.Context, args ...any) { + l.Log(ctx, LevelError, args) +} + +func (l *simpleLogger) FatalContext(ctx context.Context, args ...any) { + l.Log(ctx, LevelFatal, args) +} + +func (l *simpleLogger) PanicContext(ctx context.Context, args ...any) { + l.Log(ctx, LevelPanic, args) +} diff --git a/log/export.go b/log/export.go new file mode 100644 index 00000000..0373794f --- /dev/null +++ b/log/export.go @@ -0,0 +1,69 @@ +package log + +import ( + "context" + "os" + "time" +) + +var std ContextLogger + +func init() { + std = NewFactory(Formatter{BaseTime: time.Now()}, os.Stderr).Logger() +} + +func Trace(args ...any) { + std.Trace(args...) +} + +func Debug(args ...any) { + std.Debug(args...) +} + +func Info(args ...any) { + std.Info(args...) +} + +func Warn(args ...any) { + std.Warn(args...) +} + +func Error(args ...any) { + std.Error(args...) +} + +func Fatal(args ...any) { + std.Fatal(args...) +} + +func Panic(args ...any) { + std.Panic(args...) +} + +func TraceContext(ctx context.Context, args ...any) { + std.TraceContext(ctx, args...) +} + +func DebugContext(ctx context.Context, args ...any) { + std.DebugContext(ctx, args...) +} + +func InfoContext(ctx context.Context, args ...any) { + std.InfoContext(ctx, args...) +} + +func WarnContext(ctx context.Context, args ...any) { + std.WarnContext(ctx, args...) +} + +func ErrorContext(ctx context.Context, args ...any) { + std.ErrorContext(ctx, args...) +} + +func FatalContext(ctx context.Context, args ...any) { + std.FatalContext(ctx, args...) +} + +func PanicContext(ctx context.Context, args ...any) { + std.PanicContext(ctx, args...) +} diff --git a/log/factory.go b/log/factory.go new file mode 100644 index 00000000..6aab0401 --- /dev/null +++ b/log/factory.go @@ -0,0 +1,45 @@ +package log + +import ( + "context" + + "github.com/sagernet/sing/common/observable" +) + +type Factory interface { + Level() Level + SetLevel(level Level) + Logger() ContextLogger + NewLogger(tag string) ContextLogger +} + +type ObservableFactory interface { + Factory + observable.Observable[Entry] +} + +type Entry struct { + Level Level + Message string +} + +type Logger interface { + Trace(args ...any) + Debug(args ...any) + Info(args ...any) + Warn(args ...any) + Error(args ...any) + Fatal(args ...any) + Panic(args ...any) +} + +type ContextLogger interface { + Logger + TraceContext(ctx context.Context, args ...any) + DebugContext(ctx context.Context, args ...any) + InfoContext(ctx context.Context, args ...any) + WarnContext(ctx context.Context, args ...any) + ErrorContext(ctx context.Context, args ...any) + FatalContext(ctx context.Context, args ...any) + PanicContext(ctx context.Context, args ...any) +} diff --git a/log/format.go b/log/format.go new file mode 100644 index 00000000..583be463 --- /dev/null +++ b/log/format.go @@ -0,0 +1,83 @@ +package log + +import ( + "context" + "strconv" + "strings" + "time" + + F "github.com/sagernet/sing/common/format" + + "github.com/logrusorgru/aurora" +) + +type Formatter struct { + BaseTime time.Time + DisableColors bool + DisableTimestamp bool + FullTimestamp bool + TimestampFormat string +} + +func (f Formatter) Format(ctx context.Context, level Level, tag string, message string, timestamp time.Time) string { + levelString := strings.ToUpper(FormatLevel(level)) + if !f.DisableColors { + switch level { + case LevelDebug, LevelTrace: + levelString = aurora.White(levelString).String() + case LevelInfo: + levelString = aurora.Cyan(levelString).String() + case LevelWarn: + levelString = aurora.Yellow(levelString).String() + case LevelError, LevelFatal, LevelPanic: + levelString = aurora.Red(levelString).String() + } + } + if tag != "" { + message = tag + ": " + message + } + var id uint32 + var hasId bool + if ctx != nil { + id, hasId = IDFromContext(ctx) + } + if hasId { + var color aurora.Color + color = aurora.Color(uint8(id)) + color %= 215 + row := uint(color / 36) + column := uint(color % 36) + + var r, g, b float32 + r = float32(row * 51) + g = float32(column / 6 * 51) + b = float32((column % 6) * 51) + luma := 0.2126*r + 0.7152*g + 0.0722*b + if luma < 60 { + row = 5 - row + column = 35 - column + color = aurora.Color(row*36 + column) + } + color += 16 + color = color << 16 + color |= 1 << 14 + message = F.ToString("[", aurora.Colorize(id, color).String(), "] ", message) + } + switch { + case f.DisableTimestamp: + message = levelString + " " + message + case f.FullTimestamp: + message = F.ToString(int(timestamp.Sub(f.BaseTime)/time.Second)) + " " + levelString + " " + message + default: + message = levelString + "[" + xd(int(timestamp.Sub(f.BaseTime)/time.Second), 4) + "] " + message + } + return message +} + +func xd(value int, x int) string { + message := strconv.Itoa(value) + for len(message) < x { + message = "0" + message + } + return message +} diff --git a/log/id.go b/log/id.go index 4f77b77c..3ced3cb2 100644 --- a/log/id.go +++ b/log/id.go @@ -11,23 +11,13 @@ func init() { random.InitializeSeed() } -var idType = (*idContext)(nil) +type idKey struct{} -type idContext struct { - context.Context - id uint32 +func ContextWithNewID(ctx context.Context) context.Context { + return context.WithValue(ctx, (*idKey)(nil), rand.Uint32()) } -func (c *idContext) Value(key any) any { - if key == idType { - return c - } - return c.Context.Value(key) -} - -func ContextWithID(ctx context.Context) context.Context { - if ctx.Value(idType) != nil { - return ctx - } - return &idContext{ctx, rand.Uint32()} +func IDFromContext(ctx context.Context) (uint32, bool) { + id, loaded := ctx.Value((*idKey)(nil)).(uint32) + return id, loaded } diff --git a/log/level.go b/log/level.go new file mode 100644 index 00000000..b216fa34 --- /dev/null +++ b/log/level.go @@ -0,0 +1,59 @@ +package log + +import ( + E "github.com/sagernet/sing/common/exceptions" +) + +type Level = uint8 + +const ( + LevelPanic Level = iota + LevelFatal + LevelError + LevelWarn + LevelInfo + LevelDebug + LevelTrace +) + +func FormatLevel(level Level) string { + switch level { + case LevelTrace: + return "trace" + case LevelDebug: + return "debug" + case LevelInfo: + return "info" + case LevelWarn: + return "warn" + case LevelError: + return "error" + case LevelFatal: + return "fatal" + case LevelPanic: + return "panic" + default: + return "unknown" + } +} + +func ParseLevel(level string) (Level, error) { + switch level { + case "trace": + return LevelTrace, nil + case "debug": + return LevelDebug, nil + case "info": + return LevelInfo, nil + case "warn", "warning": + return LevelWarn, nil + case "error": + return LevelError, nil + case "fatal": + return LevelFatal, nil + case "panic": + return LevelPanic, nil + default: + return LevelTrace, E.New("unknown log level: ", level) + } +} diff --git a/log/log.go b/log/log.go deleted file mode 100644 index d0cce52d..00000000 --- a/log/log.go +++ /dev/null @@ -1,30 +0,0 @@ -package log - -import ( - "context" - - "github.com/sagernet/sing-box/option" -) - -type Logger interface { - Start() error - Close() error - Trace(args ...interface{}) - Debug(args ...interface{}) - Info(args ...interface{}) - Print(args ...interface{}) - Warn(args ...interface{}) - Warning(args ...interface{}) - Error(args ...interface{}) - Fatal(args ...interface{}) - Panic(args ...interface{}) - WithContext(ctx context.Context) Logger - WithPrefix(prefix string) Logger -} - -func NewLogger(options option.LogOption) (Logger, error) { - if options.Disabled { - return NewNopLogger(), nil - } - return NewLogrusLogger(options) -} diff --git a/log/logrus.go b/log/logrus.go deleted file mode 100644 index 61d8f658..00000000 --- a/log/logrus.go +++ /dev/null @@ -1,74 +0,0 @@ -package log - -import ( - "context" - "os" - - "github.com/sagernet/sing-box/option" - "github.com/sagernet/sing/common" - E "github.com/sagernet/sing/common/exceptions" - F "github.com/sagernet/sing/common/format" - - "github.com/sirupsen/logrus" -) - -var _ Logger = (*logrusLogger)(nil) - -type logrusLogger struct { - abstractLogrusLogger - outputPath string - output *os.File -} - -type abstractLogrusLogger interface { - logrus.Ext1FieldLogger - WithContext(ctx context.Context) *logrus.Entry -} - -func NewLogrusLogger(options option.LogOption) (*logrusLogger, error) { - logger := logrus.New() - logger.SetLevel(logrus.TraceLevel) - logger.SetFormatter(&LogrusTextFormatter{ - DisableColors: options.DisableColor || options.Output != "", - DisableTimestamp: !options.Timestamp && options.Output != "", - FullTimestamp: options.Timestamp, - }) - logger.AddHook(new(logrusHook)) - var err error - if options.Level != "" { - logger.Level, err = logrus.ParseLevel(options.Level) - if err != nil { - return nil, err - } - } - return &logrusLogger{logger, options.Output, nil}, nil -} - -func (l *logrusLogger) Start() error { - if l.outputPath != "" { - output, err := os.OpenFile(l.outputPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) - if err != nil { - return E.Cause(err, "open log output") - } - l.abstractLogrusLogger.(*logrus.Logger).SetOutput(output) - } - return nil -} - -func (l *logrusLogger) Close() error { - return common.Close(common.PtrOrNil(l.output)) -} - -func (l *logrusLogger) WithContext(ctx context.Context) Logger { - return &logrusLogger{abstractLogrusLogger: l.abstractLogrusLogger.WithContext(ctx)} -} - -func (l *logrusLogger) WithPrefix(prefix string) Logger { - if entry, isEntry := l.abstractLogrusLogger.(*logrus.Entry); isEntry { - loadedPrefix := entry.Data["prefix"] - if loadedPrefix != "" { - prefix = F.ToString(loadedPrefix, prefix) - } - } - return &logrusLogger{abstractLogrusLogger: l.WithField("prefix", prefix)} -} diff --git a/log/logrus_hook.go b/log/logrus_hook.go deleted file mode 100644 index ac92b206..00000000 --- a/log/logrus_hook.go +++ /dev/null @@ -1,49 +0,0 @@ -package log - -import ( - F "github.com/sagernet/sing/common/format" - - "github.com/logrusorgru/aurora" - "github.com/sirupsen/logrus" -) - -type logrusHook struct{} - -func (h *logrusHook) Levels() []logrus.Level { - return logrus.AllLevels -} - -func (h *logrusHook) Fire(entry *logrus.Entry) error { - if prefix, loaded := entry.Data["prefix"]; loaded { - prefixStr := prefix.(string) - delete(entry.Data, "prefix") - entry.Message = prefixStr + entry.Message - } - var idCtx *idContext - if entry.Context != nil { - idCtx, _ = entry.Context.Value(idType).(*idContext) - } - if idCtx != nil { - var color aurora.Color - color = aurora.Color(uint8(idCtx.id)) - color %= 215 - row := uint(color / 36) - column := uint(color % 36) - - var r, g, b float32 - r = float32(row * 51) - g = float32(column / 6 * 51) - b = float32((column % 6) * 51) - luma := 0.2126*r + 0.7152*g + 0.0722*b - if luma < 60 { - row = 5 - row - column = 35 - column - color = aurora.Color(row*36 + column) - } - color += 16 - color = color << 16 - color |= 1 << 14 - entry.Message = F.ToString("[", aurora.Colorize(idCtx.id, color).String(), "] ", entry.Message) - } - return nil -} diff --git a/log/logrus_text_formatter.go b/log/logrus_text_formatter.go deleted file mode 100644 index 35ae5ec9..00000000 --- a/log/logrus_text_formatter.go +++ /dev/null @@ -1,83 +0,0 @@ -package log - -import ( - "bytes" - "fmt" - "strings" - "time" - - "github.com/sirupsen/logrus" -) - -const ( - red = 31 - yellow = 33 - blue = 36 - gray = 37 -) - -var baseTimestamp time.Time - -func init() { - baseTimestamp = time.Now() -} - -type LogrusTextFormatter struct { - DisableColors bool - DisableTimestamp bool - FullTimestamp bool - TimestampFormat string -} - -func (f *LogrusTextFormatter) Format(entry *logrus.Entry) ([]byte, error) { - var b *bytes.Buffer - if entry.Buffer != nil { - b = entry.Buffer - } else { - b = &bytes.Buffer{} - } - timestampFormat := f.TimestampFormat - if timestampFormat == "" { - timestampFormat = "-0700 2006-01-02 15:04:05" - } - f.print(b, entry, timestampFormat) - b.WriteByte('\n') - return b.Bytes(), nil -} - -func (f *LogrusTextFormatter) print(b *bytes.Buffer, entry *logrus.Entry, timestampFormat string) { - var levelColor int - switch entry.Level { - case logrus.DebugLevel, logrus.TraceLevel: - levelColor = gray - case logrus.WarnLevel: - levelColor = yellow - case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel: - levelColor = red - case logrus.InfoLevel: - levelColor = blue - default: - levelColor = blue - } - - levelText := strings.ToUpper(entry.Level.String()) - if !f.DisableColors { - switch { - case f.DisableTimestamp: - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m %-44s", levelColor, levelText, entry.Message) - case !f.FullTimestamp: - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), entry.Message) - default: - fmt.Fprintf(b, "%s \x1b[%dm%s\x1b[0m %-44s", entry.Time.Format(timestampFormat), levelColor, levelText, entry.Message) - } - } else { - switch { - case f.DisableTimestamp: - fmt.Fprintf(b, "%s %-44s", levelText, entry.Message) - case !f.FullTimestamp: - fmt.Fprintf(b, "%s[%04d] %-44s", levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), entry.Message) - default: - fmt.Fprintf(b, "[%s] %s %-44s", entry.Time.Format(timestampFormat), levelText, entry.Message) - } - } -} diff --git a/log/nop.go b/log/nop.go index 1ab8d71a..3a39a315 100644 --- a/log/nop.go +++ b/log/nop.go @@ -2,53 +2,67 @@ package log import "context" -var _ Logger = (*nopLogger)(nil) +var _ Factory = (*nopFactory)(nil) -type nopLogger struct{} +type nopFactory struct{} -func NewNopLogger() Logger { - return (*nopLogger)(nil) +func NewNOPFactory() Factory { + return (*nopFactory)(nil) } -func (l *nopLogger) Start() error { - return nil +func (f *nopFactory) Level() Level { + return LevelTrace } -func (l *nopLogger) Close() error { - return nil +func (f *nopFactory) SetLevel(level Level) { } -func (l *nopLogger) Trace(args ...interface{}) { +func (f *nopFactory) Logger() ContextLogger { + return f } -func (l *nopLogger) Debug(args ...interface{}) { +func (f *nopFactory) NewLogger(tag string) ContextLogger { + return f } -func (l *nopLogger) Info(args ...interface{}) { +func (f *nopFactory) Trace(args ...any) { } -func (l *nopLogger) Print(args ...interface{}) { +func (f *nopFactory) Debug(args ...any) { } -func (l *nopLogger) Warn(args ...interface{}) { +func (f *nopFactory) Info(args ...any) { } -func (l *nopLogger) Warning(args ...interface{}) { +func (f *nopFactory) Warn(args ...any) { } -func (l *nopLogger) Error(args ...interface{}) { +func (f *nopFactory) Error(args ...any) { } -func (l *nopLogger) Fatal(args ...interface{}) { +func (f *nopFactory) Fatal(args ...any) { } -func (l *nopLogger) Panic(args ...interface{}) { +func (f *nopFactory) Panic(args ...any) { } -func (l *nopLogger) WithContext(ctx context.Context) Logger { - return l +func (f *nopFactory) TraceContext(ctx context.Context, args ...any) { } -func (l *nopLogger) WithPrefix(prefix string) Logger { - return l +func (f *nopFactory) DebugContext(ctx context.Context, args ...any) { +} + +func (f *nopFactory) InfoContext(ctx context.Context, args ...any) { +} + +func (f *nopFactory) WarnContext(ctx context.Context, args ...any) { +} + +func (f *nopFactory) ErrorContext(ctx context.Context, args ...any) { +} + +func (f *nopFactory) FatalContext(ctx context.Context, args ...any) { +} + +func (f *nopFactory) PanicContext(ctx context.Context, args ...any) { } diff --git a/log/observable.go b/log/observable.go new file mode 100644 index 00000000..15eea5e3 --- /dev/null +++ b/log/observable.go @@ -0,0 +1,136 @@ +package log + +import ( + "context" + "io" + "os" + "time" + + F "github.com/sagernet/sing/common/format" + "github.com/sagernet/sing/common/observable" +) + +var _ Factory = (*observableFactory)(nil) + +type observableFactory struct { + formatter Formatter + writer io.Writer + level Level + subscriber *observable.Subscriber[Entry] + observer *observable.Observer[Entry] +} + +func NewObservableFactory(formatter Formatter, writer io.Writer) ObservableFactory { + factory := &observableFactory{ + formatter: formatter, + writer: writer, + level: LevelTrace, + subscriber: observable.NewSubscriber[Entry](128), + } + factory.observer = observable.NewObserver[Entry](factory.subscriber, 64) + return factory +} + +func (f *observableFactory) Level() Level { + return f.level +} + +func (f *observableFactory) SetLevel(level Level) { + f.level = level +} + +func (f *observableFactory) Logger() ContextLogger { + return f.NewLogger("") +} + +func (f *observableFactory) NewLogger(tag string) ContextLogger { + return &observableLogger{f, tag} +} + +func (f *observableFactory) Subscribe() (subscription observable.Subscription[Entry], done <-chan struct{}, err error) { + return f.observer.Subscribe() +} + +func (f *observableFactory) UnSubscribe(sub observable.Subscription[Entry]) { + f.observer.UnSubscribe(sub) +} + +var _ ContextLogger = (*observableLogger)(nil) + +type observableLogger struct { + *observableFactory + tag string +} + +func (l *observableLogger) Log(ctx context.Context, level Level, args []any) { + if level > l.level { + return + } + message := l.formatter.Format(ctx, level, l.tag, F.ToString(args...), time.Now()) + "\n" + if level == LevelPanic { + panic(message) + } + l.writer.Write([]byte(message)) + if level == LevelFatal { + os.Exit(1) + } + if l.subscriber != nil { + l.subscriber.Emit(Entry{level, message}) + } +} + +func (l *observableLogger) Trace(args ...any) { + l.Log(nil, LevelTrace, args) +} + +func (l *observableLogger) Debug(args ...any) { + l.Log(nil, LevelDebug, args) +} + +func (l *observableLogger) Info(args ...any) { + l.Log(nil, LevelInfo, args) +} + +func (l *observableLogger) Warn(args ...any) { + l.Log(nil, LevelWarn, args) +} + +func (l *observableLogger) Error(args ...any) { + l.Log(nil, LevelError, args) +} + +func (l *observableLogger) Fatal(args ...any) { + l.Log(nil, LevelFatal, args) +} + +func (l *observableLogger) Panic(args ...any) { + l.Log(nil, LevelPanic, args) +} + +func (l *observableLogger) TraceContext(ctx context.Context, args ...any) { + l.Log(ctx, LevelTrace, args) +} + +func (l *observableLogger) DebugContext(ctx context.Context, args ...any) { + l.Log(ctx, LevelDebug, args) +} + +func (l *observableLogger) InfoContext(ctx context.Context, args ...any) { + l.Log(ctx, LevelInfo, args) +} + +func (l *observableLogger) WarnContext(ctx context.Context, args ...any) { + l.Log(ctx, LevelWarn, args) +} + +func (l *observableLogger) ErrorContext(ctx context.Context, args ...any) { + l.Log(ctx, LevelError, args) +} + +func (l *observableLogger) FatalContext(ctx context.Context, args ...any) { + l.Log(ctx, LevelFatal, args) +} + +func (l *observableLogger) PanicContext(ctx context.Context, args ...any) { + l.Log(ctx, LevelPanic, args) +} diff --git a/outbound/block.go b/outbound/block.go index 685c00bf..b16e6170 100644 --- a/outbound/block.go +++ b/outbound/block.go @@ -18,7 +18,7 @@ type Block struct { myOutboundAdapter } -func NewBlock(logger log.Logger, tag string) *Block { +func NewBlock(logger log.ContextLogger, tag string) *Block { return &Block{ myOutboundAdapter{ protocol: C.TypeBlock, @@ -30,23 +30,23 @@ func NewBlock(logger log.Logger, tag string) *Block { } func (h *Block) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { - h.logger.WithContext(ctx).Info("blocked connection to ", destination) + h.logger.InfoContext(ctx, "blocked connection to ", destination) return nil, io.EOF } func (h *Block) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - h.logger.WithContext(ctx).Info("blocked packet connection to ", destination) + h.logger.InfoContext(ctx, "blocked packet connection to ", destination) return nil, io.EOF } func (h *Block) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { conn.Close() - h.logger.WithContext(ctx).Info("blocked connection to ", metadata.Destination) + h.logger.InfoContext(ctx, "blocked connection to ", metadata.Destination) return nil } func (h *Block) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { conn.Close() - h.logger.WithContext(ctx).Info("blocked packet connection to ", metadata.Destination) + h.logger.InfoContext(ctx, "blocked packet connection to ", metadata.Destination) return nil } diff --git a/outbound/builder.go b/outbound/builder.go index e5817aa3..03d42129 100644 --- a/outbound/builder.go +++ b/outbound/builder.go @@ -7,31 +7,23 @@ import ( "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" - F "github.com/sagernet/sing/common/format" ) -func New(router adapter.Router, logger log.Logger, index int, options option.Outbound) (adapter.Outbound, error) { +func New(router adapter.Router, logger log.ContextLogger, options option.Outbound) (adapter.Outbound, error) { if common.IsEmpty(options) { return nil, E.New("empty outbound config") } - var tag string - if options.Tag != "" { - tag = options.Tag - } else { - tag = F.ToString(index) - } - outboundLogger := logger.WithPrefix(F.ToString("outbound/", options.Type, "[", tag, "]: ")) switch options.Type { case C.TypeDirect: - return NewDirect(router, outboundLogger, options.Tag, options.DirectOptions), nil + return NewDirect(router, logger, options.Tag, options.DirectOptions), nil case C.TypeBlock: - return NewBlock(outboundLogger, options.Tag), nil + return NewBlock(logger, options.Tag), nil case C.TypeSocks: - return NewSocks(router, outboundLogger, options.Tag, options.SocksOptions) + return NewSocks(router, logger, options.Tag, options.SocksOptions) case C.TypeHTTP: - return NewHTTP(router, outboundLogger, options.Tag, options.HTTPOptions), nil + return NewHTTP(router, logger, options.Tag, options.HTTPOptions), nil case C.TypeShadowsocks: - return NewShadowsocks(router, outboundLogger, options.Tag, options.ShadowsocksOptions) + return NewShadowsocks(router, logger, options.Tag, options.ShadowsocksOptions) default: return nil, E.New("unknown outbound type: ", options.Type) } diff --git a/outbound/default.go b/outbound/default.go index 29a8e48d..722d8cf6 100644 --- a/outbound/default.go +++ b/outbound/default.go @@ -18,7 +18,7 @@ import ( type myOutboundAdapter struct { protocol string - logger log.Logger + logger log.ContextLogger tag string network []string } diff --git a/outbound/direct.go b/outbound/direct.go index 8a8d85c3..317d3d86 100644 --- a/outbound/direct.go +++ b/outbound/direct.go @@ -23,7 +23,7 @@ type Direct struct { overrideDestination M.Socksaddr } -func NewDirect(router adapter.Router, logger log.Logger, tag string, options option.DirectOutboundOptions) *Direct { +func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) *Direct { outbound := &Direct{ myOutboundAdapter: myOutboundAdapter{ protocol: C.TypeDirect, @@ -62,9 +62,9 @@ func (h *Direct) DialContext(ctx context.Context, network string, destination M. } switch network { case C.NetworkTCP: - h.logger.WithContext(ctx).Info("outbound connection to ", destination) + h.logger.InfoContext(ctx, "outbound connection to ", destination) case C.NetworkUDP: - h.logger.WithContext(ctx).Info("outbound packet connection to ", destination) + h.logger.InfoContext(ctx, "outbound packet connection to ", destination) } return h.dialer.DialContext(ctx, network, destination) } @@ -73,7 +73,7 @@ func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net ctx, metadata := adapter.AppendContext(ctx) metadata.Outbound = h.tag metadata.Destination = destination - h.logger.WithContext(ctx).Info("outbound packet connection") + h.logger.InfoContext(ctx, "outbound packet connection") return h.dialer.ListenPacket(ctx, destination) } diff --git a/outbound/http.go b/outbound/http.go index 5e59eab0..4c47a150 100644 --- a/outbound/http.go +++ b/outbound/http.go @@ -22,7 +22,7 @@ type HTTP struct { client *http.Client } -func NewHTTP(router adapter.Router, logger log.Logger, tag string, options option.HTTPOutboundOptions) *HTTP { +func NewHTTP(router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) *HTTP { return &HTTP{ myOutboundAdapter{ protocol: C.TypeHTTP, @@ -38,7 +38,7 @@ func (h *HTTP) DialContext(ctx context.Context, network string, destination M.So ctx, metadata := adapter.AppendContext(ctx) metadata.Outbound = h.tag metadata.Destination = destination - h.logger.WithContext(ctx).Info("outbound connection to ", destination) + h.logger.InfoContext(ctx, "outbound connection to ", destination) return h.client.DialContext(ctx, network, destination) } diff --git a/outbound/shadowsocks.go b/outbound/shadowsocks.go index 8c1d5108..a9044a73 100644 --- a/outbound/shadowsocks.go +++ b/outbound/shadowsocks.go @@ -25,7 +25,7 @@ type Shadowsocks struct { serverAddr M.Socksaddr } -func NewShadowsocks(router adapter.Router, logger log.Logger, tag string, options option.ShadowsocksOutboundOptions) (*Shadowsocks, error) { +func NewShadowsocks(router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksOutboundOptions) (*Shadowsocks, error) { method, err := shadowimpl.FetchMethod(options.Method, options.Password) if err != nil { return nil, err @@ -49,14 +49,14 @@ func (h *Shadowsocks) DialContext(ctx context.Context, network string, destinati metadata.Destination = destination switch network { case C.NetworkTCP: - h.logger.WithContext(ctx).Info("outbound connection to ", destination) + h.logger.InfoContext(ctx, "outbound connection to ", destination) outConn, err := h.dialer.DialContext(ctx, C.NetworkTCP, h.serverAddr) if err != nil { return nil, err } return h.method.DialEarlyConn(outConn, destination), nil case C.NetworkUDP: - h.logger.WithContext(ctx).Info("outbound packet connection to ", destination) + h.logger.InfoContext(ctx, "outbound packet connection to ", destination) outConn, err := h.dialer.DialContext(ctx, C.NetworkUDP, h.serverAddr) if err != nil { return nil, err @@ -71,7 +71,7 @@ func (h *Shadowsocks) ListenPacket(ctx context.Context, destination M.Socksaddr) ctx, metadata := adapter.AppendContext(ctx) metadata.Outbound = h.tag metadata.Destination = destination - h.logger.WithContext(ctx).Info("outbound packet connection to ", h.serverAddr) + h.logger.InfoContext(ctx, "outbound packet connection to ", h.serverAddr) outConn, err := h.dialer.DialContext(ctx, "udp", h.serverAddr) if err != nil { return nil, err diff --git a/outbound/socks.go b/outbound/socks.go index 644fb765..981d8fb2 100644 --- a/outbound/socks.go +++ b/outbound/socks.go @@ -21,7 +21,7 @@ type Socks struct { client *socks.Client } -func NewSocks(router adapter.Router, logger log.Logger, tag string, options option.SocksOutboundOptions) (*Socks, error) { +func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, options option.SocksOutboundOptions) (*Socks, error) { detour := dialer.NewOutbound(router, options.OutboundDialerOptions) var version socks.Version var err error @@ -50,9 +50,9 @@ func (h *Socks) DialContext(ctx context.Context, network string, destination M.S metadata.Destination = destination switch network { case C.NetworkTCP: - h.logger.WithContext(ctx).Info("outbound connection to ", destination) + h.logger.InfoContext(ctx, "outbound connection to ", destination) case C.NetworkUDP: - h.logger.WithContext(ctx).Info("outbound packet connection to ", destination) + h.logger.InfoContext(ctx, "outbound packet connection to ", destination) default: panic("unknown network " + network) } @@ -63,7 +63,7 @@ func (h *Socks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net. ctx, metadata := adapter.AppendContext(ctx) metadata.Outbound = h.tag metadata.Destination = destination - h.logger.WithContext(ctx).Info("outbound packet connection to ", destination) + h.logger.InfoContext(ctx, "outbound packet connection to ", destination) return h.client.ListenPacket(ctx, destination) } diff --git a/route/router.go b/route/router.go index e54aa752..5dee2ec9 100644 --- a/route/router.go +++ b/route/router.go @@ -39,8 +39,8 @@ var _ adapter.Router = (*Router)(nil) type Router struct { ctx context.Context - logger log.Logger - dnsLogger log.Logger + logger log.ContextLogger + dnsLogger log.ContextLogger outboundByTag map[string]adapter.Outbound rules []adapter.Rule @@ -68,11 +68,11 @@ type Router struct { interfaceMonitor iffmonitor.InterfaceMonitor } -func NewRouter(ctx context.Context, logger log.Logger, options option.RouteOptions, dnsOptions option.DNSOptions) (*Router, error) { +func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.ContextLogger, options option.RouteOptions, dnsOptions option.DNSOptions) (*Router, error) { router := &Router{ ctx: ctx, - logger: logger.WithPrefix("router: "), - dnsLogger: logger.WithPrefix("dns: "), + logger: logger, + dnsLogger: dnsLogger, outboundByTag: make(map[string]adapter.Outbound), rules: make([]adapter.Rule, 0, len(options.Rules)), dnsRules: make([]adapter.Rule, 0, len(dnsOptions.Rules)), @@ -397,9 +397,9 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad metadata.Destination.Fqdn = metadata.Domain } if metadata.Domain != "" { - r.logger.WithContext(ctx).Debug("sniffed protocol: ", metadata.Protocol, ", domain: ", metadata.Domain) + r.logger.DebugContext(ctx, "sniffed protocol: ", metadata.Protocol, ", domain: ", metadata.Domain) } else { - r.logger.WithContext(ctx).Debug("sniffed protocol: ", metadata.Protocol) + r.logger.DebugContext(ctx, "sniffed protocol: ", metadata.Protocol) } } if !buffer.IsEmpty() { @@ -412,7 +412,7 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad return err } metadata.DestinationAddresses = addresses - r.dnsLogger.WithContext(ctx).Debug("resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]") + r.dnsLogger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]") } detour := r.match(ctx, metadata, r.defaultOutboundForConnection) if !common.Contains(detour.Network(), C.NetworkTCP) { @@ -441,9 +441,9 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m metadata.Destination.Fqdn = metadata.Domain } if metadata.Domain != "" { - r.logger.WithContext(ctx).Debug("sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain) + r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain) } else { - r.logger.WithContext(ctx).Debug("sniffed packet protocol: ", metadata.Protocol) + r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol) } } conn = bufio.NewCachedPacketConn(conn, buffer, originDestination) @@ -454,7 +454,7 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m return err } metadata.DestinationAddresses = addresses - r.dnsLogger.WithContext(ctx).Debug("resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]") + r.dnsLogger.DebugContext(ctx, "resolved [", strings.Join(F.MapToString(metadata.DestinationAddresses), " "), "]") } detour := r.match(ctx, metadata, r.defaultOutboundForPacketConnection) if !common.Contains(detour.Network(), C.NetworkUDP) { @@ -480,11 +480,11 @@ func (r *Router) match(ctx context.Context, metadata adapter.InboundContext, def for i, rule := range r.rules { if rule.Match(&metadata) { detour := rule.Outbound() - r.logger.WithContext(ctx).Debug("match[", i, "] ", rule.String(), " => ", detour) + r.logger.DebugContext(ctx, "match[", i, "] ", rule.String(), " => ", detour) if outbound, loaded := r.Outbound(detour); loaded { return outbound } - r.logger.WithContext(ctx).Error("outbound not found: ", detour) + r.logger.ErrorContext(ctx, "outbound not found: ", detour) } } return defaultOutbound @@ -493,17 +493,17 @@ func (r *Router) match(ctx context.Context, metadata adapter.InboundContext, def func (r *Router) matchDNS(ctx context.Context) dns.Transport { metadata := adapter.ContextFrom(ctx) if metadata == nil { - r.dnsLogger.WithContext(ctx).Warn("no context: ", reflect.TypeOf(ctx)) + r.dnsLogger.WarnContext(ctx, "no context: ", reflect.TypeOf(ctx)) return r.defaultTransport } for i, rule := range r.dnsRules { if rule.Match(metadata) { detour := rule.Outbound() - r.dnsLogger.WithContext(ctx).Debug("match[", i, "] ", rule.String(), " => ", detour) + r.dnsLogger.DebugContext(ctx, "match[", i, "] ", rule.String(), " => ", detour) if transport, loaded := r.transportMap[detour]; loaded { return transport } - r.dnsLogger.WithContext(ctx).Error("transport not found: ", detour) + r.dnsLogger.ErrorContext(ctx, "transport not found: ", detour) } } return r.defaultTransport diff --git a/route/rule.go b/route/rule.go index 7fc88ead..2fb2d52f 100644 --- a/route/rule.go +++ b/route/rule.go @@ -12,7 +12,7 @@ import ( F "github.com/sagernet/sing/common/format" ) -func NewRule(router adapter.Router, logger log.Logger, options option.Rule) (adapter.Rule, error) { +func NewRule(router adapter.Router, logger log.ContextLogger, options option.Rule) (adapter.Rule, error) { if common.IsEmptyByEquals(options) { return nil, E.New("empty rule config") } @@ -53,7 +53,7 @@ type RuleItem interface { String() string } -func NewDefaultRule(router adapter.Router, logger log.Logger, options option.DefaultRule) (*DefaultRule, error) { +func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options option.DefaultRule) (*DefaultRule, error) { rule := &DefaultRule{ outbound: options.Outbound, } @@ -263,7 +263,7 @@ func (r *LogicalRule) Close() error { return nil } -func NewLogicalRule(router adapter.Router, logger log.Logger, options option.LogicalRule) (*LogicalRule, error) { +func NewLogicalRule(router adapter.Router, logger log.ContextLogger, options option.LogicalRule) (*LogicalRule, error) { r := &LogicalRule{ rules: make([]*DefaultRule, len(options.Rules)), outbound: options.Outbound, diff --git a/route/rule_dns.go b/route/rule_dns.go index 6d20fe2a..88b78da7 100644 --- a/route/rule_dns.go +++ b/route/rule_dns.go @@ -12,7 +12,7 @@ import ( F "github.com/sagernet/sing/common/format" ) -func NewDNSRule(router adapter.Router, logger log.Logger, options option.DNSRule) (adapter.Rule, error) { +func NewDNSRule(router adapter.Router, logger log.ContextLogger, options option.DNSRule) (adapter.Rule, error) { if common.IsEmptyByEquals(options) { return nil, E.New("empty rule config") } @@ -47,7 +47,7 @@ type DefaultDNSRule struct { outbound string } -func NewDefaultDNSRule(router adapter.Router, logger log.Logger, options option.DefaultDNSRule) (*DefaultDNSRule, error) { +func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options option.DefaultDNSRule) (*DefaultDNSRule, error) { rule := &DefaultDNSRule{ outbound: options.Server, } @@ -224,7 +224,7 @@ func (r *LogicalDNSRule) Close() error { return nil } -func NewLogicalDNSRule(router adapter.Router, logger log.Logger, options option.LogicalDNSRule) (*LogicalDNSRule, error) { +func NewLogicalDNSRule(router adapter.Router, logger log.ContextLogger, options option.LogicalDNSRule) (*LogicalDNSRule, error) { r := &LogicalDNSRule{ rules: make([]*DefaultDNSRule, len(options.Rules)), outbound: options.Server, diff --git a/route/rule_geoip.go b/route/rule_geoip.go index f171b8f2..29cdf86a 100644 --- a/route/rule_geoip.go +++ b/route/rule_geoip.go @@ -11,13 +11,13 @@ var _ RuleItem = (*GeoIPItem)(nil) type GeoIPItem struct { router adapter.Router - logger log.Logger + logger log.ContextLogger isSource bool codes []string codeMap map[string]bool } -func NewGeoIPItem(router adapter.Router, logger log.Logger, isSource bool, codes []string) *GeoIPItem { +func NewGeoIPItem(router adapter.Router, logger log.ContextLogger, isSource bool, codes []string) *GeoIPItem { codeMap := make(map[string]bool) for _, code := range codes { codeMap[code] = true diff --git a/route/rule_geosite.go b/route/rule_geosite.go index 547ec1c9..5fdbfe59 100644 --- a/route/rule_geosite.go +++ b/route/rule_geosite.go @@ -12,12 +12,12 @@ var _ RuleItem = (*GeositeItem)(nil) type GeositeItem struct { router adapter.Router - logger log.Logger + logger log.ContextLogger codes []string matchers []adapter.Rule } -func NewGeositeItem(router adapter.Router, logger log.Logger, codes []string) *GeositeItem { +func NewGeositeItem(router adapter.Router, logger log.ContextLogger, codes []string) *GeositeItem { return &GeositeItem{ router: router, logger: logger, diff --git a/test/go.mod b/test/go.mod index 8e0980e1..9007141a 100644 --- a/test/go.mod +++ b/test/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/docker/docker v20.10.17+incompatible github.com/docker/go-connections v0.4.0 - github.com/sagernet/sing v0.0.0-20220711103842-d3fb2260ef61 + github.com/sagernet/sing v0.0.0-20220712060558-029ab1ce4f91 github.com/sagernet/sing-box v0.0.0 github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.8.0 diff --git a/test/go.sum b/test/go.sum index 40d678d8..6119fbea 100644 --- a/test/go.sum +++ b/test/go.sum @@ -52,8 +52,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/sagernet/sing v0.0.0-20220711103842-d3fb2260ef61 h1:sOx7t+MFssiCAY2afRHQSmkWZNpLQnjF0Hwv/TNVMvk= -github.com/sagernet/sing v0.0.0-20220711103842-d3fb2260ef61/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c= +github.com/sagernet/sing v0.0.0-20220712060558-029ab1ce4f91 h1:fYsRChEViZHDvrOLp7fbswYCH3txaVyAl1zB0cnSNlc= +github.com/sagernet/sing v0.0.0-20220712060558-029ab1ce4f91/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c= github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619 h1:oHbOmq1WS0XaZmXp6WpxzyB2xeyRIA1/L8EJKuNntfY= github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619/go.mod h1:y2fpvoxukw3G7eApIZwkcpcG/NE4AB8pCQI0Qd8rMqk= github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 h1:whNDUGOAX5GPZkSy4G3Gv9QyIgk5SXRyjkRuP7ohF8k=