log/logger_options.go (114 lines of code) (raw):

package log import ( "fmt" "io" "os" "time" "github.com/sirupsen/logrus" ) const ( iso8601TimestampFormat = "2006-01-02T15:04:05.000Z07:00" iso8601TimestampEnvKey = "GITLAB_ISO8601_LOG_TIMESTAMP" ) type loggerConfig struct { logger *logrus.Logger level logrus.Level formatter logrus.Formatter location *time.Location outputPath string writer io.Writer // A list of warnings that will be emitted once the logger is configured warnings []string } type timezoneFormatter struct { formatter logrus.Formatter location *time.Location } // LoggerOption will configure a new logrus Logger. type LoggerOption func(*loggerConfig) // We default to time.RFC3339 (to match the Logrus default) for the timestamp format for backward compatibility reasons. // Optionally, users can opt in for ISO8601 with millisecond precision by setting (to any value) the // iso8601TimestampEnvKey environment variable. func timestampFormat() string { if _, exists := os.LookupEnv(iso8601TimestampEnvKey); exists { return iso8601TimestampFormat } return time.RFC3339 } func applyLoggerOptions(opts []LoggerOption) *loggerConfig { conf := loggerConfig{ logger: logger, level: logrus.InfoLevel, formatter: &logrus.TextFormatter{TimestampFormat: timestampFormat()}, writer: os.Stdout, } for _, v := range opts { v(&conf) } return &conf } func (l *loggerConfig) buildFormatter() logrus.Formatter { out := l.formatter if l.location != nil { out = &timezoneFormatter{formatter: out, location: l.location} } return out } func (f *timezoneFormatter) Format(entry *logrus.Entry) ([]byte, error) { // Avoid mutating the passed-in entry as callers may retain a reference to // it. Since we don't change the `Data` field, a shallow copy is sufficient. if entry != nil { entryCopy := *entry entryCopy.Time = entryCopy.Time.In(f.location) entry = &entryCopy } return f.formatter.Format(entry) } // WithFormatter allows setting the format to `text`, `json`, `color` or `combined`. In case // the input is not recognized it defaults to text with a warning. // More details of these formats: // * `text` - human readable. // * `json` - computer readable, new-line delimited JSON. // * `color` - human readable, in color. Useful for development. // * `combined` - httpd access logs. Good for legacy access log parsers. func WithFormatter(format string) LoggerOption { return func(conf *loggerConfig) { timestampFormat := timestampFormat() switch format { case "text": conf.formatter = &logrus.TextFormatter{TimestampFormat: timestampFormat} case "color": conf.formatter = &logrus.TextFormatter{TimestampFormat: timestampFormat, ForceColors: true, EnvironmentOverrideColors: true} case "json": conf.formatter = &logrus.JSONFormatter{TimestampFormat: timestampFormat} case "combined": conf.formatter = newCombinedcombinedAccessLogFormatter() default: conf.warnings = append(conf.warnings, fmt.Sprintf("unknown logging format %s, ignoring option", format)) } } } // WithTimezone allows setting the timezone that will be used for log messages. // The default behaviour is to use the local timezone, but a specific timezone // (such as time.UTC) may be preferred. func WithTimezone(location *time.Location) LoggerOption { return func(conf *loggerConfig) { conf.location = location } } // WithLogLevel is used to set the log level when defaulting to `info` is not // wanted. Other options are: `debug`, `warn`, `error`, `fatal`, and `panic`. func WithLogLevel(level string) LoggerOption { return func(conf *loggerConfig) { logrusLevel, err := logrus.ParseLevel(level) if err != nil { conf.warnings = append(conf.warnings, fmt.Sprintf("unknown log level, ignoring option: %v", err)) } else { conf.level = logrusLevel } } } // WithOutputName allows customization of the sink of the logger. Output is either: // `stdout`, `stderr`, or a path to a file. func WithOutputName(outputName string) LoggerOption { return func(conf *loggerConfig) { switch outputName { case "stdout": conf.writer = os.Stdout case "stderr": conf.writer = os.Stderr default: conf.writer = nil conf.outputPath = outputName } } } // WithWriter allows the writer to be customized. The application is responsible for closing the writer manually. func WithWriter(writer io.Writer) LoggerOption { return func(conf *loggerConfig) { conf.writer = writer } } // WithLogger allows you to configure a proprietary logger using the `Initialize` method. func WithLogger(logger *logrus.Logger) LoggerOption { return func(conf *loggerConfig) { conf.logger = logger } }