diff --git a/cmd/program.go b/cmd/program.go
index e0809eb545e2529247271a00a43ee4748375b375_Y21kL3Byb2dyYW0uZ28=..39a574cadd10d2efbd4a4437a188ed392cb516a5_Y21kL3Byb2dyYW0uZ28= 100644
--- a/cmd/program.go
+++ b/cmd/program.go
@@ -133,13 +133,13 @@
 func WithOptionsGroup[E any](
 	name string, description string, getgroup func(*Program[E]) any,
 ) Option[E] {
-	return PostInit( func (program *Program[E]) {
-			if _, err := program.Parser.AddGroup(
-				name, description, getgroup(program),
-			); err != nil {
-				panic(err)
-			}
-		})
+	return PostInit(func(program *Program[E]) {
+		if _, err := program.Parser.AddGroup(
+			name, description, getgroup(program),
+		); err != nil {
+			panic(err)
+		}
+	})
 }
 
 func WithTokenOptions[E any]() Option[E] {
@@ -196,8 +196,7 @@
 		opt(&program)
 	}
 
-	program.LoggingOptions = orusapi.MustLoggingOptions(
-		orusapi.NewLoggingOptions(&program.Logger, os.Stdout))
+	program.LoggingOptions = orusapi.NewLoggingOptions(os.Stdout)
 
 	bootstrapParser.NamespaceDelimiter = "-"
 	bootstrapParser.EnvNamespaceDelimiter = "_"
@@ -209,6 +208,15 @@
 		panic(err)
 	}
 
+	{
+		g, err := bootstrapParser.AddGroup("Logging", "Logging options", program.LoggingOptions)
+		if err != nil {
+			panic(err)
+		}
+		g.Namespace = "log"
+		g.EnvNamespace = "LOG"
+	}
+
 	if _, err := parser.AddGroup("Configuration", "Configuration file", &program.ConfigFileOption); err != nil {
 		panic(err)
 	}
@@ -250,6 +258,17 @@
 		pi(&program)
 	}
 
+	program.Parser.CommandHandler = func(command flags.Commander, args []string) error {
+		program.LoggingOptions.BuildLogger()
+		program.Logger = *program.LoggingOptions.Logger()
+
+		if command == nil {
+			return nil
+		}
+
+		return command.Execute(args)
+	}
+
 	return &program
 }
 
@@ -265,6 +284,18 @@
 
 		return 1
 	}
+
+	logLevelOption := program.BootstrapParser.FindOptionByLongName("log-level")
+	logFormatOption := program.BootstrapParser.FindOptionByLongName("log-format")
+
+	program.LoggingOptions.Lock(
+		logLevelOption.IsSet() && !logLevelOption.IsSetDefault(),
+		logFormatOption.IsSet() && !logFormatOption.IsSetDefault(),
+	)
+
+	program.LoggingOptions.BuildLogger()
+	program.Logger = *program.LoggingOptions.Logger()
+
 	if program.ConfigFileOption.ConfigFile != "" {
 		program.Logger.Debug().Str("configfile", program.ConfigFileOption.ConfigFile).Msg("parsing configuration file")
 		iniParser := flags.NewIniParser(program.Parser)
@@ -273,6 +304,8 @@
 
 			return 1
 		}
+		program.LoggingOptions.BuildLogger()
+		program.Logger = *program.LoggingOptions.Logger()
 	}
 
 	if _, err := program.Parser.Parse(); err != nil {
diff --git a/logging.go b/logging.go
index e0809eb545e2529247271a00a43ee4748375b375_bG9nZ2luZy5nbw==..39a574cadd10d2efbd4a4437a188ed392cb516a5_bG9nZ2luZy5nbw== 100644
--- a/logging.go
+++ b/logging.go
@@ -1,7 +1,6 @@
 package orusapi
 
 import (
-	"fmt"
 	"io"
 
 	"github.com/rs/zerolog"
@@ -5,8 +4,7 @@
 	"io"
 
 	"github.com/rs/zerolog"
-	"github.com/rs/zerolog/log"
 	"golang.org/x/term"
 )
 
 // NewLoggingOptions creates a LoggingOptions.
@@ -9,6 +7,6 @@
 	"golang.org/x/term"
 )
 
 // NewLoggingOptions creates a LoggingOptions.
