184 lines
4.6 KiB
Go
184 lines
4.6 KiB
Go
package logger
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
|
|
"github.com/mattn/go-colorable"
|
|
"github.com/rs/zerolog"
|
|
fallbacklog "github.com/rs/zerolog/log"
|
|
"github.com/urfave/cli/v2"
|
|
"gopkg.in/natefinch/lumberjack.v2"
|
|
)
|
|
|
|
const (
|
|
EnableTerminalLog = false
|
|
DisableTerminalLog = true
|
|
|
|
LogLevelFlag = "loglevel"
|
|
LogFileFlag = "logfile"
|
|
LogDirectoryFlag = "log-directory"
|
|
LogTransportLevelFlag = "transport-loglevel"
|
|
|
|
LogSSHDirectoryFlag = "log-directory"
|
|
LogSSHLevelFlag = "log-level"
|
|
|
|
dirPermMode = 0744 // rwxr--r--
|
|
filePermMode = 0644 // rw-r--r--
|
|
)
|
|
|
|
func fallbackLogger(err error) *zerolog.Logger {
|
|
failLog := fallbacklog.With().Logger()
|
|
fallbacklog.Error().Msgf("Falling back to a default logger due to logger setup failure: %s", err)
|
|
|
|
return &failLog
|
|
}
|
|
|
|
func newZerolog(loggerConfig *Config) *zerolog.Logger {
|
|
var writers []io.Writer
|
|
|
|
if loggerConfig.ConsoleConfig != nil {
|
|
writers = append(writers, createConsoleLogger(*loggerConfig.ConsoleConfig))
|
|
}
|
|
|
|
if loggerConfig.FileConfig != nil {
|
|
fileLogger, err := createFileLogger(*loggerConfig.FileConfig)
|
|
if err != nil {
|
|
return fallbackLogger(err)
|
|
}
|
|
|
|
writers = append(writers, fileLogger)
|
|
}
|
|
|
|
if loggerConfig.RollingConfig != nil {
|
|
rollingLogger, err := createRollingLogger(*loggerConfig.RollingConfig)
|
|
if err != nil {
|
|
return fallbackLogger(err)
|
|
}
|
|
|
|
writers = append(writers, rollingLogger)
|
|
}
|
|
|
|
multi := zerolog.MultiLevelWriter(writers...)
|
|
|
|
level, err := zerolog.ParseLevel(loggerConfig.MinLevel)
|
|
if err != nil {
|
|
return fallbackLogger(err)
|
|
}
|
|
log := zerolog.New(multi).With().Timestamp().Logger().Level(level)
|
|
|
|
return &log
|
|
}
|
|
|
|
func CreateTransportLoggerFromContext(c *cli.Context, disableTerminal bool) *zerolog.Logger {
|
|
return createFromContext(c, LogTransportLevelFlag, LogDirectoryFlag, disableTerminal)
|
|
}
|
|
|
|
func CreateLoggerFromContext(c *cli.Context, disableTerminal bool) *zerolog.Logger {
|
|
return createFromContext(c, LogLevelFlag, LogDirectoryFlag, disableTerminal)
|
|
}
|
|
|
|
func CreateSSHLoggerFromContext(c *cli.Context, disableTerminal bool) *zerolog.Logger {
|
|
return createFromContext(c, LogSSHLevelFlag, LogSSHDirectoryFlag, disableTerminal)
|
|
}
|
|
|
|
func createFromContext(
|
|
c *cli.Context,
|
|
logLevelFlagName,
|
|
logDirectoryFlagName string,
|
|
disableTerminal bool,
|
|
) *zerolog.Logger {
|
|
logLevel := c.String(logLevelFlagName)
|
|
logFile := c.String(LogFileFlag)
|
|
logDirectory := c.String(logDirectoryFlagName)
|
|
|
|
loggerConfig := CreateConfig(
|
|
logLevel,
|
|
disableTerminal,
|
|
logDirectory,
|
|
logFile,
|
|
)
|
|
|
|
log := newZerolog(loggerConfig)
|
|
if incompatibleFlagsSet := logFile != "" && logDirectory != ""; incompatibleFlagsSet {
|
|
log.Error().Msgf("Your config includes values for both %s and %s, but they are incompatible. %s takes precedence.", LogFileFlag, logDirectoryFlagName, LogFileFlag)
|
|
}
|
|
return log
|
|
}
|
|
|
|
func Create(loggerConfig *Config) *zerolog.Logger {
|
|
if loggerConfig == nil {
|
|
loggerConfig = &Config{
|
|
defaultConfig.ConsoleConfig,
|
|
nil,
|
|
nil,
|
|
defaultConfig.MinLevel,
|
|
}
|
|
}
|
|
return newZerolog(loggerConfig)
|
|
}
|
|
|
|
func createConsoleLogger(config ConsoleConfig) io.Writer {
|
|
return zerolog.ConsoleWriter{
|
|
Out: colorable.NewColorableStderr(),
|
|
NoColor: config.noColor,
|
|
}
|
|
}
|
|
|
|
func createFileLogger(config FileConfig) (io.Writer, error) {
|
|
var logFile io.Writer
|
|
fullpath := config.Fullpath()
|
|
|
|
// Try to open the existing file
|
|
logFile, err := os.OpenFile(fullpath, os.O_APPEND|os.O_WRONLY, filePermMode)
|
|
if err != nil {
|
|
// If the existing file wasn't found, or couldn't be opened, just ignore
|
|
// it and recreate a new one.
|
|
logFile, err = createLogFile(config)
|
|
// If creating a new logfile fails, then we have no choice but to error out.
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
fileLogger := zerolog.New(logFile).With().Logger()
|
|
|
|
return fileLogger, nil
|
|
}
|
|
|
|
func createLogFile(config FileConfig) (io.Writer, error) {
|
|
if config.Dirname != "" {
|
|
err := os.MkdirAll(config.Dirname, dirPermMode)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to create directories for new logfile: %s", err)
|
|
}
|
|
}
|
|
|
|
mode := os.FileMode(filePermMode)
|
|
|
|
fullPath := filepath.Join(config.Dirname, config.Filename)
|
|
logFile, err := os.OpenFile(fullPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to create a new logfile: %s", err)
|
|
}
|
|
|
|
return logFile, nil
|
|
}
|
|
|
|
func createRollingLogger(config RollingConfig) (io.Writer, error) {
|
|
if err := os.MkdirAll(config.Dirname, dirPermMode); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &lumberjack.Logger{
|
|
Filename: path.Join(config.Dirname, config.Filename),
|
|
MaxBackups: config.maxBackups,
|
|
MaxSize: config.maxSize,
|
|
MaxAge: config.maxAge,
|
|
}, nil
|
|
}
|