Newer
Older
package orusapi
import (
"fmt"
"io"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"golang.org/x/term"
// NewLoggingOptions creates a LoggingOptions.
func NewLoggingOptions(log *zerolog.Logger, output io.Writer) (*LoggingOptions, error) {
var o LoggingOptions
if err := o.Setup(log, output); err != nil {
return nil, err
}
// MustLoggingOptions panic if err is not nil.
func MustLoggingOptions(o *LoggingOptions, err error) *LoggingOptions {
if err != nil {
panic(err)
}
// 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 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"`
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"`
}
// Logger returns the latest configured logger.
func (o *LoggingOptions) Logger() zerolog.Logger {
return *o.log
}
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.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)
o.resetOutput()
}
// 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)
}
}
// Setup ...
func (o *LoggingOptions) Setup(log *zerolog.Logger, output io.Writer) error {
logLevelAutoLocked := false
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"
}
}
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))
}
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
}
}
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