open-vault/helper/logging/logger.go

136 lines
3.2 KiB
Go
Raw Normal View History

package logging
import (
"errors"
"fmt"
"io"
"path/filepath"
"strings"
log "github.com/hashicorp/go-hclog"
)
const (
UnspecifiedFormat LogFormat = iota
StandardFormat
JSONFormat
)
type LogFormat int
// LogConfig should be used to supply configuration when creating a new Vault logger
type LogConfig struct {
name string
logLevel log.Level
logFormat LogFormat
logFilePath string
}
func NewLogConfig(name string, logLevel log.Level, logFormat LogFormat, logFilePath string) LogConfig {
return LogConfig{
name: name,
logLevel: logLevel,
logFormat: logFormat,
logFilePath: strings.TrimSpace(logFilePath),
}
}
func (c LogConfig) IsFormatJson() bool {
return c.logFormat == JSONFormat
}
// Stringer implementation
func (lf LogFormat) String() string {
switch lf {
case UnspecifiedFormat:
return "unspecified"
case StandardFormat:
return "standard"
case JSONFormat:
return "json"
}
// unreachable
return "unknown"
}
// noErrorWriter is a wrapper to suppress errors when writing to w.
type noErrorWriter struct {
w io.Writer
}
func (w noErrorWriter) Write(p []byte) (n int, err error) {
_, _ = w.w.Write(p)
// We purposely return n == len(p) as if write was successful
return len(p), nil
}
// Setup creates a new logger with the specified configuration and writer
func Setup(config LogConfig, w io.Writer) (log.InterceptLogger, error) {
// Validate the log level
if config.logLevel.String() == "unknown" {
return nil, fmt.Errorf("invalid log level: %v", config.logLevel)
}
// If out is os.Stdout and Vault is being run as a Windows Service, writes will
// fail silently, which may inadvertently prevent writes to other writers.
// noErrorWriter is used as a wrapper to suppress any errors when writing to out.
writers := []io.Writer{noErrorWriter{w: w}}
if config.logFilePath != "" {
dir, fileName := filepath.Split(config.logFilePath)
if fileName == "" {
fileName = "vault-agent.log"
}
logFile := NewLogFile(dir, fileName)
if err := logFile.openNew(); err != nil {
return nil, fmt.Errorf("failed to set up file logging: %w", err)
}
writers = append(writers, logFile)
}
logger := log.NewInterceptLogger(&log.LoggerOptions{
Name: config.name,
Level: config.logLevel,
Output: io.MultiWriter(writers...),
JSONFormat: config.IsFormatJson(),
})
return logger, nil
}
// ParseLogFormat parses the log format from the provided string.
func ParseLogFormat(format string) (LogFormat, error) {
switch strings.ToLower(strings.TrimSpace(format)) {
case "":
return UnspecifiedFormat, nil
case "standard":
return StandardFormat, nil
case "json":
return JSONFormat, nil
default:
return UnspecifiedFormat, fmt.Errorf("unknown log format: %s", format)
}
}
func ParseLogLevel(logLevel string) (log.Level, error) {
var result log.Level
logLevel = strings.ToLower(strings.TrimSpace(logLevel))
switch logLevel {
case "trace":
result = log.Trace
case "debug":
result = log.Debug
case "notice", "info", "":
result = log.Info
case "warn", "warning":
result = log.Warn
case "err", "error":
result = log.Error
default:
return -1, errors.New(fmt.Sprintf("unknown log level: %s", logLevel))
}
return result, nil
}