Skip to content

Commit

Permalink
pkg/logutil: add "NewJournaldWriter"
Browse files Browse the repository at this point in the history
Signed-off-by: Gyuho Lee <gyuhox@gmail.com>
  • Loading branch information
gyuho committed Apr 24, 2018
1 parent f1b3b32 commit 9366235
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 108 deletions.
64 changes: 0 additions & 64 deletions pkg/logutil/zap.go → pkg/logutil/zap_grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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...)
}
45 changes: 1 addition & 44 deletions pkg/logutil/zap_test.go → pkg/logutil/zap_grpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 85 additions & 0 deletions pkg/logutil/zap_journald.go
Original file line number Diff line number Diff line change
@@ -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
}
41 changes: 41 additions & 0 deletions pkg/logutil/zap_journald_test.go
Original file line number Diff line number Diff line change
@@ -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")
}
}
68 changes: 68 additions & 0 deletions pkg/logutil/zap_raft.go
Original file line number Diff line number Diff line change
@@ -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 changes: 70 additions & 0 deletions pkg/logutil/zap_raft_test.go
Original file line number Diff line number Diff line change
@@ -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))
}
}

0 comments on commit 9366235

Please sign in to comment.