Skip to content

Commit 3dbc597

Browse files
Pavel Parshinpparshin
Pavel Parshin
authored andcommitted
Added option to write logs to a file
Integrated to lib provides logs rotation.
1 parent 844dca8 commit 3dbc597

File tree

9 files changed

+125
-23
lines changed

9 files changed

+125
-23
lines changed

cmd/qumomf/main.go

+44-8
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,21 @@ package main
33
import (
44
"context"
55
"flag"
6+
"fmt"
7+
"io"
68
"log/syslog"
79
"net/http"
810
"os"
911
"os/signal"
12+
"path"
1013
"syscall"
1114
"time"
1215

1316
"github.com/prometheus/client_golang/prometheus/promhttp"
1417
"github.com/rs/zerolog"
1518
"github.com/rs/zerolog/log"
19+
"golang.org/x/sys/unix"
20+
"gopkg.in/natefinch/lumberjack.v2"
1621

1722
"github.com/shmel1k/qumomf/internal/config"
1823
"github.com/shmel1k/qumomf/internal/coordinator"
@@ -80,26 +85,57 @@ func main() {
8085
func initLogger(cfg *config.Config) zerolog.Logger {
8186
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
8287

83-
logLevel, err := zerolog.ParseLevel(cfg.Qumomf.LogLevel)
88+
loggingCfg := cfg.Qumomf.Logging
89+
90+
logLevel, err := zerolog.ParseLevel(loggingCfg.Level)
8491
if err != nil {
85-
log.Warn().Msgf("Unknown Level String: '%s', defaulting to DebugLevel", cfg.Qumomf.LogLevel)
92+
log.Warn().Msgf("Unknown Level String: '%s', defaulting to DebugLevel", loggingCfg.Level)
8693
logLevel = zerolog.DebugLevel
8794
}
8895

89-
base := zerolog.New(os.Stdout).Level(logLevel).With().Timestamp().Logger()
96+
writers := make([]io.Writer, 0, 1)
97+
writers = append(writers, os.Stdout)
9098

91-
if cfg.Qumomf.EnableSysLog {
99+
if loggingCfg.SysLogEnabled {
92100
w, err := syslog.New(syslog.LOG_INFO, "qumomf")
93101
if err != nil {
94102
log.Warn().Err(err).Msg("Unable to connect to the system log daemon")
95-
return base
103+
} else {
104+
writers = append(writers, zerolog.SyslogLevelWriter(w))
105+
}
106+
}
107+
108+
if loggingCfg.FileLoggingEnabled {
109+
w, err := newRollingLogFile(&loggingCfg)
110+
if err != nil {
111+
log.Warn().Err(err).Msg("Unable to init file logger")
112+
} else {
113+
writers = append(writers, w)
96114
}
97-
syslogWriter := zerolog.SyslogLevelWriter(w)
115+
}
116+
117+
var baseLogger zerolog.Logger
118+
if len(writers) == 1 {
119+
baseLogger = zerolog.New(writers[0])
120+
} else {
121+
return zerolog.New(zerolog.MultiLevelWriter(writers...))
122+
}
123+
124+
return baseLogger.Level(logLevel).With().Timestamp().Logger()
125+
}
98126

99-
return zerolog.New(zerolog.MultiLevelWriter(os.Stdout, syslogWriter)).Level(logLevel).With().Timestamp().Logger()
127+
func newRollingLogFile(cfg *config.Logging) (io.Writer, error) {
128+
dir := path.Dir(cfg.Filename)
129+
if unix.Access(dir, unix.W_OK) != nil {
130+
return nil, fmt.Errorf("no permissions to write logs to dir: %s", dir)
100131
}
101132

102-
return base
133+
return &lumberjack.Logger{
134+
Filename: cfg.Filename,
135+
MaxBackups: cfg.MaxBackups,
136+
MaxSize: cfg.MaxSize,
137+
MaxAge: cfg.MaxAge,
138+
}, nil
103139
}
104140

105141
func initHTTPServer(port string) *http.Server {

config/qumomf.conf.yml

+16-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
qumomf:
22
# TCP port to listen.
33
port: ':8080'
4-
# Verbose level of logging: trace, debug, info, warn, error, fatal, panic.
5-
# To disable logging, pass an empty string.
6-
log_level: "debug"
7-
# Write to the local syslog daemon as well as to stdout.
8-
enable_syslog: false
4+
logging:
5+
# Verbose level of logging: trace, debug, info, warn, error, fatal, panic.
6+
# To disable logging, pass an empty string.
7+
level: 'debug'
8+
# Write logs to the local syslog daemon.
9+
syslog_enabled: false
10+
# Write logs to the file.
11+
file_enabled: true
12+
# Absolute path to the log output file.
13+
file_name: '/var/log/qumomf.log'
14+
# The max size in MB of the logfile before it's rolled.
15+
file_max_size: 256
16+
# The max number of rolled files to keep.
17+
file_max_backups: 3
18+
# The max age in days to keep a logfile.
19+
file_max_age: 5
920
# Indicates whether qumomf should run in the readonly mode:
1021
# no auto failover will be executed.
1122
# Can be overwritten by cluster-specific options.

config/qumomf.daemon.min.conf.yml

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
qumomf:
22
port: ':8080'
3-
log_level: "debug"
4-
enable_syslog: false
3+
logging:
4+
level: 'debug'
5+
syslog_enabled: false
6+
file_enabled: true
7+
file_name: '/var/log/qumomf.log'
8+
file_max_size: 256
9+
file_max_backups: 3
10+
file_max_age: 5
511
readonly: true
612
cluster_discovery_time: '5s'
713
cluster_recovery_time: '1s'

example/qumomf.yml

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
qumomf:
22
port: ':8080'
3-
log_level: "debug"
3+
logging:
4+
level: 'debug'
5+
syslog_enabled: false
6+
file_enabled: true
7+
file_name: '/home/pavel/work/qumomf/src/github.com/shmel1k/qumomf/bin/qumomf.log'
8+
file_max_size: 256
9+
file_max_backups: 3
10+
file_max_age: 5
411
readonly: true
512
cluster_discovery_time: '5s'
613
cluster_recovery_time: '1s'

go.mod

+2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ require (
1111
github.com/tarantool/go-tarantool v0.0.0-20191229181800-f4ece3508d87
1212
github.com/tinylib/msgp v1.1.1 // indirect
1313
github.com/viciious/go-tarantool v0.0.0-20190828171136-ede812c03707
14+
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82
1415
google.golang.org/appengine v1.6.5 // indirect
16+
gopkg.in/natefinch/lumberjack.v2 v2.0.0
1517
gopkg.in/vmihailenco/msgpack.v2 v2.9.1 // indirect
1618
gopkg.in/yaml.v2 v2.2.8
1719
)

go.sum

+3
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h
9898
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
9999
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
100100
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
101+
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU=
101102
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
102103
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
103104
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@@ -111,6 +112,8 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
111112
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
112113
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
113114
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
115+
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
116+
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
114117
gopkg.in/vmihailenco/msgpack.v2 v2.9.1 h1:kb0VV7NuIojvRfzwslQeP3yArBqJHW9tOl4t38VS1jM=
115118
gopkg.in/vmihailenco/msgpack.v2 v2.9.1/go.mod h1:/3Dn1Npt9+MYyLpYYXjInO/5jvMLamn+AEGwNEOatn8=
116119
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

internal/config/config.go

+26-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ import (
1010

1111
const (
1212
defaultLogLevel = "debug"
13+
defaultSysLogEnabled = false
14+
defaultFileLoggingEnabled = false
15+
defaultLogFilename = "/var/log/qumomf.log"
16+
defaultLogFileMaxSize = 256
17+
defaultLogFileMaxBackups = 3
18+
defaultLogFileMaxAge = 5
1319
defaultReadOnly = true
1420
defaultUser = "guest"
1521
defaultPassword = "guest"
@@ -31,8 +37,7 @@ type Config struct {
3137
// Qumomf is a set of global options determines qumomf's behavior.
3238
Qumomf struct {
3339
Port string `yaml:"port"`
34-
LogLevel string `yaml:"log_level"`
35-
EnableSysLog bool `yaml:"enable_syslog"`
40+
Logging Logging `yaml:"logging"`
3641
ReadOnly bool `yaml:"readonly"`
3742
ClusterDiscoveryTime time.Duration `yaml:"cluster_discovery_time"`
3843
ClusterRecoveryTime time.Duration `yaml:"cluster_recovery_time"`
@@ -57,6 +62,16 @@ type Config struct {
5762
Clusters map[string]ClusterConfig `yaml:"clusters"`
5863
}
5964

65+
type Logging struct {
66+
Level string `yaml:"level"`
67+
SysLogEnabled bool `yaml:"syslog_enabled"`
68+
FileLoggingEnabled bool `yaml:"file_enabled"`
69+
Filename string `yaml:"file_name"`
70+
MaxSize int `yaml:"file_max_size"` // megabytes
71+
MaxBackups int `yaml:"file_max_backups"` // files
72+
MaxAge int `yaml:"file_max_age"` // days
73+
}
74+
6075
type ConnectConfig struct {
6176
User *string `yaml:"user"`
6277
Password *string `yaml:"password"`
@@ -140,7 +155,15 @@ func (c *Config) withDefaults() {
140155

141156
base := &c.Qumomf
142157
base.ReadOnly = defaultReadOnly
143-
base.LogLevel = defaultLogLevel
158+
159+
base.Logging.Level = defaultLogLevel
160+
base.Logging.SysLogEnabled = defaultSysLogEnabled
161+
base.Logging.FileLoggingEnabled = defaultFileLoggingEnabled
162+
base.Logging.Filename = defaultLogFilename
163+
base.Logging.MaxSize = defaultLogFileMaxSize
164+
base.Logging.MaxBackups = defaultLogFileMaxBackups
165+
base.Logging.MaxAge = defaultLogFileMaxAge
166+
144167
base.ClusterDiscoveryTime = defaultClusterDiscoveryTime
145168
base.ClusterRecoveryTime = defaultClusterRecoveryTime
146169
base.ShardRecoveryBlockTime = defaultShardRecoveryBlockTime

internal/config/config_test.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,16 @@ func TestSetup_ValidPath(t *testing.T) {
2424
require.NotNil(t, cfg)
2525

2626
assert.Equal(t, ":8080", cfg.Qumomf.Port)
27-
assert.Equal(t, "debug", cfg.Qumomf.LogLevel)
28-
assert.True(t, cfg.Qumomf.EnableSysLog)
27+
28+
loggingCfg := cfg.Qumomf.Logging
29+
assert.Equal(t, "debug", loggingCfg.Level)
30+
assert.True(t, loggingCfg.SysLogEnabled)
31+
assert.True(t, loggingCfg.FileLoggingEnabled)
32+
assert.Equal(t, "/var/log/qumomf.log", loggingCfg.Filename)
33+
assert.Equal(t, 256, loggingCfg.MaxSize)
34+
assert.Equal(t, 3, loggingCfg.MaxBackups)
35+
assert.Equal(t, 5, loggingCfg.MaxAge)
36+
2937
assert.True(t, cfg.Qumomf.ReadOnly)
3038
assert.Equal(t, 60*time.Second, cfg.Qumomf.ClusterDiscoveryTime)
3139
assert.Equal(t, 5*time.Second, cfg.Qumomf.ClusterRecoveryTime)

internal/config/testdata/qumomf-full.conf.yml

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
qumomf:
22
port: ':8080'
3-
log_level: "debug"
4-
enable_syslog: true
3+
logging:
4+
level: 'debug'
5+
syslog_enabled: true
6+
file_enabled: true
7+
file_name: '/var/log/qumomf.log'
8+
file_max_size: 256
9+
file_max_backups: 3
10+
file_max_age: 5
511
readonly: true
612
cluster_discovery_time: '60s'
713
cluster_recovery_time: '5s'

0 commit comments

Comments
 (0)