-func NewLoggingOptions(log *zerolog.Logger, output io.Writer) (*LoggingOptions, error) {
+func NewLoggingOptions(output io.Writer) *LoggingOptions {
 	var o LoggingOptions
@@ -14,5 +12,3 @@
 	var o LoggingOptions
-	if err := o.Setup(log, output); err != nil {
-		return nil, err
-	}
+	o.Setup(output)
 
@@ -18,14 +14,5 @@
 
-	return &o, nil
-}
-
-// MustLoggingOptions panic if err is not nil.
-func MustLoggingOptions(o *LoggingOptions, err error) *LoggingOptions {
-	if err != nil {
-		panic(err)
-	}
-
-	return o
+	return &o
 }
 
 // DefaultLogger ...
@@ -40,8 +27,10 @@
 
 // LoggingOptions holds the logging options.
 type LoggingOptions struct {
-	Level   func(string) error `long:"level" env:"LEVEL" ini-name:"log-level" choice:"trace" choice:"debug" choice:"info" choice:"warn" choice:"error" choice:"fatal" choice:"panic" choice:"auto" default:"auto" description:"log level. 'auto' selects 'info' when stdout is a tty, 'error' otherwise."` //nolint:lll
-	Format  func(string) error `long:"format" env:"FORMAT" ini-name:"log-format" choice:"json" choice:"pretty" choice:"auto" default:"auto" description:"Logs format. 'auto' selects 'pretty' if stdout is a tty."`                                                                                        //nolint:lll
-	Verbose func()             `short:"v" long:"verbose" no-ini:"t" description:"Increase log verbosity. Can be repeated"`
+	Level   string `long:"level" env:"LEVEL" ini-name:"level" choice:"trace" choice:"debug" choice:"info" choice:"warn" choice:"error" choice:"fatal" choice:"panic" choice:"auto" default:"auto" description:"log level. 'auto' selects 'info' when stdout is a tty, 'error' otherwise."` //nolint:lll
+	Format  string `long:"format" env:"FORMAT" ini-name:"format" choice:"json" choice:"pretty" choice:"auto" default:"auto" description:"Logs format. 'auto' selects 'pretty' if stdout is a tty."`                                                                                        //nolint:lll
+	Verbose func() `short:"v" long:"verbose" no-ini:"t" description:"Increase log verbosity. Can be repeated"`
+
+	logOutput io.Writer `no-flag:"t"`
 
 	logFinalOutput io.Writer                   `no-flag:"t"`
@@ -46,6 +35,5 @@
 
 	logFinalOutput io.Writer                   `no-flag:"t"`
-	logOutput      io.Writer                   `no-flag:"t"`
 	wrappedOutput  io.Writer                   `no-flag:"t"`
 	logWrappers    []func(io.Writer) io.Writer `no-flag:"t"`
 	log            *zerolog.Logger             `no-flag:"t"`
@@ -49,6 +37,22 @@
 	wrappedOutput  io.Writer                   `no-flag:"t"`
 	logWrappers    []func(io.Writer) io.Writer `no-flag:"t"`
 	log            *zerolog.Logger             `no-flag:"t"`
+
+	minLevel      zerolog.Level
+	verboseCount  int `no-flag:"t"`
+	lockedVerbose bool
+	lockedLevel   string
+	lockedFormat  string
+}
+
+func (o *LoggingOptions) Lock(lockLevel, lockFormat bool) {
+	o.lockedVerbose = true
+	if lockLevel {
+		o.lockedLevel = o.Level
+	}
+	if lockFormat {
+		o.lockedFormat = o.Format
+	}
 }
 
 // Logger returns the latest configured logger.
@@ -52,11 +56,15 @@
 }
 
 // Logger returns the latest configured logger.
-func (o *LoggingOptions) Logger() zerolog.Logger {
-	return *o.log
+func (o *LoggingOptions) Logger() *zerolog.Logger {
+	if o.log == nil {
+		o.BuildLogger()
+	}
+
+	return o.log
 }
 
 func (o *LoggingOptions) Output() io.Writer {
 	return o.wrappedOutput
 }
 
@@ -57,18 +65,9 @@
 }
 
 func (o *LoggingOptions) Output() io.Writer {
 	return o.wrappedOutput
 }
 
