package log import ( "context" "io" "os" "time" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/service/filemanager" ) 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 { Context context.Context 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 = filemanager.OpenFile(options.Context, 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 }