mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge pull request #12774 from hexfusion/add-log-rotate
server: add support for log rotation
This commit is contained in:
commit
6decbe15db
@ -161,6 +161,8 @@ Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
- Setting this flag enables `SO_REUSEADDR` which allows binding to an address in `TIME_WAIT` state, improving etcd restart time.
|
||||
- Reduce [around 30% memory allocation by logging range response size without marshal](https://github.com/etcd-io/etcd/pull/12871).
|
||||
- `ETCD_VERIFY="all"` enviroment triggers [additional verification of consistency](https://github.com/etcd-io/etcd/pull/) of etcd data-dir files.
|
||||
- Add [`etcd --enable-log-rotation`](https://github.com/etcd-io/etcd/pull/12774) boolean flag which enables log rotation if true.
|
||||
- Add [`etcd --log-rotation-config-json`](https://github.com/etcd-io/etcd/pull/12774) flag which allows passthrough of JSON config to configure log rotation for a file output target.
|
||||
### Package `runtime`
|
||||
|
||||
- Optimize [`runtime.FDUsage` by removing unnecessary sorting](https://github.com/etcd-io/etcd/pull/12214).
|
||||
@ -171,6 +173,8 @@ Note that any `etcd_debugging_*` metrics are experimental and subject to change.
|
||||
- Use `embed.Config.LogLevel` instead.
|
||||
- Add [`embed.Config.ZapLoggerBuilder`](https://github.com/etcd-io/etcd/pull/11147) to allow creating a custom zap logger.
|
||||
- Replace [global `*zap.Logger` with etcd server logger object](https://github.com/etcd-io/etcd/pull/12212).
|
||||
- Add [`embed.Config.EnableLogRotation`](https://github.com/etcd-io/etcd/pull/12774) which enables log rotation if true.
|
||||
- Add [`embed.Config.LogRotationConfigJSON`](https://github.com/etcd-io/etcd/pull/12774) to allow passthrough of JSON config to configure log rotation for a file output target.
|
||||
|
||||
### Package `clientv3`
|
||||
|
||||
|
@ -638,6 +638,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "gopkg.in/natefinch/lumberjack.v2",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT License",
|
||||
"confidence": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "gopkg.in/yaml.v2",
|
||||
"licenses": [
|
||||
|
@ -469,6 +469,7 @@ gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
3
go.sum
3
go.sum
@ -11,6 +11,7 @@ cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqCl
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
@ -477,6 +478,8 @@ gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
@ -68,6 +68,15 @@ const (
|
||||
StdErrLogOutput = "stderr"
|
||||
StdOutLogOutput = "stdout"
|
||||
|
||||
// DefaultLogRotationConfig is the default configuration used for log rotation.
|
||||
// Log rotation is disabled by default.
|
||||
// MaxSize = 100 // MB
|
||||
// MaxAge = 0 // days (no limit)
|
||||
// MaxBackups = 0 // no limit
|
||||
// LocalTime = false // use computers local time, UTC by default
|
||||
// Compress = false // compress the rotated log in gzip format
|
||||
DefaultLogRotationConfig = `{"maxsize": 100, "maxage": 0, "maxbackups": 0, "localtime": false, "compress": false}`
|
||||
|
||||
// DefaultStrictReconfigCheck is the default value for "--strict-reconfig-check" flag.
|
||||
// It's enabled by default.
|
||||
DefaultStrictReconfigCheck = true
|
||||
@ -86,6 +95,7 @@ var (
|
||||
ErrConflictBootstrapFlags = fmt.Errorf("multiple discovery or bootstrap flags are set. " +
|
||||
"Choose one of \"initial-cluster\", \"discovery\" or \"discovery-srv\"")
|
||||
ErrUnsetAdvertiseClientURLsFlag = fmt.Errorf("--advertise-client-urls is required when --listen-client-urls is set explicitly")
|
||||
ErrLogRotationInvalidLogOutput = fmt.Errorf("--log-outputs requires a single file path when --log-rotate-config-json is defined")
|
||||
|
||||
DefaultInitialAdvertisePeerURLs = "http://localhost:2380"
|
||||
DefaultAdvertiseClientURLs = "http://localhost:2379"
|
||||
@ -320,7 +330,10 @@ type Config struct {
|
||||
// - file path to append server logs to.
|
||||
// It can be multiple when "Logger" is zap.
|
||||
LogOutputs []string `json:"log-outputs"`
|
||||
|
||||
// EnableLogRotation enables log rotation of a single LogOutputs file target.
|
||||
EnableLogRotation bool `json:"enable-log-rotation"`
|
||||
// LogRotationConfigJSON is a passthrough allowing a log rotation JSON config to be passed directly.
|
||||
LogRotationConfigJSON string `json:"log-rotation-config-json"`
|
||||
// ZapLoggerBuilder is used to build the zap logger.
|
||||
ZapLoggerBuilder func(*Config) error
|
||||
|
||||
@ -440,12 +453,14 @@ func NewConfig() *Config {
|
||||
|
||||
PreVote: true,
|
||||
|
||||
loggerMu: new(sync.RWMutex),
|
||||
logger: nil,
|
||||
Logger: "zap",
|
||||
LogOutputs: []string{DefaultLogOutput},
|
||||
LogLevel: logutil.DefaultLogLevel,
|
||||
EnableGRPCGateway: true,
|
||||
loggerMu: new(sync.RWMutex),
|
||||
logger: nil,
|
||||
Logger: "zap",
|
||||
LogOutputs: []string{DefaultLogOutput},
|
||||
LogLevel: logutil.DefaultLogLevel,
|
||||
EnableLogRotation: false,
|
||||
LogRotationConfigJSON: DefaultLogRotationConfig,
|
||||
EnableGRPCGateway: true,
|
||||
|
||||
ExperimentalDowngradeCheckTime: DefaultDowngradeCheckTime,
|
||||
ExperimentalMemoryMlock: false,
|
||||
|
@ -16,8 +16,11 @@ package embed
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"go.etcd.io/etcd/client/pkg/v3/logutil"
|
||||
@ -26,6 +29,7 @@ import (
|
||||
"go.uber.org/zap/zapgrpc"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
// GetLogger returns the logger.
|
||||
@ -54,6 +58,11 @@ func (cfg *Config) setupLogging() error {
|
||||
}
|
||||
}
|
||||
}
|
||||
if cfg.EnableLogRotation {
|
||||
if err := setupLogRotation(cfg.LogOutputs, cfg.LogRotationConfigJSON); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
outputPaths, errOutputPaths := make([]string, 0), make([]string, 0)
|
||||
isJournal := false
|
||||
@ -75,8 +84,15 @@ func (cfg *Config) setupLogging() error {
|
||||
errOutputPaths = append(errOutputPaths, StdOutLogOutput)
|
||||
|
||||
default:
|
||||
outputPaths = append(outputPaths, v)
|
||||
errOutputPaths = append(errOutputPaths, v)
|
||||
var path string
|
||||
if cfg.EnableLogRotation {
|
||||
// append rotate scheme to logs managed by lumberjack log rotation
|
||||
path = fmt.Sprintf("rotate:%s", v)
|
||||
} else {
|
||||
path = v
|
||||
}
|
||||
outputPaths = append(outputPaths, path)
|
||||
errOutputPaths = append(errOutputPaths, path)
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,3 +227,48 @@ func (cfg *Config) SetupGlobalLoggers() {
|
||||
zap.ReplaceGlobals(lg)
|
||||
}
|
||||
}
|
||||
|
||||
type logRotationConfig struct {
|
||||
*lumberjack.Logger
|
||||
}
|
||||
|
||||
// Sync implements zap.Sink
|
||||
func (logRotationConfig) Sync() error { return nil }
|
||||
|
||||
// setupLogRotation initializes log rotation for a single file path target.
|
||||
func setupLogRotation(logOutputs []string, logRotateConfigJSON string) error {
|
||||
var logRotationConfig logRotationConfig
|
||||
outputFilePaths := 0
|
||||
for _, v := range logOutputs {
|
||||
switch v {
|
||||
case DefaultLogOutput, StdErrLogOutput, StdOutLogOutput:
|
||||
continue
|
||||
default:
|
||||
outputFilePaths++
|
||||
}
|
||||
}
|
||||
// log rotation requires file target
|
||||
if len(logOutputs) == 1 && outputFilePaths == 0 {
|
||||
return ErrLogRotationInvalidLogOutput
|
||||
}
|
||||
// support max 1 file target for log rotation
|
||||
if outputFilePaths > 1 {
|
||||
return ErrLogRotationInvalidLogOutput
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(logRotateConfigJSON), &logRotationConfig); err != nil {
|
||||
var unmarshalTypeError *json.UnmarshalTypeError
|
||||
var syntaxError *json.SyntaxError
|
||||
switch {
|
||||
case errors.As(err, &syntaxError):
|
||||
return fmt.Errorf("improperly formatted log rotation config: %w", err)
|
||||
case errors.As(err, &unmarshalTypeError):
|
||||
return fmt.Errorf("invalid log rotation config: %w", err)
|
||||
}
|
||||
}
|
||||
zap.RegisterSink("rotate", func(u *url.URL) (zap.Sink, error) {
|
||||
logRotationConfig.Filename = u.Path
|
||||
return &logRotationConfig, nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
package embed
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
@ -289,3 +290,77 @@ func TestPeerURLsMapAndTokenFromSRV(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogRotation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
logOutputs []string
|
||||
logRotationConfig string
|
||||
wantErr bool
|
||||
wantErrMsg error
|
||||
}{
|
||||
{
|
||||
name: "mixed log output targets",
|
||||
logOutputs: []string{"stderr", "/tmp/path"},
|
||||
logRotationConfig: `{"maxsize": 1}`,
|
||||
},
|
||||
{
|
||||
name: "no file targets",
|
||||
logOutputs: []string{"stderr"},
|
||||
logRotationConfig: `{"maxsize": 1}`,
|
||||
wantErr: true,
|
||||
wantErrMsg: ErrLogRotationInvalidLogOutput,
|
||||
},
|
||||
{
|
||||
name: "multiple file targets",
|
||||
logOutputs: []string{"/tmp/path1", "/tmp/path2"},
|
||||
logRotationConfig: DefaultLogRotationConfig,
|
||||
wantErr: true,
|
||||
wantErrMsg: ErrLogRotationInvalidLogOutput,
|
||||
},
|
||||
{
|
||||
name: "default output",
|
||||
logRotationConfig: `{"maxsize": 1}`,
|
||||
wantErr: true,
|
||||
wantErrMsg: ErrLogRotationInvalidLogOutput,
|
||||
},
|
||||
{
|
||||
name: "default log rotation config",
|
||||
logOutputs: []string{"/tmp/path"},
|
||||
logRotationConfig: DefaultLogRotationConfig,
|
||||
},
|
||||
{
|
||||
name: "invalid logger config",
|
||||
logOutputs: []string{"/tmp/path"},
|
||||
logRotationConfig: `{"maxsize": true}`,
|
||||
wantErr: true,
|
||||
wantErrMsg: errors.New("invalid log rotation config: json: cannot unmarshal bool into Go struct field logRotationConfig.maxsize of type int"),
|
||||
},
|
||||
{
|
||||
name: "improperly formatted logger config",
|
||||
logOutputs: []string{"/tmp/path"},
|
||||
logRotationConfig: `{"maxsize": true`,
|
||||
wantErr: true,
|
||||
wantErrMsg: errors.New("improperly formatted log rotation config: unexpected end of JSON input"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := NewConfig()
|
||||
cfg.Logger = "zap"
|
||||
cfg.LogOutputs = tt.logOutputs
|
||||
cfg.EnableLogRotation = true
|
||||
cfg.LogRotationConfigJSON = tt.logRotationConfig
|
||||
err := cfg.Validate()
|
||||
if err != nil && !tt.wantErr {
|
||||
t.Errorf("test %q, unexpected error %v", tt.name, err)
|
||||
}
|
||||
if err != nil && tt.wantErr && tt.wantErrMsg.Error() != err.Error() {
|
||||
t.Errorf("test %q, expected error: %+v, got: %+v", tt.name, tt.wantErrMsg, err)
|
||||
}
|
||||
if err == nil && tt.wantErr {
|
||||
t.Errorf("test %q, expected error, got nil", tt.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -236,6 +236,8 @@ func newConfig() *config {
|
||||
fs.StringVar(&cfg.ec.Logger, "logger", "zap", "Currently only supports 'zap' for structured logging.")
|
||||
fs.Var(flags.NewUniqueStringsValue(embed.DefaultLogOutput), "log-outputs", "Specify 'stdout' or 'stderr' to skip journald logging even when running under systemd, or list of comma separated output targets.")
|
||||
fs.StringVar(&cfg.ec.LogLevel, "log-level", logutil.DefaultLogLevel, "Configures log level. Only supports debug, info, warn, error, panic, or fatal. Default 'info'.")
|
||||
fs.BoolVar(&cfg.ec.EnableLogRotation, "enable-log-rotation", false, "Enable log rotation of a single log-outputs file target.")
|
||||
fs.StringVar(&cfg.ec.LogRotationConfigJSON, "log-rotation-config-json", embed.DefaultLogRotationConfig, "Configures log rotation if enabled with a JSON logger config. Default: MaxSize=100(MB), MaxAge=0(days,no limit), MaxBackups=0(no limit), LocalTime=false(UTC), Compress=false(gzip)")
|
||||
|
||||
// version
|
||||
fs.BoolVar(&cfg.printVersion, "version", false, "Print the version and exit.")
|
||||
|
@ -188,6 +188,10 @@ Logging:
|
||||
Specify 'stdout' or 'stderr' to skip journald logging even when running under systemd, or list of comma separated output targets.
|
||||
--log-level 'info'
|
||||
Configures log level. Only supports debug, info, warn, error, panic, or fatal.
|
||||
--enable-log-rotation 'false'
|
||||
Enable log rotation of a single log-outputs file target.
|
||||
--log-rotation-config-json '{"maxsize": 100, "maxage": 0, "maxbackups": 0, "localtime": false, "compress": false}'
|
||||
Configures log rotation if enabled with a JSON logger config. MaxSize(MB), MaxAge(days,0=no limit), MaxBackups(0=no limit), LocalTime(use computers local time), Compress(gzip)".
|
||||
|
||||
v2 Proxy (to be deprecated in v4):
|
||||
--proxy 'off'
|
||||
|
@ -40,6 +40,7 @@ require (
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013
|
||||
google.golang.org/grpc v1.36.1
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
|
@ -463,6 +463,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
@ -472,6 +472,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
Loading…
x
Reference in New Issue
Block a user