-func (o *LoggingOptions) resetOutput() {
-	out := o.logOutput
-	for _, wrapper := range o.logWrappers {
-		out = wrapper(out)
-	}
-	o.wrappedOutput = out
-	*o.log = o.log.Output(out)
-}
-
 // AddLogWrapper adds a log wrapper to the stack.
 func (o *LoggingOptions) AddLogWrapper(wrapper func(io.Writer) io.Writer) {
 	o.logWrappers = append(o.logWrappers, wrapper)
@@ -72,8 +71,7 @@
 // AddLogWrapper adds a log wrapper to the stack.
 func (o *LoggingOptions) AddLogWrapper(wrapper func(io.Writer) io.Writer) {
 	o.logWrappers = append(o.logWrappers, wrapper)
-	o.resetOutput()
 }
 
 // SetMinLoggingLevel makes sure the logging level is not under a given value.
 func (o *LoggingOptions) SetMinLoggingLevel(level zerolog.Level) {
@@ -76,7 +74,46 @@
 }
 
 // SetMinLoggingLevel makes sure the logging level is not under a given value.
 func (o *LoggingOptions) SetMinLoggingLevel(level zerolog.Level) {
-	if level < o.log.GetLevel() {
-		*o.log = log.Level(level)
+	o.minLevel = level
+}
+
+func parseOutputFormat(format string, finalOutput io.Writer) io.Writer {
+	switch format {
+	case "pretty":
+		return ConsoleWriter{Out: finalOutput}
+	case "json":
+		return finalOutput
+	default:
+		panic("unknown 'log-format' value: " + format)
+	}
+}
+
+func parseLogLevel(value string, finalOutput io.Writer) zerolog.Level {
+	if value == "auto" || value == "" {
+		if outputFile, hasFd := finalOutput.(interface{ Fd() uintptr }); hasFd &&
+			term.IsTerminal(int(outputFile.Fd())) {
+			value = zerolog.LevelInfoValue
+		} else {
+			value = zerolog.LevelWarnValue
+		}
+	}
+
+	level, err := zerolog.ParseLevel(value)
+	if err != nil {
+		panic(err)
+	}
+
+	return level
+}
+
+func (o LoggingOptions) buildOutput() io.Writer {
+	format := o.GetFormat()
+	if format == "auto" || format == "" {
+		if outputFile, hasFd := o.logFinalOutput.(interface{ Fd() uintptr }); hasFd &&
+			term.IsTerminal(int(outputFile.Fd())) {
+			format = "pretty"
+		} else {
+			format = "json"
+		}
 	}
@@ -82,4 +119,46 @@
 	}
+
+	out := parseOutputFormat(format, o.logFinalOutput)
+
+	for _, wrapper := range o.logWrappers {
+		out = wrapper(out)
+	}
+
+	return out
+}
+
+func (o LoggingOptions) GetLevel() string {
+	if o.lockedLevel != "" {
+		return o.lockedLevel
+	}
+
+	return o.Level
+}
+
+func (o LoggingOptions) GetFormat() string {
+	if o.lockedFormat != "" {
+		return o.lockedFormat
+	}
+
+	return o.Format
+}
+
+// BuildLogger rebuild the logger even if already initialized
+func (o *LoggingOptions) BuildLogger() {
+	out := o.buildOutput()
+	o.wrappedOutput = out
+
+	level := parseLogLevel(o.GetLevel(), o.logFinalOutput)
+	level -= zerolog.Level(o.verboseCount)
+
+	log := zerolog.
+		New(out).
+		With().
+		Timestamp().
+		Logger().
+		Level(level)
+
+	o.log = &log
 }
 
 // Setup ...
@@ -83,8 +162,6 @@
 }
 
 // Setup ...
-func (o *LoggingOptions) Setup(log *zerolog.Logger, output io.Writer) error {
-	logLevelAutoLocked := false
-
+func (o *LoggingOptions) Setup(output io.Writer) {
 	o.logFinalOutput = output
 
@@ -89,14 +166,6 @@
 	o.logFinalOutput = output
 
-	o.log = log
-	*o.log = DefaultLogger(output)
-
-	o.Format = func(format string) error {
-		if format == "auto" {
-			if outputFile, hasFd := o.logFinalOutput.(interface{ Fd() uintptr }); hasFd &&
-				term.IsTerminal(int(outputFile.Fd())) {
-				format = "pretty"
-			} else {
-				format = "json"
-			}
+	o.Verbose = func() {
+		if !o.lockedVerbose {
+			o.verboseCount++
 		}
@@ -102,16 +171,2 @@
 		}
-		switch format {
-		case "pretty":
-			o.logOutput = ConsoleWriter{Out: o.logFinalOutput}
-		case "json":
-			o.logOutput = o.logFinalOutput
-		default:
-			return fmt.Errorf("invalid log-format: %s", format)
-		}
-		o.resetOutput()
-
-		return nil
-	}
-	o.Verbose = func() {
-		*o.log = o.log.Level(o.log.GetLevel() - zerolog.Level(1))
 	}
@@ -117,34 +172,2 @@
 	}
-	o.Level = func(value string) error {
-		if value == "auto" {
-			if logLevelAutoLocked {
-				// The current call is at best redondant, at worse called by
-				// default after some potential --verbose that would be ignored
-				return nil
-			}
-			if outputFile, hasFd := o.logFinalOutput.(interface{ Fd() uintptr }); hasFd &&
-				term.IsTerminal(int(outputFile.Fd())) {
-				value = zerolog.LevelInfoValue
-			} else {
-				value = zerolog.LevelWarnValue
-			}
-		}
-
-		level, err := zerolog.ParseLevel(value)
-		if err != nil {
-			return err
-		}
-		*o.log = o.log.Level(level)
-
-		return nil
-	}
-	if err := o.Format("auto"); err != nil {
-		return err
-	}
-	if err := o.Level("auto"); err != nil {
-		return err
-	}
-	logLevelAutoLocked = true
-
-	return nil
 }
diff --git a/logging_test.go b/logging_test.go
index e0809eb545e2529247271a00a43ee4748375b375_bG9nZ2luZ190ZXN0Lmdv..39a574cadd10d2efbd4a4437a188ed392cb516a5_bG9nZ2luZ190ZXN0Lmdv 100644
--- a/logging_test.go
+++ b/logging_test.go
@@ -6,7 +6,6 @@
 
 	"github.com/rs/zerolog"
 	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
 
 	"orus.io/orus-io/go-orusapi"
 )
