Kaynağa Gözat

Merge pull request #6 from AlekSi/thread-safe

Make log.New() thread safe
Vishal Rana 9 yıl önce
ebeveyn
işleme
758e12ff8c
3 değiştirilmiş dosya ile 83 ekleme ve 51 silme
  1. 2 0
      .travis.yml
  2. 35 23
      log/log.go
  3. 46 28
      log/log_test.go

+ 2 - 0
.travis.yml

@@ -1,11 +1,13 @@
 language: go
 go:
+    - 1.5.2
     - tip
 before_install:
     - go get github.com/modocache/gover
     - go get github.com/mattn/goveralls
     - go get golang.org/x/tools/cmd/cover
 script:
+    - go test -race ./...
     - go test -coverprofile=color.coverprofile ./color
     - go test -coverprofile=gytes.coverprofile ./gytes
     - go test -coverprofile=log.coverprofile ./log

+ 35 - 23
log/log.go

@@ -15,6 +15,8 @@ import (
 type (
 	Logger struct {
 		level  Level
+		levels []string
+		color  color.Color
 		out    io.Writer
 		prefix string
 		mu     sync.Mutex
@@ -34,7 +36,6 @@ const (
 
 var (
 	global = New("-")
-	levels []string
 )
 
 func New(prefix string) (l *Logger) {
@@ -46,6 +47,26 @@ func New(prefix string) (l *Logger) {
 	return
 }
 
+func (l *Logger) initLevels() {
+	l.levels = []string{
+		l.color.Blue("DEBUG"),
+		l.color.Green("INFO"),
+		l.color.Yellow("WARN"),
+		l.color.Red("ERROR"),
+		l.color.RedBg("FATAL"),
+	}
+}
+
+func (l *Logger) DisableColor() {
+	l.color.Disable()
+	l.initLevels()
+}
+
+func (l *Logger) EnableColor() {
+	l.color.Enable()
+	l.initLevels()
+}
+
 func (l *Logger) SetPrefix(p string) {
 	l.prefix = p
 }
@@ -60,18 +81,15 @@ func (l *Logger) Level() Level {
 
 func (l *Logger) SetOutput(w io.Writer) {
 	l.out = w
-	color.Disable()
+	l.DisableColor()
 
 	if w, ok := w.(*os.File); ok && isatty.IsTerminal(w.Fd()) {
-		color.Enable()
+		l.EnableColor()
 	}
-
-	// NOTE: Reintialize levels to reflect color enable/disable call.
-	initLevels()
 }
 
 func (l *Logger) Print(i ...interface{}) {
-	fmt.Println(i...)
+	fmt.Fprintln(l.out, i...)
 }
 
 func (l *Logger) Printf(format string, args ...interface{}) {
@@ -121,6 +139,14 @@ func (l *Logger) Fatalf(format string, args ...interface{}) {
 	os.Exit(1)
 }
 
+func DisableColor() {
+	global.DisableColor()
+}
+
+func EnableColor() {
+	global.EnableColor()
+}
+
 func SetPrefix(p string) {
 	global.SetPrefix(p)
 }
@@ -187,26 +213,12 @@ func (l *Logger) log(v Level, format string, args ...interface{}) {
 
 	if v >= l.level {
 		if format == "" && len(args) > 0 {
-			args[0] = fmt.Sprintf("%s|%s|%s", levels[v], l.prefix, args[0])
+			args[0] = fmt.Sprintf("%s|%s|%s", l.levels[v], l.prefix, args[0])
 			fmt.Fprintln(l.out, args...)
 		} else {
 			// TODO: Improve performance
-			f := fmt.Sprintf("%s|%s|%s\n", levels[v], l.prefix, format)
+			f := fmt.Sprintf("%s|%s|%s\n", l.levels[v], l.prefix, format)
 			fmt.Fprintf(l.out, f, args...)
 		}
 	}
 }
-
-func initLevels() {
-	levels = []string{
-		color.Blue("DEBUG"),
-		color.Green("INFO"),
-		color.Yellow("WARN"),
-		color.Red("ERROR"),
-		color.RedBg("FATAL"),
-	}
-}
-
-func init() {
-	initLevels()
-}

+ 46 - 28
log/log_test.go

@@ -2,30 +2,62 @@ package log
 
 import (
 	"bytes"
-	"testing"
-
 	"os"
 	"os/exec"
+	"sync"
+	"testing"
 
 	"github.com/stretchr/testify/assert"
 )
 
-func TestLog(t *testing.T) {
-	l := New("test")
+func test(l *Logger, t *testing.T) {
 	b := new(bytes.Buffer)
 	l.SetOutput(b)
-	test(l, DEBUG, t)
-	assert.Contains(t, b.String(), "debug")
-	assert.Contains(t, b.String(), "debugf")
-	assert.Contains(t, b.String(), "warn")
-	assert.Contains(t, b.String(), "warnf")
+	l.DisableColor()
+	l.SetLevel(WARN)
 
-	b.Reset()
-	SetOutput(b)
-	test(global, WARN, t)
+	l.Print("print")
+	l.Printf("print%s", "f")
+	l.Debug("debug")
+	l.Debugf("debug%s", "f")
+	l.Info("info")
+	l.Infof("info%s", "f")
+	l.Warn("warn")
+	l.Warnf("warn%s", "f")
+	l.Error("error")
+	l.Errorf("error%s", "f")
+
+	assert.Contains(t, b.String(), "print\n")
+	assert.Contains(t, b.String(), "\nprintf\n")
+	assert.NotContains(t, b.String(), "debug")
+	assert.NotContains(t, b.String(), "debugf")
 	assert.NotContains(t, b.String(), "info")
-	assert.Contains(t, b.String(), "warn")
-	println(b.String())
+	assert.NotContains(t, b.String(), "infof")
+	assert.Contains(t, b.String(), "\nWARN|"+l.prefix+"|warn\n")
+	assert.Contains(t, b.String(), "\nWARN|"+l.prefix+"|warnf\n")
+	assert.Contains(t, b.String(), "\nERROR|"+l.prefix+"|error\n")
+	assert.Contains(t, b.String(), "\nERROR|"+l.prefix+"|errorf\n")
+}
+
+func TestLog(t *testing.T) {
+	l := New("test")
+	test(l, t)
+}
+
+func TestGlobal(t *testing.T) {
+	test(global, t)
+}
+
+func TestLogConcurrent(t *testing.T) {
+	var wg sync.WaitGroup
+	for i := 0; i < 2; i++ {
+		wg.Add(1)
+		go func() {
+			TestLog(t)
+			wg.Done()
+		}()
+	}
+	wg.Wait()
 }
 
 func TestFatal(t *testing.T) {
@@ -56,17 +88,3 @@ func loggerFatalTest(t *testing.T, env string, contains string) {
 	}
 	t.Fatalf("process ran with err %v, want exit status 1", err)
 }
-
-func test(l *Logger, v Level, t *testing.T) {
-	l.SetLevel(v)
-	l.Print("print")
-	l.Printf("print%s", "f")
-	l.Debug("debug")
-	l.Debugf("debug%s", "f")
-	l.Info("info")
-	l.Infof("info%s", "f")
-	l.Warn("warn")
-	l.Warnf("warn%s", "f")
-	l.Error("error")
-	l.Errorf("error%s", "f")
-}