mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
pkg/logutil: add "NewJournaldWriter"
Signed-off-by: Gyuho Lee <gyuhox@gmail.com>
This commit is contained in:
parent
e73f909fef
commit
fcbb30364a
@ -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"
|
||||
@ -102,65 +100,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...)
|
||||
}
|
@ -66,50 +66,7 @@ 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:")) {
|
||||
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)
|
||||
|
||||
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:")) {
|
||||
if !bytes.Contains(data, []byte("logutil/zap_grpc_test.go:")) {
|
||||
t.Fatalf("unexpected caller; %q", string(data))
|
||||
}
|
||||
}
|
85
pkg/logutil/zap_journald.go
Normal file
85
pkg/logutil/zap_journald.go
Normal file
@ -0,0 +1,85 @@
|
||||
// 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"
|
||||
"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
|
||||
}
|
42
pkg/logutil/zap_journald_test.go
Normal file
42
pkg/logutil/zap_journald_test.go
Normal file
@ -0,0 +1,42 @@
|
||||
// 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"
|
||||
"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")
|
||||
}
|
||||
}
|
68
pkg/logutil/zap_raft.go
Normal file
68
pkg/logutil/zap_raft.go
Normal file
@ -0,0 +1,68 @@
|
||||
package logutil
|
||||
|
||||
import (
|
||||
"github.com/coreos/etcd/raft"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// 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...)
|
||||
}
|
70
pkg/logutil/zap_raft_test.go
Normal file
70
pkg/logutil/zap_raft_test.go
Normal file
@ -0,0 +1,70 @@
|
||||
// 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"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user