Merge pull request #9624 from gyuho/journald

*: support journald with structured logging zap, rename to "--log-outputs"
release-3.4
Gyuho Lee 2018-04-25 14:22:03 -07:00 committed by GitHub
commit ccf959c0d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1041 additions and 450 deletions

View File

@ -1,3 +1,5 @@
## [v3.1.14](https://github.com/coreos/etcd/releases/tag/v3.1.14) (2018-04-24)
See [code changes](https://github.com/coreos/etcd/compare/v3.1.13...v3.1.14) and [v3.1 upgrade guide](https://github.com/coreos/etcd/blob/master/Documentation/upgrades/upgrade_3_1.md) for any breaking changes.
@ -6,7 +8,7 @@ See [code changes](https://github.com/coreos/etcd/compare/v3.1.13...v3.1.14) and
- Add [`etcd_server_is_leader`](https://github.com/coreos/etcd/pull/9587) Prometheus metric.
### Added: `etcd`
### `etcd`
- Add [`--initial-election-tick-advance`](https://github.com/coreos/etcd/pull/9591) flag to configure initial election tick fast-forward.
- By default, `--initial-election-tick-advance=true`, then local member fast-forwards election ticks to speed up "initial" leader election trigger.
@ -18,6 +20,10 @@ See [code changes](https://github.com/coreos/etcd/compare/v3.1.13...v3.1.14) and
- If single-node, it advances ticks regardless.
- Address [disruptive rejoining follower node](https://github.com/coreos/etcd/issues/9333).
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
## [v3.1.13](https://github.com/coreos/etcd/releases/tag/v3.1.13) (2018-03-29)

View File

@ -17,7 +17,7 @@ See [code changes](https://github.com/coreos/etcd/compare/v3.2.18...v3.2.19) and
- However, a certificate whose SAN field does [not include any domain names but only IP addresses](https://github.com/coreos/etcd/issues/9541) would request `*tls.ClientHelloInfo` with an empty `ServerName` field, thus failing to trigger the TLS reload on initial TLS handshake; this becomes a problem when expired certificates need to be replaced online.
- Now, `(*tls.Config).Certificates` is created empty on initial TLS client handshake, first to trigger `(*tls.Config).GetCertificate`, and then to populate rest of the certificates on every new TLS connection, even when client SNI is empty (e.g. cert only includes IPs).
### Added: `etcd`
### `etcd`
- Add [`--initial-election-tick-advance`](https://github.com/coreos/etcd/pull/9591) flag to configure initial election tick fast-forward.
- By default, `--initial-election-tick-advance=true`, then local member fast-forwards election ticks to speed up "initial" leader election trigger.
@ -29,6 +29,10 @@ See [code changes](https://github.com/coreos/etcd/compare/v3.2.18...v3.2.19) and
- If single-node, it advances ticks regardless.
- Address [disruptive rejoining follower node](https://github.com/coreos/etcd/issues/9333).
### Go
- Compile with [*Go 1.8.7*](https://golang.org/doc/devel/release.html#go1.8).
## [v3.2.18](https://github.com/coreos/etcd/releases/tag/v3.2.18) (2018-03-29)
@ -149,7 +153,7 @@ See [code changes](https://github.com/coreos/etcd/compare/v3.2.11...v3.2.12) and
- Fix [error message of `Revision` compactor](https://github.com/coreos/etcd/pull/8999) in server-side.
### Added: `clientv3`
### `clientv3`
- Add [`MaxCallSendMsgSize` and `MaxCallRecvMsgSize`](https://github.com/coreos/etcd/pull/9047) fields to [`clientv3.Config`](https://godoc.org/github.com/coreos/etcd/clientv3#Config).
- Fix [exceeded response size limit error in client-side](https://github.com/coreos/etcd/issues/9043).
@ -295,7 +299,7 @@ See [code changes](https://github.com/coreos/etcd/compare/v3.2.5...v3.2.6) and [
See [code changes](https://github.com/coreos/etcd/compare/v3.2.4...v3.2.5) and [v3.2 upgrade guide](https://github.com/coreos/etcd/blob/master/Documentation/upgrades/upgrade_3_2.md) for any breaking changes.
### Added: v3 `etcdctl`
### v3 `etcdctl`
- Return non-zero exit code on unhealthy `endpoint health`.
@ -453,7 +457,7 @@ See [security doc](https://github.com/coreos/etcd/blob/master/Documentation/op-g
- Logging, monitoring
- Server warns large snapshot operations.
### Added: `etcd`
### `etcd`
- Add `--enable-v2` flag to enable v2 API server.
- `--enable-v2=true` by default.
@ -468,14 +472,14 @@ See [security doc](https://github.com/coreos/etcd/blob/master/Documentation/op-g
- If compaction succeeds or requested revision has already been compacted, it resets period timer and removes used compacted revision from historical revision records (e.g. start next revision collect and compaction from previously collected revisions).
- If compaction fails, it retries in 5 minutes.
### Added: `clientv3`
### `clientv3`
- STM prefetching.
- Add namespace feature.
- Add `ErrOldCluster` with server version checking.
- Translate `WithPrefix()` into `WithFromKey()` for empty key.
### Added: v3 `etcdctl`
### v3 `etcdctl`
- Add `check perf` command.
- Add `--from-key` flag to role grant-permission command.
@ -485,13 +489,13 @@ See [security doc](https://github.com/coreos/etcd/blob/master/Documentation/op-g
- Allow snapshot over 512MB.
### Added: `grpc-proxy`
### `grpc-proxy`
- Proxy endpoint discovery.
- Namespaces.
- Coalesce lease requests.
### Added: `gateway`
### `gateway`
- Support [DNS SRV priority](https://github.com/coreos/etcd/pull/7882) for [smart proxy routing](https://github.com/coreos/etcd/issues/4378).

View File

@ -17,7 +17,7 @@ See [code changes](https://github.com/coreos/etcd/compare/v3.3.3...v3.3.4) and [
- However, a certificate whose SAN field does [not include any domain names but only IP addresses](https://github.com/coreos/etcd/issues/9541) would request `*tls.ClientHelloInfo` with an empty `ServerName` field, thus failing to trigger the TLS reload on initial TLS handshake; this becomes a problem when expired certificates need to be replaced online.
- Now, `(*tls.Config).Certificates` is created empty on initial TLS client handshake, first to trigger `(*tls.Config).GetCertificate`, and then to populate rest of the certificates on every new TLS connection, even when client SNI is empty (e.g. cert only includes IPs).
### Added: `etcd`
### `etcd`
- Add [`--initial-election-tick-advance`](https://github.com/coreos/etcd/pull/9591) flag to configure initial election tick fast-forward.
- By default, `--initial-election-tick-advance=true`, then local member fast-forwards election ticks to speed up "initial" leader election trigger.
@ -29,7 +29,7 @@ See [code changes](https://github.com/coreos/etcd/compare/v3.3.3...v3.3.4) and [
- If single-node, it advances ticks regardless.
- Address [disruptive rejoining follower node](https://github.com/coreos/etcd/issues/9333).
### Added: `embed`
### Package `embed`
- Add [`embed.Config.InitialElectionTickAdvance`](https://github.com/coreos/etcd/pull/9591) to enable/disable initial election tick fast-forward.
- `embed.NewConfig()` would return `*embed.Config` with `InitialElectionTickAdvance` as true by default.
@ -156,7 +156,7 @@ See [code changes](https://github.com/coreos/etcd/compare/v3.2.0...v3.3.0) and [
- To deprecate [`/v3alpha`](https://github.com/coreos/etcd/issues/8125) in v3.4.
- In v3.3, `curl -L http://localhost:2379/v3alpha/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` still works as a fallback to `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'`, but `curl -L http://localhost:2379/v3alpha/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` won't work in v3.4. Use `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` instead.
- Change `--auto-compaction-retention` flag to [accept string values](https://github.com/coreos/etcd/pull/8563) with [finer granularity](https://github.com/coreos/etcd/issues/8503).
- Now that `--auto-compaction-retention` accepts string values, etcd configuration YAML file `log-output` field must be changed to `string` type.
- Now that `--auto-compaction-retention` accepts string values, etcd configuration YAML file `auto-compaction-retention` field must be changed to `string` type.
- Previously, `--config-file etcd.config.yaml` can have `auto-compaction-retention: 24` field, now must be `auto-compaction-retention: "24"` or `auto-compaction-retention: "24h"`.
- If configured as `--auto-compaction-mode periodic --auto-compaction-retention "24h"`, the time duration value for `--auto-compaction-retention` flag must be valid for [`time.ParseDuration`](https://golang.org/pkg/time/#ParseDuration) function in Go.
@ -205,7 +205,7 @@ See [security doc](https://github.com/coreos/etcd/blob/master/Documentation/op-g
- Provide user's role on [auth permission error](https://github.com/coreos/etcd/pull/8164).
- Fix [auth store panic with disabled token](https://github.com/coreos/etcd/pull/8695).
### Added: `etcd`
### `etcd`
- Add [`--experimental-initial-corrupt-check`](https://github.com/coreos/etcd/pull/8554) flag to [check cluster database hashes before serving client/peer traffic](https://github.com/coreos/etcd/issues/8313).
- `--experimental-initial-corrupt-check=false` by default.
@ -225,7 +225,7 @@ See [security doc](https://github.com/coreos/etcd/blob/master/Documentation/op-g
- Useful for [bypassing critical APIs when monitoring etcd](https://github.com/coreos/etcd/issues/8060).
- Add [`--auto-compaction-mode`](https://github.com/coreos/etcd/pull/8123) flag to [support revision-based compaction](https://github.com/coreos/etcd/issues/8098).
- Change `--auto-compaction-retention` flag to [accept string values](https://github.com/coreos/etcd/pull/8563) with [finer granularity](https://github.com/coreos/etcd/issues/8503).
- Now that `--auto-compaction-retention` accepts string values, etcd configuration YAML file `log-output` field must be changed to `string` type.
- Now that `--auto-compaction-retention` accepts string values, etcd configuration YAML file `auto-compaction-retention` field must be changed to `string` type.
- Previously, `--config-file etcd.config.yaml` can have `auto-compaction-retention: 24` field, now must be `auto-compaction-retention: "24"` or `auto-compaction-retention: "24h"`.
- If configured as `--auto-compaction-mode periodic --auto-compaction-retention "24h"`, the time duration value for `--auto-compaction-retention` flag must be valid for [`time.ParseDuration`](https://golang.org/pkg/time/#ParseDuration) function in Go.
- e.g. `--auto-compaction-mode=revision --auto-compaction-retention=1000` automatically `Compact` on `"latest revision" - 1000` every 5-minute (when latest revision is 30000, compact on revision 29000).
@ -253,7 +253,7 @@ See [security doc](https://github.com/coreos/etcd/blob/master/Documentation/op-g
- Address [error on shadowed environment variables](https://github.com/coreos/etcd/issues/8380).
- etcd v3.4 will exit on this error.
### Added: API
### API
- Support [ranges in transaction comparisons](https://github.com/coreos/etcd/pull/8025) for [disconnected linearized reads](https://github.com/coreos/etcd/issues/7924).
- Add [nested transactions](https://github.com/coreos/etcd/pull/8102) to extend [proxy use cases](https://github.com/coreos/etcd/issues/7857).
@ -261,7 +261,7 @@ See [security doc](https://github.com/coreos/etcd/blob/master/Documentation/op-g
- Add [lease list](https://github.com/coreos/etcd/pull/8358).
- Add [hash by revision](https://github.com/coreos/etcd/pull/8263) for [better corruption checking against boltdb](https://github.com/coreos/etcd/issues/8016).
### Added: `clientv3`
### `clientv3`
- Add [health balancer](https://github.com/coreos/etcd/pull/8545) to fix [watch API hangs](https://github.com/coreos/etcd/issues/7247), improve [endpoint switch under network faults](https://github.com/coreos/etcd/issues/7941).
- [Refactor balancer](https://github.com/coreos/etcd/pull/8840) and add [client-side keepalive pings](https://github.com/coreos/etcd/pull/8199) to handle [network partitions](https://github.com/coreos/etcd/issues/8711).
@ -285,7 +285,7 @@ See [security doc](https://github.com/coreos/etcd/blob/master/Documentation/op-g
- Fix [`concurrency/stm` `Put` with serializable snapshot](https://github.com/coreos/etcd/pull/8439).
- Use store revision from first fetch to resolve write conflicts instead of modified revision.
### Added: v3 `etcdctl`
### v3 `etcdctl`
- Add [`--discovery-srv`](https://github.com/coreos/etcd/pull/8462) flag.
- Add [`--keepalive-time`, `--keepalive-timeout`](https://github.com/coreos/etcd/pull/8663) flags.
@ -311,11 +311,11 @@ See [security doc](https://github.com/coreos/etcd/blob/master/Documentation/op-g
- Handle [empty key permission](https://github.com/coreos/etcd/pull/8514) in `etcdctl`.
### Added: v2 `etcdctl`
### v2 `etcdctl`
- Add [`backup --with-v3`](https://github.com/coreos/etcd/pull/8479) flag.
### Added: `grpc-proxy`
### `grpc-proxy`
- Add [`grpc-proxy start --experimental-leasing-prefix`](https://github.com/coreos/etcd/pull/8341) flag.
- For disconnected linearized reads.
@ -336,7 +336,7 @@ See [security doc](https://github.com/coreos/etcd/blob/master/Documentation/op-g
- Fix [KV API `PrevKv` flag handling](https://github.com/coreos/etcd/pull/8366).
- Fix [KV API `KeysOnly` flag handling](https://github.com/coreos/etcd/pull/8552).
### Added: gRPC gateway
### gRPC gateway
- Replace [gRPC gateway](https://github.com/grpc-ecosystem/grpc-gateway) endpoint `/v3alpha` with [`/v3beta`](https://github.com/coreos/etcd/pull/8880).
- To deprecate [`/v3alpha`](https://github.com/coreos/etcd/issues/8125) in v3.4.

View File

@ -1,6 +1,6 @@
## v3.4.0 (TBD 2018-06-01)
## v3.4.0 (TBD 2018-07-01)
See [code changes](https://github.com/coreos/etcd/compare/v3.3.0...v3.4.0) and [v3.4 upgrade guide](https://github.com/coreos/etcd/blob/master/Documentation/upgrades/upgrade_3_4.md) for any breaking changes.
@ -56,6 +56,14 @@ See [code changes](https://github.com/coreos/etcd/compare/v3.3.0...v3.4.0) and [
- e.g. exit with error on `ETCDCTL_ENDPOINTS=abc.com ETCDCTL_API=3 etcdctl endpoint health --endpoints=def.com`.
- Change [`etcdserverpb.AuthRoleRevokePermissionRequest/key,range_end` fields type from `string` to `bytes`](https://github.com/coreos/etcd/pull/9433).
- Change [`embed.Config.CorsInfo` in `*cors.CORSInfo` type to `embed.Config.CORS` in `map[string]struct{}` type](https://github.com/coreos/etcd/pull/9490).
- Remove [`embed.Config.SetupLogging`](https://github.com/coreos/etcd/pull/9572).
- Now logger is set up automatically based on [`embed.Config.Logger`, `embed.Config.LogOutputs`, `embed.Config.Debug` fields](https://github.com/coreos/etcd/pull/9572).
- Rename [`etcd --log-output` to `--log-outputs`](https://github.com/coreos/etcd/pull/9624) to support multiple log outputs.
- **`etcd --log-output`** will be deprecated in v3.5.
- Rename [**`embed.Config.LogOutput`** to **`embed.Config.LogOutputs`**](https://github.com/coreos/etcd/pull/9624) to support multiple log outputs.
- Change [**`embed.Config.LogOutputs`** type from `string` to `[]string`](https://github.com/coreos/etcd/pull/9579) to support multiple log outputs.
- Now that `--log-outputs` accepts multiple writers, etcd configuration YAML file `log-outputs` field must be changed to `[]string` type.
- Previously, `--config-file etcd.config.yaml` can have `log-outputs: default` field, now must be `log-outputs: [default]`.
- Change v3 `etcdctl snapshot` exit codes with [`snapshot` package](https://github.com/coreos/etcd/pull/9118/commits/df689f4280e1cce4b9d61300be13ca604d41670a).
- Exit on error with exit code 1 (no more exit code 5 or 6 on `snapshot save/restore` commands).
- Migrate dependency management tool from `glide` to [`golang/dep`](https://github.com/coreos/etcd/pull/9155).
@ -72,16 +80,11 @@ See [code changes](https://github.com/coreos/etcd/compare/v3.3.0...v3.4.0) and [
- Previously, `OpenForRead(dirpath string, snap walpb.Snapshot) (*WAL, error)`, now `OpenForRead(lg *zap.Logger, dirpath string, snap walpb.Snapshot) (*WAL, error)`.
- Previously, `Repair(dirpath string) bool`, now `Repair(lg *zap.Logger, dirpath string) bool`.
- Previously, `Create(dirpath string, metadata []byte) (*WAL, error)`, now `Create(lg *zap.Logger, dirpath string, metadata []byte) (*WAL, error)`.
- Remove [`embed.Config.SetupLogging`](https://github.com/coreos/etcd/pull/9572).
- Now logger is set up automatically based on [`embed.Config.Logger`, `embed.Config.LogOutput`, `embed.Config.Debug` fields](https://github.com/coreos/etcd/pull/9572).
- Change [`embed.Config.LogOutput` type from `string` to `[]string`](https://github.com/coreos/etcd/pull/9579) to support multiple log outputs.
- Now that `--log-output` accepts multiple writers, etcd configuration YAML file `log-output` field must be changed to `[]string` type.
- Previously, `--config-file etcd.config.yaml` can have `log-output: default` field, now must be `log-output: [default]`.
- Remove [`pkg/cors` package](https://github.com/coreos/etcd/pull/9490).
- Move `"github.com/coreos/etcd/snap"` to [`"github.com/coreos/etcd/raftsnap"`](https://github.com/coreos/etcd/pull/9211).
- Move `"github.com/coreos/etcd/etcdserver/auth"` to [`"github.com/coreos/etcd/etcdserver/v2auth"`](https://github.com/coreos/etcd/pull/9275).
- Move `"github.com/coreos/etcd/error"` to [`"github.com/coreos/etcd/etcdserver/v2error"`](https://github.com/coreos/etcd/pull/9274).
- Move `"github.com/coreos/etcd/store"` to [`"github.com/coreos/etcd/etcdserver/v2store"`](https://github.com/coreos/etcd/pull/9274).
- Move internal package `"github.com/coreos/etcd/snap"` to [`"github.com/coreos/etcd/raftsnap"`](https://github.com/coreos/etcd/pull/9211).
- Move internal package `"github.com/coreos/etcd/etcdserver/auth"` to [`"github.com/coreos/etcd/etcdserver/v2auth"`](https://github.com/coreos/etcd/pull/9275).
- Move internal package `"github.com/coreos/etcd/error"` to [`"github.com/coreos/etcd/etcdserver/v2error"`](https://github.com/coreos/etcd/pull/9274).
- Move internal package `"github.com/coreos/etcd/store"` to [`"github.com/coreos/etcd/etcdserver/v2store"`](https://github.com/coreos/etcd/pull/9274).
### Dependency
@ -123,7 +126,7 @@ See [security doc](https://github.com/coreos/etcd/blob/master/Documentation/op-g
- However, a certificate whose SAN field does [not include any domain names but only IP addresses](https://github.com/coreos/etcd/issues/9541) would request `*tls.ClientHelloInfo` with an empty `ServerName` field, thus failing to trigger the TLS reload on initial TLS handshake; this becomes a problem when expired certificates need to be replaced online.
- Now, `(*tls.Config).Certificates` is created empty on initial TLS client handshake, first to trigger `(*tls.Config).GetCertificate`, and then to populate rest of the certificates on every new TLS connection, even when client SNI is empty (e.g. cert only includes IPs).
### Added: `etcd`
### `etcd`
- Add [`--initial-election-tick-advance`](https://github.com/coreos/etcd/pull/9591) flag to configure initial election tick fast-forward.
- By default, `--initial-election-tick-advance=true`, then local member fast-forwards election ticks to speed up "initial" leader election trigger.
@ -153,25 +156,32 @@ See [security doc](https://github.com/coreos/etcd/blob/master/Documentation/op-g
- If `--discovery-srv-name="foo"`, then query `_etcd-server-ssl-foo._tcp.[YOUR_HOST]` and `_etcd-server-foo._tcp.[YOUR_HOST]`.
- Useful for operating multiple etcd clusters under the same domain.
- Support [`etcd --cors`](https://github.com/coreos/etcd/pull/9490) in v3 HTTP requests (gRPC gateway).
- Rename [`etcd --log-output` to `--log-outputs`](https://github.com/coreos/etcd/pull/9624) to support multiple log outputs.
- **`etcd --log-output`** will be deprecated in v3.5.
- Add [`--logger`](https://github.com/coreos/etcd/pull/9572) flag to support [structured logger and logging to file](https://github.com/coreos/etcd/issues/9438) in server-side.
- e.g. `--logger=capnslog --log-output=default` is the default setting and same as previous etcd server logging format.
- TODO: `--logger=zap` is experimental, and journald logging may not work when etcd runs as PID 1.
- e.g. `--logger=zap --log-output=/tmp/test.log` will log server operations in [JSON-encoded format](https://godoc.org/go.uber.org/zap#NewProductionEncoderConfig) and writes logs to the specified file `/tmp/test.log`.
- e.g. `--logger=zap --log-output=default` will log server operations in [JSON-encoded format](https://godoc.org/go.uber.org/zap#NewProductionEncoderConfig) and writes logs to `os.Stderr` (detect systemd journald TODO).
- e.g. `--logger=zap --log-output=stderr` will log server operations in [JSON-encoded format](https://godoc.org/go.uber.org/zap#NewProductionEncoderConfig) and writes logs to `os.Stderr` (bypass journald TODO).
- e.g. `--logger=zap --log-output=stdout` will log server operations in [JSON-encoded format](https://godoc.org/go.uber.org/zap#NewProductionEncoderConfig) and writes logs to `os.Stdout` (bypass journald TODO).
- e.g. `--logger=zap --log-output=a.log,b.log,c.log,stdout` [writes server logs to multiple files `a.log`, `b.log` and `c.log` at the same time](https://github.com/coreos/etcd/pull/9579) and outputs to `stdout`, in [JSON-encoded format](https://godoc.org/go.uber.org/zap#NewProductionEncoderConfig).
- e.g. `--logger=zap --log-output=/dev/null` will discard all server logs.
- e.g. `--logger=capnslog --log-outputs=default` is the default setting and same as previous etcd server logging format.
- e.g. `--logger=zap --log-outputs=default` will log server operations in [JSON-encoded format](https://godoc.org/go.uber.org/zap#NewProductionEncoderConfig) and writes logs to `os.Stderr`.
- e.g. If etcd parent process ID (`ppid`) is 1 (e.g. run with systemd), `--logger=zap --log-outputs=default` will [redirect server logs to local systemd journal](https://github.com/coreos/etcd/pull/9624) in [JSON-encoded format](https://godoc.org/go.uber.org/zap#NewProductionEncoderConfig). And if write to journald fails, it writes to `os.Stderr` as a fallback.
- e.g. `--logger=zap --log-outputs=stderr` will log server operations in [JSON-encoded format](https://godoc.org/go.uber.org/zap#NewProductionEncoderConfig) and writes logs to `os.Stderr`. Use this to override journald log redirects.
- e.g. `--logger=zap --log-outputs=stdout` will log server operations in [JSON-encoded format](https://godoc.org/go.uber.org/zap#NewProductionEncoderConfig) and writes logs to `os.Stdout` Use this to override journald log redirects.
- e.g. `--logger=zap --log-outputs=a.log` will log server operations in [JSON-encoded format](https://godoc.org/go.uber.org/zap#NewProductionEncoderConfig) and writes logs to the specified file `a.log`.
- e.g. `--logger=zap --log-outputs=a.log,b.log,c.log,stdout` [writes server logs to multiple files `a.log`, `b.log` and `c.log` at the same time](https://github.com/coreos/etcd/pull/9579) and outputs to `stdout`, in [JSON-encoded format](https://godoc.org/go.uber.org/zap#NewProductionEncoderConfig).
- e.g. `--logger=zap --log-outputs=/dev/null` will discard all server logs.
### Added: `embed`
### Package `embed`
- Add [`embed.Config.InitialElectionTickAdvance`](https://github.com/coreos/etcd/pull/9591) to enable/disable initial election tick fast-forward.
- `embed.NewConfig()` would return `*embed.Config` with `InitialElectionTickAdvance` as true by default.
- Add [`embed.Config.Logger`](https://github.com/coreos/etcd/pull/9518) to support [structured logger `zap`](https://github.com/uber-go/zap) in server-side.
- Define [`embed.CompactorModePeriodic`](https://godoc.org/github.com/coreos/etcd/embed#pkg-variables) for `compactor.ModePeriodic`.
- Define [`embed.CompactorModeRevision`](https://godoc.org/github.com/coreos/etcd/embed#pkg-variables) for `compactor.ModeRevision`.
- Change [`embed.Config.CorsInfo` in `*cors.CORSInfo` type to `embed.Config.CORS` in `map[string]struct{}` type](https://github.com/coreos/etcd/pull/9490).
- Remove [`embed.Config.SetupLogging`](https://github.com/coreos/etcd/pull/9572).
- Now logger is set up automatically based on [`embed.Config.Logger`, `embed.Config.LogOutputs`, `embed.Config.Debug` fields](https://github.com/coreos/etcd/pull/9572).
- Rename [**`embed.Config.LogOutput`** to **`embed.Config.LogOutputs`**](https://github.com/coreos/etcd/pull/9624) to support multiple log outputs.
- Change [**`embed.Config.LogOutputs`** type from `string` to `[]string`](https://github.com/coreos/etcd/pull/9579) to support multiple log outputs.
### Added: API
### API
- Add [`snapshot`](https://github.com/coreos/etcd/pull/9118) package for snapshot restore/save operations (see [`godoc.org/github.com/etcd/snapshot`](https://godoc.org/github.com/coreos/etcd/snapshot) for more).
- Add [`watch_id` field to `etcdserverpb.WatchCreateRequest`](https://github.com/coreos/etcd/pull/9065), allow user-provided watch ID to `mvcc`.
@ -181,7 +191,7 @@ See [security doc](https://github.com/coreos/etcd/blob/master/Documentation/op-g
- e.g. `"etcdserver: no leader", "NOSPACE", "CORRUPT"`
- Add [`dbSizeInUse` field to `etcdserverpb.StatusResponse`](https://github.com/coreos/etcd/pull/9256) for actual DB size after compaction.
### Added: v3 `etcdctl`
### v3 `etcdctl`
- Add [`check datascale`](https://github.com/coreos/etcd/pull/9185) command.
- Add [`check datascale --auto-compact, --auto-defrag`](https://github.com/coreos/etcd/pull/9351) flags.
@ -192,7 +202,7 @@ See [security doc](https://github.com/coreos/etcd/blob/master/Documentation/op-g
- Add [`endpoint health --write-out` support](https://github.com/coreos/etcd/pull/9540).
- Previously, [`endpoint health --write-out json` did not work](https://github.com/coreos/etcd/issues/9532).
### Added: gRPC gateway
### gRPC gateway
- Replace [gRPC gateway](https://github.com/grpc-ecosystem/grpc-gateway) endpoint `/v3beta` with [`/v3`](https://github.com/coreos/etcd/pull/9298).
- Deprecated [`/v3alpha`](https://github.com/coreos/etcd/pull/9298).
@ -235,5 +245,5 @@ See [security doc](https://github.com/coreos/etcd/blob/master/Documentation/op-g
### Go
- Require *Go 1.10+*.
- Compile with [*Go 1.10*](https://golang.org/doc/devel/release.html#go1.10).
- Compile with [*Go 1.10.1*](https://golang.org/doc/devel/release.html#go1.10).

View File

@ -10,7 +10,7 @@ See [code changes](https://github.com/coreos/etcd/compare/v3.4.0...v3.5.0) and [
- Deprecated [`/v3beta`](https://github.com/coreos/etcd/pull/9298).
- `curl -L http://localhost:2379/v3beta/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` does work in v3.5. Use `curl -L http://localhost:2379/v3/kv/put -X POST -d '{"key": "Zm9v", "value": "YmFy"}'` instead.
### Added: gRPC gateway
### gRPC gateway
- [gRPC gateway](https://github.com/grpc-ecosystem/grpc-gateway) only supports [`/v3`](TODO) endpoint.
- Deprecated [`/v3beta`](https://github.com/coreos/etcd/pull/9298).

View File

@ -317,7 +317,7 @@ The security flags help to [build a secure etcd cluster][security].
+ default: capnslog
+ env variable: ETCD_LOGGER
### --log-output
### --log-outputs
+ Specify 'stdout' or 'stderr' to skip journald logging even when running under systemd, or list of comma separated output targets.
+ default: default
+ env variable: ETCD_LOG_OUTPUT

View File

@ -14,7 +14,7 @@ Highlighted breaking changes in 3.3.
#### Change in `--auto-compaction-retention` flag
Changed `--auto-compaction-retention` flag to [accept string values](https://github.com/coreos/etcd/pull/8563) with [finer granularity](https://github.com/coreos/etcd/issues/8503). Now that `--auto-compaction-retention` accepts string values, etcd configuration YAML file `log-output` field must be changed to `string` type. Previously, `--config-file etcd.config.yaml` can have `auto-compaction-retention: 24` field, now must be `auto-compaction-retention: "24"` or `auto-compaction-retention: "24h"`. If configured as `--auto-compaction-mode periodic --auto-compaction-retention "24h"`, the time duration value for `--auto-compaction-retention` flag must be valid for [`time.ParseDuration`](https://golang.org/pkg/time/#ParseDuration) function in Go.
Changed `--auto-compaction-retention` flag to [accept string values](https://github.com/coreos/etcd/pull/8563) with [finer granularity](https://github.com/coreos/etcd/issues/8503). Now that `--auto-compaction-retention` accepts string values, etcd configuration YAML file `auto-compaction-retention` field must be changed to `string` type. Previously, `--config-file etcd.config.yaml` can have `auto-compaction-retention: 24` field, now must be `auto-compaction-retention: "24"` or `auto-compaction-retention: "24h"`. If configured as `--auto-compaction-mode periodic --auto-compaction-retention "24h"`, the time duration value for `--auto-compaction-retention` flag must be valid for [`time.ParseDuration`](https://golang.org/pkg/time/#ParseDuration) function in Go.
```diff
# etcd.config.yaml

View File

@ -26,6 +26,15 @@ Highlighted breaking changes in 3.4.
+etcd --peer-trusted-ca-file ca-peer.crt
```
Rename [`etcd --log-output` to `--log-outputs`](https://github.com/coreos/etcd/pull/9624) to support multiple log outputs.
**`etcd --log-output`** will be deprecated in v3.5.
```diff
-etcd --log-output stderr
+etcd --log-outputs stderr,a.log
```
#### Change in `pkg/transport`
Deprecated `pkg/transport.TLSInfo.CAFile` field.
@ -79,24 +88,41 @@ cfg := &embed.Config{Debug: false}
-cfg.SetupLogging()
```
Changed [`embed.Config.LogOutput` type from `string` to `[]string`](https://github.com/coreos/etcd/pull/9579) to support multiple log outputs.
Renamed [**`embed.Config.LogOutput`** to **`embed.Config.LogOutputs`**](https://github.com/coreos/etcd/pull/9624) to support multiple log outputs. And changed [`embed.Config.LogOutput` type from `string` to `[]string`](https://github.com/coreos/etcd/pull/9579) to support multiple log outputs.
```diff
import "github.com/coreos/etcd/embed"
cfg := &embed.Config{Debug: false}
-cfg.LogOutput = "stderr"
+cfg.LogOutput = []string{"stderr"}
+cfg.LogOutputs = []string{"stderr"}
```
#### Change in `etcd --log-output` (now `--log-outputs`)
Rename [`etcd --log-output` to `--log-outputs`](https://github.com/coreos/etcd/pull/9624) to support multiple log outputs.
**`etcd --log-output`** will be deprecated in v3.5.
```diff
-etcd --log-output stderr
+etcd --log-outputs stderr,a.log
```
```diff
# Specify 'stdout' or 'stderr' to skip journald logging even when running under systemd.
-log-output: [default]
+log-outputs: [default]
```
#### Change in `etcd --config-file`
Now that `log-output` accepts multiple writers, etcd configuration YAML file `log-output` field must be changed to `[]string` type as below:
Now that `log-outputs` (old field name `log-output`) accepts multiple writers, etcd configuration YAML file `log-outputs` field must be changed to `[]string` type as below:
```diff
# Specify 'stdout' or 'stderr' to skip journald logging even when running under systemd.
-log-output: default
+log-output: [default]
+log-outputs: [default]
```
### Server upgrade checklists

View File

@ -0,0 +1,149 @@
## Upgrade etcd from 3.4 to 3.5
In the general case, upgrading from etcd 3.4 to 3.5 can be a zero-downtime, rolling upgrade:
- one by one, stop the etcd v3.4 processes and replace them with etcd v3.5 processes
- after running all v3.5 processes, new features in v3.5 are available to the cluster
Before [starting an upgrade](#upgrade-procedure), read through the rest of this guide to prepare.
### Upgrade checklists
**NOTE:** When [migrating from v2 with no v3 data](https://github.com/coreos/etcd/issues/9480), etcd server v3.2+ panics when etcd restores from existing snapshots but no v3 `ETCD_DATA_DIR/member/snap/db` file. This happens when the server had migrated from v2 with no previous v3 data. This also prevents accidental v3 data loss (e.g. `db` file might have been moved). etcd requires that post v3 migration can only happen with v3 data. Do not upgrade to newer v3 versions until v3.0 server contains v3 data.
Highlighted breaking changes in 3.5.
#### Deprecated in `etcd --log-output`
Rename [`etcd --log-output` to `--log-outputs`](https://github.com/coreos/etcd/pull/9624) to support multiple log outputs.
**`etcd --log-output`** is deprecated in v3.5.
```diff
-$ etcd --log-output stderr
+$ etcd --log-outputs stderr,a.log
```
### Server upgrade checklists
#### Upgrade requirements
To upgrade an existing etcd deployment to 3.5, the running cluster must be 3.4 or greater. If it's before 3.4, please [upgrade to 3.4](upgrade_3_3.md) before upgrading to 3.5.
Also, to ensure a smooth rolling upgrade, the running cluster must be healthy. Check the health of the cluster by using the `etcdctl endpoint health` command before proceeding.
#### Preparation
Before upgrading etcd, always test the services relying on etcd in a staging environment before deploying the upgrade to the production environment.
Before beginning, [backup the etcd data](../op-guide/maintenance.md#snapshot-backup). Should something go wrong with the upgrade, it is possible to use this backup to [downgrade](#downgrade) back to existing etcd version. Please note that the `snapshot` command only backs up the v3 data. For v2 data, see [backing up v2 datastore](../v2/admin_guide.md#backing-up-the-datastore).
#### Mixed versions
While upgrading, an etcd cluster supports mixed versions of etcd members, and operates with the protocol of the lowest common version. The cluster is only considered upgraded once all of its members are upgraded to version 3.5. Internally, etcd members negotiate with each other to determine the overall cluster version, which controls the reported version and the supported features.
#### Limitations
Note: If the cluster only has v3 data and no v2 data, it is not subject to this limitation.
If the cluster is serving a v2 data set larger than 50MB, each newly upgraded member may take up to two minutes to catch up with the existing cluster. Check the size of a recent snapshot to estimate the total data size. In other words, it is safest to wait for 2 minutes between upgrading each member.
For a much larger total data size, 100MB or more , this one-time process might take even more time. Administrators of very large etcd clusters of this magnitude can feel free to contact the [etcd team][etcd-contact] before upgrading, and we'll be happy to provide advice on the procedure.
#### Downgrade
If all members have been upgraded to v3.5, the cluster will be upgraded to v3.5, and downgrade from this completed state is **not possible**. If any single member is still v3.4, however, the cluster and its operations remains "v3.4", and it is possible from this mixed cluster state to return to using a v3.4 etcd binary on all members.
Please [backup the data directory](../op-guide/maintenance.md#snapshot-backup) of all etcd members to make downgrading the cluster possible even after it has been completely upgraded.
### Upgrade procedure
This example shows how to upgrade a 3-member v3.4 ectd cluster running on a local machine.
#### 1. Check upgrade requirements
Is the cluster healthy and running v3.4.x?
```
$ ETCDCTL_API=3 etcdctl endpoint health --endpoints=localhost:2379,localhost:22379,localhost:32379
localhost:2379 is healthy: successfully committed proposal: took = 6.600684ms
localhost:22379 is healthy: successfully committed proposal: took = 8.540064ms
localhost:32379 is healthy: successfully committed proposal: took = 8.763432ms
$ curl http://localhost:2379/version
{"etcdserver":"3.4.0","etcdcluster":"3.4.0"}
```
#### 2. Stop the existing etcd process
When each etcd process is stopped, expected errors will be logged by other cluster members. This is normal since a cluster member connection has been (temporarily) broken:
```
14:13:31.491746 I | raft: c89feb932daef420 [term 3] received MsgTimeoutNow from 6d4f535bae3ab960 and starts an election to get leadership.
14:13:31.491769 I | raft: c89feb932daef420 became candidate at term 4
14:13:31.491788 I | raft: c89feb932daef420 received MsgVoteResp from c89feb932daef420 at term 4
14:13:31.491797 I | raft: c89feb932daef420 [logterm: 3, index: 9] sent MsgVote request to 6d4f535bae3ab960 at term 4
14:13:31.491805 I | raft: c89feb932daef420 [logterm: 3, index: 9] sent MsgVote request to 9eda174c7df8a033 at term 4
14:13:31.491815 I | raft: raft.node: c89feb932daef420 lost leader 6d4f535bae3ab960 at term 4
14:13:31.524084 I | raft: c89feb932daef420 received MsgVoteResp from 6d4f535bae3ab960 at term 4
14:13:31.524108 I | raft: c89feb932daef420 [quorum:2] has received 2 MsgVoteResp votes and 0 vote rejections
14:13:31.524123 I | raft: c89feb932daef420 became leader at term 4
14:13:31.524136 I | raft: raft.node: c89feb932daef420 elected leader c89feb932daef420 at term 4
14:13:31.592650 W | rafthttp: lost the TCP streaming connection with peer 6d4f535bae3ab960 (stream MsgApp v2 reader)
14:13:31.592825 W | rafthttp: lost the TCP streaming connection with peer 6d4f535bae3ab960 (stream Message reader)
14:13:31.693275 E | rafthttp: failed to dial 6d4f535bae3ab960 on stream Message (dial tcp [::1]:2380: getsockopt: connection refused)
14:13:31.693289 I | rafthttp: peer 6d4f535bae3ab960 became inactive
14:13:31.936678 W | rafthttp: lost the TCP streaming connection with peer 6d4f535bae3ab960 (stream Message writer)
```
It's a good idea at this point to [backup the etcd data](../op-guide/maintenance.md#snapshot-backup) to provide a downgrade path should any problems occur:
```
$ etcdctl snapshot save backup.db
```
#### 3. Drop-in etcd v3.5 binary and start the new etcd process
The new v3.5 etcd will publish its information to the cluster:
```
14:14:25.363225 I | etcdserver: published {Name:s1 ClientURLs:[http://localhost:2379]} to cluster a9ededbffcb1b1f1
```
Verify that each member, and then the entire cluster, becomes healthy with the new v3.5 etcd binary:
```
$ ETCDCTL_API=3 /etcdctl endpoint health --endpoints=localhost:2379,localhost:22379,localhost:32379
localhost:22379 is healthy: successfully committed proposal: took = 5.540129ms
localhost:32379 is healthy: successfully committed proposal: took = 7.321771ms
localhost:2379 is healthy: successfully committed proposal: took = 10.629901ms
```
Upgraded members will log warnings like the following until the entire cluster is upgraded. This is expected and will cease after all etcd cluster members are upgraded to v3.5:
```
14:15:17.071804 W | etcdserver: member c89feb932daef420 has a higher version 3.5.0
14:15:21.073110 W | etcdserver: the local etcd version 3.4.0 is not up-to-date
14:15:21.073142 W | etcdserver: member 6d4f535bae3ab960 has a higher version 3.5.0
14:15:21.073157 W | etcdserver: the local etcd version 3.4.0 is not up-to-date
14:15:21.073164 W | etcdserver: member c89feb932daef420 has a higher version 3.5.0
```
#### 4. Repeat step 2 to step 3 for all other members
#### 5. Finish
When all members are upgraded, the cluster will report upgrading to 3.5 successfully:
```
14:15:54.536901 N | etcdserver/membership: updated the cluster version from 3.4 to 3.5
14:15:54.537035 I | etcdserver/api: enabled capabilities for version 3.5
```
```
$ ETCDCTL_API=3 /etcdctl endpoint health --endpoints=localhost:2379,localhost:22379,localhost:32379
localhost:2379 is healthy: successfully committed proposal: took = 2.312897ms
localhost:22379 is healthy: successfully committed proposal: took = 2.553476ms
localhost:32379 is healthy: successfully committed proposal: took = 2.517902ms
```
[etcd-contact]: https://groups.google.com/forum/#!forum/etcd-dev

View File

@ -1,5 +1,5 @@
# Use goreman to run `go get github.com/mattn/goreman`
etcd1: bin/etcd --name infra1 --listen-client-urls http://127.0.0.1:2379 --advertise-client-urls http://127.0.0.1:2379 --listen-peer-urls http://127.0.0.1:12380 --initial-advertise-peer-urls http://127.0.0.1:12380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof --logger zap --log-output stderr
etcd2: bin/etcd --name infra2 --listen-client-urls http://127.0.0.1:22379 --advertise-client-urls http://127.0.0.1:22379 --listen-peer-urls http://127.0.0.1:22380 --initial-advertise-peer-urls http://127.0.0.1:22380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof --logger zap --log-output stderr
etcd3: bin/etcd --name infra3 --listen-client-urls http://127.0.0.1:32379 --advertise-client-urls http://127.0.0.1:32379 --listen-peer-urls http://127.0.0.1:32380 --initial-advertise-peer-urls http://127.0.0.1:32380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof --logger zap --log-output stderr
etcd1: bin/etcd --name infra1 --listen-client-urls http://127.0.0.1:2379 --advertise-client-urls http://127.0.0.1:2379 --listen-peer-urls http://127.0.0.1:12380 --initial-advertise-peer-urls http://127.0.0.1:12380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof --logger zap --log-outputs stderr
etcd2: bin/etcd --name infra2 --listen-client-urls http://127.0.0.1:22379 --advertise-client-urls http://127.0.0.1:22379 --listen-peer-urls http://127.0.0.1:22380 --initial-advertise-peer-urls http://127.0.0.1:22380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof --logger zap --log-outputs stderr
etcd3: bin/etcd --name infra3 --listen-client-urls http://127.0.0.1:32379 --advertise-client-urls http://127.0.0.1:32379 --listen-peer-urls http://127.0.0.1:32380 --initial-advertise-peer-urls http://127.0.0.1:32380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --enable-pprof --logger zap --log-outputs stderr
#proxy: bin/etcd grpc-proxy start --endpoints=127.0.0.1:2379,127.0.0.1:22379,127.0.0.1:32379 --listen-addr=127.0.0.1:23790 --advertise-client-url=127.0.0.1:23790 --enable-pprof

View File

@ -16,6 +16,7 @@ package embed
import (
"crypto/tls"
"errors"
"fmt"
"io/ioutil"
"net"
@ -23,6 +24,7 @@ import (
"net/url"
"os"
"path/filepath"
"reflect"
"sort"
"strings"
"sync"
@ -41,6 +43,7 @@ import (
"github.com/coreos/pkg/capnslog"
"github.com/ghodss/yaml"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
)
@ -260,27 +263,40 @@ type Config struct {
ListenMetricsUrls []url.URL
ListenMetricsUrlsJSON string `json:"listen-metrics-urls"`
// logger logs server-side operations. The default is nil,
// and "setupLogging" must be called before starting server.
// Do not set logger directly.
loggerMu *sync.RWMutex
logger *zap.Logger
loggerConfig zap.Config
// Logger is logger options: "zap", "capnslog".
// WARN: "capnslog" is being deprecated in v3.5.
Logger string `json:"logger"`
// LogOutput is either:
// DeprecatedLogOutput is to be deprecated in v3.5.
// Just here for safe migration in v3.4.
DeprecatedLogOutput []string `json:"log-output"`
// LogOutputs is either:
// - "default" as os.Stderr,
// - "stderr" as os.Stderr,
// - "stdout" as os.Stdout,
// - file path to append server logs to.
// It can be multiple when "Logger" is zap.
LogOutput []string `json:"log-output"`
LogOutputs []string `json:"log-outputs"`
// Debug is true, to enable debug level logging.
Debug bool `json:"debug"`
// logger logs server-side operations. The default is nil,
// and "setupLogging" must be called before starting server.
// Do not set logger directly.
loggerMu *sync.RWMutex
logger *zap.Logger
// loggerConfig is server logger configuration for Raft logger.
// Must be either: "loggerConfig != nil" or "loggerCore != nil && loggerWriteSyncer != nil".
loggerConfig *zap.Config
// loggerCore is "zapcore.Core" for raft logger.
// Must be either: "loggerConfig != nil" or "loggerCore != nil && loggerWriteSyncer != nil".
loggerCore zapcore.Core
loggerWriteSyncer zapcore.WriteSyncer
// TO BE DEPRECATED
// LogPkgLevels is being deprecated in v3.5.
// Only valid if "logger" option is "capnslog".
// WARN: DO NOT USE THIS!
@ -358,12 +374,13 @@ func NewConfig() *Config {
PreVote: false, // TODO: enable by default in v3.5
loggerMu: new(sync.RWMutex),
logger: nil,
Logger: "capnslog",
LogOutput: []string{DefaultLogOutput},
Debug: false,
LogPkgLevels: "",
loggerMu: new(sync.RWMutex),
logger: nil,
Logger: "capnslog",
DeprecatedLogOutput: []string{DefaultLogOutput},
LogOutputs: []string{DefaultLogOutput},
Debug: false,
LogPkgLevels: "",
}
cfg.InitialCluster = cfg.InitialClusterFromName(cfg.Name)
return cfg
@ -396,6 +413,34 @@ var grpcLogOnce = new(sync.Once)
// setupLogging initializes etcd logging.
// Must be called after flag parsing or finishing configuring embed.Config.
func (cfg *Config) setupLogging() error {
// handle "DeprecatedLogOutput" in v3.4
// TODO: remove "DeprecatedLogOutput" in v3.5
len1 := len(cfg.DeprecatedLogOutput)
len2 := len(cfg.LogOutputs)
if len1 != len2 {
switch {
case len1 > len2: // deprecate "log-output" flag is used
fmt.Fprintln(os.Stderr, "'--log-output' flag has been deprecated! Please use '--log-outputs'!")
cfg.LogOutputs = cfg.DeprecatedLogOutput
case len1 < len2: // "--log-outputs" flag has been set with multiple writers
cfg.DeprecatedLogOutput = []string{}
}
} else {
if len1 > 1 {
return errors.New("both '--log-output' and '--log-outputs' are set; only set '--log-outputs'")
}
if len1 < 1 {
return errors.New("either '--log-output' or '--log-outputs' flag must be set")
}
if reflect.DeepEqual(cfg.DeprecatedLogOutput, cfg.LogOutputs) && cfg.DeprecatedLogOutput[0] != DefaultLogOutput {
return fmt.Errorf("'--log-output=%q' and '--log-outputs=%q' are incompatible; only set --log-outputs", cfg.DeprecatedLogOutput, cfg.LogOutputs)
}
if !reflect.DeepEqual(cfg.DeprecatedLogOutput, []string{DefaultLogOutput}) {
fmt.Fprintf(os.Stderr, "Deprecated '--log-output' flag is set to %q\n", cfg.DeprecatedLogOutput)
fmt.Fprintln(os.Stderr, "Please use '--log-outputs' flag")
}
}
switch cfg.Logger {
case "capnslog": // TODO: deprecate this in v3.5
cfg.ClientTLSInfo.HandshakeFailure = logTLSHandshakeFailure
@ -423,14 +468,14 @@ func (cfg *Config) setupLogging() error {
repoLog.SetLogLevel(settings)
}
if len(cfg.LogOutput) != 1 {
fmt.Printf("expected only 1 value in 'log-output', got %v\n", cfg.LogOutput)
if len(cfg.LogOutputs) != 1 {
fmt.Printf("expected only 1 value in 'log-output', got %v\n", cfg.LogOutputs)
os.Exit(1)
}
// capnslog initially SetFormatter(NewDefaultFormatter(os.Stderr))
// where NewDefaultFormatter returns NewJournaldFormatter when syscall.Getppid() == 1
// specify 'stdout' or 'stderr' to skip journald logging even when running under systemd
output := cfg.LogOutput[0]
output := cfg.LogOutputs[0]
switch output {
case "stdout":
capnslog.SetFormatter(capnslog.NewPrettyFormatter(os.Stdout, cfg.Debug))
@ -442,11 +487,11 @@ func (cfg *Config) setupLogging() error {
}
case "zap":
if len(cfg.LogOutput) == 0 {
cfg.LogOutput = []string{DefaultLogOutput}
if len(cfg.LogOutputs) == 0 {
cfg.LogOutputs = []string{DefaultLogOutput}
}
if len(cfg.LogOutput) > 1 {
for _, v := range cfg.LogOutput {
if len(cfg.LogOutputs) > 1 {
for _, v := range cfg.LogOutputs {
if v == DefaultLogOutput {
panic(fmt.Errorf("multi logoutput for %q is not supported yet", DefaultLogOutput))
}
@ -468,19 +513,17 @@ func (cfg *Config) setupLogging() error {
ErrorOutputPaths: make([]string, 0),
}
outputPaths, errOutputPaths := make(map[string]struct{}), make(map[string]struct{})
for _, v := range cfg.LogOutput {
isJournald := false
for _, v := range cfg.LogOutputs {
switch v {
case DefaultLogOutput:
if syscall.Getppid() == 1 {
// capnslog initially SetFormatter(NewDefaultFormatter(os.Stderr))
// where "NewDefaultFormatter" returns "NewJournaldFormatter"
// when syscall.Getppid() == 1, specify 'stdout' or 'stderr' to
// skip journald logging even when running under systemd
// TODO: capnlog.NewJournaldFormatter()
fmt.Println("running under init, which may be systemd!")
outputPaths["stderr"] = struct{}{}
errOutputPaths["stderr"] = struct{}{}
continue
// specify 'stdout' or 'stderr' to override this redirects
// when syscall.Getppid() == 1
isJournald = true
break
}
outputPaths["stderr"] = struct{}{}
@ -499,38 +542,66 @@ func (cfg *Config) setupLogging() error {
errOutputPaths[v] = struct{}{}
}
}
for v := range outputPaths {
lcfg.OutputPaths = append(lcfg.OutputPaths, v)
}
for v := range errOutputPaths {
lcfg.ErrorOutputPaths = append(lcfg.ErrorOutputPaths, v)
}
sort.Strings(lcfg.OutputPaths)
sort.Strings(lcfg.ErrorOutputPaths)
if cfg.Debug {
lcfg.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
grpc.EnableTracing = true
}
var err error
cfg.logger, err = lcfg.Build()
if err != nil {
return err
}
cfg.loggerConfig = lcfg
grpcLogOnce.Do(func() {
// debug true, enable info, warning, error
// debug false, only discard info
var gl grpclog.LoggerV2
gl, err = logutil.NewGRPCLoggerV2(lcfg)
if err == nil {
grpclog.SetLoggerV2(gl)
if !isJournald {
for v := range outputPaths {
lcfg.OutputPaths = append(lcfg.OutputPaths, v)
}
})
if err != nil {
return err
for v := range errOutputPaths {
lcfg.ErrorOutputPaths = append(lcfg.ErrorOutputPaths, v)
}
sort.Strings(lcfg.OutputPaths)
sort.Strings(lcfg.ErrorOutputPaths)
if cfg.Debug {
lcfg.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
grpc.EnableTracing = true
}
var err error
cfg.logger, err = lcfg.Build()
if err != nil {
return err
}
cfg.loggerConfig = &lcfg
cfg.loggerCore = nil
cfg.loggerWriteSyncer = nil
grpcLogOnce.Do(func() {
// debug true, enable info, warning, error
// debug false, only discard info
var gl grpclog.LoggerV2
gl, err = logutil.NewGRPCLoggerV2(lcfg)
if err == nil {
grpclog.SetLoggerV2(gl)
}
})
if err != nil {
return err
}
} else {
// use stderr as fallback
syncer := getZapWriteSyncer()
lvl := zap.NewAtomicLevelAt(zap.InfoLevel)
if cfg.Debug {
lvl = zap.NewAtomicLevelAt(zap.DebugLevel)
grpc.EnableTracing = true
}
cr := zapcore.NewCore(
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
syncer,
lvl,
)
cfg.logger = zap.New(cr, zap.AddCaller(), zap.ErrorOutput(syncer))
cfg.loggerConfig = nil
cfg.loggerCore = cr
cfg.loggerWriteSyncer = syncer
grpcLogOnce.Do(func() {
grpclog.SetLoggerV2(logutil.NewGRPCLoggerV2FromZapCore(cr, syncer))
})
}
logTLSHandshakeFailure := func(conn *tls.Conn, err error) {

View File

@ -34,7 +34,7 @@ func TestConfigFileOtherFields(t *testing.T) {
PeerSecurityCfgFile securityConfig `json:"peer-transport-security"`
ForceNewCluster bool `json:"force-new-cluster"`
Logger string `json:"logger"`
LogOutputs []string `json:"log-output"`
LogOutputs []string `json:"log-outputs"`
Debug bool `json:"debug"`
}{
ctls,
@ -157,7 +157,7 @@ func mustCreateCfgFile(t *testing.T, b []byte) *os.File {
func TestAutoCompactionModeInvalid(t *testing.T) {
cfg := NewConfig()
cfg.Logger = "zap"
cfg.LogOutput = []string{"/dev/null"}
cfg.LogOutputs = []string{"/dev/null"}
cfg.Debug = false
cfg.AutoCompactionMode = "period"
err := cfg.Validate()

View File

@ -190,6 +190,8 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) {
PreVote: cfg.PreVote,
Logger: cfg.logger,
LoggerConfig: cfg.loggerConfig,
LoggerCore: cfg.loggerCore,
LoggerWriteSyncer: cfg.loggerWriteSyncer,
Debug: cfg.Debug,
ForceNewCluster: cfg.ForceNewCluster,
}

28
embed/journald_unix.go Normal file
View File

@ -0,0 +1,28 @@
// Copyright 2018 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !windows
package embed
import (
"os"
"github.com/coreos/etcd/pkg/logutil"
"go.uber.org/zap/zapcore"
)
func getZapWriteSyncer() zapcore.WriteSyncer {
return zapcore.AddSync(logutil.NewJournaldWriter(os.Stderr))
}

27
embed/journald_windows.go Normal file
View File

@ -0,0 +1,27 @@
// Copyright 2018 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build windows
package embed
import (
"os"
"go.uber.org/zap/zapcore"
)
func getZapWriteSyncer() zapcore.WriteSyncer {
return zapcore.AddSync(os.Stderr)
}

View File

@ -132,7 +132,7 @@ debug: false
log-package-levels:
# Specify 'stdout' or 'stderr' to skip journald logging even when running under systemd.
log-output: [default]
log-outputs: [default]
# Force to create a new one member cluster.
force-new-cluster: false

View File

@ -218,7 +218,8 @@ func newConfig() *config {
// logging
fs.StringVar(&cfg.ec.Logger, "logger", "capnslog", "Specify 'zap' for structured logging or 'capnslog'.")
fs.Var(flags.NewUniqueStringsValue(embed.DefaultLogOutput), "log-output", "Specify 'stdout' or 'stderr' to skip journald logging even when running under systemd, or list of comma separated output targets.")
fs.Var(flags.NewUniqueStringsValue(embed.DefaultLogOutput), "log-output", "DEPRECATED: use '--log-outputs'.")
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.BoolVar(&cfg.ec.Debug, "debug", false, "Enable debug-level logging for etcd.")
fs.StringVar(&cfg.ec.LogPkgLevels, "log-package-levels", "", "(To be deprecated) Specify a particular log level for each etcd package (eg: 'etcdmain=CRITICAL,etcdserver=DEBUG').")
@ -306,13 +307,23 @@ func (cfg *config) configFromCmdLine() error {
cfg.ec.CORS = flags.UniqueURLsMapFromFlag(cfg.cf.flagSet, "cors")
cfg.ec.HostWhitelist = flags.UniqueStringsMapFromFlag(cfg.cf.flagSet, "host-whitelist")
outputs := flags.UniqueStringsMapFromFlag(cfg.cf.flagSet, "log-output")
oss := make([]string, 0, len(outputs))
for v := range outputs {
oss = append(oss, v)
// TODO: remove this in v3.5
output := flags.UniqueStringsMapFromFlag(cfg.cf.flagSet, "log-output")
oss1 := make([]string, 0, len(output))
for v := range output {
oss1 = append(oss1, v)
}
sort.Strings(oss)
cfg.ec.LogOutput = oss
sort.Strings(oss1)
cfg.ec.DeprecatedLogOutput = oss1
outputs := flags.UniqueStringsMapFromFlag(cfg.cf.flagSet, "log-outputs")
oss2 := make([]string, 0, len(outputs))
for v := range outputs {
oss2 = append(oss2, v)
}
sort.Strings(oss2)
cfg.ec.LogOutputs = oss2
cfg.ec.ClusterState = cfg.cf.clusterState.String()
cfg.cp.Fallback = cfg.cf.fallback.String()

View File

@ -160,7 +160,7 @@ Profiling:
Logging:
--logger 'capnslog'
Specify 'zap' for structured logging or 'capnslog'.
--log-output 'default'
--log-outputs 'default'
Specify 'stdout' or 'stderr' to skip journald logging even when running under systemd, or list of comma separated output targets.
--debug 'false'
Enable debug-level logging for etcd.

View File

@ -27,6 +27,7 @@ import (
"github.com/coreos/etcd/pkg/types"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// ServerConfig holds the configuration of etcd as taken from the command line or discovery.
@ -115,8 +116,14 @@ type ServerConfig struct {
// Logger logs server-side operations.
// If not nil, it disables "capnslog" and uses the given logger.
Logger *zap.Logger
// LoggerConfig is server logger configuration for Raft logger.
LoggerConfig zap.Config
// Must be either: "LoggerConfig != nil" or "LoggerCore != nil && LoggerWriteSyncer != nil".
LoggerConfig *zap.Config
// LoggerCore is "zapcore.Core" for raft logger.
// Must be either: "LoggerConfig != nil" or "LoggerCore != nil && LoggerWriteSyncer != nil".
LoggerCore zapcore.Core
LoggerWriteSyncer zapcore.WriteSyncer
Debug bool

View File

@ -468,9 +468,13 @@ func startNode(cfg ServerConfig, cl *membership.RaftCluster, ids []types.ID) (id
}
if cfg.Logger != nil {
// called after capnslog setting in "init" function
c.Logger, err = logutil.NewRaftLogger(cfg.LoggerConfig)
if err != nil {
log.Fatalf("cannot create raft logger %v", err)
if cfg.LoggerConfig != nil {
c.Logger, err = logutil.NewRaftLogger(cfg.LoggerConfig)
if err != nil {
log.Fatalf("cannot create raft logger %v", err)
}
} else if cfg.LoggerCore != nil && cfg.LoggerWriteSyncer != nil {
c.Logger = logutil.NewRaftLoggerFromZapCore(cfg.LoggerCore, cfg.LoggerWriteSyncer)
}
}
@ -519,9 +523,13 @@ func restartNode(cfg ServerConfig, snapshot *raftpb.Snapshot) (types.ID, *member
if cfg.Logger != nil {
// called after capnslog setting in "init" function
var err error
c.Logger, err = logutil.NewRaftLogger(cfg.LoggerConfig)
if err != nil {
log.Fatalf("cannot create raft logger %v", err)
if cfg.LoggerConfig != nil {
c.Logger, err = logutil.NewRaftLogger(cfg.LoggerConfig)
if err != nil {
log.Fatalf("cannot create raft logger %v", err)
}
} else if cfg.LoggerCore != nil && cfg.LoggerWriteSyncer != nil {
c.Logger = logutil.NewRaftLoggerFromZapCore(cfg.LoggerCore, cfg.LoggerWriteSyncer)
}
}
@ -611,9 +619,13 @@ func restartAsStandaloneNode(cfg ServerConfig, snapshot *raftpb.Snapshot) (types
}
if cfg.Logger != nil {
// called after capnslog setting in "init" function
c.Logger, err = logutil.NewRaftLogger(cfg.LoggerConfig)
if err != nil {
log.Fatalf("cannot create raft logger %v", err)
if cfg.LoggerConfig != nil {
c.Logger, err = logutil.NewRaftLogger(cfg.LoggerConfig)
if err != nil {
log.Fatalf("cannot create raft logger %v", err)
}
} else if cfg.LoggerCore != nil && cfg.LoggerWriteSyncer != nil {
c.Logger = logutil.NewRaftLoggerFromZapCore(cfg.LoggerCore, cfg.LoggerWriteSyncer)
}
}

View File

@ -34,7 +34,7 @@ agent-configs:
pre-vote: true
initial-corrupt-check: true
logger: zap
log-output: [/tmp/etcd-functional-1/etcd.log]
log-outputs: [/tmp/etcd-functional-1/etcd.log]
debug: true
client-cert-data: ""
client-cert-path: ""
@ -85,7 +85,7 @@ agent-configs:
pre-vote: true
initial-corrupt-check: true
logger: zap
log-output: [/tmp/etcd-functional-2/etcd.log]
log-outputs: [/tmp/etcd-functional-2/etcd.log]
debug: true
client-cert-data: ""
client-cert-path: ""
@ -136,7 +136,7 @@ agent-configs:
pre-vote: true
initial-corrupt-check: true
logger: zap
log-output: [/tmp/etcd-functional-3/etcd.log]
log-outputs: [/tmp/etcd-functional-3/etcd.log]
debug: true
client-cert-data: ""
client-cert-path: ""

View File

@ -88,11 +88,11 @@ func (srv *Server) handleTesterRequest(req *rpcpb.Request) (resp *rpcpb.Response
// just archive the first file
func (srv *Server) createEtcdLogFile() error {
var err error
srv.etcdLogFile, err = os.Create(srv.Member.Etcd.LogOutput[0])
srv.etcdLogFile, err = os.Create(srv.Member.Etcd.LogOutputs[0])
if err != nil {
return err
}
srv.lg.Info("created etcd log file", zap.String("path", srv.Member.Etcd.LogOutput[0]))
srv.lg.Info("created etcd log file", zap.String("path", srv.Member.Etcd.LogOutputs[0]))
return nil
}
@ -665,7 +665,7 @@ func (srv *Server) handle_SIGQUIT_ETCD_AND_ARCHIVE_DATA() (*rpcpb.Response, erro
// TODO: support separate WAL directory
if err = archive(
srv.Member.BaseDir,
srv.Member.Etcd.LogOutput[0],
srv.Member.Etcd.LogOutputs[0],
srv.Member.Etcd.DataDir,
); err != nil {
return nil, err

View File

@ -59,7 +59,7 @@ var etcdFields = []string{
"InitialCorruptCheck",
"Logger",
"LogOutput",
"LogOutputs",
"Debug",
}
@ -167,7 +167,7 @@ func (e *Etcd) EmbedConfig() (cfg *embed.Config, err error) {
cfg.ExperimentalInitialCorruptCheck = e.InitialCorruptCheck
cfg.Logger = e.Logger
cfg.LogOutput = e.LogOutput
cfg.LogOutputs = e.LogOutputs
cfg.Debug = e.Debug
return cfg, nil

View File

@ -57,9 +57,9 @@ func TestEtcd(t *testing.T) {
PreVote: true,
InitialCorruptCheck: true,
Logger: "zap",
LogOutput: []string{"/tmp/etcd-functional-1/etcd.log"},
Debug: true,
Logger: "zap",
LogOutputs: []string{"/tmp/etcd-functional-1/etcd.log"},
Debug: true,
}
exps := []string{
@ -84,7 +84,7 @@ func TestEtcd(t *testing.T) {
"--pre-vote=true",
"--experimental-initial-corrupt-check=true",
"--logger=zap",
"--log-output=/tmp/etcd-functional-1/etcd.log",
"--log-outputs=/tmp/etcd-functional-1/etcd.log",
"--debug=true",
}
fs := e.Flags()
@ -133,7 +133,7 @@ func TestEtcd(t *testing.T) {
expc.PreVote = true
expc.ExperimentalInitialCorruptCheck = true
expc.Logger = "zap"
expc.LogOutput = []string{"/tmp/etcd-functional-1/etcd.log"}
expc.LogOutputs = []string{"/tmp/etcd-functional-1/etcd.log"}
expc.Debug = true
cfg, err := e.EmbedConfig()
if err != nil {

View File

@ -760,9 +760,9 @@ type Etcd struct {
PreVote bool `protobuf:"varint,63,opt,name=PreVote,proto3" json:"PreVote,omitempty" yaml:"pre-vote"`
InitialCorruptCheck bool `protobuf:"varint,64,opt,name=InitialCorruptCheck,proto3" json:"InitialCorruptCheck,omitempty" yaml:"initial-corrupt-check"`
Logger string `protobuf:"bytes,71,opt,name=Logger,proto3" json:"Logger,omitempty" yaml:"logger"`
// LogOutput is the log file to store current etcd server logs.
LogOutput []string `protobuf:"bytes,72,rep,name=LogOutput" json:"LogOutput,omitempty" yaml:"log-output"`
Debug bool `protobuf:"varint,73,opt,name=Debug,proto3" json:"Debug,omitempty" yaml:"debug"`
// LogOutputs is the log file to store current etcd server logs.
LogOutputs []string `protobuf:"bytes,72,rep,name=LogOutputs" json:"LogOutputs,omitempty" yaml:"log-outputs"`
Debug bool `protobuf:"varint,73,opt,name=Debug,proto3" json:"Debug,omitempty" yaml:"debug"`
}
func (m *Etcd) Reset() { *m = Etcd{} }
@ -1789,8 +1789,8 @@ func (m *Etcd) MarshalTo(dAtA []byte) (int, error) {
i = encodeVarintRpc(dAtA, i, uint64(len(m.Logger)))
i += copy(dAtA[i:], m.Logger)
}
if len(m.LogOutput) > 0 {
for _, s := range m.LogOutput {
if len(m.LogOutputs) > 0 {
for _, s := range m.LogOutputs {
dAtA[i] = 0xc2
i++
dAtA[i] = 0x4
@ -2209,8 +2209,8 @@ func (m *Etcd) Size() (n int) {
if l > 0 {
n += 2 + l + sovRpc(uint64(l))
}
if len(m.LogOutput) > 0 {
for _, s := range m.LogOutput {
if len(m.LogOutputs) > 0 {
for _, s := range m.LogOutputs {
l = len(s)
n += 2 + l + sovRpc(uint64(l))
}
@ -4845,7 +4845,7 @@ func (m *Etcd) Unmarshal(dAtA []byte) error {
iNdEx = postIndex
case 72:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field LogOutput", wireType)
return fmt.Errorf("proto: wrong wireType = %d for field LogOutputs", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
@ -4870,7 +4870,7 @@ func (m *Etcd) Unmarshal(dAtA []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.LogOutput = append(m.LogOutput, string(dAtA[iNdEx:postIndex]))
m.LogOutputs = append(m.LogOutputs, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
case 73:
if wireType != 0 {
@ -5026,179 +5026,179 @@ var fileDescriptorRpc = []byte{
0xf5, 0x36, 0x45, 0x49, 0x96, 0xae, 0x5e, 0xd4, 0xc8, 0xb2, 0xe1, 0x97, 0x20, 0xc3, 0x71, 0x7e,
0xb2, 0x12, 0xd8, 0xf9, 0xd9, 0x39, 0x79, 0x38, 0x4d, 0x1c, 0x90, 0x82, 0x2d, 0x56, 0x10, 0x49,
0x0f, 0x21, 0xdb, 0x59, 0xf1, 0x40, 0xe4, 0x48, 0xe2, 0x31, 0x05, 0x30, 0xc0, 0xd0, 0x91, 0xb2,
0xeb, 0xaa, 0xdb, 0x36, 0x7d, 0x9c, 0xf6, 0x9c, 0xae, 0xba, 0x6e, 0xda, 0x7f, 0xc3, 0x79, 0xb5,
0x69, 0xbb, 0x6a, 0x17, 0x3c, 0x6d, 0xba, 0xe9, 0xaa, 0x0b, 0x9e, 0xbe, 0x57, 0x3d, 0x33, 0x03,
0x88, 0x03, 0x80, 0x94, 0xb4, 0xb2, 0xe7, 0xde, 0xef, 0xfb, 0xe6, 0xce, 0xdc, 0x99, 0xb9, 0x17,
0x14, 0xcc, 0xf9, 0xed, 0x7a, 0x7b, 0xfb, 0xb6, 0xdf, 0xae, 0xdf, 0x6a, 0xfb, 0x1e, 0xf5, 0xd0,
0x18, 0x37, 0x5c, 0xd2, 0x77, 0x9b, 0x74, 0xaf, 0xb3, 0x7d, 0xab, 0xee, 0xed, 0xdf, 0xde, 0xf5,
0x76, 0xbd, 0xdb, 0xdc, 0xbb, 0xdd, 0xd9, 0xe1, 0x23, 0x3e, 0xe0, 0xff, 0x13, 0x2c, 0xed, 0xbb,
0x19, 0x38, 0x8b, 0xc9, 0x87, 0x1d, 0x12, 0x50, 0x74, 0x0b, 0x26, 0xcb, 0x6d, 0xe2, 0x3b, 0xb4,
0xe9, 0xb9, 0x4a, 0x66, 0x39, 0xb3, 0x32, 0x7b, 0x27, 0x77, 0x8b, 0xab, 0xde, 0x3a, 0xb2, 0xe3,
0x3e, 0x04, 0xdd, 0x80, 0xf1, 0x4d, 0xb2, 0xbf, 0x4d, 0x7c, 0x65, 0x64, 0x39, 0xb3, 0x32, 0x75,
0x67, 0x26, 0x04, 0x0b, 0x23, 0x0e, 0x9d, 0x0c, 0x66, 0x93, 0x80, 0x12, 0x5f, 0xc9, 0xc6, 0x60,
0xc2, 0x88, 0x43, 0xa7, 0xf6, 0xd7, 0x11, 0x98, 0xae, 0xba, 0x4e, 0x3b, 0xd8, 0xf3, 0x68, 0xd1,
0xdd, 0xf1, 0xd0, 0x12, 0x80, 0x50, 0x28, 0x39, 0xfb, 0x84, 0xc7, 0x33, 0x89, 0x25, 0x0b, 0x5a,
0x85, 0x9c, 0x18, 0x15, 0x5a, 0x4d, 0xe2, 0xd2, 0x2d, 0x6c, 0x05, 0xca, 0xc8, 0x72, 0x76, 0x65,
0x12, 0xa7, 0xec, 0x48, 0xeb, 0x6b, 0x57, 0x1c, 0xba, 0xc7, 0x23, 0x99, 0xc4, 0x31, 0x1b, 0xd3,
0x8b, 0xc6, 0x0f, 0x9a, 0x2d, 0x52, 0x6d, 0x7e, 0x4c, 0x94, 0x51, 0x8e, 0x4b, 0xd9, 0xd1, 0xab,
0x30, 0x1f, 0xd9, 0x6c, 0x8f, 0x3a, 0x2d, 0x0e, 0x1e, 0xe3, 0xe0, 0xb4, 0x43, 0x56, 0xe6, 0xc6,
0x0d, 0x72, 0xa8, 0x8c, 0x2f, 0x67, 0x56, 0xb2, 0x38, 0x65, 0x97, 0x23, 0x5d, 0x77, 0x82, 0x3d,
0xe5, 0x2c, 0xc7, 0xc5, 0x6c, 0xb2, 0x1e, 0x26, 0xcf, 0x9b, 0x01, 0xcb, 0xd7, 0x44, 0x5c, 0x2f,
0xb2, 0x23, 0x04, 0xa3, 0xb6, 0xe7, 0x3d, 0x53, 0x26, 0x79, 0x70, 0xfc, 0xff, 0xda, 0xcf, 0x32,
0x30, 0x81, 0x49, 0xd0, 0xf6, 0xdc, 0x80, 0x20, 0x05, 0xce, 0x56, 0x3b, 0xf5, 0x3a, 0x09, 0x02,
0xbe, 0xc7, 0x13, 0x38, 0x1a, 0xa2, 0xf3, 0x30, 0x5e, 0xa5, 0x0e, 0xed, 0x04, 0x3c, 0xbf, 0x93,
0x38, 0x1c, 0x49, 0x79, 0xcf, 0x1e, 0x97, 0xf7, 0x37, 0xe3, 0xf9, 0xe4, 0x7b, 0x39, 0x75, 0x67,
0x21, 0x04, 0xcb, 0x2e, 0x1c, 0x03, 0x6a, 0x9f, 0x4c, 0x47, 0x13, 0xa0, 0xd7, 0x60, 0xc2, 0xa4,
0xf5, 0x86, 0x79, 0x40, 0xea, 0xe2, 0x04, 0xe4, 0xcf, 0xf5, 0xba, 0x6a, 0xee, 0xd0, 0xd9, 0x6f,
0xdd, 0xd3, 0x08, 0xad, 0x37, 0x74, 0x72, 0x40, 0xea, 0x1a, 0x3e, 0x42, 0xa1, 0xbb, 0x30, 0x69,
0xec, 0x12, 0x97, 0x1a, 0x8d, 0x86, 0xaf, 0x4c, 0x71, 0xca, 0x62, 0xaf, 0xab, 0xce, 0x0b, 0x8a,
0xc3, 0x5c, 0xba, 0xd3, 0x68, 0xf8, 0x1a, 0xee, 0xe3, 0x90, 0x05, 0xf3, 0x0f, 0x9c, 0x66, 0xab,
0xed, 0x35, 0x5d, 0xba, 0x6e, 0xdb, 0x15, 0x4e, 0x9e, 0xe6, 0xe4, 0xa5, 0x5e, 0x57, 0xbd, 0x24,
0xc8, 0x3b, 0x11, 0x44, 0xdf, 0xa3, 0xb4, 0x1d, 0xaa, 0xa4, 0x89, 0x48, 0x87, 0xb3, 0x79, 0x27,
0x20, 0x6b, 0x4d, 0x5f, 0x21, 0x5c, 0x63, 0xa1, 0xd7, 0x55, 0xe7, 0x84, 0xc6, 0xb6, 0x13, 0x10,
0xbd, 0xd1, 0xf4, 0x35, 0x1c, 0x61, 0xd0, 0x43, 0x98, 0x63, 0xd1, 0x8b, 0xd3, 0x5a, 0xf1, 0xbd,
0x83, 0x43, 0xe5, 0x33, 0x9e, 0x89, 0xfc, 0x95, 0x5e, 0x57, 0x55, 0xa4, 0xb5, 0xd6, 0x39, 0x44,
0x6f, 0x33, 0x8c, 0x86, 0x93, 0x2c, 0x64, 0xc0, 0x0c, 0x33, 0x55, 0x08, 0xf1, 0x85, 0xcc, 0xe7,
0x42, 0xe6, 0x52, 0xaf, 0xab, 0x9e, 0x97, 0x64, 0xda, 0x84, 0xf8, 0x91, 0x48, 0x9c, 0x81, 0x2a,
0x80, 0xfa, 0xaa, 0xa6, 0xdb, 0xe0, 0x0b, 0x53, 0x3e, 0xe5, 0xf9, 0xcf, 0xab, 0xbd, 0xae, 0x7a,
0x39, 0x1d, 0x0e, 0x09, 0x61, 0x1a, 0x1e, 0xc0, 0x45, 0xff, 0x0f, 0xa3, 0xcc, 0xaa, 0xfc, 0x52,
0xbc, 0x11, 0x53, 0x61, 0xfa, 0x99, 0x2d, 0x3f, 0xd7, 0xeb, 0xaa, 0x53, 0x7d, 0x41, 0x0d, 0x73,
0x28, 0xca, 0xc3, 0x22, 0xfb, 0xb7, 0xec, 0xf6, 0x0f, 0x73, 0x40, 0x3d, 0x9f, 0x28, 0xbf, 0x4a,
0x6b, 0xe0, 0xc1, 0x50, 0xb4, 0x06, 0xb3, 0x22, 0x90, 0x02, 0xf1, 0xe9, 0x9a, 0x43, 0x1d, 0xe5,
0xfb, 0xfc, 0xce, 0xe7, 0x2f, 0xf7, 0xba, 0xea, 0x05, 0x31, 0x67, 0x18, 0x7f, 0x9d, 0xf8, 0x54,
0x6f, 0x38, 0xd4, 0xd1, 0x70, 0x82, 0x13, 0x57, 0xe1, 0x0f, 0xc7, 0x27, 0xc7, 0xaa, 0xb4, 0x1d,
0xba, 0x17, 0x53, 0xe1, 0x0f, 0x8b, 0x01, 0x33, 0xc2, 0xb2, 0x41, 0x0e, 0x79, 0x28, 0x3f, 0x10,
0x22, 0x52, 0x5e, 0x42, 0x91, 0x67, 0xe4, 0x30, 0x8c, 0x24, 0xce, 0x88, 0x49, 0xf0, 0x38, 0x7e,
0x78, 0x9c, 0x84, 0x08, 0x23, 0xce, 0x40, 0x36, 0x2c, 0x08, 0x83, 0xed, 0x77, 0x02, 0x4a, 0x1a,
0x05, 0x83, 0xc7, 0xf2, 0x23, 0x21, 0x74, 0xad, 0xd7, 0x55, 0xaf, 0xc6, 0x84, 0xa8, 0x80, 0xe9,
0x75, 0x27, 0x0c, 0x69, 0x10, 0x7d, 0x80, 0x2a, 0x0f, 0xef, 0xc7, 0xa7, 0x50, 0x15, 0x51, 0x0e,
0xa2, 0xa3, 0xf7, 0x60, 0x9a, 0x9d, 0xc9, 0xa3, 0xdc, 0xfd, 0x43, 0xc8, 0x5d, 0xec, 0x75, 0xd5,
0x45, 0x21, 0xc7, 0xcf, 0xb0, 0x94, 0xb9, 0x18, 0x5e, 0xe6, 0xf3, 0x70, 0xfe, 0x79, 0x0c, 0x5f,
0x84, 0x11, 0xc3, 0xa3, 0x77, 0x60, 0x8a, 0x8d, 0xa3, 0x7c, 0xfd, 0x4b, 0xd0, 0x95, 0x5e, 0x57,
0x3d, 0x27, 0xd1, 0xfb, 0xd9, 0x92, 0xd1, 0x12, 0x99, 0xcf, 0xfd, 0xef, 0xe1, 0x64, 0x31, 0xb5,
0x8c, 0x46, 0x25, 0x98, 0x67, 0xc3, 0x78, 0x8e, 0xfe, 0x93, 0x4d, 0xde, 0x3f, 0x2e, 0x91, 0xca,
0x50, 0x9a, 0x9a, 0xd2, 0xe3, 0x21, 0xfd, 0xf7, 0x44, 0x3d, 0x11, 0x59, 0x9a, 0x8a, 0xde, 0x4d,
0x14, 0xd2, 0x3f, 0x8c, 0x26, 0x57, 0x17, 0x84, 0xee, 0x68, 0x63, 0x63, 0x35, 0xf6, 0xad, 0x44,
0x4d, 0xf8, 0xe3, 0xa9, 0x8b, 0xc2, 0xcf, 0xa7, 0xa3, 0x36, 0x82, 0xbd, 0xaf, 0x6c, 0x6d, 0xec,
0x7d, 0xcd, 0x24, 0xdf, 0x57, 0xb6, 0x11, 0xe1, 0xfb, 0x1a, 0x62, 0xd0, 0xab, 0x70, 0xb6, 0x44,
0xe8, 0x47, 0x9e, 0xff, 0x4c, 0xd4, 0xb1, 0x3c, 0xea, 0x75, 0xd5, 0x59, 0x01, 0x77, 0x85, 0x43,
0xc3, 0x11, 0x04, 0x5d, 0x87, 0x51, 0xfe, 0xfa, 0x8b, 0x2d, 0x92, 0x5e, 0x28, 0xf1, 0xdc, 0x73,
0x27, 0x2a, 0xc0, 0xec, 0x1a, 0x69, 0x39, 0x87, 0x96, 0x43, 0x89, 0x5b, 0x3f, 0xdc, 0x0c, 0x78,
0xa5, 0x99, 0x91, 0x9f, 0x85, 0x06, 0xf3, 0xeb, 0x2d, 0x01, 0xd0, 0xf7, 0x03, 0x0d, 0x27, 0x28,
0xe8, 0xdb, 0x90, 0x8b, 0x5b, 0xf0, 0x73, 0x5e, 0x73, 0x66, 0xe4, 0x9a, 0x93, 0x94, 0xd1, 0xfd,
0xe7, 0x1a, 0x4e, 0xf1, 0xd0, 0x07, 0xb0, 0xb8, 0xd5, 0x6e, 0x38, 0x94, 0x34, 0x12, 0x71, 0xcd,
0x70, 0xc1, 0xeb, 0xbd, 0xae, 0xaa, 0x0a, 0xc1, 0x8e, 0x80, 0xe9, 0xe9, 0xf8, 0x06, 0x2b, 0xa0,
0x37, 0x00, 0xb0, 0xd7, 0x71, 0x1b, 0x56, 0x73, 0xbf, 0x49, 0x95, 0xc5, 0xe5, 0xcc, 0xca, 0x58,
0xfe, 0x7c, 0xaf, 0xab, 0x22, 0xa1, 0xe7, 0x33, 0x9f, 0xde, 0x62, 0x4e, 0x0d, 0x4b, 0x48, 0x94,
0x87, 0x59, 0xf3, 0xa0, 0x49, 0xcb, 0x6e, 0xc1, 0x09, 0x08, 0x2b, 0x92, 0xca, 0xf9, 0x54, 0x35,
0x3a, 0x68, 0x52, 0xdd, 0x73, 0x75, 0x56, 0x58, 0x3b, 0x3e, 0xd1, 0x70, 0x82, 0x81, 0xde, 0x86,
0x29, 0xd3, 0x75, 0xb6, 0x5b, 0xa4, 0xd2, 0xf6, 0xbd, 0x1d, 0xe5, 0x02, 0x17, 0xb8, 0xd0, 0xeb,
0xaa, 0x0b, 0xa1, 0x00, 0x77, 0xea, 0x6d, 0xe6, 0xd5, 0xb0, 0x8c, 0x45, 0xf7, 0x60, 0x8a, 0xc9,
0xf0, 0xc5, 0x6c, 0x06, 0x8a, 0xca, 0xf7, 0x41, 0x3a, 0xa6, 0x75, 0x5e, 0x88, 0xf9, 0x26, 0xb0,
0xc5, 0xcb, 0x60, 0x36, 0x2d, 0x1b, 0x56, 0xf7, 0x3a, 0x3b, 0x3b, 0x2d, 0xa2, 0x2c, 0x27, 0xa7,
0xe5, 0xdc, 0x40, 0x78, 0x43, 0x6a, 0x88, 0x45, 0x2f, 0xc3, 0x18, 0x1b, 0x06, 0xca, 0x35, 0xd6,
0x89, 0xe6, 0x73, 0xbd, 0xae, 0x3a, 0xdd, 0x27, 0x05, 0x1a, 0x16, 0x6e, 0xb4, 0x21, 0x75, 0x1c,
0x05, 0x6f, 0x7f, 0xdf, 0x71, 0x1b, 0x81, 0xa2, 0x71, 0xce, 0xd5, 0x5e, 0x57, 0xbd, 0x98, 0xec,
0x38, 0xea, 0x21, 0x46, 0x6e, 0x38, 0x22, 0x1e, 0x3b, 0x8e, 0xb8, 0xe3, 0xba, 0xc4, 0x67, 0x1d,
0x10, 0xbf, 0x96, 0x37, 0x93, 0x55, 0xca, 0xe7, 0x7e, 0xde, 0x2d, 0x45, 0x55, 0x2a, 0x4e, 0x41,
0x45, 0xc8, 0x99, 0x07, 0x94, 0xf8, 0xae, 0xd3, 0x3a, 0x92, 0x59, 0xe5, 0x32, 0x52, 0x40, 0x24,
0x44, 0xc8, 0x42, 0x29, 0x1a, 0xba, 0x03, 0x93, 0x55, 0xea, 0x93, 0x20, 0x20, 0x7e, 0xa0, 0x10,
0xbe, 0x28, 0xa9, 0x6d, 0x0b, 0x22, 0x97, 0x86, 0xfb, 0x30, 0x74, 0x1b, 0x26, 0x0a, 0x7b, 0xa4,
0xfe, 0x8c, 0x51, 0x76, 0x38, 0x45, 0xba, 0xd5, 0xf5, 0xd0, 0xa3, 0xe1, 0x23, 0x10, 0x2b, 0x89,
0x82, 0xbd, 0x41, 0x0e, 0x79, 0xfb, 0xcd, 0x9b, 0xa6, 0x31, 0xf9, 0x7c, 0x89, 0x99, 0xf8, 0x53,
0x1b, 0x34, 0x3f, 0x26, 0x1a, 0x8e, 0x33, 0xd0, 0x23, 0x40, 0x31, 0x83, 0xe5, 0xf8, 0xbb, 0x44,
0x74, 0x4d, 0x63, 0xf9, 0xe5, 0x5e, 0x57, 0xbd, 0x32, 0x50, 0x47, 0x6f, 0x31, 0x9c, 0x86, 0x07,
0x90, 0xd1, 0x13, 0x38, 0xd7, 0xb7, 0x76, 0x76, 0x76, 0x9a, 0x07, 0xd8, 0x71, 0x77, 0x89, 0xf2,
0x85, 0x10, 0xd5, 0x7a, 0x5d, 0x75, 0x29, 0x2d, 0xca, 0x81, 0xba, 0xcf, 0x90, 0x1a, 0x1e, 0x28,
0x80, 0x1c, 0xb8, 0x30, 0xc8, 0x6e, 0x1f, 0xb8, 0xca, 0x97, 0x42, 0xfb, 0xe5, 0x5e, 0x57, 0xd5,
0x8e, 0xd5, 0xd6, 0xe9, 0x81, 0xab, 0xe1, 0x61, 0x3a, 0x68, 0x1d, 0xe6, 0x8e, 0x5c, 0xf6, 0x81,
0x5b, 0x6e, 0x07, 0xca, 0x57, 0x42, 0x5a, 0x3a, 0x01, 0x92, 0x34, 0x3d, 0x70, 0x75, 0xaf, 0x1d,
0x68, 0x38, 0x49, 0x43, 0xef, 0x47, 0xb9, 0x11, 0xc5, 0x3d, 0x10, 0x1d, 0xe4, 0x98, 0x5c, 0x80,
0x43, 0x1d, 0xd1, 0x16, 0x04, 0x47, 0xa9, 0x09, 0x09, 0xe8, 0xf5, 0xe8, 0x08, 0x3d, 0xaa, 0x54,
0x45, 0xef, 0x38, 0x26, 0xf7, 0xf1, 0x21, 0xfb, 0xc3, 0x76, 0xff, 0x10, 0x3d, 0xaa, 0x54, 0xb5,
0xef, 0xcc, 0x89, 0x6e, 0x93, 0xbd, 0xe2, 0xfd, 0xaf, 0x46, 0xf9, 0x15, 0x77, 0x9d, 0x7d, 0xa2,
0x61, 0xee, 0x94, 0xeb, 0xc8, 0xc8, 0x29, 0xea, 0xc8, 0x2a, 0x8c, 0x3f, 0x31, 0x2c, 0x86, 0xce,
0x26, 0xcb, 0xc8, 0x47, 0x4e, 0x4b, 0x80, 0x43, 0x04, 0x2a, 0xc3, 0xc2, 0x3a, 0x71, 0x7c, 0xba,
0x4d, 0x1c, 0x5a, 0x74, 0x29, 0xf1, 0x9f, 0x3b, 0xad, 0xb0, 0x4a, 0x64, 0xe5, 0xdd, 0xdc, 0x8b,
0x40, 0x7a, 0x33, 0x44, 0x69, 0x78, 0x10, 0x13, 0x15, 0x61, 0xde, 0x6c, 0x91, 0x3a, 0xfb, 0xee,
0xb6, 0x9b, 0xfb, 0xc4, 0xeb, 0xd0, 0xcd, 0x80, 0x57, 0x8b, 0xac, 0x7c, 0xcb, 0x49, 0x08, 0xd1,
0xa9, 0xc0, 0x68, 0x38, 0xcd, 0x62, 0x17, 0xdd, 0x6a, 0x06, 0x94, 0xb8, 0xd2, 0x77, 0xf3, 0x62,
0xf2, 0xe5, 0x69, 0x71, 0x44, 0xd4, 0xe2, 0x77, 0xfc, 0x56, 0xa0, 0xe1, 0x14, 0x0d, 0x61, 0x58,
0x30, 0x1a, 0xcf, 0x89, 0x4f, 0x9b, 0x01, 0x91, 0xd4, 0xce, 0x73, 0x35, 0xe9, 0x02, 0x39, 0x11,
0x28, 0x2e, 0x38, 0x88, 0x8c, 0xde, 0x8e, 0x5a, 0x5d, 0xa3, 0x43, 0x3d, 0xdb, 0xaa, 0x86, 0xaf,
0xbe, 0x94, 0x1b, 0xa7, 0x43, 0x3d, 0x9d, 0x32, 0x81, 0x38, 0x92, 0xbd, 0x83, 0xfd, 0xd6, 0xdb,
0xe8, 0xd0, 0x3d, 0x45, 0xe1, 0xdc, 0x21, 0xdd, 0xba, 0xd3, 0x49, 0x74, 0xeb, 0x8c, 0x82, 0xbe,
0x25, 0x8b, 0xb0, 0x0f, 0x7e, 0xe5, 0x62, 0xf2, 0xc3, 0x93, 0xb3, 0x77, 0x9a, 0xec, 0xf1, 0x4f,
0x60, 0xfb, 0xd1, 0x6f, 0x90, 0x43, 0x4e, 0xbe, 0x94, 0x3c, 0x59, 0xec, 0xe6, 0x08, 0x6e, 0x1c,
0x89, 0xac, 0x54, 0x2b, 0xcd, 0x05, 0x2e, 0x27, 0x1b, 0x7d, 0xa9, 0x4d, 0x13, 0x3a, 0x83, 0x68,
0x6c, 0x2f, 0x44, 0xba, 0x58, 0x0f, 0xc7, 0xb3, 0xa2, 0xf2, 0xac, 0x48, 0x7b, 0x11, 0xe6, 0x98,
0xf7, 0x7e, 0x22, 0x21, 0x09, 0x0a, 0xb2, 0x61, 0xfe, 0x28, 0x45, 0x47, 0x3a, 0xcb, 0x5c, 0x47,
0x7a, 0x6d, 0x9a, 0x6e, 0x93, 0x36, 0x9d, 0x96, 0xde, 0xcf, 0xb2, 0x24, 0x99, 0x16, 0x60, 0xa5,
0x99, 0xfd, 0x3f, 0xca, 0xef, 0x35, 0x9e, 0xa3, 0x64, 0x7f, 0xdc, 0x4f, 0xb2, 0x0c, 0x66, 0x1f,
0xa8, 0xbc, 0x53, 0x8f, 0xa7, 0x59, 0xe3, 0x12, 0xd2, 0x81, 0x13, 0xed, 0x7d, 0x2a, 0xd7, 0x03,
0xb8, 0xac, 0xa3, 0x8d, 0x7a, 0x7f, 0xbe, 0xdf, 0xd7, 0x87, 0x7f, 0x2a, 0x88, 0xed, 0x8e, 0xc1,
0xa3, 0xc5, 0x44, 0xe9, 0x7e, 0x69, 0x68, 0xb3, 0x2f, 0xc8, 0x32, 0x18, 0x6d, 0x26, 0x9a, 0x73,
0xae, 0x70, 0xe3, 0xa4, 0xde, 0x5c, 0x08, 0xa5, 0x99, 0xac, 0xe3, 0x2a, 0x8a, 0x54, 0x14, 0x5a,
0x1d, 0xfe, 0x83, 0xdb, 0xcd, 0xe4, 0xd9, 0x89, 0x52, 0x55, 0x17, 0x00, 0x0d, 0x27, 0x18, 0xec,
0x46, 0xc7, 0x2d, 0x55, 0xea, 0x50, 0x12, 0x36, 0x02, 0xd2, 0x06, 0x27, 0x84, 0xf4, 0x80, 0xc1,
0x34, 0x3c, 0x88, 0x9c, 0xd6, 0xb4, 0xbd, 0x67, 0xc4, 0x55, 0x5e, 0x39, 0x49, 0x93, 0x32, 0x58,
0x4a, 0x93, 0x93, 0xd1, 0x7d, 0x98, 0x89, 0x3e, 0x0f, 0x0a, 0x5e, 0xc7, 0xa5, 0xca, 0x5d, 0xfe,
0x16, 0xca, 0x05, 0x26, 0xfa, 0x0e, 0xa9, 0x33, 0x3f, 0x2b, 0x30, 0x32, 0x1e, 0x59, 0x30, 0xff,
0xa8, 0xe3, 0x51, 0x27, 0xef, 0xd4, 0x9f, 0x11, 0xb7, 0x91, 0x3f, 0xa4, 0x24, 0x50, 0x5e, 0xe7,
0x22, 0x52, 0xfb, 0xfd, 0x21, 0x83, 0xe8, 0xdb, 0x02, 0xa3, 0x6f, 0x33, 0x90, 0x86, 0xd3, 0x44,
0x56, 0x4a, 0x2a, 0x3e, 0x79, 0xec, 0x51, 0xa2, 0xdc, 0x4f, 0x3e, 0x57, 0x6d, 0x9f, 0xe8, 0xcf,
0x3d, 0xb6, 0x3b, 0x11, 0x46, 0xde, 0x11, 0xcf, 0xf7, 0x3b, 0x6d, 0xca, 0xbb, 0x1a, 0xe5, 0xfd,
0xe4, 0x31, 0x3e, 0xda, 0x11, 0x81, 0xd2, 0x79, 0x1f, 0x24, 0xed, 0x88, 0x44, 0x46, 0x37, 0x61,
0xdc, 0xf2, 0x76, 0x77, 0x89, 0xaf, 0x3c, 0xe4, 0x1b, 0x3b, 0xdf, 0xeb, 0xaa, 0x33, 0xe1, 0x45,
0xe7, 0x76, 0x0d, 0x87, 0x00, 0x74, 0x17, 0x26, 0x2d, 0x6f, 0xb7, 0xdc, 0xa1, 0xed, 0x0e, 0x55,
0xd6, 0xf9, 0x75, 0x96, 0x6a, 0x6b, 0xcb, 0xdb, 0xd5, 0x3d, 0xee, 0xd3, 0x70, 0x1f, 0xc7, 0x3a,
0xdb, 0x35, 0xb2, 0xdd, 0xd9, 0x55, 0x8a, 0x3c, 0x4a, 0xa9, 0xb3, 0x6d, 0x30, 0xb3, 0x86, 0x85,
0x7b, 0xf5, 0xa7, 0x59, 0xe9, 0x67, 0x64, 0x34, 0x07, 0x53, 0xa5, 0xb2, 0x5d, 0xab, 0xda, 0x06,
0xb6, 0xcd, 0xb5, 0xdc, 0x19, 0x74, 0x1e, 0x50, 0xb1, 0x54, 0xb4, 0x8b, 0x86, 0x25, 0x8c, 0x35,
0xd3, 0x2e, 0xac, 0xe5, 0x00, 0xe5, 0x60, 0x1a, 0x9b, 0x92, 0x65, 0x8a, 0x59, 0xaa, 0xc5, 0x87,
0xb6, 0x89, 0x37, 0x85, 0xe5, 0x1c, 0x5a, 0x86, 0x2b, 0xd5, 0xe2, 0xc3, 0x47, 0x5b, 0x45, 0x81,
0xa9, 0x19, 0xa5, 0xb5, 0x1a, 0x36, 0x37, 0xcb, 0x8f, 0xcd, 0xda, 0x9a, 0x61, 0x1b, 0xb9, 0x45,
0x34, 0x0f, 0x33, 0x55, 0xe3, 0xb1, 0x59, 0xab, 0x96, 0x8c, 0x4a, 0x75, 0xbd, 0x6c, 0xe7, 0x96,
0xd0, 0x35, 0xb8, 0xca, 0x84, 0xcb, 0xd8, 0xac, 0x45, 0x13, 0x3c, 0xc0, 0xe5, 0xcd, 0x3e, 0x44,
0x45, 0x17, 0x61, 0x71, 0xb0, 0x6b, 0x99, 0xb1, 0x53, 0x53, 0x1a, 0xb8, 0xb0, 0x5e, 0x8c, 0xe6,
0x5c, 0x41, 0xb7, 0xe1, 0x95, 0xe3, 0xa2, 0xe2, 0xe3, 0xaa, 0x5d, 0xae, 0xd4, 0x8c, 0x87, 0x66,
0xc9, 0xce, 0xdd, 0x44, 0x57, 0xe1, 0x62, 0xde, 0x32, 0x0a, 0x1b, 0xeb, 0x65, 0xcb, 0xac, 0x55,
0x4c, 0x13, 0xd7, 0x2a, 0x65, 0x6c, 0xd7, 0xec, 0xa7, 0x35, 0xfc, 0x34, 0xd7, 0x40, 0x2a, 0x5c,
0xde, 0x2a, 0x0d, 0x07, 0x10, 0x74, 0x09, 0x16, 0xd7, 0x4c, 0xcb, 0xf8, 0x20, 0xe5, 0x7a, 0x91,
0x41, 0x57, 0xe0, 0xc2, 0x56, 0x69, 0xb0, 0xf7, 0xb3, 0xcc, 0xea, 0xdf, 0x00, 0x46, 0xd9, 0xf7,
0x07, 0x52, 0xe0, 0x5c, 0xb4, 0xb7, 0xe5, 0x92, 0x59, 0x7b, 0x50, 0xb6, 0xac, 0xf2, 0x13, 0x13,
0xe7, 0xce, 0x84, 0xab, 0x49, 0x79, 0x6a, 0x5b, 0x25, 0xbb, 0x68, 0xd5, 0x6c, 0x5c, 0x7c, 0xf8,
0xd0, 0xc4, 0xfd, 0x1d, 0xca, 0x20, 0x04, 0xb3, 0x11, 0xc1, 0x32, 0x8d, 0x35, 0x13, 0xe7, 0x46,
0xd0, 0x4d, 0xb8, 0x11, 0xb7, 0x0d, 0xa3, 0x67, 0x65, 0xfa, 0xa3, 0xad, 0x32, 0xde, 0xda, 0xcc,
0x8d, 0xb2, 0x43, 0x13, 0xd9, 0x0c, 0xcb, 0xca, 0x8d, 0xa1, 0xeb, 0xa0, 0x46, 0x5b, 0x2c, 0xed,
0x6e, 0x2c, 0x72, 0x40, 0xf7, 0xe0, 0x8d, 0x13, 0x40, 0xc3, 0xa2, 0x98, 0x62, 0x29, 0x19, 0xc0,
0x0d, 0xd7, 0x33, 0x8d, 0x5e, 0x87, 0xd7, 0x86, 0xba, 0x87, 0x89, 0xce, 0xa0, 0x07, 0x90, 0x1f,
0xc0, 0x12, 0xab, 0x0c, 0x2d, 0xe2, 0x5c, 0x86, 0x42, 0x11, 0x35, 0x3c, 0x84, 0x05, 0x6c, 0xd8,
0x85, 0xf5, 0xdc, 0x2c, 0x5a, 0x85, 0x97, 0x87, 0x1e, 0x87, 0xf8, 0x26, 0x34, 0x90, 0x01, 0xef,
0x9e, 0x0e, 0x3b, 0x2c, 0x6c, 0x82, 0x5e, 0x82, 0xe5, 0xe1, 0x12, 0xe1, 0x96, 0xec, 0xa0, 0x77,
0xe0, 0xcd, 0x93, 0x50, 0xc3, 0xa6, 0xd8, 0x3d, 0x7e, 0x8a, 0xf0, 0x18, 0xec, 0xb1, 0xbb, 0x37,
0x1c, 0xc5, 0x0e, 0x46, 0x13, 0xfd, 0x1f, 0x68, 0x03, 0x0f, 0x7b, 0x7c, 0x5b, 0x5e, 0x64, 0xd0,
0x2d, 0xb8, 0x89, 0x8d, 0xd2, 0x5a, 0x79, 0xb3, 0x76, 0x0a, 0xfc, 0x67, 0x19, 0xf4, 0x1e, 0xbc,
0x7d, 0x32, 0x70, 0xd8, 0x02, 0x3f, 0xcf, 0x20, 0x13, 0xde, 0x3f, 0xf5, 0x7c, 0xc3, 0x64, 0xbe,
0xc8, 0xa0, 0x6b, 0x70, 0x65, 0x30, 0x3f, 0xcc, 0xc3, 0x97, 0x19, 0xb4, 0x02, 0xd7, 0x8f, 0x9d,
0x29, 0x44, 0x7e, 0x95, 0x41, 0x6f, 0xc1, 0xdd, 0xe3, 0x20, 0xc3, 0xc2, 0xf8, 0x75, 0x06, 0xdd,
0x87, 0x7b, 0xa7, 0x98, 0x63, 0x98, 0xc0, 0x6f, 0x8e, 0x59, 0x47, 0x98, 0xec, 0xaf, 0x4f, 0x5e,
0x47, 0x88, 0xfc, 0x6d, 0x06, 0x2d, 0xc1, 0xc5, 0xc1, 0x10, 0x76, 0x26, 0x7e, 0x97, 0x41, 0x37,
0x60, 0xf9, 0x58, 0x25, 0x06, 0xfb, 0x7d, 0x06, 0x29, 0xb0, 0x50, 0x2a, 0xd7, 0x1e, 0x18, 0x45,
0xab, 0xf6, 0xa4, 0x68, 0xaf, 0xd7, 0xaa, 0x36, 0x36, 0xab, 0xd5, 0xdc, 0x2f, 0x46, 0x58, 0x28,
0x31, 0x4f, 0xa9, 0x1c, 0x3a, 0x6b, 0x0f, 0xca, 0xb8, 0x66, 0x15, 0x1f, 0x9b, 0x25, 0x86, 0xfc,
0x74, 0x04, 0xcd, 0x01, 0x30, 0x58, 0xa5, 0x5c, 0x2c, 0xd9, 0xd5, 0xdc, 0xf7, 0xb2, 0x68, 0x06,
0x26, 0xcc, 0xa7, 0xb6, 0x89, 0x4b, 0x86, 0x95, 0xfb, 0x7b, 0x76, 0x75, 0x1f, 0x26, 0xa2, 0x9f,
0x38, 0xd0, 0x38, 0x8c, 0x6c, 0x3c, 0xce, 0x9d, 0x41, 0x93, 0x30, 0x66, 0x99, 0x46, 0xd5, 0xcc,
0x65, 0xd0, 0x02, 0xcc, 0x99, 0x96, 0x59, 0xb0, 0x8b, 0xe5, 0x52, 0x0d, 0x6f, 0x95, 0x4a, 0xfc,
0xf1, 0xcc, 0xc1, 0xf4, 0x13, 0x76, 0xf3, 0x23, 0x4b, 0x16, 0x2d, 0xc2, 0xbc, 0x55, 0x2e, 0x6c,
0xd4, 0xb0, 0x51, 0x30, 0x71, 0x64, 0x1e, 0x65, 0x40, 0x2e, 0x14, 0x59, 0xc6, 0x56, 0xf3, 0x70,
0x36, 0xfc, 0x7d, 0x04, 0x4d, 0xc1, 0xd9, 0x8d, 0xc7, 0xb5, 0x75, 0xa3, 0xba, 0x9e, 0x3b, 0xd3,
0x47, 0x9a, 0x4f, 0x2b, 0x45, 0xcc, 0x66, 0x06, 0x18, 0x3f, 0x9a, 0x70, 0x1a, 0x26, 0x4a, 0xe5,
0x5a, 0x61, 0xdd, 0x2c, 0x6c, 0xe4, 0xb2, 0x77, 0xee, 0xc3, 0xa4, 0xed, 0x3b, 0x6e, 0xd0, 0xf6,
0x7c, 0x8a, 0xee, 0xc8, 0x83, 0xd9, 0xf0, 0x57, 0xda, 0xf0, 0xef, 0xc5, 0x97, 0xe6, 0x8e, 0xc6,
0xe2, 0x4f, 0x89, 0xda, 0x99, 0x95, 0xcc, 0x6b, 0x99, 0xfc, 0xb9, 0x17, 0x7f, 0x5e, 0x3a, 0xf3,
0xe2, 0x9b, 0xa5, 0xcc, 0xd7, 0xdf, 0x2c, 0x65, 0xfe, 0xf4, 0xcd, 0x52, 0xe6, 0x27, 0x7f, 0x59,
0x3a, 0xb3, 0x3d, 0xce, 0xff, 0xde, 0x7c, 0xf7, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xf0, 0xdc,
0x19, 0xd8, 0xb8, 0x1e, 0x00, 0x00,
0xee, 0x39, 0xdd, 0xb6, 0xe9, 0xe3, 0xb4, 0xe7, 0x74, 0xd5, 0x75, 0xd3, 0xfe, 0x1b, 0xce, 0xab,
0x4d, 0xdb, 0x55, 0xbb, 0xe0, 0x69, 0xd3, 0x4d, 0x57, 0x5d, 0xf0, 0xf4, 0xbd, 0xea, 0x99, 0x19,
0x40, 0x1c, 0x00, 0xa4, 0xa4, 0x95, 0x3d, 0xf7, 0x7e, 0xdf, 0x37, 0x77, 0xe6, 0xce, 0xcc, 0xbd,
0xa0, 0x60, 0xce, 0x6f, 0xd7, 0xdb, 0xdb, 0xb7, 0xfd, 0x76, 0xfd, 0x56, 0xdb, 0xf7, 0xa8, 0x87,
0xc6, 0xb8, 0xe1, 0x92, 0xbe, 0xdb, 0xa4, 0x7b, 0x9d, 0xed, 0x5b, 0x75, 0x6f, 0xff, 0xf6, 0xae,
0xb7, 0xeb, 0xdd, 0xe6, 0xde, 0xed, 0xce, 0x0e, 0x1f, 0xf1, 0x01, 0xff, 0x9f, 0x60, 0x69, 0xdf,
0xcd, 0xc0, 0x59, 0x4c, 0x3e, 0xec, 0x90, 0x80, 0xa2, 0x5b, 0x30, 0x59, 0x6e, 0x13, 0xdf, 0xa1,
0x4d, 0xcf, 0x55, 0x32, 0xcb, 0x99, 0x95, 0xd9, 0x3b, 0xb9, 0x5b, 0x5c, 0xf5, 0xd6, 0x91, 0x1d,
0xf7, 0x21, 0xe8, 0x06, 0x8c, 0x6f, 0x92, 0xfd, 0x6d, 0xe2, 0x2b, 0x23, 0xcb, 0x99, 0x95, 0xa9,
0x3b, 0x33, 0x21, 0x58, 0x18, 0x71, 0xe8, 0x64, 0x30, 0x9b, 0x04, 0x94, 0xf8, 0x4a, 0x36, 0x06,
0x13, 0x46, 0x1c, 0x3a, 0xb5, 0xbf, 0x8e, 0xc0, 0x74, 0xd5, 0x75, 0xda, 0xc1, 0x9e, 0x47, 0x8b,
0xee, 0x8e, 0x87, 0x96, 0x00, 0x84, 0x42, 0xc9, 0xd9, 0x27, 0x3c, 0x9e, 0x49, 0x2c, 0x59, 0xd0,
0x2a, 0xe4, 0xc4, 0xa8, 0xd0, 0x6a, 0x12, 0x97, 0x6e, 0x61, 0x2b, 0x50, 0x46, 0x96, 0xb3, 0x2b,
0x93, 0x38, 0x65, 0x47, 0x5a, 0x5f, 0xbb, 0xe2, 0xd0, 0x3d, 0x1e, 0xc9, 0x24, 0x8e, 0xd9, 0x98,
0x5e, 0x34, 0x7e, 0xd0, 0x6c, 0x91, 0x6a, 0xf3, 0x63, 0xa2, 0x8c, 0x72, 0x5c, 0xca, 0x8e, 0x5e,
0x85, 0xf9, 0xc8, 0x66, 0x7b, 0xd4, 0x69, 0x71, 0xf0, 0x18, 0x07, 0xa7, 0x1d, 0xb2, 0x32, 0x37,
0x6e, 0x90, 0x43, 0x65, 0x7c, 0x39, 0xb3, 0x92, 0xc5, 0x29, 0xbb, 0x1c, 0xe9, 0xba, 0x13, 0xec,
0x29, 0x67, 0x39, 0x2e, 0x66, 0x93, 0xf5, 0x30, 0x79, 0xde, 0x0c, 0x58, 0xbe, 0x26, 0xe2, 0x7a,
0x91, 0x1d, 0x21, 0x18, 0xb5, 0x3d, 0xef, 0x99, 0x32, 0xc9, 0x83, 0xe3, 0xff, 0xd7, 0x7e, 0x96,
0x81, 0x09, 0x4c, 0x82, 0xb6, 0xe7, 0x06, 0x04, 0x29, 0x70, 0xb6, 0xda, 0xa9, 0xd7, 0x49, 0x10,
0xf0, 0x3d, 0x9e, 0xc0, 0xd1, 0x10, 0x9d, 0x87, 0xf1, 0x2a, 0x75, 0x68, 0x27, 0xe0, 0xf9, 0x9d,
0xc4, 0xe1, 0x48, 0xca, 0x7b, 0xf6, 0xb8, 0xbc, 0xbf, 0x19, 0xcf, 0x27, 0xdf, 0xcb, 0xa9, 0x3b,
0x0b, 0x21, 0x58, 0x76, 0xe1, 0x18, 0x50, 0xfb, 0x64, 0x3a, 0x9a, 0x00, 0xbd, 0x06, 0x13, 0x26,
0xad, 0x37, 0xcc, 0x03, 0x52, 0x17, 0x27, 0x20, 0x7f, 0xae, 0xd7, 0x55, 0x73, 0x87, 0xce, 0x7e,
0xeb, 0x9e, 0x46, 0x68, 0xbd, 0xa1, 0x93, 0x03, 0x52, 0xd7, 0xf0, 0x11, 0x0a, 0xdd, 0x85, 0x49,
0x63, 0x97, 0xb8, 0xd4, 0x68, 0x34, 0x7c, 0x65, 0x8a, 0x53, 0x16, 0x7b, 0x5d, 0x75, 0x5e, 0x50,
0x1c, 0xe6, 0xd2, 0x9d, 0x46, 0xc3, 0xd7, 0x70, 0x1f, 0x87, 0x2c, 0x98, 0x7f, 0xe0, 0x34, 0x5b,
0x6d, 0xaf, 0xe9, 0xd2, 0x75, 0xdb, 0xae, 0x70, 0xf2, 0x34, 0x27, 0x2f, 0xf5, 0xba, 0xea, 0x25,
0x41, 0xde, 0x89, 0x20, 0xfa, 0x1e, 0xa5, 0xed, 0x50, 0x25, 0x4d, 0x44, 0x3a, 0x9c, 0xcd, 0x3b,
0x01, 0x59, 0x6b, 0xfa, 0x0a, 0xe1, 0x1a, 0x0b, 0xbd, 0xae, 0x3a, 0x27, 0x34, 0xb6, 0x9d, 0x80,
0xe8, 0x8d, 0xa6, 0xaf, 0xe1, 0x08, 0x83, 0x1e, 0xc2, 0x1c, 0x8b, 0x5e, 0x9c, 0xd6, 0x8a, 0xef,
0x1d, 0x1c, 0x2a, 0x9f, 0xf1, 0x4c, 0xe4, 0xaf, 0xf4, 0xba, 0xaa, 0x22, 0xad, 0xb5, 0xce, 0x21,
0x7a, 0x9b, 0x61, 0x34, 0x9c, 0x64, 0x21, 0x03, 0x66, 0x98, 0xa9, 0x42, 0x88, 0x2f, 0x64, 0x3e,
0x17, 0x32, 0x97, 0x7a, 0x5d, 0xf5, 0xbc, 0x24, 0xd3, 0x26, 0xc4, 0x8f, 0x44, 0xe2, 0x0c, 0x54,
0x01, 0xd4, 0x57, 0x35, 0xdd, 0x06, 0x5f, 0x98, 0xf2, 0x29, 0xcf, 0x7f, 0x5e, 0xed, 0x75, 0xd5,
0xcb, 0xe9, 0x70, 0x48, 0x08, 0xd3, 0xf0, 0x00, 0x2e, 0xfa, 0x7f, 0x18, 0x65, 0x56, 0xe5, 0x97,
0xe2, 0x8d, 0x98, 0x0a, 0xd3, 0xcf, 0x6c, 0xf9, 0xb9, 0x5e, 0x57, 0x9d, 0xea, 0x0b, 0x6a, 0x98,
0x43, 0x51, 0x1e, 0x16, 0xd9, 0xbf, 0x65, 0xb7, 0x7f, 0x98, 0x03, 0xea, 0xf9, 0x44, 0xf9, 0x55,
0x5a, 0x03, 0x0f, 0x86, 0xa2, 0x35, 0x98, 0x15, 0x81, 0x14, 0x88, 0x4f, 0xd7, 0x1c, 0xea, 0x28,
0xdf, 0xe7, 0x77, 0x3e, 0x7f, 0xb9, 0xd7, 0x55, 0x2f, 0x88, 0x39, 0xc3, 0xf8, 0xeb, 0xc4, 0xa7,
0x7a, 0xc3, 0xa1, 0x8e, 0x86, 0x13, 0x9c, 0xb8, 0x0a, 0x7f, 0x38, 0x3e, 0x39, 0x56, 0xa5, 0xed,
0xd0, 0xbd, 0x98, 0x0a, 0x7f, 0x58, 0x0c, 0x98, 0x11, 0x96, 0x0d, 0x72, 0xc8, 0x43, 0xf9, 0x81,
0x10, 0x91, 0xf2, 0x12, 0x8a, 0x3c, 0x23, 0x87, 0x61, 0x24, 0x71, 0x46, 0x4c, 0x82, 0xc7, 0xf1,
0xc3, 0xe3, 0x24, 0x44, 0x18, 0x71, 0x06, 0xb2, 0x61, 0x41, 0x18, 0x6c, 0xbf, 0x13, 0x50, 0xd2,
0x28, 0x18, 0x3c, 0x96, 0x1f, 0x09, 0xa1, 0x6b, 0xbd, 0xae, 0x7a, 0x35, 0x26, 0x44, 0x05, 0x4c,
0xaf, 0x3b, 0x61, 0x48, 0x83, 0xe8, 0x03, 0x54, 0x79, 0x78, 0x3f, 0x3e, 0x85, 0xaa, 0x88, 0x72,
0x10, 0x1d, 0xbd, 0x07, 0xd3, 0xec, 0x4c, 0x1e, 0xe5, 0xee, 0x1f, 0x42, 0xee, 0x62, 0xaf, 0xab,
0x2e, 0x0a, 0x39, 0x7e, 0x86, 0xa5, 0xcc, 0xc5, 0xf0, 0x32, 0x9f, 0x87, 0xf3, 0xcf, 0x63, 0xf8,
0x22, 0x8c, 0x18, 0x1e, 0xbd, 0x03, 0x53, 0x6c, 0x1c, 0xe5, 0xeb, 0x5f, 0x82, 0xae, 0xf4, 0xba,
0xea, 0x39, 0x89, 0xde, 0xcf, 0x96, 0x8c, 0x96, 0xc8, 0x7c, 0xee, 0x7f, 0x0f, 0x27, 0x8b, 0xa9,
0x65, 0x34, 0x2a, 0xc1, 0x3c, 0x1b, 0xc6, 0x73, 0xf4, 0x9f, 0x6c, 0xf2, 0xfe, 0x71, 0x89, 0x54,
0x86, 0xd2, 0xd4, 0x94, 0x1e, 0x0f, 0xe9, 0xbf, 0x27, 0xea, 0x89, 0xc8, 0xd2, 0x54, 0xf4, 0x6e,
0xa2, 0x90, 0xfe, 0x61, 0x34, 0xb9, 0xba, 0x20, 0x74, 0x47, 0x1b, 0x1b, 0xab, 0xb1, 0x6f, 0x25,
0x6a, 0xc2, 0x1f, 0x4f, 0x5d, 0x14, 0x7e, 0x3e, 0x1d, 0xb5, 0x11, 0xec, 0x7d, 0x65, 0x6b, 0x63,
0xef, 0x6b, 0x26, 0xf9, 0xbe, 0xb2, 0x8d, 0x08, 0xdf, 0xd7, 0x10, 0x83, 0x5e, 0x85, 0xb3, 0x25,
0x42, 0x3f, 0xf2, 0xfc, 0x67, 0xa2, 0x8e, 0xe5, 0x51, 0xaf, 0xab, 0xce, 0x0a, 0xb8, 0x2b, 0x1c,
0x1a, 0x8e, 0x20, 0xe8, 0x3a, 0x8c, 0xf2, 0xd7, 0x5f, 0x6c, 0x91, 0xf4, 0x42, 0x89, 0xe7, 0x9e,
0x3b, 0x51, 0x01, 0x66, 0xd7, 0x48, 0xcb, 0x39, 0xb4, 0x1c, 0x4a, 0xdc, 0xfa, 0xe1, 0x66, 0xc0,
0x2b, 0xcd, 0x8c, 0xfc, 0x2c, 0x34, 0x98, 0x5f, 0x6f, 0x09, 0x80, 0xbe, 0x1f, 0x68, 0x38, 0x41,
0x41, 0xdf, 0x86, 0x5c, 0xdc, 0x82, 0x9f, 0xf3, 0x9a, 0x33, 0x23, 0xd7, 0x9c, 0xa4, 0x8c, 0xee,
0x3f, 0xd7, 0x70, 0x8a, 0x87, 0x3e, 0x80, 0xc5, 0xad, 0x76, 0xc3, 0xa1, 0xa4, 0x91, 0x88, 0x6b,
0x86, 0x0b, 0x5e, 0xef, 0x75, 0x55, 0x55, 0x08, 0x76, 0x04, 0x4c, 0x4f, 0xc7, 0x37, 0x58, 0x01,
0xbd, 0x01, 0x80, 0xbd, 0x8e, 0xdb, 0xb0, 0x9a, 0xfb, 0x4d, 0xaa, 0x2c, 0x2e, 0x67, 0x56, 0xc6,
0xf2, 0xe7, 0x7b, 0x5d, 0x15, 0x09, 0x3d, 0x9f, 0xf9, 0xf4, 0x16, 0x73, 0x6a, 0x58, 0x42, 0xa2,
0x3c, 0xcc, 0x9a, 0x07, 0x4d, 0x5a, 0x76, 0x0b, 0x4e, 0x40, 0x58, 0x91, 0x54, 0xce, 0xa7, 0xaa,
0xd1, 0x41, 0x93, 0xea, 0x9e, 0xab, 0xb3, 0xc2, 0xda, 0xf1, 0x89, 0x86, 0x13, 0x0c, 0xf4, 0x36,
0x4c, 0x99, 0xae, 0xb3, 0xdd, 0x22, 0x95, 0xb6, 0xef, 0xed, 0x28, 0x17, 0xb8, 0xc0, 0x85, 0x5e,
0x57, 0x5d, 0x08, 0x05, 0xb8, 0x53, 0x6f, 0x33, 0xaf, 0x86, 0x65, 0x2c, 0xba, 0x07, 0x53, 0x4c,
0x86, 0x2f, 0x66, 0x33, 0x50, 0x54, 0xbe, 0x0f, 0xd2, 0x31, 0xad, 0xf3, 0x42, 0xcc, 0x37, 0x81,
0x2d, 0x5e, 0x06, 0xb3, 0x69, 0xd9, 0xb0, 0xba, 0xd7, 0xd9, 0xd9, 0x69, 0x11, 0x65, 0x39, 0x39,
0x2d, 0xe7, 0x06, 0xc2, 0x1b, 0x52, 0x43, 0x2c, 0x7a, 0x19, 0xc6, 0xd8, 0x30, 0x50, 0xae, 0xb1,
0x4e, 0x34, 0x9f, 0xeb, 0x75, 0xd5, 0xe9, 0x3e, 0x29, 0xd0, 0xb0, 0x70, 0xa3, 0x0d, 0xa9, 0xe3,
0x28, 0x78, 0xfb, 0xfb, 0x8e, 0xdb, 0x08, 0x14, 0x8d, 0x73, 0xae, 0xf6, 0xba, 0xea, 0xc5, 0x64,
0xc7, 0x51, 0x0f, 0x31, 0x72, 0xc3, 0x11, 0xf1, 0xd8, 0x71, 0xc4, 0x1d, 0xd7, 0x25, 0x3e, 0xeb,
0x80, 0xf8, 0xb5, 0xbc, 0x99, 0xac, 0x52, 0x3e, 0xf7, 0xf3, 0x6e, 0x29, 0xaa, 0x52, 0x71, 0x0a,
0x2a, 0x42, 0xce, 0x3c, 0xa0, 0xc4, 0x77, 0x9d, 0xd6, 0x91, 0xcc, 0x2a, 0x97, 0x91, 0x02, 0x22,
0x21, 0x42, 0x16, 0x4a, 0xd1, 0xd0, 0x1d, 0x98, 0xac, 0x52, 0x9f, 0x04, 0x01, 0xf1, 0x03, 0x85,
0xf0, 0x45, 0x49, 0x6d, 0x5b, 0x10, 0xb9, 0x34, 0xdc, 0x87, 0xa1, 0xdb, 0x30, 0x51, 0xd8, 0x23,
0xf5, 0x67, 0x8c, 0xb2, 0xc3, 0x29, 0xd2, 0xad, 0xae, 0x87, 0x1e, 0x0d, 0x1f, 0x81, 0x58, 0x49,
0x14, 0xec, 0x0d, 0x72, 0xc8, 0xdb, 0x6f, 0xde, 0x34, 0x8d, 0xc9, 0xe7, 0x4b, 0xcc, 0xc4, 0x9f,
0xda, 0xa0, 0xf9, 0x31, 0xd1, 0x70, 0x9c, 0x81, 0x1e, 0x01, 0x8a, 0x19, 0x2c, 0xc7, 0xdf, 0x25,
0xa2, 0x6b, 0x1a, 0xcb, 0x2f, 0xf7, 0xba, 0xea, 0x95, 0x81, 0x3a, 0x7a, 0x8b, 0xe1, 0x34, 0x3c,
0x80, 0x8c, 0x9e, 0xc0, 0xb9, 0xbe, 0xb5, 0xb3, 0xb3, 0xd3, 0x3c, 0xc0, 0x8e, 0xbb, 0x4b, 0x94,
0x2f, 0x84, 0xa8, 0xd6, 0xeb, 0xaa, 0x4b, 0x69, 0x51, 0x0e, 0xd4, 0x7d, 0x86, 0xd4, 0xf0, 0x40,
0x01, 0xe4, 0xc0, 0x85, 0x41, 0x76, 0xfb, 0xc0, 0x55, 0xbe, 0x14, 0xda, 0x2f, 0xf7, 0xba, 0xaa,
0x76, 0xac, 0xb6, 0x4e, 0x0f, 0x5c, 0x0d, 0x0f, 0xd3, 0x41, 0xeb, 0x30, 0x77, 0xe4, 0xb2, 0x0f,
0xdc, 0x72, 0x3b, 0x50, 0xbe, 0x12, 0xd2, 0xd2, 0x09, 0x90, 0xa4, 0xe9, 0x81, 0xab, 0x7b, 0xed,
0x40, 0xc3, 0x49, 0x1a, 0x7a, 0x3f, 0xca, 0x8d, 0x28, 0xee, 0x81, 0xe8, 0x20, 0xc7, 0xe4, 0x02,
0x1c, 0xea, 0x88, 0xb6, 0x20, 0x38, 0x4a, 0x4d, 0x48, 0x40, 0xaf, 0x47, 0x47, 0xe8, 0x51, 0xa5,
0x2a, 0x7a, 0xc7, 0x31, 0xb9, 0x8f, 0x0f, 0xd9, 0x1f, 0xb6, 0xfb, 0x87, 0xe8, 0x51, 0xa5, 0xaa,
0x7d, 0x67, 0x4e, 0x74, 0x9b, 0xec, 0x15, 0xef, 0x7f, 0x35, 0xca, 0xaf, 0xb8, 0xeb, 0xec, 0x13,
0x0d, 0x73, 0xa7, 0x5c, 0x47, 0x46, 0x4e, 0x51, 0x47, 0x56, 0x61, 0xfc, 0x89, 0x61, 0x31, 0x74,
0x36, 0x59, 0x46, 0x3e, 0x72, 0x5a, 0x02, 0x1c, 0x22, 0x50, 0x19, 0x16, 0xd6, 0x89, 0xe3, 0xd3,
0x6d, 0xe2, 0xd0, 0xa2, 0x4b, 0x89, 0xff, 0xdc, 0x69, 0x85, 0x55, 0x22, 0x2b, 0xef, 0xe6, 0x5e,
0x04, 0xd2, 0x9b, 0x21, 0x4a, 0xc3, 0x83, 0x98, 0xa8, 0x08, 0xf3, 0x66, 0x8b, 0xd4, 0xd9, 0x77,
0xb7, 0xdd, 0xdc, 0x27, 0x5e, 0x87, 0x6e, 0x06, 0xbc, 0x5a, 0x64, 0xe5, 0x5b, 0x4e, 0x42, 0x88,
0x4e, 0x05, 0x46, 0xc3, 0x69, 0x16, 0xbb, 0xe8, 0x56, 0x33, 0xa0, 0xc4, 0x95, 0xbe, 0x9b, 0x17,
0x93, 0x2f, 0x4f, 0x8b, 0x23, 0xa2, 0x16, 0xbf, 0xe3, 0xb7, 0x02, 0x0d, 0xa7, 0x68, 0x08, 0xc3,
0x82, 0xd1, 0x78, 0x4e, 0x7c, 0xda, 0x0c, 0x88, 0xa4, 0x76, 0x9e, 0xab, 0x49, 0x17, 0xc8, 0x89,
0x40, 0x71, 0xc1, 0x41, 0x64, 0xf4, 0x76, 0xd4, 0xea, 0x1a, 0x1d, 0xea, 0xd9, 0x56, 0x35, 0x7c,
0xf5, 0xa5, 0xdc, 0x38, 0x1d, 0xea, 0xe9, 0x94, 0x09, 0xc4, 0x91, 0xec, 0x1d, 0xec, 0xb7, 0xde,
0x46, 0x87, 0xee, 0x29, 0x0a, 0xe7, 0x0e, 0xe9, 0xd6, 0x9d, 0x4e, 0xa2, 0x5b, 0x67, 0x14, 0xf4,
0x2d, 0x59, 0x84, 0x7d, 0xf0, 0x2b, 0x17, 0x93, 0x1f, 0x9e, 0x9c, 0xbd, 0xd3, 0x64, 0x8f, 0x7f,
0x02, 0xdb, 0x8f, 0x7e, 0x83, 0x1c, 0x72, 0xf2, 0xa5, 0xe4, 0xc9, 0x62, 0x37, 0x47, 0x70, 0xe3,
0x48, 0x64, 0xa5, 0x5a, 0x69, 0x2e, 0x70, 0x39, 0xd9, 0xe8, 0x4b, 0x6d, 0x9a, 0xd0, 0x19, 0x44,
0x63, 0x7b, 0x21, 0xd2, 0xc5, 0x7a, 0x38, 0x9e, 0x15, 0x95, 0x67, 0x45, 0xda, 0x8b, 0x30, 0xc7,
0xbc, 0xf7, 0x13, 0x09, 0x49, 0x50, 0x90, 0x0d, 0xf3, 0x47, 0x29, 0x3a, 0xd2, 0x59, 0xe6, 0x3a,
0xd2, 0x6b, 0xd3, 0x74, 0x9b, 0xb4, 0xe9, 0xb4, 0xf4, 0x7e, 0x96, 0x25, 0xc9, 0xb4, 0x00, 0x2b,
0xcd, 0xec, 0xff, 0x51, 0x7e, 0xaf, 0xf1, 0x1c, 0x25, 0xfb, 0xe3, 0x7e, 0x92, 0x65, 0x30, 0xfb,
0x40, 0xe5, 0x9d, 0x7a, 0x3c, 0xcd, 0x1a, 0x97, 0x90, 0x0e, 0x9c, 0x68, 0xef, 0x53, 0xb9, 0x1e,
0xc0, 0x65, 0x1d, 0x6d, 0xd4, 0xfb, 0xf3, 0xfd, 0xbe, 0x3e, 0xfc, 0x53, 0x41, 0x6c, 0x77, 0x0c,
0x1e, 0x2d, 0x26, 0x4a, 0xf7, 0x4b, 0x43, 0x9b, 0x7d, 0x41, 0x96, 0xc1, 0x68, 0x33, 0xd1, 0x9c,
0x73, 0x85, 0x1b, 0x27, 0xf5, 0xe6, 0x42, 0x28, 0xcd, 0x64, 0x1d, 0x57, 0x51, 0xa4, 0xa2, 0xd0,
0xea, 0xf0, 0x1f, 0xdc, 0x6e, 0x26, 0xcf, 0x4e, 0x94, 0xaa, 0xba, 0x00, 0x68, 0x38, 0xc1, 0x60,
0x37, 0x3a, 0x6e, 0xa9, 0x52, 0x87, 0x92, 0xb0, 0x11, 0x90, 0x36, 0x38, 0x21, 0xa4, 0x07, 0x0c,
0xa6, 0xe1, 0x41, 0xe4, 0xb4, 0xa6, 0xed, 0x3d, 0x23, 0xae, 0xf2, 0xca, 0x49, 0x9a, 0x94, 0xc1,
0x52, 0x9a, 0x9c, 0x8c, 0xee, 0xc3, 0x4c, 0xf4, 0x79, 0x50, 0xf0, 0x3a, 0x2e, 0x55, 0xee, 0xf2,
0xb7, 0x50, 0x2e, 0x30, 0xd1, 0x77, 0x48, 0x9d, 0xf9, 0x59, 0x81, 0x91, 0xf1, 0xc8, 0x82, 0xf9,
0x47, 0x1d, 0x8f, 0x3a, 0x79, 0xa7, 0xfe, 0x8c, 0xb8, 0x8d, 0xfc, 0x21, 0x25, 0x81, 0xf2, 0x3a,
0x17, 0x91, 0xda, 0xef, 0x0f, 0x19, 0x44, 0xdf, 0x16, 0x18, 0x7d, 0x9b, 0x81, 0x34, 0x9c, 0x26,
0xb2, 0x52, 0x52, 0xf1, 0xc9, 0x63, 0x8f, 0x12, 0xe5, 0x7e, 0xf2, 0xb9, 0x6a, 0xfb, 0x44, 0x7f,
0xee, 0xb1, 0xdd, 0x89, 0x30, 0xf2, 0x8e, 0x78, 0xbe, 0xdf, 0x69, 0x53, 0xde, 0xd5, 0x28, 0xef,
0x27, 0x8f, 0xf1, 0xd1, 0x8e, 0x08, 0x94, 0xce, 0xfb, 0x20, 0x69, 0x47, 0x24, 0x32, 0xba, 0x09,
0xe3, 0x96, 0xb7, 0xbb, 0x4b, 0x7c, 0xe5, 0x21, 0xdf, 0xd8, 0xf9, 0x5e, 0x57, 0x9d, 0x09, 0x2f,
0x3a, 0xb7, 0x6b, 0x38, 0x04, 0xb0, 0x96, 0xde, 0xf2, 0x76, 0xcb, 0x1d, 0xda, 0xee, 0xd0, 0x40,
0x59, 0xe7, 0xf7, 0x59, 0x6a, 0xe9, 0x5b, 0xde, 0xae, 0xee, 0x09, 0xa7, 0x86, 0x25, 0x24, 0x6b,
0x6e, 0xd7, 0xc8, 0x76, 0x67, 0x57, 0x29, 0xf2, 0x40, 0xa5, 0xe6, 0xb6, 0xc1, 0xcc, 0x1a, 0x16,
0xee, 0xd5, 0x9f, 0x66, 0xa5, 0x5f, 0x92, 0xd1, 0x1c, 0x4c, 0x95, 0xca, 0x76, 0xad, 0x6a, 0x1b,
0xd8, 0x36, 0xd7, 0x72, 0x67, 0xd0, 0x79, 0x40, 0xc5, 0x52, 0xd1, 0x2e, 0x1a, 0x96, 0x30, 0xd6,
0x4c, 0xbb, 0xb0, 0x96, 0x03, 0x94, 0x83, 0x69, 0x6c, 0x4a, 0x96, 0x29, 0x66, 0xa9, 0x16, 0x1f,
0xda, 0x26, 0xde, 0x14, 0x96, 0x73, 0x68, 0x19, 0xae, 0x54, 0x8b, 0x0f, 0x1f, 0x6d, 0x15, 0x05,
0xa6, 0x66, 0x94, 0xd6, 0x6a, 0xd8, 0xdc, 0x2c, 0x3f, 0x36, 0x6b, 0x6b, 0x86, 0x6d, 0xe4, 0x16,
0xd1, 0x3c, 0xcc, 0x54, 0x8d, 0xc7, 0x66, 0xad, 0x5a, 0x32, 0x2a, 0xd5, 0xf5, 0xb2, 0x9d, 0x5b,
0x42, 0xd7, 0xe0, 0x2a, 0x13, 0x2e, 0x63, 0xb3, 0x16, 0x4d, 0xf0, 0x00, 0x97, 0x37, 0xfb, 0x10,
0x15, 0x5d, 0x84, 0xc5, 0xc1, 0xae, 0x65, 0xc6, 0x4e, 0x4d, 0x69, 0xe0, 0xc2, 0x7a, 0x31, 0x9a,
0x73, 0x05, 0xdd, 0x86, 0x57, 0x8e, 0x8b, 0x8a, 0x8f, 0xab, 0x76, 0xb9, 0x52, 0x33, 0x1e, 0x9a,
0x25, 0x3b, 0x77, 0x13, 0x5d, 0x85, 0x8b, 0x79, 0xcb, 0x28, 0x6c, 0xac, 0x97, 0x2d, 0xb3, 0x56,
0x31, 0x4d, 0x5c, 0xab, 0x94, 0xb1, 0x5d, 0xb3, 0x9f, 0xd6, 0xf0, 0xd3, 0x5c, 0x03, 0xa9, 0x70,
0x79, 0xab, 0x34, 0x1c, 0x40, 0xd0, 0x25, 0x58, 0x5c, 0x33, 0x2d, 0xe3, 0x83, 0x94, 0xeb, 0x45,
0x06, 0x5d, 0x81, 0x0b, 0x5b, 0xa5, 0xc1, 0xde, 0xcf, 0x32, 0xab, 0x7f, 0x03, 0x18, 0x65, 0x9f,
0x20, 0x48, 0x81, 0x73, 0xd1, 0xde, 0x96, 0x4b, 0x66, 0xed, 0x41, 0xd9, 0xb2, 0xca, 0x4f, 0x4c,
0x9c, 0x3b, 0x13, 0xae, 0x26, 0xe5, 0xa9, 0x6d, 0x95, 0xec, 0xa2, 0x55, 0xb3, 0x71, 0xf1, 0xe1,
0x43, 0x13, 0xf7, 0x77, 0x28, 0x83, 0x10, 0xcc, 0x46, 0x04, 0xcb, 0x34, 0xd6, 0x4c, 0x9c, 0x1b,
0x41, 0x37, 0xe1, 0x46, 0xdc, 0x36, 0x8c, 0x9e, 0x95, 0xe9, 0x8f, 0xb6, 0xca, 0x78, 0x6b, 0x33,
0x37, 0xca, 0x0e, 0x4d, 0x64, 0x33, 0x2c, 0x2b, 0x37, 0x86, 0xae, 0x83, 0x1a, 0x6d, 0xb1, 0xb4,
0xbb, 0xb1, 0xc8, 0x01, 0xdd, 0x83, 0x37, 0x4e, 0x00, 0x0d, 0x8b, 0x62, 0x8a, 0xa5, 0x64, 0x00,
0x37, 0x5c, 0xcf, 0x34, 0x7a, 0x1d, 0x5e, 0x1b, 0xea, 0x1e, 0x26, 0x3a, 0x83, 0x1e, 0x40, 0x7e,
0x00, 0x4b, 0xac, 0x32, 0xb4, 0x88, 0x73, 0x19, 0x0a, 0x45, 0xd4, 0xf0, 0x10, 0x16, 0xb0, 0x61,
0x17, 0xd6, 0x73, 0xb3, 0x68, 0x15, 0x5e, 0x1e, 0x7a, 0x1c, 0xe2, 0x9b, 0xd0, 0x40, 0x06, 0xbc,
0x7b, 0x3a, 0xec, 0xb0, 0xb0, 0x09, 0x7a, 0x09, 0x96, 0x87, 0x4b, 0x84, 0x5b, 0xb2, 0x83, 0xde,
0x81, 0x37, 0x4f, 0x42, 0x0d, 0x9b, 0x62, 0xf7, 0xf8, 0x29, 0xc2, 0x63, 0xb0, 0xc7, 0xee, 0xde,
0x70, 0x14, 0x3b, 0x18, 0x4d, 0xf4, 0x7f, 0xa0, 0x0d, 0x3c, 0xec, 0xf1, 0x6d, 0x79, 0x91, 0x41,
0xb7, 0xe0, 0x26, 0x36, 0x4a, 0x6b, 0xe5, 0xcd, 0xda, 0x29, 0xf0, 0x9f, 0x65, 0xd0, 0x7b, 0xf0,
0xf6, 0xc9, 0xc0, 0x61, 0x0b, 0xfc, 0x3c, 0x83, 0x4c, 0x78, 0xff, 0xd4, 0xf3, 0x0d, 0x93, 0xf9,
0x22, 0x83, 0xae, 0xc1, 0x95, 0xc1, 0xfc, 0x30, 0x0f, 0x5f, 0x66, 0xd0, 0x0a, 0x5c, 0x3f, 0x76,
0xa6, 0x10, 0xf9, 0x55, 0x06, 0xbd, 0x05, 0x77, 0x8f, 0x83, 0x0c, 0x0b, 0xe3, 0xd7, 0x19, 0x74,
0x1f, 0xee, 0x9d, 0x62, 0x8e, 0x61, 0x02, 0xbf, 0x39, 0x66, 0x1d, 0x61, 0xb2, 0xbf, 0x3e, 0x79,
0x1d, 0x21, 0xf2, 0xb7, 0x19, 0xb4, 0x04, 0x17, 0x07, 0x43, 0xd8, 0x99, 0xf8, 0x5d, 0x06, 0xdd,
0x80, 0xe5, 0x63, 0x95, 0x18, 0xec, 0xf7, 0x19, 0xa4, 0xc0, 0x42, 0xa9, 0x5c, 0x7b, 0x60, 0x14,
0xad, 0xda, 0x93, 0xa2, 0xbd, 0x5e, 0xab, 0xda, 0xd8, 0xac, 0x56, 0x73, 0xbf, 0x18, 0x61, 0xa1,
0xc4, 0x3c, 0xa5, 0x72, 0xe8, 0xac, 0x3d, 0x28, 0xe3, 0x9a, 0x55, 0x7c, 0x6c, 0x96, 0x18, 0xf2,
0xd3, 0x11, 0x34, 0x07, 0xc0, 0x60, 0x95, 0x72, 0xb1, 0x64, 0x57, 0x73, 0xdf, 0xcb, 0xa2, 0x19,
0x98, 0x30, 0x9f, 0xda, 0x26, 0x2e, 0x19, 0x56, 0xee, 0xef, 0xd9, 0xd5, 0x7d, 0x98, 0x88, 0x7e,
0xe5, 0x40, 0xe3, 0x30, 0xb2, 0xf1, 0x38, 0x77, 0x06, 0x4d, 0xc2, 0x98, 0x65, 0x1a, 0x55, 0x33,
0x97, 0x41, 0x0b, 0x30, 0x67, 0x5a, 0x66, 0xc1, 0x2e, 0x96, 0x4b, 0x35, 0xbc, 0x55, 0x2a, 0xf1,
0xc7, 0x33, 0x07, 0xd3, 0x4f, 0xd8, 0xcd, 0x8f, 0x2c, 0x59, 0xb4, 0x08, 0xf3, 0x56, 0xb9, 0xb0,
0x51, 0xc3, 0x46, 0xc1, 0xc4, 0x91, 0x79, 0x94, 0x01, 0xb9, 0x50, 0x64, 0x19, 0x5b, 0xcd, 0xc3,
0xd9, 0xf0, 0x27, 0x12, 0x34, 0x05, 0x67, 0x37, 0x1e, 0xd7, 0xd6, 0x8d, 0xea, 0x7a, 0xee, 0x4c,
0x1f, 0x69, 0x3e, 0xad, 0x14, 0x31, 0x9b, 0x19, 0x60, 0xfc, 0x68, 0xc2, 0x69, 0x98, 0x28, 0x95,
0x6b, 0x85, 0x75, 0xb3, 0xb0, 0x91, 0xcb, 0xde, 0xb9, 0x0f, 0x93, 0xb6, 0xef, 0xb8, 0x41, 0xdb,
0xf3, 0x29, 0xba, 0x23, 0x0f, 0x66, 0xc3, 0x1f, 0x6a, 0xc3, 0x3f, 0x19, 0x5f, 0x9a, 0x3b, 0x1a,
0x8b, 0xbf, 0x26, 0x6a, 0x67, 0x56, 0x32, 0xaf, 0x65, 0xf2, 0xe7, 0x5e, 0xfc, 0x79, 0xe9, 0xcc,
0x8b, 0x6f, 0x96, 0x32, 0x5f, 0x7f, 0xb3, 0x94, 0xf9, 0xd3, 0x37, 0x4b, 0x99, 0x9f, 0xfc, 0x65,
0xe9, 0xcc, 0xf6, 0x38, 0xff, 0x93, 0xf3, 0xdd, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0x42, 0x92,
0x53, 0x23, 0xbb, 0x1e, 0x00, 0x00,
}

View File

@ -203,8 +203,8 @@ message Etcd {
bool InitialCorruptCheck = 64 [(gogoproto.moretags) = "yaml:\"initial-corrupt-check\""];
string Logger = 71 [(gogoproto.moretags) = "yaml:\"logger\""];
// LogOutput is the log file to store current etcd server logs.
repeated string LogOutput = 72 [(gogoproto.moretags) = "yaml:\"log-output\""];
// LogOutputs is the log file to store current etcd server logs.
repeated string LogOutputs = 72 [(gogoproto.moretags) = "yaml:\"log-outputs\""];
bool Debug = 73 [(gogoproto.moretags) = "yaml:\"debug\""];
}

View File

@ -322,10 +322,10 @@ func read(lg *zap.Logger, fpath string) (*Cluster, error) {
clus.Members[i].ClientCertData = string(data)
}
if len(mem.Etcd.LogOutput) == 0 {
return nil, fmt.Errorf("mem.Etcd.LogOutput cannot be empty")
if len(mem.Etcd.LogOutputs) == 0 {
return nil, fmt.Errorf("mem.Etcd.LogOutputs cannot be empty")
}
for _, v := range mem.Etcd.LogOutput {
for _, v := range mem.Etcd.LogOutputs {
switch v {
case "stderr", "stdout", "/dev/null", "default":
default:

View File

@ -63,7 +63,7 @@ func Test_read(t *testing.T) {
PreVote: true,
InitialCorruptCheck: true,
Logger: "zap",
LogOutput: []string{"/tmp/etcd-functional-1/etcd.log"},
LogOutputs: []string{"/tmp/etcd-functional-1/etcd.log"},
Debug: true,
},
ClientCertData: "",
@ -116,7 +116,7 @@ func Test_read(t *testing.T) {
PreVote: true,
InitialCorruptCheck: true,
Logger: "zap",
LogOutput: []string{"/tmp/etcd-functional-2/etcd.log"},
LogOutputs: []string{"/tmp/etcd-functional-2/etcd.log"},
Debug: true,
},
ClientCertData: "",
@ -169,7 +169,7 @@ func Test_read(t *testing.T) {
PreVote: true,
InitialCorruptCheck: true,
Logger: "zap",
LogOutput: []string{"/tmp/etcd-functional-3/etcd.log"},
LogOutputs: []string{"/tmp/etcd-functional-3/etcd.log"},
Debug: true,
},
ClientCertData: "",

View File

@ -53,7 +53,7 @@ func TestEmbedEtcd(t *testing.T) {
for i := range tests {
tests[i].cfg = *embed.NewConfig()
tests[i].cfg.Logger = "zap"
tests[i].cfg.LogOutput = []string{"/dev/null"}
tests[i].cfg.LogOutputs = []string{"/dev/null"}
tests[i].cfg.Debug = false
}
@ -180,7 +180,7 @@ func newEmbedURLs(secure bool, n int) (urls []url.URL) {
func setupEmbedCfg(cfg *embed.Config, curls []url.URL, purls []url.URL) {
cfg.Logger = "zap"
cfg.LogOutput = []string{"/dev/null"}
cfg.LogOutputs = []string{"/dev/null"}
cfg.Debug = false
cfg.ClusterState = "new"

View File

@ -15,8 +15,6 @@
package logutil
import (
"github.com/coreos/etcd/raft"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"google.golang.org/grpc/grpclog"
@ -33,6 +31,15 @@ func NewGRPCLoggerV2(lcfg zap.Config) (grpclog.LoggerV2, error) {
return &zapGRPCLogger{lg: lg, sugar: lg.Sugar()}, nil
}
// NewGRPCLoggerV2FromZapCore creates "grpclog.LoggerV2" from "zap.Core"
// and "zapcore.WriteSyncer". It discards all INFO level logging in gRPC,
// if debug level is not enabled in "*zap.Logger".
func NewGRPCLoggerV2FromZapCore(cr zapcore.Core, syncer zapcore.WriteSyncer) grpclog.LoggerV2 {
// "AddCallerSkip" to annotate caller outside of "logutil"
lg := zap.New(cr, zap.AddCaller(), zap.AddCallerSkip(1), zap.ErrorOutput(syncer))
return &zapGRPCLogger{lg: lg, sugar: lg.Sugar()}
}
type zapGRPCLogger struct {
lg *zap.Logger
sugar *zap.SugaredLogger
@ -102,65 +109,3 @@ func (zl *zapGRPCLogger) V(l int) bool {
}
return true
}
// NewRaftLogger converts "*zap.Logger" to "raft.Logger".
func NewRaftLogger(lcfg zap.Config) (raft.Logger, error) {
lg, err := lcfg.Build(zap.AddCallerSkip(1)) // to annotate caller outside of "logutil"
if err != nil {
return nil, err
}
return &zapRaftLogger{lg: lg, sugar: lg.Sugar()}, nil
}
type zapRaftLogger struct {
lg *zap.Logger
sugar *zap.SugaredLogger
}
func (zl *zapRaftLogger) Debug(args ...interface{}) {
zl.sugar.Debug(args...)
}
func (zl *zapRaftLogger) Debugf(format string, args ...interface{}) {
zl.sugar.Debugf(format, args...)
}
func (zl *zapRaftLogger) Error(args ...interface{}) {
zl.sugar.Error(args...)
}
func (zl *zapRaftLogger) Errorf(format string, args ...interface{}) {
zl.sugar.Errorf(format, args...)
}
func (zl *zapRaftLogger) Info(args ...interface{}) {
zl.sugar.Info(args...)
}
func (zl *zapRaftLogger) Infof(format string, args ...interface{}) {
zl.sugar.Infof(format, args...)
}
func (zl *zapRaftLogger) Warning(args ...interface{}) {
zl.sugar.Warn(args...)
}
func (zl *zapRaftLogger) Warningf(format string, args ...interface{}) {
zl.sugar.Warnf(format, args...)
}
func (zl *zapRaftLogger) Fatal(args ...interface{}) {
zl.sugar.Fatal(args...)
}
func (zl *zapRaftLogger) Fatalf(format string, args ...interface{}) {
zl.sugar.Fatalf(format, args...)
}
func (zl *zapRaftLogger) Panic(args ...interface{}) {
zl.sugar.Panic(args...)
}
func (zl *zapRaftLogger) Panicf(format string, args ...interface{}) {
zl.sugar.Panicf(format, args...)
}

View File

@ -20,10 +20,12 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"time"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func TestNewGRPCLoggerV2(t *testing.T) {
@ -66,50 +68,24 @@ func TestNewGRPCLoggerV2(t *testing.T) {
if !bytes.Contains(data, []byte("etcd-logutil-2")) {
t.Fatalf("can't find data in log %q", string(data))
}
if !bytes.Contains(data, []byte("logutil/zap_test.go:")) {
if !bytes.Contains(data, []byte("logutil/zap_grpc_test.go:")) {
t.Fatalf("unexpected caller; %q", string(data))
}
}
func TestNewRaftLogger(t *testing.T) {
logPath := filepath.Join(os.TempDir(), fmt.Sprintf("test-log-%d", time.Now().UnixNano()))
defer os.RemoveAll(logPath)
func TestNewGRPCLoggerV2FromZapCore(t *testing.T) {
buf := bytes.NewBuffer(nil)
syncer := zapcore.AddSync(buf)
cr := zapcore.NewCore(
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
syncer,
zap.NewAtomicLevelAt(zap.InfoLevel),
)
lcfg := zap.Config{
Level: zap.NewAtomicLevelAt(zap.DebugLevel),
Development: false,
Sampling: &zap.SamplingConfig{
Initial: 100,
Thereafter: 100,
},
Encoding: "json",
EncoderConfig: zap.NewProductionEncoderConfig(),
OutputPaths: []string{logPath},
ErrorOutputPaths: []string{logPath},
}
gl, err := NewRaftLogger(lcfg)
if err != nil {
t.Fatal(err)
}
gl.Info("etcd-logutil-1")
data, err := ioutil.ReadFile(logPath)
if err != nil {
t.Fatal(err)
}
if !bytes.Contains(data, []byte("etcd-logutil-1")) {
t.Fatalf("can't find data in log %q", string(data))
}
gl.Warning("etcd-logutil-2")
data, err = ioutil.ReadFile(logPath)
if err != nil {
t.Fatal(err)
}
if !bytes.Contains(data, []byte("etcd-logutil-2")) {
t.Fatalf("can't find data in log %q", string(data))
}
if !bytes.Contains(data, []byte("logutil/zap_test.go:")) {
t.Fatalf("unexpected caller; %q", string(data))
lg := NewGRPCLoggerV2FromZapCore(cr, syncer)
lg.Warning("TestNewGRPCLoggerV2FromZapCore")
txt := buf.String()
if !strings.Contains(txt, "TestNewGRPCLoggerV2FromZapCore") {
t.Fatalf("unexpected log %q", txt)
}
}

View File

@ -0,0 +1,87 @@
// Copyright 2018 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !windows
package logutil
import (
"bytes"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"github.com/coreos/go-systemd/journal"
"go.uber.org/zap/zapcore"
)
// NewJournaldWriter wraps "io.Writer" to redirect log output
// to the local systemd journal. If journald send fails, it fails
// back to writing to the original writer.
// The decode overhead is only <30µs per write.
// Reference: https://github.com/coreos/pkg/blob/master/capnslog/journald_formatter.go
func NewJournaldWriter(wr io.Writer) io.Writer {
return &journaldWriter{Writer: wr}
}
type journaldWriter struct {
io.Writer
}
type logLine struct {
Level string `json:"level"`
Caller string `json:"caller"`
}
func (w *journaldWriter) Write(p []byte) (int, error) {
line := &logLine{}
if err := json.NewDecoder(bytes.NewReader(p)).Decode(line); err != nil {
return 0, err
}
var pri journal.Priority
switch line.Level {
case zapcore.DebugLevel.String():
pri = journal.PriDebug
case zapcore.InfoLevel.String():
pri = journal.PriInfo
case zapcore.WarnLevel.String():
pri = journal.PriWarning
case zapcore.ErrorLevel.String():
pri = journal.PriErr
case zapcore.DPanicLevel.String():
pri = journal.PriCrit
case zapcore.PanicLevel.String():
pri = journal.PriCrit
case zapcore.FatalLevel.String():
pri = journal.PriCrit
default:
panic(fmt.Errorf("unknown log level: %q", line.Level))
}
err := journal.Send(string(p), pri, map[string]string{
"PACKAGE": filepath.Dir(line.Caller),
"SYSLOG_IDENTIFIER": filepath.Base(os.Args[0]),
})
if err != nil {
fmt.Println("FAILED TO WRITE TO JOURNALD", err, string(p))
return w.Writer.Write(p)
}
return 0, nil
}

View File

@ -0,0 +1,44 @@
// Copyright 2018 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !windows
package logutil
import (
"bytes"
"testing"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func TestNewJournaldWriter(t *testing.T) {
buf := bytes.NewBuffer(nil)
syncer := zapcore.AddSync(NewJournaldWriter(buf))
cr := zapcore.NewCore(
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
syncer,
zap.NewAtomicLevelAt(zap.InfoLevel),
)
lg := zap.New(cr, zap.AddCaller(), zap.ErrorOutput(syncer))
defer lg.Sync()
lg.Info("TestNewJournaldWriter")
if buf.String() == "" {
// check with "journalctl -f"
t.Log("sent logs successfully to journald")
}
}

97
pkg/logutil/zap_raft.go Normal file
View File

@ -0,0 +1,97 @@
// Copyright 2018 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package logutil
import (
"errors"
"github.com/coreos/etcd/raft"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// NewRaftLogger converts "*zap.Logger" to "raft.Logger".
func NewRaftLogger(lcfg *zap.Config) (raft.Logger, error) {
if lcfg == nil {
return nil, errors.New("nil zap.Config")
}
lg, err := lcfg.Build(zap.AddCallerSkip(1)) // to annotate caller outside of "logutil"
if err != nil {
return nil, err
}
return &zapRaftLogger{lg: lg, sugar: lg.Sugar()}, nil
}
// NewRaftLoggerFromZapCore creates "raft.Logger" from "zap.Core"
// and "zapcore.WriteSyncer".
func NewRaftLoggerFromZapCore(cr zapcore.Core, syncer zapcore.WriteSyncer) raft.Logger {
// "AddCallerSkip" to annotate caller outside of "logutil"
lg := zap.New(cr, zap.AddCaller(), zap.AddCallerSkip(1), zap.ErrorOutput(syncer))
return &zapRaftLogger{lg: lg, sugar: lg.Sugar()}
}
type zapRaftLogger struct {
lg *zap.Logger
sugar *zap.SugaredLogger
}
func (zl *zapRaftLogger) Debug(args ...interface{}) {
zl.sugar.Debug(args...)
}
func (zl *zapRaftLogger) Debugf(format string, args ...interface{}) {
zl.sugar.Debugf(format, args...)
}
func (zl *zapRaftLogger) Error(args ...interface{}) {
zl.sugar.Error(args...)
}
func (zl *zapRaftLogger) Errorf(format string, args ...interface{}) {
zl.sugar.Errorf(format, args...)
}
func (zl *zapRaftLogger) Info(args ...interface{}) {
zl.sugar.Info(args...)
}
func (zl *zapRaftLogger) Infof(format string, args ...interface{}) {
zl.sugar.Infof(format, args...)
}
func (zl *zapRaftLogger) Warning(args ...interface{}) {
zl.sugar.Warn(args...)
}
func (zl *zapRaftLogger) Warningf(format string, args ...interface{}) {
zl.sugar.Warnf(format, args...)
}
func (zl *zapRaftLogger) Fatal(args ...interface{}) {
zl.sugar.Fatal(args...)
}
func (zl *zapRaftLogger) Fatalf(format string, args ...interface{}) {
zl.sugar.Fatalf(format, args...)
}
func (zl *zapRaftLogger) Panic(args ...interface{}) {
zl.sugar.Panic(args...)
}
func (zl *zapRaftLogger) Panicf(format string, args ...interface{}) {
zl.sugar.Panicf(format, args...)
}

View File

@ -0,0 +1,89 @@
// Copyright 2018 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package logutil
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"time"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func TestNewRaftLogger(t *testing.T) {
logPath := filepath.Join(os.TempDir(), fmt.Sprintf("test-log-%d", time.Now().UnixNano()))
defer os.RemoveAll(logPath)
lcfg := &zap.Config{
Level: zap.NewAtomicLevelAt(zap.DebugLevel),
Development: false,
Sampling: &zap.SamplingConfig{
Initial: 100,
Thereafter: 100,
},
Encoding: "json",
EncoderConfig: zap.NewProductionEncoderConfig(),
OutputPaths: []string{logPath},
ErrorOutputPaths: []string{logPath},
}
gl, err := NewRaftLogger(lcfg)
if err != nil {
t.Fatal(err)
}
gl.Info("etcd-logutil-1")
data, err := ioutil.ReadFile(logPath)
if err != nil {
t.Fatal(err)
}
if !bytes.Contains(data, []byte("etcd-logutil-1")) {
t.Fatalf("can't find data in log %q", string(data))
}
gl.Warning("etcd-logutil-2")
data, err = ioutil.ReadFile(logPath)
if err != nil {
t.Fatal(err)
}
if !bytes.Contains(data, []byte("etcd-logutil-2")) {
t.Fatalf("can't find data in log %q", string(data))
}
if !bytes.Contains(data, []byte("logutil/zap_raft_test.go:")) {
t.Fatalf("unexpected caller; %q", string(data))
}
}
func TestNewRaftLoggerFromZapCore(t *testing.T) {
buf := bytes.NewBuffer(nil)
syncer := zapcore.AddSync(buf)
cr := zapcore.NewCore(
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
syncer,
zap.NewAtomicLevelAt(zap.InfoLevel),
)
lg := NewRaftLoggerFromZapCore(cr, syncer)
lg.Info("TestNewRaftLoggerFromZapCore")
txt := buf.String()
if !strings.Contains(txt, "TestNewRaftLoggerFromZapCore") {
t.Fatalf("unexpected log %q", txt)
}
}

View File

@ -64,7 +64,7 @@ func TestSnapshotV3RestoreMultiMemberAdd(t *testing.T) {
cfg := embed.NewConfig()
cfg.Logger = "zap"
cfg.LogOutput = []string{"/dev/null"}
cfg.LogOutputs = []string{"/dev/null"}
cfg.Debug = false
cfg.Name = "3"
cfg.InitialClusterToken = testClusterTkn

View File

@ -44,7 +44,7 @@ func TestSnapshotV3RestoreSingle(t *testing.T) {
cfg := embed.NewConfig()
cfg.Logger = "zap"
cfg.LogOutput = []string{"/dev/null"}
cfg.LogOutputs = []string{"/dev/null"}
cfg.Debug = false
cfg.Name = "s1"
cfg.InitialClusterToken = testClusterTkn
@ -153,7 +153,7 @@ func createSnapshotFile(t *testing.T, kvs []kv) string {
cfg := embed.NewConfig()
cfg.Logger = "zap"
cfg.LogOutput = []string{"/dev/null"}
cfg.LogOutputs = []string{"/dev/null"}
cfg.Debug = false
cfg.Name = "default"
cfg.ClusterState = "new"
@ -220,7 +220,7 @@ func restoreCluster(t *testing.T, clusterN int, dbPath string) (
for i := 0; i < clusterN; i++ {
cfg := embed.NewConfig()
cfg.Logger = "zap"
cfg.LogOutput = []string{"/dev/null"}
cfg.LogOutputs = []string{"/dev/null"}
cfg.Debug = false
cfg.Name = fmt.Sprintf("%d", i)
cfg.InitialClusterToken = testClusterTkn