-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b9125e3
commit c35457e
Showing
5 changed files
with
177 additions
and
81 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package log | ||
|
||
import ( | ||
"io" | ||
"sync" | ||
"sync/atomic" | ||
) | ||
|
||
// SwapLogger wraps another logger that may be safely replaced while other | ||
// goroutines use the SwapLogger concurrently. The zero value for a SwapLogger | ||
// will discard all log events without error. | ||
// | ||
// SwapLogger serves well as a package global logger that can be changed by | ||
// importers. | ||
type SwapLogger struct { | ||
logger atomic.Value | ||
} | ||
|
||
type loggerStruct struct { | ||
Logger | ||
} | ||
|
||
// Log implements the Logger interface by forwarding keyvals to the currently | ||
// wrapped logger. It does not log anything if the wrapped logger is nil. | ||
func (l *SwapLogger) Log(keyvals ...interface{}) error { | ||
s, ok := l.logger.Load().(loggerStruct) | ||
if !ok || s.Logger == nil { | ||
return nil | ||
} | ||
return s.Log(keyvals...) | ||
} | ||
|
||
// Swap replaces the currently wrapped logger with logger. Swap may be called | ||
// concurrently with calls to Log from other goroutines. | ||
func (l *SwapLogger) Swap(logger Logger) { | ||
l.logger.Store(loggerStruct{logger}) | ||
} | ||
|
||
// SyncWriter synchronizes concurrent writes to an io.Writer. | ||
type SyncWriter struct { | ||
mu sync.Mutex | ||
w io.Writer | ||
} | ||
|
||
// NewSyncWriter returns a new SyncWriter. The returned writer is safe for | ||
// concurrent use by multiple goroutines. | ||
func NewSyncWriter(w io.Writer) *SyncWriter { | ||
return &SyncWriter{w: w} | ||
} | ||
|
||
// Write writes p to the underlying io.Writer. If another write is already in | ||
// progress, the calling goroutine blocks until the SyncWriter is available. | ||
func (w *SyncWriter) Write(p []byte) (n int, err error) { | ||
w.mu.Lock() | ||
n, err = w.w.Write(p) | ||
w.mu.Unlock() | ||
return n, err | ||
} | ||
|
||
// syncLogger provides concurrent safe logging for another Logger. | ||
type syncLogger struct { | ||
mu sync.Mutex | ||
logger Logger | ||
} | ||
|
||
// NewSyncLogger returns a logger that synchronizes concurrent use of the | ||
// wrapped logger. When multiple goroutines use the SyncLogger concurrently | ||
// only one goroutine will be allowed to log to the wrapped logger at a time. | ||
// The other goroutines will block until the logger is available. | ||
func NewSyncLogger(logger Logger) Logger { | ||
return &syncLogger{logger: logger} | ||
} | ||
|
||
// Log logs keyvals to the underlying Logger. If another log is already in | ||
// progress, the calling goroutine blocks until the syncLogger is available. | ||
func (l *syncLogger) Log(keyvals ...interface{}) error { | ||
l.mu.Lock() | ||
err := l.logger.Log(keyvals...) | ||
l.mu.Unlock() | ||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package log_test | ||
|
||
import ( | ||
"bytes" | ||
"io" | ||
"testing" | ||
|
||
"github.com/go-kit/kit/log" | ||
) | ||
|
||
func TestSwapLogger(t *testing.T) { | ||
var logger log.SwapLogger | ||
|
||
// Zero value does not panic or error. | ||
err := logger.Log("k", "v") | ||
if got, want := err, error(nil); got != want { | ||
t.Errorf("got %v, want %v", got, want) | ||
} | ||
|
||
buf := &bytes.Buffer{} | ||
json := log.NewJSONLogger(buf) | ||
logger.Swap(json) | ||
|
||
if err := logger.Log("k", "v"); err != nil { | ||
t.Error(err) | ||
} | ||
if got, want := buf.String(), `{"k":"v"}`+"\n"; got != want { | ||
t.Errorf("got %v, want %v", got, want) | ||
} | ||
|
||
buf.Reset() | ||
prefix := log.NewLogfmtLogger(buf) | ||
logger.Swap(prefix) | ||
|
||
if err := logger.Log("k", "v"); err != nil { | ||
t.Error(err) | ||
} | ||
if got, want := buf.String(), "k=v\n"; got != want { | ||
t.Errorf("got %v, want %v", got, want) | ||
} | ||
|
||
buf.Reset() | ||
logger.Swap(nil) | ||
|
||
if err := logger.Log("k", "v"); err != nil { | ||
t.Error(err) | ||
} | ||
if got, want := buf.String(), ""; got != want { | ||
t.Errorf("got %v, want %v", got, want) | ||
} | ||
} | ||
|
||
func TestSwapLoggerConcurrency(t *testing.T) { | ||
testConcurrency(t, &log.SwapLogger{}) | ||
} | ||
|
||
func TestSyncLoggerConcurrency(t *testing.T) { | ||
var w io.Writer | ||
w = &bytes.Buffer{} | ||
logger := log.NewLogfmtLogger(w) | ||
logger = log.NewSyncLogger(logger) | ||
testConcurrency(t, logger) | ||
} | ||
|
||
func TestSyncWriterConcurrency(t *testing.T) { | ||
var w io.Writer | ||
w = &bytes.Buffer{} | ||
w = log.NewSyncWriter(w) | ||
testConcurrency(t, log.NewLogfmtLogger(w)) | ||
} |