Skip to content
Snippets Groups Projects
logging.go 4.08 KiB
Newer Older
package orusapi

import (
	"io"

	"github.com/rs/zerolog"
// NewLoggingOptions creates a LoggingOptions.
func NewLoggingOptions(output io.Writer) *LoggingOptions {
	var o LoggingOptions
Axel Prel's avatar
Axel Prel committed

// DefaultLogger ...
func DefaultLogger(output io.Writer) zerolog.Logger {
	return zerolog.
		New(output).
		With().
		Timestamp().
		Logger().
		Level(zerolog.WarnLevel)
}

// LoggingOptions holds the logging options.
type LoggingOptions struct {
	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"`
	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.
func (o *LoggingOptions) Logger() *zerolog.Logger {
	if o.log == nil {
		o.BuildLogger()
	}

	return o.log
func (o *LoggingOptions) Output() io.Writer {
	return o.wrappedOutput
}

// AddLogWrapper adds a log wrapper to the stack.
Christophe de Vienne's avatar
Christophe de Vienne committed
func (o *LoggingOptions) AddLogWrapper(wrapper func(io.Writer) io.Writer) {
	o.logWrappers = append(o.logWrappers, wrapper)
}

// SetMinLoggingLevel makes sure the logging level is not under a given value.
func (o *LoggingOptions) SetMinLoggingLevel(level zerolog.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"
		}

	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
func (o *LoggingOptions) Setup(output io.Writer) {
	o.logFinalOutput = output

	o.Verbose = func() {
		if !o.lockedVerbose {
			o.verboseCount++