diff --git a/cmd/cli/components/di.go b/cmd/cli/components/di.go index 96f5a0f..51f25d1 100644 --- a/cmd/cli/components/di.go +++ b/cmd/cli/components/di.go @@ -37,7 +37,12 @@ func GetRepository() (storage.Repository, error) { } func GetLogger() (zerolog.Logger, error) { - return do.Invoke[zerolog.Logger](diInjector) + log, err := do.Invoke[*loggerAdapter](diInjector) + if err != nil { + return zerolog.Nop(), err + } + + return log.entity.log, nil } func GetDimensionMatcher() (*dimension.Matcher, error) { @@ -57,14 +62,34 @@ func SetupDI(ctx context.Context, cfgpath string, verbose bool, logAsJSON bool) diInjector = do.New() - do.Provide(diInjector, func(i *do.Injector) (zerolog.Logger, error) { + do.Provide(diInjector, func(i *do.Injector) (*loggerAdapter, error) { tsSet := func(wr *zerolog.ConsoleWriter) { wr.TimeFormat = time.RFC3339 } - var writer io.Writer = zerolog.NewConsoleWriter(tsSet) + var outfile *os.File + var output io.Writer + switch cfg.Log.Output { + case "", "stdout": + output = os.Stdout + case "stderr": + output = os.Stderr + default: + outfile, err = os.Create(cfg.Log.Output) + if err != nil { + return nil, fmt.Errorf("creating file for logging: %w", err) + } + + output = zerolog.SyncWriter(outfile) + } + + var writer io.Writer if logAsJSON { - writer = os.Stdout + writer = output + } else { + writer = zerolog.NewConsoleWriter(tsSet, func(w *zerolog.ConsoleWriter) { + w.Out = output + }) } log := zerolog. @@ -73,12 +98,20 @@ func SetupDI(ctx context.Context, cfgpath string, verbose bool, logAsJSON bool) Timestamp(). Str("app", "converter"). Logger() - if verbose { - return log.Level(zerolog.DebugLevel), nil + if verbose { + log = log.Level(zerolog.DebugLevel) + } else { + log = log.Level(zerolog.InfoLevel) } - return log.Level(zerolog.InfoLevel), nil + out := &logger{ + log: log, + underlyingFile: outfile, + } + return &loggerAdapter{ + entity: out, + }, nil }) do.Provide[eway.Client](diInjector, func(i *do.Injector) (eway.Client, error) { @@ -162,6 +195,19 @@ func parseSettings(cfgpath string) (cfg settings, err error) { return cfg, nil } +type logger struct { + log zerolog.Logger + underlyingFile *os.File +} + +func (l *logger) Close() error { + if l.underlyingFile == nil { + return nil + } + + return l.underlyingFile.Close() +} + type entityCloserAdapter[T io.Closer] struct { entity T } @@ -172,3 +218,4 @@ func (a entityCloserAdapter[T]) Shutdown() error { type storageRepositoryAdapter entityCloserAdapter[storage.Repository] type badgerDBAdapter entityCloserAdapter[*badger.DB] +type loggerAdapter entityCloserAdapter[*logger] diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 203ce30..2409baf 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -64,6 +64,10 @@ func setupDI() cli.BeforeFunc { } cfgpath := cmd.String("config") + if cfgpath == "" { + return errors.New("no config path provided") + } + debugLevel := cmd.Bool("verbose") jsonFormat := cmd.Bool("json") @@ -96,9 +100,12 @@ func setupCLI() *cli.Command { Version: fmt.Sprintf("%s (%s) %s", rooteway.Version(), rooteway.Commit(), rooteway.BuildTime()), Flags: []cli.Flag{ &cli.StringFlag{ - Name: "config", - Usage: "path to config in TOML format", - Value: "config.toml", + Name: "config", + Usage: "path to config in TOML format", + Value: "config.toml", + Sources: cli.NewValueSourceChain( + cli.EnvVar("EWAY_CONFIG"), + ), TakesFile: true, }, &cli.BoolFlag{ diff --git a/internal/config/log.go b/internal/config/log.go index 32859c4..5625a50 100644 --- a/internal/config/log.go +++ b/internal/config/log.go @@ -52,4 +52,5 @@ func (l *LogFormat) UnmarshalText(data []byte) (err error) { type Log struct { Level string `json:"level" toml:"level"` Format string `json:"format" toml:"format"` + Output string `json:"output" toml:"output"` }