TUN-3607: Set up single-file logger with zerolog

This commit is contained in:
Areg Harutyunyan 2020-12-08 12:16:25 -06:00 committed by Adam Chalmers
parent 9bc1c0c70b
commit 2ea491b1d0
3 changed files with 95 additions and 31 deletions

View File

@ -271,7 +271,7 @@ func (d *deleteMockTunnelStore) CleanupConnections(tunnelID uuid.UUID) error {
func Test_subcommandContext_Delete(t *testing.T) { func Test_subcommandContext_Delete(t *testing.T) {
type fields struct { type fields struct {
c *cli.Context c *cli.Context
logger logger.Service log *zerolog.Logger
isUIEnabled bool isUIEnabled bool
fs fileSystem fs fileSystem
tunnelstoreClient *deleteMockTunnelStore tunnelstoreClient *deleteMockTunnelStore
@ -283,8 +283,7 @@ func Test_subcommandContext_Delete(t *testing.T) {
newCertPath := "new_cert.json" newCertPath := "new_cert.json"
tunnelID1 := uuid.MustParse("df5ed608-b8b4-4109-89f3-9f2cf199df64") tunnelID1 := uuid.MustParse("df5ed608-b8b4-4109-89f3-9f2cf199df64")
tunnelID2 := uuid.MustParse("af5ed608-b8b4-4109-89f3-9f2cf199df64") tunnelID2 := uuid.MustParse("af5ed608-b8b4-4109-89f3-9f2cf199df64")
logger, err := logger.New() log := zerolog.Nop()
require.NoError(t, err)
var tests = []struct { var tests = []struct {
name string name string
@ -296,7 +295,7 @@ func Test_subcommandContext_Delete(t *testing.T) {
{ {
name: "clean up continues if credentials are not found", name: "clean up continues if credentials are not found",
fields: fields{ fields: fields{
logger: logger, log: &log,
fs: mockFileSystem{ fs: mockFileSystem{
rf: func(filePath string) ([]byte, error) { rf: func(filePath string) ([]byte, error) {
return nil, errors.New("file not found") return nil, errors.New("file not found")
@ -307,7 +306,7 @@ func Test_subcommandContext_Delete(t *testing.T) {
flagSet := flag.NewFlagSet("test0", flag.PanicOnError) flagSet := flag.NewFlagSet("test0", flag.PanicOnError)
flagSet.String(CredFileFlag, newCertPath, "") flagSet.String(CredFileFlag, newCertPath, "")
c := cli.NewContext(cli.NewApp(), flagSet, nil) c := cli.NewContext(cli.NewApp(), flagSet, nil)
err = c.Set(CredFileFlag, newCertPath) _ = c.Set(CredFileFlag, newCertPath)
return c return c
}(), }(),
tunnelstoreClient: newDeleteMockTunnelStore( tunnelstoreClient: newDeleteMockTunnelStore(
@ -331,7 +330,7 @@ func Test_subcommandContext_Delete(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
sc := &subcommandContext{ sc := &subcommandContext{
c: tt.fields.c, c: tt.fields.c,
logger: tt.fields.logger, log: tt.fields.log,
isUIEnabled: tt.fields.isUIEnabled, isUIEnabled: tt.fields.isUIEnabled,
fs: tt.fields.fs, fs: tt.fields.fs,
tunnelstoreClient: tt.fields.tunnelstoreClient, tunnelstoreClient: tt.fields.tunnelstoreClient,

View File

@ -1,5 +1,7 @@
package logger package logger
import "path/filepath"
var defaultConfig = createDefaultConfig() var defaultConfig = createDefaultConfig()
// Logging configuration // Logging configuration
@ -16,11 +18,16 @@ type ConsoleConfig struct {
} }
type FileConfig struct { type FileConfig struct {
Filepath string Dirname string
Filename string
}
func (fc *FileConfig) Fullpath() string {
return filepath.Join(fc.Dirname, fc.Filename)
} }
type RollingConfig struct { type RollingConfig struct {
Directory string Dirname string
Filename string Filename string
maxSize int // megabytes maxSize int // megabytes
@ -34,18 +41,19 @@ func createDefaultConfig() Config {
const RollingMaxSize = 1 // Mb const RollingMaxSize = 1 // Mb
const RollingMaxBackups = 5 // files const RollingMaxBackups = 5 // files
const RollingMaxAge = 0 // Keep forever const RollingMaxAge = 0 // Keep forever
const rollingLogFilename = "cloudflared.log" const defaultLogFilename = "cloudflared.log"
return Config{ return Config{
ConsoleConfig: &ConsoleConfig{ ConsoleConfig: &ConsoleConfig{
noColor: false, noColor: false,
}, },
FileConfig: &FileConfig{ FileConfig: &FileConfig{
Filepath: "", Dirname: "",
Filename: defaultLogFilename,
}, },
RollingConfig: &RollingConfig{ RollingConfig: &RollingConfig{
Directory: "", Dirname: "",
Filename: rollingLogFilename, Filename: defaultLogFilename,
maxSize: RollingMaxSize, maxSize: RollingMaxSize,
maxBackups: RollingMaxBackups, maxBackups: RollingMaxBackups,
maxAge: RollingMaxAge, maxAge: RollingMaxAge,
@ -57,7 +65,7 @@ func createDefaultConfig() Config {
func CreateConfig( func CreateConfig(
minLevel string, minLevel string,
disableTerminal bool, disableTerminal bool,
rollingLogPath, rollingLogFilename, nonRollingLogFilePath string, rollingLogPath, nonRollingLogFilePath string,
) *Config { ) *Config {
var console *ConsoleConfig var console *ConsoleConfig
if !disableTerminal { if !disableTerminal {
@ -65,13 +73,11 @@ func CreateConfig(
} }
var file *FileConfig var file *FileConfig
if nonRollingLogFilePath != "" {
file = createFileConfig(nonRollingLogFilePath)
}
var rolling *RollingConfig var rolling *RollingConfig
if rollingLogPath != "" { if rollingLogPath != "" {
rolling = createRollingConfig(rollingLogPath, rollingLogFilename) rolling = createRollingConfig(rollingLogPath)
} else if nonRollingLogFilePath != "" {
file = createFileConfig(nonRollingLogFilePath)
} }
if minLevel == "" { if minLevel == "" {
@ -93,24 +99,27 @@ func createConsoleConfig() *ConsoleConfig {
} }
} }
func createFileConfig(filepath string) *FileConfig { func createFileConfig(fullpath string) *FileConfig {
if filepath == "" { if fullpath == "" {
filepath = defaultConfig.FileConfig.Filepath return defaultConfig.FileConfig
} }
dirname, filename := filepath.Split(fullpath)
return &FileConfig{ return &FileConfig{
Filepath: filepath, Dirname: dirname,
Filename: filename,
} }
} }
func createRollingConfig(directory, filename string) *RollingConfig { func createRollingConfig(directory string) *RollingConfig {
if directory == "" { if directory == "" {
directory = defaultConfig.RollingConfig.Directory directory = defaultConfig.RollingConfig.Dirname
} }
return &RollingConfig{ return &RollingConfig{
Directory: directory, Dirname: directory,
Filename: filename, Filename: defaultConfig.RollingConfig.Filename,
maxSize: defaultConfig.RollingConfig.maxSize, maxSize: defaultConfig.RollingConfig.maxSize,
maxBackups: defaultConfig.RollingConfig.maxBackups, maxBackups: defaultConfig.RollingConfig.maxBackups,
maxAge: defaultConfig.RollingConfig.maxAge, maxAge: defaultConfig.RollingConfig.maxAge,

View File

@ -1,6 +1,7 @@
package logger package logger
import ( import (
"fmt"
"io" "io"
"os" "os"
"path" "path"
@ -22,6 +23,9 @@ const (
LogSSHDirectoryFlag = "log-directory" LogSSHDirectoryFlag = "log-directory"
LogSSHLevelFlag = "log-level" LogSSHLevelFlag = "log-level"
dirPermMode = 0744 // rwxr--r--
filePermMode = 0644 // rw-r--r--
) )
func fallbackLogger(err error) *zerolog.Logger { func fallbackLogger(err error) *zerolog.Logger {
@ -38,6 +42,15 @@ func newZerolog(loggerConfig *Config) *zerolog.Logger {
writers = append(writers, createConsoleLogger(*loggerConfig.ConsoleConfig)) 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 { if loggerConfig.RollingConfig != nil {
rollingLogger, err := createRollingLogger(*loggerConfig.RollingConfig) rollingLogger, err := createRollingLogger(*loggerConfig.RollingConfig)
if err != nil { if err != nil {
@ -84,11 +97,14 @@ func createFromContext(
logLevel, logLevel,
disableTerminal, disableTerminal,
logDirectory, logDirectory,
defaultConfig.RollingConfig.Filename,
logFile, logFile,
) )
return newZerolog(loggerConfig) 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 { func Create(loggerConfig *Config) *zerolog.Logger {
@ -106,13 +122,53 @@ func createConsoleLogger(config ConsoleConfig) io.Writer {
} }
} }
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)
logFile, err := os.OpenFile(config.Filename, 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) { func createRollingLogger(config RollingConfig) (io.Writer, error) {
if err := os.MkdirAll(config.Directory, 0744); err != nil { if err := os.MkdirAll(config.Dirname, dirPermMode); err != nil {
return nil, err return nil, err
} }
return &lumberjack.Logger{ return &lumberjack.Logger{
Filename: path.Join(config.Directory, config.Filename), Filename: path.Join(config.Dirname, config.Filename),
MaxBackups: config.maxBackups, MaxBackups: config.maxBackups,
MaxSize: config.maxSize, MaxSize: config.maxSize,
MaxAge: config.maxAge, MaxAge: config.maxAge,