From 93662351a7405348a104e3a01a75e6651c704b21 Mon Sep 17 00:00:00 2001 From: Gyuho Lee Date: Tue, 24 Apr 2018 11:59:49 -0700 Subject: [PATCH] pkg/logutil: add "NewJournaldWriter" Signed-off-by: Gyuho Lee --- pkg/logutil/{zap.go => zap_grpc.go} | 64 -------------- pkg/logutil/{zap_test.go => zap_grpc_test.go} | 45 +--------- pkg/logutil/zap_journald.go | 85 +++++++++++++++++++ pkg/logutil/zap_journald_test.go | 41 +++++++++ pkg/logutil/zap_raft.go | 68 +++++++++++++++ pkg/logutil/zap_raft_test.go | 70 +++++++++++++++ 6 files changed, 265 insertions(+), 108 deletions(-) rename pkg/logutil/{zap.go => zap_grpc.go} (63%) rename pkg/logutil/{zap_test.go => zap_grpc_test.go} (61%) create mode 100644 pkg/logutil/zap_journald.go create mode 100644 pkg/logutil/zap_journald_test.go create mode 100644 pkg/logutil/zap_raft.go create mode 100644 pkg/logutil/zap_raft_test.go diff --git a/pkg/logutil/zap.go b/pkg/logutil/zap_grpc.go similarity index 63% rename from pkg/logutil/zap.go rename to pkg/logutil/zap_grpc.go index 440173911a0a..0a28dbf858df 100644 --- a/pkg/logutil/zap.go +++ b/pkg/logutil/zap_grpc.go @@ -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...) -} diff --git a/pkg/logutil/zap_test.go b/pkg/logutil/zap_grpc_test.go similarity index 61% rename from pkg/logutil/zap_test.go rename to pkg/logutil/zap_grpc_test.go index 15ac2c006522..95b114869872 100644 --- a/pkg/logutil/zap_test.go +++ b/pkg/logutil/zap_grpc_test.go @@ -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)) } } diff --git a/pkg/logutil/zap_journald.go b/pkg/logutil/zap_journald.go new file mode 100644 index 000000000000..4489b990553b --- /dev/null +++ b/pkg/logutil/zap_journald.go @@ -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 +} diff --git a/pkg/logutil/zap_journald_test.go b/pkg/logutil/zap_journald_test.go new file mode 100644 index 000000000000..f545518b3577 --- /dev/null +++ b/pkg/logutil/zap_journald_test.go @@ -0,0 +1,41 @@ +// 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("hello") + if buf.String() == "" { + t.Log("sent logs successfully to journald") + } +} diff --git a/pkg/logutil/zap_raft.go b/pkg/logutil/zap_raft.go new file mode 100644 index 000000000000..4dd754bc74f9 --- /dev/null +++ b/pkg/logutil/zap_raft.go @@ -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...) +} diff --git a/pkg/logutil/zap_raft_test.go b/pkg/logutil/zap_raft_test.go new file mode 100644 index 000000000000..b7b402c981b8 --- /dev/null +++ b/pkg/logutil/zap_raft_test.go @@ -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)) + } +}