@@ -14,6 +13,5 @@
 func TestLogging(t *testing.T) {
 	var (
 		buf bytes.Buffer
-		log zerolog.Logger
 	)
 
@@ -18,5 +16,6 @@
 	)
 
-	var o orusapi.LoggingOptions
-	require.NoError(t, o.Setup(&log, &buf))
+	t.Run("verbose flag", func(t *testing.T) {
+		var o orusapi.LoggingOptions
+		o.Setup(&buf)
 
@@ -22,3 +21,5 @@
 
-	assert.Equal(t, zerolog.WarnLevel, o.Logger().GetLevel())
+		o.Level = "warn"
+		o.BuildLogger()
+		assert.Equal(t, zerolog.WarnLevel, o.Logger().GetLevel())
 
@@ -24,4 +25,5 @@
 
-	o.Verbose()
-	assert.Equal(t, zerolog.InfoLevel, o.Logger().GetLevel())
+		o.Verbose()
+		o.BuildLogger()
+		assert.Equal(t, zerolog.InfoLevel, o.Logger().GetLevel())
 
@@ -27,4 +29,5 @@
 
-	o.Verbose()
-	assert.Equal(t, zerolog.DebugLevel, o.Logger().GetLevel())
+		o.Verbose()
+		o.BuildLogger()
+		assert.Equal(t, zerolog.DebugLevel, o.Logger().GetLevel())
 
@@ -30,4 +33,6 @@
 
-	o.Verbose()
-	assert.Equal(t, zerolog.TraceLevel, o.Logger().GetLevel())
+		o.Verbose()
+		o.BuildLogger()
+		assert.Equal(t, zerolog.TraceLevel, o.Logger().GetLevel())
+	})
 
@@ -33,4 +38,8 @@
 
-	require.NoError(t, o.Level(zerolog.LevelFatalValue))
-	assert.Equal(t, zerolog.FatalLevel, o.Logger().GetLevel())
+	t.Run("level flag", func(t *testing.T) {
+		var o orusapi.LoggingOptions
+		o.Setup(&buf)
+		o.Level = zerolog.LevelFatalValue
+		o.BuildLogger()
+		assert.Equal(t, zerolog.FatalLevel, o.Logger().GetLevel())
 
@@ -36,4 +45,6 @@
 
-	require.NoError(t, o.Level(zerolog.LevelInfoValue))
-	assert.Equal(t, zerolog.InfoLevel, o.Logger().GetLevel())
+		o.Level = zerolog.LevelInfoValue
+		o.BuildLogger()
+		assert.Equal(t, zerolog.InfoLevel, o.Logger().GetLevel())
+	})
 
@@ -39,5 +50,10 @@
 
-	require.NoError(t, o.Format("pretty"))
-	log.Warn().Msg("this is a warning")
-	assert.Contains(t, buf.String(), "WRN")
+	t.Run("format flag", func(t *testing.T) {
+		var o orusapi.LoggingOptions
+		o.Setup(&buf)
+		o.Format = "pretty"
+		o.BuildLogger()
+		log := o.Logger()
+		log.Warn().Msg("this is a warning")
+		assert.Contains(t, buf.String(), "WRN")
 
@@ -43,6 +59,9 @@
 
-	buf.Reset()
-	require.NoError(t, o.Format("json"))
-	log.Warn().Msg("this is a warning")
-	assert.Contains(t, buf.String(), "{")
+		buf.Reset()
+		o.Format = "json"
+		o.BuildLogger()
+		log = o.Logger()
+		log.Warn().Msg("this is a warning")
+		assert.Contains(t, buf.String(), "{")
+	})
 }