ソースを参照

Refactor log factory constructor

世界 2 年 前
コミット
28aa4c4d1f

+ 27 - 67
box.go

@@ -9,7 +9,6 @@ import (
 	"time"
 
 	"github.com/sagernet/sing-box/adapter"
-	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/experimental"
 	"github.com/sagernet/sing-box/experimental/libbox/platform"
 	"github.com/sagernet/sing-box/inbound"
@@ -31,18 +30,25 @@ type Box struct {
 	outbounds    []adapter.Outbound
 	logFactory   log.Factory
 	logger       log.ContextLogger
-	logFile      *os.File
 	preServices  map[string]adapter.Service
 	postServices map[string]adapter.Service
 	done         chan struct{}
 }
 
-func New(ctx context.Context, options option.Options, platformInterface platform.Interface) (*Box, error) {
-	createdAt := time.Now()
+type Options struct {
+	option.Options
+	Context           context.Context
+	PlatformInterface platform.Interface
+}
 
+func New(options Options) (*Box, error) {
+	ctx := options.Context
+	if ctx == nil {
+		ctx = context.Background()
+	}
+	createdAt := time.Now()
 	experimentalOptions := common.PtrValueOrDefault(options.Experimental)
 	applyDebugOptions(common.PtrValueOrDefault(experimentalOptions.Debug))
-
 	var needClashAPI bool
 	var needV2RayAPI bool
 	if experimentalOptions.ClashAPI != nil && experimentalOptions.ClashAPI.ExternalController != "" {
@@ -51,60 +57,20 @@ func New(ctx context.Context, options option.Options, platformInterface platform
 	if experimentalOptions.V2RayAPI != nil && experimentalOptions.V2RayAPI.Listen != "" {
 		needV2RayAPI = true
 	}
-
-	logOptions := common.PtrValueOrDefault(options.Log)
-
-	var logFactory log.Factory
-	var observableLogFactory log.ObservableFactory
-	var logFile *os.File
-	var logWriter io.Writer
-	if logOptions.Disabled {
-		observableLogFactory = log.NewNOPFactory()
-		logFactory = observableLogFactory
-	} else {
-		switch logOptions.Output {
-		case "":
-			if platformInterface != nil {
-				logWriter = io.Discard
-			} else {
-				logWriter = os.Stdout
-			}
-		case "stderr":
-			logWriter = os.Stderr
-		case "stdout":
-			logWriter = os.Stdout
-		default:
-			var err error
-			logFile, err = os.OpenFile(C.BasePath(logOptions.Output), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
-			if err != nil {
-				return nil, err
-			}
-			logWriter = logFile
-		}
-		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",
-		}
-		if needClashAPI {
-			observableLogFactory = log.NewObservableFactory(logFormatter, logWriter, platformInterface)
-			logFactory = observableLogFactory
-		} else {
-			logFactory = log.NewFactory(logFormatter, logWriter, platformInterface)
-		}
-		if logOptions.Level != "" {
-			logLevel, err := log.ParseLevel(logOptions.Level)
-			if err != nil {
-				return nil, E.Cause(err, "parse log level")
-			}
-			logFactory.SetLevel(logLevel)
-		} else {
-			logFactory.SetLevel(log.LevelTrace)
-		}
+	var defaultLogWriter io.Writer
+	if options.PlatformInterface != nil {
+		defaultLogWriter = io.Discard
+	}
+	logFactory, err := log.New(log.Options{
+		Options:        common.PtrValueOrDefault(options.Log),
+		Observable:     needClashAPI,
+		DefaultWriter:  defaultLogWriter,
+		BaseTime:       createdAt,
+		PlatformWriter: options.PlatformInterface,
+	})
+	if err != nil {
+		return nil, E.Cause(err, "create log factory")
 	}
-
 	router, err := route.NewRouter(
 		ctx,
 		logFactory,
@@ -112,7 +78,7 @@ func New(ctx context.Context, options option.Options, platformInterface platform
 		common.PtrValueOrDefault(options.DNS),
 		common.PtrValueOrDefault(options.NTP),
 		options.Inbounds,
-		platformInterface,
+		options.PlatformInterface,
 	)
 	if err != nil {
 		return nil, E.Cause(err, "parse route options")
@@ -132,7 +98,7 @@ func New(ctx context.Context, options option.Options, platformInterface platform
 			router,
 			logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
 			inboundOptions,
-			platformInterface,
+			options.PlatformInterface,
 		)
 		if err != nil {
 			return nil, E.Cause(err, "parse inbound[", i, "]")
@@ -169,7 +135,7 @@ func New(ctx context.Context, options option.Options, platformInterface platform
 	preServices := make(map[string]adapter.Service)
 	postServices := make(map[string]adapter.Service)
 	if needClashAPI {
-		clashServer, err := experimental.NewClashServer(router, observableLogFactory, common.PtrValueOrDefault(options.Experimental.ClashAPI))
+		clashServer, err := experimental.NewClashServer(router, logFactory.(log.ObservableFactory), common.PtrValueOrDefault(options.Experimental.ClashAPI))
 		if err != nil {
 			return nil, E.Cause(err, "create clash api server")
 		}
@@ -191,7 +157,6 @@ func New(ctx context.Context, options option.Options, platformInterface platform
 		createdAt:    createdAt,
 		logFactory:   logFactory,
 		logger:       logFactory.Logger(),
-		logFile:      logFile,
 		preServices:  preServices,
 		postServices: postServices,
 		done:         make(chan struct{}),
@@ -330,11 +295,6 @@ func (s *Box) Close() error {
 			return E.Cause(err, "close log factory")
 		})
 	}
-	if s.logFile != nil {
-		errors = E.Append(errors, s.logFile.Close(), func(err error) error {
-			return E.Cause(err, "close log file")
-		})
-	}
 	return errors
 }
 

+ 4 - 1
cmd/sing-box/cmd_check.go

@@ -31,7 +31,10 @@ func check() error {
 		return err
 	}
 	ctx, cancel := context.WithCancel(context.Background())
-	instance, err := box.New(ctx, options, nil)
+	instance, err := box.New(box.Options{
+		Context: ctx,
+		Options: options,
+	})
 	if err == nil {
 		instance.Close()
 	}

+ 4 - 1
cmd/sing-box/cmd_run.go

@@ -127,7 +127,10 @@ func create() (*box.Box, context.CancelFunc, error) {
 		options.Log.DisableColor = true
 	}
 	ctx, cancel := context.WithCancel(context.Background())
-	instance, err := box.New(ctx, options, nil)
+	instance, err := box.New(box.Options{
+		Context: ctx,
+		Options: options,
+	})
 	if err != nil {
 		cancel()
 		return nil, nil, E.Cause(err, "create service")

+ 1 - 3
cmd/sing-box/cmd_tools.go

@@ -1,8 +1,6 @@
 package main
 
 import (
-	"context"
-
 	"github.com/sagernet/sing-box"
 	E "github.com/sagernet/sing/common/exceptions"
 	N "github.com/sagernet/sing/common/network"
@@ -27,7 +25,7 @@ func createPreStartedClient() (*box.Box, error) {
 	if err != nil {
 		return nil, err
 	}
-	instance, err := box.New(context.Background(), options, nil)
+	instance, err := box.New(box.Options{Options: options})
 	if err != nil {
 		return nil, E.Cause(err, "create service")
 	}

+ 4 - 1
experimental/libbox/config.go

@@ -26,7 +26,10 @@ func CheckConfig(configContent string) error {
 	}
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
-	instance, err := box.New(ctx, options, nil)
+	instance, err := box.New(box.Options{
+		Context: ctx,
+		Options: options,
+	})
 	if err == nil {
 		instance.Close()
 	}

+ 5 - 1
experimental/libbox/service.go

@@ -30,7 +30,11 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
 		return nil, err
 	}
 	ctx, cancel := context.WithCancel(context.Background())
-	instance, err := box.New(ctx, options, &platformInterfaceWrapper{platformInterface, platformInterface.UseProcFS()})
+	instance, err := box.New(box.Options{
+		Context:           ctx,
+		Options:           options,
+		PlatformInterface: &platformInterfaceWrapper{platformInterface, platformInterface.UseProcFS()},
+	})
 	if err != nil {
 		cancel()
 		return nil, E.Cause(err, "create service")

+ 4 - 0
log/default.go

@@ -49,6 +49,10 @@ func (f *simpleFactory) NewLogger(tag string) ContextLogger {
 	return &simpleLogger{f, tag}
 }
 
+func (f *simpleFactory) Close() error {
+	return nil
+}
+
 var _ ContextLogger = (*simpleLogger)(nil)
 
 type simpleLogger struct {

+ 1 - 0
log/factory.go

@@ -15,6 +15,7 @@ type Factory interface {
 	SetLevel(level Level)
 	Logger() ContextLogger
 	NewLogger(tag string) ContextLogger
+	Close() error
 }
 
 type ObservableFactory interface {

+ 110 - 0
log/log.go

@@ -0,0 +1,110 @@
+package log
+
+import (
+	"io"
+	"os"
+	"time"
+
+	C "github.com/sagernet/sing-box/constant"
+	"github.com/sagernet/sing-box/option"
+	"github.com/sagernet/sing/common"
+	E "github.com/sagernet/sing/common/exceptions"
+)
+
+type factoryWithFile struct {
+	Factory
+	file *os.File
+}
+
+func (f *factoryWithFile) Close() error {
+	return common.Close(
+		f.Factory,
+		common.PtrOrNil(f.file),
+	)
+}
+
+type observableFactoryWithFile struct {
+	ObservableFactory
+	file *os.File
+}
+
+func (f *observableFactoryWithFile) Close() error {
+	return common.Close(
+		f.ObservableFactory,
+		common.PtrOrNil(f.file),
+	)
+}
+
+type Options struct {
+	Options        option.LogOptions
+	Observable     bool
+	DefaultWriter  io.Writer
+	BaseTime       time.Time
+	PlatformWriter io.Writer
+}
+
+func New(options Options) (Factory, error) {
+	logOptions := options.Options
+
+	if logOptions.Disabled {
+		return NewNOPFactory(), nil
+	}
+
+	var logFile *os.File
+	var logWriter io.Writer
+
+	switch logOptions.Output {
+	case "":
+		logWriter = options.DefaultWriter
+		if logWriter == nil {
+			logWriter = os.Stderr
+		}
+	case "stderr":
+		logWriter = os.Stderr
+	case "stdout":
+		logWriter = os.Stdout
+	default:
+		var err error
+		logFile, err = os.OpenFile(C.BasePath(logOptions.Output), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
+		if err != nil {
+			return nil, err
+		}
+		logWriter = logFile
+	}
+	logFormatter := Formatter{
+		BaseTime:         options.BaseTime,
+		DisableColors:    logOptions.DisableColor || logFile != nil,
+		DisableTimestamp: !logOptions.Timestamp && logFile != nil,
+		FullTimestamp:    logOptions.Timestamp,
+		TimestampFormat:  "-0700 2006-01-02 15:04:05",
+	}
+	var factory Factory
+	if options.Observable {
+		factory = NewObservableFactory(logFormatter, logWriter, options.PlatformWriter)
+	} else {
+		factory = NewFactory(logFormatter, logWriter, options.PlatformWriter)
+	}
+	if logOptions.Level != "" {
+		logLevel, err := ParseLevel(logOptions.Level)
+		if err != nil {
+			return nil, E.Cause(err, "parse log level")
+		}
+		factory.SetLevel(logLevel)
+	} else {
+		factory.SetLevel(LevelTrace)
+	}
+	if logFile != nil {
+		if options.Observable {
+			factory = &observableFactoryWithFile{
+				ObservableFactory: factory.(ObservableFactory),
+				file:              logFile,
+			}
+		} else {
+			factory = &factoryWithFile{
+				Factory: factory,
+				file:    logFile,
+			}
+		}
+	}
+	return factory, nil
+}

+ 4 - 0
log/nop.go

@@ -72,6 +72,10 @@ func (f *nopFactory) FatalContext(ctx context.Context, args ...any) {
 func (f *nopFactory) PanicContext(ctx context.Context, args ...any) {
 }
 
+func (f *nopFactory) Close() error {
+	return nil
+}
+
 func (f *nopFactory) Subscribe() (subscription observable.Subscription[Entry], done <-chan struct{}, err error) {
 	return nil, nil, os.ErrInvalid
 }