Newer
Older
package orusapi
import (
"fmt"
"io"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"golang.org/x/crypto/ssh/terminal"
)
// 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
}
return &o, nil
}
// MustLoggingOptions panic if err is not nil
func MustLoggingOptions(o *LoggingOptions, err error) *LoggingOptions {
if err != nil {
panic(err)
}
return o
}
// 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."`
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."`
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()
}
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// 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 {
var 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 && terminal.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 && terminal.IsTerminal(int(outputFile.Fd())) {
value = "info"
} else {
value = "warn"
}
}
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
}