Browse code

Merge pull request #8770 from LK4D4/logrus_support

Logrus support

Jessie Frazelle authored on 2014/10/28 01:05:24
Showing 89 changed files
... ...
@@ -23,6 +23,7 @@ import (
23 23
 	"text/template"
24 24
 	"time"
25 25
 
26
+	log "github.com/Sirupsen/logrus"
26 27
 	"github.com/docker/docker/api"
27 28
 	"github.com/docker/docker/dockerversion"
28 29
 	"github.com/docker/docker/engine"
... ...
@@ -30,7 +31,6 @@ import (
30 30
 	"github.com/docker/docker/nat"
31 31
 	"github.com/docker/docker/opts"
32 32
 	"github.com/docker/docker/pkg/archive"
33
-	"github.com/docker/docker/pkg/log"
34 33
 	flag "github.com/docker/docker/pkg/mflag"
35 34
 	"github.com/docker/docker/pkg/parsers"
36 35
 	"github.com/docker/docker/pkg/parsers/filters"
... ...
@@ -11,9 +11,9 @@ import (
11 11
 	"runtime"
12 12
 	"strings"
13 13
 
14
+	log "github.com/Sirupsen/logrus"
14 15
 	"github.com/docker/docker/api"
15 16
 	"github.com/docker/docker/dockerversion"
16
-	"github.com/docker/docker/pkg/log"
17 17
 	"github.com/docker/docker/pkg/promise"
18 18
 	"github.com/docker/docker/pkg/stdcopy"
19 19
 	"github.com/docker/docker/pkg/term"
... ...
@@ -16,10 +16,10 @@ import (
16 16
 	"strings"
17 17
 	"syscall"
18 18
 
19
+	log "github.com/Sirupsen/logrus"
19 20
 	"github.com/docker/docker/api"
20 21
 	"github.com/docker/docker/dockerversion"
21 22
 	"github.com/docker/docker/engine"
22
-	"github.com/docker/docker/pkg/log"
23 23
 	"github.com/docker/docker/pkg/stdcopy"
24 24
 	"github.com/docker/docker/pkg/term"
25 25
 	"github.com/docker/docker/registry"
... ...
@@ -5,8 +5,8 @@ import (
5 5
 	"mime"
6 6
 	"strings"
7 7
 
8
+	log "github.com/Sirupsen/logrus"
8 9
 	"github.com/docker/docker/engine"
9
-	"github.com/docker/docker/pkg/log"
10 10
 	"github.com/docker/docker/pkg/parsers"
11 11
 	"github.com/docker/docker/pkg/version"
12 12
 )
... ...
@@ -23,10 +23,10 @@ import (
23 23
 	"github.com/docker/libcontainer/user"
24 24
 	"github.com/gorilla/mux"
25 25
 
26
+	log "github.com/Sirupsen/logrus"
26 27
 	"github.com/docker/docker/api"
27 28
 	"github.com/docker/docker/engine"
28 29
 	"github.com/docker/docker/pkg/listenbuffer"
29
-	"github.com/docker/docker/pkg/log"
30 30
 	"github.com/docker/docker/pkg/parsers"
31 31
 	"github.com/docker/docker/pkg/stdcopy"
32 32
 	"github.com/docker/docker/pkg/systemd"
... ...
@@ -14,8 +14,8 @@ import (
14 14
 	"regexp"
15 15
 	"strings"
16 16
 
17
+	log "github.com/Sirupsen/logrus"
17 18
 	"github.com/docker/docker/nat"
18
-	"github.com/docker/docker/pkg/log"
19 19
 	flag "github.com/docker/docker/pkg/mflag"
20 20
 	"github.com/docker/docker/runconfig"
21 21
 )
... ...
@@ -27,10 +27,10 @@ import (
27 27
 	"path"
28 28
 	"strings"
29 29
 
30
+	log "github.com/Sirupsen/logrus"
30 31
 	"github.com/docker/docker/builder/parser"
31 32
 	"github.com/docker/docker/daemon"
32 33
 	"github.com/docker/docker/engine"
33
-	"github.com/docker/docker/pkg/log"
34 34
 	"github.com/docker/docker/pkg/tarsum"
35 35
 	"github.com/docker/docker/registry"
36 36
 	"github.com/docker/docker/runconfig"
... ...
@@ -18,11 +18,11 @@ import (
18 18
 	"syscall"
19 19
 	"time"
20 20
 
21
+	log "github.com/Sirupsen/logrus"
21 22
 	"github.com/docker/docker/builder/parser"
22 23
 	"github.com/docker/docker/daemon"
23 24
 	imagepkg "github.com/docker/docker/image"
24 25
 	"github.com/docker/docker/pkg/archive"
25
-	"github.com/docker/docker/pkg/log"
26 26
 	"github.com/docker/docker/pkg/parsers"
27 27
 	"github.com/docker/docker/pkg/symlink"
28 28
 	"github.com/docker/docker/pkg/system"
... ...
@@ -6,10 +6,10 @@ import (
6 6
 	"os"
7 7
 	"time"
8 8
 
9
+	log "github.com/Sirupsen/logrus"
9 10
 	"github.com/docker/docker/engine"
10 11
 	"github.com/docker/docker/pkg/ioutils"
11 12
 	"github.com/docker/docker/pkg/jsonlog"
12
-	"github.com/docker/docker/pkg/log"
13 13
 	"github.com/docker/docker/pkg/promise"
14 14
 	"github.com/docker/docker/utils"
15 15
 )
... ...
@@ -17,6 +17,7 @@ import (
17 17
 	"github.com/docker/libcontainer/devices"
18 18
 	"github.com/docker/libcontainer/label"
19 19
 
20
+	log "github.com/Sirupsen/logrus"
20 21
 	"github.com/docker/docker/daemon/execdriver"
21 22
 	"github.com/docker/docker/engine"
22 23
 	"github.com/docker/docker/image"
... ...
@@ -25,7 +26,6 @@ import (
25 25
 	"github.com/docker/docker/pkg/archive"
26 26
 	"github.com/docker/docker/pkg/broadcastwriter"
27 27
 	"github.com/docker/docker/pkg/ioutils"
28
-	"github.com/docker/docker/pkg/log"
29 28
 	"github.com/docker/docker/pkg/networkfs/etchosts"
30 29
 	"github.com/docker/docker/pkg/networkfs/resolvconf"
31 30
 	"github.com/docker/docker/pkg/promise"
... ...
@@ -14,6 +14,7 @@ import (
14 14
 
15 15
 	"github.com/docker/libcontainer/label"
16 16
 
17
+	log "github.com/Sirupsen/logrus"
17 18
 	"github.com/docker/docker/daemon/execdriver"
18 19
 	"github.com/docker/docker/daemon/execdriver/execdrivers"
19 20
 	"github.com/docker/docker/daemon/execdriver/lxc"
... ...
@@ -29,7 +30,6 @@ import (
29 29
 	"github.com/docker/docker/pkg/broadcastwriter"
30 30
 	"github.com/docker/docker/pkg/graphdb"
31 31
 	"github.com/docker/docker/pkg/ioutils"
32
-	"github.com/docker/docker/pkg/log"
33 32
 	"github.com/docker/docker/pkg/namesgenerator"
34 33
 	"github.com/docker/docker/pkg/parsers"
35 34
 	"github.com/docker/docker/pkg/parsers/kernel"
... ...
@@ -304,7 +304,7 @@ func (daemon *Daemon) restore() error {
304 304
 	)
305 305
 
306 306
 	if !debug {
307
-		log.Infof("Loading containers: ")
307
+		log.Infof("Loading containers: start.")
308 308
 	}
309 309
 	dir, err := ioutil.ReadDir(daemon.repository)
310 310
 	if err != nil {
... ...
@@ -392,7 +392,8 @@ func (daemon *Daemon) restore() error {
392 392
 	}
393 393
 
394 394
 	if !debug {
395
-		log.Infof(": done.")
395
+		fmt.Println()
396
+		log.Infof("Loading containers: done.")
396 397
 	}
397 398
 
398 399
 	return nil
... ...
@@ -3,10 +3,10 @@
3 3
 package daemon
4 4
 
5 5
 import (
6
+	log "github.com/Sirupsen/logrus"
6 7
 	"github.com/docker/docker/daemon/graphdriver"
7 8
 	"github.com/docker/docker/daemon/graphdriver/aufs"
8 9
 	"github.com/docker/docker/graph"
9
-	"github.com/docker/docker/pkg/log"
10 10
 )
11 11
 
12 12
 // Given the graphdriver ad, if it is aufs, then migrate it.
... ...
@@ -5,8 +5,8 @@ import (
5 5
 	"os"
6 6
 	"path"
7 7
 
8
+	log "github.com/Sirupsen/logrus"
8 9
 	"github.com/docker/docker/engine"
9
-	"github.com/docker/docker/pkg/log"
10 10
 )
11 11
 
12 12
 func (daemon *Daemon) ContainerRm(job *engine.Job) engine.Status {
... ...
@@ -9,12 +9,12 @@ import (
9 9
 	"strings"
10 10
 	"sync"
11 11
 
12
+	log "github.com/Sirupsen/logrus"
12 13
 	"github.com/docker/docker/daemon/execdriver"
13 14
 	"github.com/docker/docker/daemon/execdriver/lxc"
14 15
 	"github.com/docker/docker/engine"
15 16
 	"github.com/docker/docker/pkg/broadcastwriter"
16 17
 	"github.com/docker/docker/pkg/ioutils"
17
-	"github.com/docker/docker/pkg/log"
18 18
 	"github.com/docker/docker/pkg/promise"
19 19
 	"github.com/docker/docker/runconfig"
20 20
 	"github.com/docker/docker/utils"
... ...
@@ -17,8 +17,8 @@ import (
17 17
 
18 18
 	"github.com/kr/pty"
19 19
 
20
+	log "github.com/Sirupsen/logrus"
20 21
 	"github.com/docker/docker/daemon/execdriver"
21
-	"github.com/docker/docker/pkg/log"
22 22
 	"github.com/docker/docker/pkg/term"
23 23
 	"github.com/docker/docker/utils"
24 24
 	"github.com/docker/libcontainer/cgroups"
... ...
@@ -30,9 +30,9 @@ import (
30 30
 	"sync"
31 31
 	"syscall"
32 32
 
33
+	log "github.com/Sirupsen/logrus"
33 34
 	"github.com/docker/docker/daemon/graphdriver"
34 35
 	"github.com/docker/docker/pkg/archive"
35
-	"github.com/docker/docker/pkg/log"
36 36
 	mountpk "github.com/docker/docker/pkg/mount"
37 37
 	"github.com/docker/docker/utils"
38 38
 	"github.com/docker/libcontainer/label"
... ...
@@ -4,7 +4,7 @@ import (
4 4
 	"os/exec"
5 5
 	"syscall"
6 6
 
7
-	"github.com/docker/docker/pkg/log"
7
+	log "github.com/Sirupsen/logrus"
8 8
 )
9 9
 
10 10
 func Unmount(target string) error {
... ...
@@ -7,7 +7,7 @@ import (
7 7
 	"os"
8 8
 	"syscall"
9 9
 
10
-	"github.com/docker/docker/pkg/log"
10
+	log "github.com/Sirupsen/logrus"
11 11
 )
12 12
 
13 13
 func stringToLoopName(src string) [LoNameSize]uint8 {
... ...
@@ -18,8 +18,8 @@ import (
18 18
 	"syscall"
19 19
 	"time"
20 20
 
21
+	log "github.com/Sirupsen/logrus"
21 22
 	"github.com/docker/docker/daemon/graphdriver"
22
-	"github.com/docker/docker/pkg/log"
23 23
 	"github.com/docker/docker/pkg/parsers"
24 24
 	"github.com/docker/docker/pkg/units"
25 25
 	"github.com/docker/libcontainer/label"
... ...
@@ -9,7 +9,7 @@ import (
9 9
 	"runtime"
10 10
 	"syscall"
11 11
 
12
-	"github.com/docker/docker/pkg/log"
12
+	log "github.com/Sirupsen/logrus"
13 13
 )
14 14
 
15 15
 type DevmapperLogger interface {
... ...
@@ -8,8 +8,8 @@ import (
8 8
 	"os"
9 9
 	"path"
10 10
 
11
+	log "github.com/Sirupsen/logrus"
11 12
 	"github.com/docker/docker/daemon/graphdriver"
12
-	"github.com/docker/docker/pkg/log"
13 13
 	"github.com/docker/docker/pkg/mount"
14 14
 	"github.com/docker/docker/pkg/units"
15 15
 )
... ...
@@ -4,9 +4,9 @@ import (
4 4
 	"fmt"
5 5
 	"time"
6 6
 
7
+	log "github.com/Sirupsen/logrus"
7 8
 	"github.com/docker/docker/pkg/archive"
8 9
 	"github.com/docker/docker/pkg/ioutils"
9
-	"github.com/docker/docker/pkg/log"
10 10
 	"github.com/docker/docker/utils"
11 11
 )
12 12
 
... ...
@@ -4,9 +4,9 @@ import (
4 4
 	"os"
5 5
 	"runtime"
6 6
 
7
+	log "github.com/Sirupsen/logrus"
7 8
 	"github.com/docker/docker/dockerversion"
8 9
 	"github.com/docker/docker/engine"
9
-	"github.com/docker/docker/pkg/log"
10 10
 	"github.com/docker/docker/pkg/parsers/kernel"
11 11
 	"github.com/docker/docker/pkg/parsers/operatingsystem"
12 12
 	"github.com/docker/docker/registry"
... ...
@@ -8,9 +8,9 @@ import (
8 8
 	"os"
9 9
 	"strconv"
10 10
 
11
+	log "github.com/Sirupsen/logrus"
11 12
 	"github.com/docker/docker/engine"
12 13
 	"github.com/docker/docker/pkg/jsonlog"
13
-	"github.com/docker/docker/pkg/log"
14 14
 	"github.com/docker/docker/pkg/tailfile"
15 15
 	"github.com/docker/docker/pkg/timeutils"
16 16
 )
... ...
@@ -6,8 +6,8 @@ import (
6 6
 	"sync"
7 7
 	"time"
8 8
 
9
+	log "github.com/Sirupsen/logrus"
9 10
 	"github.com/docker/docker/daemon/execdriver"
10
-	"github.com/docker/docker/pkg/log"
11 11
 	"github.com/docker/docker/runconfig"
12 12
 )
13 13
 
... ...
@@ -8,13 +8,13 @@ import (
8 8
 	"strings"
9 9
 	"sync"
10 10
 
11
+	log "github.com/Sirupsen/logrus"
11 12
 	"github.com/docker/docker/daemon/networkdriver"
12 13
 	"github.com/docker/docker/daemon/networkdriver/ipallocator"
13 14
 	"github.com/docker/docker/daemon/networkdriver/portallocator"
14 15
 	"github.com/docker/docker/daemon/networkdriver/portmapper"
15 16
 	"github.com/docker/docker/engine"
16 17
 	"github.com/docker/docker/pkg/iptables"
17
-	"github.com/docker/docker/pkg/log"
18 18
 	"github.com/docker/docker/pkg/networkfs/resolvconf"
19 19
 	"github.com/docker/docker/pkg/parsers/kernel"
20 20
 	"github.com/docker/libcontainer/netlink"
... ...
@@ -6,9 +6,9 @@ import (
6 6
 	"net"
7 7
 	"sync"
8 8
 
9
+	log "github.com/Sirupsen/logrus"
9 10
 	"github.com/docker/docker/daemon/networkdriver/portallocator"
10 11
 	"github.com/docker/docker/pkg/iptables"
11
-	"github.com/docker/docker/pkg/log"
12 12
 )
13 13
 
14 14
 type mapping struct {
... ...
@@ -10,9 +10,9 @@ import (
10 10
 	"strings"
11 11
 	"syscall"
12 12
 
13
+	log "github.com/Sirupsen/logrus"
13 14
 	"github.com/docker/docker/daemon/execdriver"
14 15
 	"github.com/docker/docker/pkg/archive"
15
-	"github.com/docker/docker/pkg/log"
16 16
 	"github.com/docker/docker/pkg/symlink"
17 17
 	"github.com/docker/docker/volumes"
18 18
 )
... ...
@@ -3,6 +3,7 @@
3 3
 package main
4 4
 
5 5
 import (
6
+	log "github.com/Sirupsen/logrus"
6 7
 	"github.com/docker/docker/builder"
7 8
 	"github.com/docker/docker/builtins"
8 9
 	"github.com/docker/docker/daemon"
... ...
@@ -10,7 +11,6 @@ import (
10 10
 	_ "github.com/docker/docker/daemon/execdriver/native"
11 11
 	"github.com/docker/docker/dockerversion"
12 12
 	"github.com/docker/docker/engine"
13
-	"github.com/docker/docker/pkg/log"
14 13
 	flag "github.com/docker/docker/pkg/mflag"
15 14
 	"github.com/docker/docker/pkg/signal"
16 15
 )
... ...
@@ -28,6 +28,7 @@ func main() {
28 28
 	if reexec.Init() {
29 29
 		return
30 30
 	}
31
+
31 32
 	flag.Parse()
32 33
 	// FIXME: validate daemon flags here
33 34
 
... ...
@@ -39,6 +40,8 @@ func main() {
39 39
 		os.Setenv("DEBUG", "1")
40 40
 	}
41 41
 
42
+	initLogging(*flDebug)
43
+
42 44
 	if len(flHosts) == 0 {
43 45
 		defaultHost := os.Getenv("DOCKER_HOST")
44 46
 		if defaultHost == "" || *flDaemon {
45 47
new file mode 100644
... ...
@@ -0,0 +1,16 @@
0
+package main
1
+
2
+import (
3
+	"os"
4
+
5
+	log "github.com/Sirupsen/logrus"
6
+)
7
+
8
+func initLogging(debug bool) {
9
+	log.SetOutput(os.Stderr)
10
+	if debug {
11
+		log.SetLevel(log.DebugLevel)
12
+	} else {
13
+		log.SetLevel(log.InfoLevel)
14
+	}
15
+}
... ...
@@ -6,6 +6,8 @@ import (
6 6
 	"io"
7 7
 	"strings"
8 8
 	"time"
9
+
10
+	log "github.com/Sirupsen/logrus"
9 11
 )
10 12
 
11 13
 // A job is the fundamental unit of work in the docker engine.
... ...
@@ -66,10 +68,12 @@ func (job *Job) Run() error {
66 66
 		return fmt.Errorf("%s: job has already completed", job.Name)
67 67
 	}
68 68
 	// Log beginning and end of the job
69
-	job.Eng.Logf("+job %s", job.CallString())
70
-	defer func() {
71
-		job.Eng.Logf("-job %s%s", job.CallString(), job.StatusString())
72
-	}()
69
+	if job.Eng.Logging {
70
+		log.Infof("+job %s", job.CallString())
71
+		defer func() {
72
+			log.Infof("-job %s%s", job.CallString(), job.StatusString())
73
+		}()
74
+	}
73 75
 	var errorMessage = bytes.NewBuffer(nil)
74 76
 	job.Stderr.Add(errorMessage)
75 77
 	if job.handler == nil {
... ...
@@ -7,9 +7,9 @@ import (
7 7
 	"os"
8 8
 	"path"
9 9
 
10
+	log "github.com/Sirupsen/logrus"
10 11
 	"github.com/docker/docker/engine"
11 12
 	"github.com/docker/docker/pkg/archive"
12
-	"github.com/docker/docker/pkg/log"
13 13
 	"github.com/docker/docker/pkg/parsers"
14 14
 )
15 15
 
... ...
@@ -12,11 +12,11 @@ import (
12 12
 	"syscall"
13 13
 	"time"
14 14
 
15
+	log "github.com/Sirupsen/logrus"
15 16
 	"github.com/docker/docker/daemon/graphdriver"
16 17
 	"github.com/docker/docker/dockerversion"
17 18
 	"github.com/docker/docker/image"
18 19
 	"github.com/docker/docker/pkg/archive"
19
-	"github.com/docker/docker/pkg/log"
20 20
 	"github.com/docker/docker/pkg/truncindex"
21 21
 	"github.com/docker/docker/runconfig"
22 22
 	"github.com/docker/docker/utils"
... ...
@@ -7,10 +7,10 @@ import (
7 7
 	"os"
8 8
 	"path"
9 9
 
10
+	log "github.com/Sirupsen/logrus"
10 11
 	"github.com/docker/docker/engine"
11 12
 	"github.com/docker/docker/image"
12 13
 	"github.com/docker/docker/pkg/archive"
13
-	"github.com/docker/docker/pkg/log"
14 14
 )
15 15
 
16 16
 // Loads a set of images into the repository. This is the complementary of ImageExport.
... ...
@@ -12,9 +12,9 @@ import (
12 12
 	"strings"
13 13
 	"time"
14 14
 
15
+	log "github.com/Sirupsen/logrus"
15 16
 	"github.com/docker/docker/engine"
16 17
 	"github.com/docker/docker/image"
17
-	"github.com/docker/docker/pkg/log"
18 18
 	"github.com/docker/docker/registry"
19 19
 	"github.com/docker/docker/utils"
20 20
 	"github.com/docker/libtrust"
... ...
@@ -7,9 +7,9 @@ import (
7 7
 	"os"
8 8
 	"path"
9 9
 
10
+	log "github.com/Sirupsen/logrus"
10 11
 	"github.com/docker/docker/engine"
11 12
 	"github.com/docker/docker/pkg/archive"
12
-	"github.com/docker/docker/pkg/log"
13 13
 	"github.com/docker/docker/registry"
14 14
 	"github.com/docker/docker/utils"
15 15
 )
... ...
@@ -4,9 +4,9 @@ import (
4 4
 	"fmt"
5 5
 	"io"
6 6
 
7
+	log "github.com/Sirupsen/logrus"
7 8
 	"github.com/docker/docker/engine"
8 9
 	"github.com/docker/docker/image"
9
-	"github.com/docker/docker/pkg/log"
10 10
 )
11 11
 
12 12
 func (s *TagStore) Install(eng *engine.Engine) error {
... ...
@@ -53,6 +53,8 @@ clone hg code.google.com/p/gosqlite 74691fb6f837
53 53
 
54 54
 clone git github.com/docker/libtrust d273ef2565ca
55 55
 
56
+clone git github.com/Sirupsen/logrus v0.5.1
57
+
56 58
 # get Go tip's archive/tar, for xattr support and improved performance
57 59
 # TODO after Go 1.4 drops, bump our minimum supported version and drop this vendored dep
58 60
 if [ "$1" = '--go' ]; then
... ...
@@ -9,8 +9,8 @@ import (
9 9
 	"strconv"
10 10
 	"time"
11 11
 
12
+	log "github.com/Sirupsen/logrus"
12 13
 	"github.com/docker/docker/pkg/archive"
13
-	"github.com/docker/docker/pkg/log"
14 14
 	"github.com/docker/docker/runconfig"
15 15
 	"github.com/docker/docker/utils"
16 16
 )
... ...
@@ -9,9 +9,9 @@ import (
9 9
 	"testing"
10 10
 	"time"
11 11
 
12
+	log "github.com/Sirupsen/logrus"
12 13
 	"github.com/docker/docker/api/client"
13 14
 	"github.com/docker/docker/daemon"
14
-	"github.com/docker/docker/pkg/log"
15 15
 	"github.com/docker/docker/pkg/term"
16 16
 	"github.com/docker/docker/utils"
17 17
 	"github.com/docker/libtrust"
... ...
@@ -16,12 +16,12 @@ import (
16 16
 	"testing"
17 17
 	"time"
18 18
 
19
+	log "github.com/Sirupsen/logrus"
19 20
 	"github.com/docker/docker/daemon"
20 21
 	"github.com/docker/docker/engine"
21 22
 	"github.com/docker/docker/image"
22 23
 	"github.com/docker/docker/nat"
23 24
 	"github.com/docker/docker/pkg/ioutils"
24
-	"github.com/docker/docker/pkg/log"
25 25
 	"github.com/docker/docker/reexec"
26 26
 	"github.com/docker/docker/runconfig"
27 27
 	"github.com/docker/docker/utils"
... ...
@@ -18,20 +18,23 @@ import (
18 18
 	"github.com/docker/docker/builtins"
19 19
 	"github.com/docker/docker/daemon"
20 20
 	"github.com/docker/docker/engine"
21
-	"github.com/docker/docker/pkg/log"
22 21
 	flag "github.com/docker/docker/pkg/mflag"
23 22
 	"github.com/docker/docker/pkg/sysinfo"
24 23
 	"github.com/docker/docker/runconfig"
25 24
 	"github.com/docker/docker/utils"
26 25
 )
27 26
 
27
+type Fataler interface {
28
+	Fatal(...interface{})
29
+}
30
+
28 31
 // This file contains utility functions for docker's unit test suite.
29 32
 // It has to be named XXX_test.go, apparently, in other to access private functions
30 33
 // from other XXX_test.go functions.
31 34
 
32 35
 // Create a temporary daemon suitable for unit testing.
33 36
 // Call t.Fatal() at the first error.
34
-func mkDaemon(f log.Fataler) *daemon.Daemon {
37
+func mkDaemon(f Fataler) *daemon.Daemon {
35 38
 	eng := newTestEngine(f, false, "")
36 39
 	return mkDaemonFromEngine(eng, f)
37 40
 	// FIXME:
... ...
@@ -40,7 +43,7 @@ func mkDaemon(f log.Fataler) *daemon.Daemon {
40 40
 	// [...]
41 41
 }
42 42
 
43
-func createNamedTestContainer(eng *engine.Engine, config *runconfig.Config, f log.Fataler, name string) (shortId string) {
43
+func createNamedTestContainer(eng *engine.Engine, config *runconfig.Config, f Fataler, name string) (shortId string) {
44 44
 	job := eng.Job("create", name)
45 45
 	if err := job.ImportEnv(config); err != nil {
46 46
 		f.Fatal(err)
... ...
@@ -53,23 +56,23 @@ func createNamedTestContainer(eng *engine.Engine, config *runconfig.Config, f lo
53 53
 	return engine.Tail(outputBuffer, 1)
54 54
 }
55 55
 
56
-func createTestContainer(eng *engine.Engine, config *runconfig.Config, f log.Fataler) (shortId string) {
56
+func createTestContainer(eng *engine.Engine, config *runconfig.Config, f Fataler) (shortId string) {
57 57
 	return createNamedTestContainer(eng, config, f, "")
58 58
 }
59 59
 
60
-func startContainer(eng *engine.Engine, id string, t log.Fataler) {
60
+func startContainer(eng *engine.Engine, id string, t Fataler) {
61 61
 	job := eng.Job("start", id)
62 62
 	if err := job.Run(); err != nil {
63 63
 		t.Fatal(err)
64 64
 	}
65 65
 }
66 66
 
67
-func containerRun(eng *engine.Engine, id string, t log.Fataler) {
67
+func containerRun(eng *engine.Engine, id string, t Fataler) {
68 68
 	startContainer(eng, id, t)
69 69
 	containerWait(eng, id, t)
70 70
 }
71 71
 
72
-func containerFileExists(eng *engine.Engine, id, dir string, t log.Fataler) bool {
72
+func containerFileExists(eng *engine.Engine, id, dir string, t Fataler) bool {
73 73
 	c := getContainer(eng, id, t)
74 74
 	if err := c.Mount(); err != nil {
75 75
 		t.Fatal(err)
... ...
@@ -84,7 +87,7 @@ func containerFileExists(eng *engine.Engine, id, dir string, t log.Fataler) bool
84 84
 	return true
85 85
 }
86 86
 
87
-func containerAttach(eng *engine.Engine, id string, t log.Fataler) (io.WriteCloser, io.ReadCloser) {
87
+func containerAttach(eng *engine.Engine, id string, t Fataler) (io.WriteCloser, io.ReadCloser) {
88 88
 	c := getContainer(eng, id, t)
89 89
 	i, err := c.StdinPipe()
90 90
 	if err != nil {
... ...
@@ -97,31 +100,31 @@ func containerAttach(eng *engine.Engine, id string, t log.Fataler) (io.WriteClos
97 97
 	return i, o
98 98
 }
99 99
 
100
-func containerWait(eng *engine.Engine, id string, t log.Fataler) int {
100
+func containerWait(eng *engine.Engine, id string, t Fataler) int {
101 101
 	ex, _ := getContainer(eng, id, t).WaitStop(-1 * time.Second)
102 102
 	return ex
103 103
 }
104 104
 
105
-func containerWaitTimeout(eng *engine.Engine, id string, t log.Fataler) error {
105
+func containerWaitTimeout(eng *engine.Engine, id string, t Fataler) error {
106 106
 	_, err := getContainer(eng, id, t).WaitStop(500 * time.Millisecond)
107 107
 	return err
108 108
 }
109 109
 
110
-func containerKill(eng *engine.Engine, id string, t log.Fataler) {
110
+func containerKill(eng *engine.Engine, id string, t Fataler) {
111 111
 	if err := eng.Job("kill", id).Run(); err != nil {
112 112
 		t.Fatal(err)
113 113
 	}
114 114
 }
115 115
 
116
-func containerRunning(eng *engine.Engine, id string, t log.Fataler) bool {
116
+func containerRunning(eng *engine.Engine, id string, t Fataler) bool {
117 117
 	return getContainer(eng, id, t).IsRunning()
118 118
 }
119 119
 
120
-func containerAssertExists(eng *engine.Engine, id string, t log.Fataler) {
120
+func containerAssertExists(eng *engine.Engine, id string, t Fataler) {
121 121
 	getContainer(eng, id, t)
122 122
 }
123 123
 
124
-func containerAssertNotExists(eng *engine.Engine, id string, t log.Fataler) {
124
+func containerAssertNotExists(eng *engine.Engine, id string, t Fataler) {
125 125
 	daemon := mkDaemonFromEngine(eng, t)
126 126
 	if c := daemon.Get(id); c != nil {
127 127
 		t.Fatal(fmt.Errorf("Container %s should not exist", id))
... ...
@@ -130,7 +133,7 @@ func containerAssertNotExists(eng *engine.Engine, id string, t log.Fataler) {
130 130
 
131 131
 // assertHttpNotError expect the given response to not have an error.
132 132
 // Otherwise the it causes the test to fail.
133
-func assertHttpNotError(r *httptest.ResponseRecorder, t log.Fataler) {
133
+func assertHttpNotError(r *httptest.ResponseRecorder, t Fataler) {
134 134
 	// Non-error http status are [200, 400)
135 135
 	if r.Code < http.StatusOK || r.Code >= http.StatusBadRequest {
136 136
 		t.Fatal(fmt.Errorf("Unexpected http error: %v", r.Code))
... ...
@@ -139,14 +142,14 @@ func assertHttpNotError(r *httptest.ResponseRecorder, t log.Fataler) {
139 139
 
140 140
 // assertHttpError expect the given response to have an error.
141 141
 // Otherwise the it causes the test to fail.
142
-func assertHttpError(r *httptest.ResponseRecorder, t log.Fataler) {
142
+func assertHttpError(r *httptest.ResponseRecorder, t Fataler) {
143 143
 	// Non-error http status are [200, 400)
144 144
 	if !(r.Code < http.StatusOK || r.Code >= http.StatusBadRequest) {
145 145
 		t.Fatal(fmt.Errorf("Unexpected http success code: %v", r.Code))
146 146
 	}
147 147
 }
148 148
 
149
-func getContainer(eng *engine.Engine, id string, t log.Fataler) *daemon.Container {
149
+func getContainer(eng *engine.Engine, id string, t Fataler) *daemon.Container {
150 150
 	daemon := mkDaemonFromEngine(eng, t)
151 151
 	c := daemon.Get(id)
152 152
 	if c == nil {
... ...
@@ -155,7 +158,7 @@ func getContainer(eng *engine.Engine, id string, t log.Fataler) *daemon.Containe
155 155
 	return c
156 156
 }
157 157
 
158
-func mkDaemonFromEngine(eng *engine.Engine, t log.Fataler) *daemon.Daemon {
158
+func mkDaemonFromEngine(eng *engine.Engine, t Fataler) *daemon.Daemon {
159 159
 	iDaemon := eng.Hack_GetGlobalVar("httpapi.daemon")
160 160
 	if iDaemon == nil {
161 161
 		panic("Legacy daemon field not set in engine")
... ...
@@ -167,7 +170,7 @@ func mkDaemonFromEngine(eng *engine.Engine, t log.Fataler) *daemon.Daemon {
167 167
 	return daemon
168 168
 }
169 169
 
170
-func newTestEngine(t log.Fataler, autorestart bool, root string) *engine.Engine {
170
+func newTestEngine(t Fataler, autorestart bool, root string) *engine.Engine {
171 171
 	if root == "" {
172 172
 		if dir, err := newTestDirectory(unitTestStoreBase); err != nil {
173 173
 			t.Fatal(err)
... ...
@@ -200,7 +203,7 @@ func newTestEngine(t log.Fataler, autorestart bool, root string) *engine.Engine
200 200
 	return eng
201 201
 }
202 202
 
203
-func NewTestEngine(t log.Fataler) *engine.Engine {
203
+func NewTestEngine(t Fataler) *engine.Engine {
204 204
 	return newTestEngine(t, false, "")
205 205
 }
206 206
 
... ...
@@ -18,8 +18,8 @@ import (
18 18
 
19 19
 	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
20 20
 
21
+	log "github.com/Sirupsen/logrus"
21 22
 	"github.com/docker/docker/pkg/fileutils"
22
-	"github.com/docker/docker/pkg/log"
23 23
 	"github.com/docker/docker/pkg/pools"
24 24
 	"github.com/docker/docker/pkg/promise"
25 25
 	"github.com/docker/docker/pkg/system"
... ...
@@ -12,7 +12,7 @@ import (
12 12
 
13 13
 	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
14 14
 
15
-	"github.com/docker/docker/pkg/log"
15
+	log "github.com/Sirupsen/logrus"
16 16
 	"github.com/docker/docker/pkg/pools"
17 17
 	"github.com/docker/docker/pkg/system"
18 18
 )
... ...
@@ -6,8 +6,8 @@ import (
6 6
 	"sync"
7 7
 	"time"
8 8
 
9
+	log "github.com/Sirupsen/logrus"
9 10
 	"github.com/docker/docker/pkg/jsonlog"
10
-	"github.com/docker/docker/pkg/log"
11 11
 )
12 12
 
13 13
 // BroadcastWriter accumulate multiple io.WriteCloser by stream.
... ...
@@ -1,7 +1,7 @@
1 1
 package fileutils
2 2
 
3 3
 import (
4
-	"github.com/docker/docker/pkg/log"
4
+	log "github.com/Sirupsen/logrus"
5 5
 	"path/filepath"
6 6
 )
7 7
 
... ...
@@ -6,7 +6,7 @@ import (
6 6
 	"net/http"
7 7
 	"time"
8 8
 
9
-	"github.com/docker/docker/pkg/log"
9
+	log "github.com/Sirupsen/logrus"
10 10
 )
11 11
 
12 12
 type resumableRequestReader struct {
... ...
@@ -9,7 +9,7 @@ import (
9 9
 	"strconv"
10 10
 	"strings"
11 11
 
12
-	"github.com/docker/docker/pkg/log"
12
+	log "github.com/Sirupsen/logrus"
13 13
 )
14 14
 
15 15
 type Action string
16 16
deleted file mode 100644
... ...
@@ -1,114 +0,0 @@
1
-package log
2
-
3
-import (
4
-	"fmt"
5
-	"io"
6
-	"os"
7
-	"runtime"
8
-	"strings"
9
-	"time"
10
-
11
-	"github.com/docker/docker/pkg/timeutils"
12
-)
13
-
14
-type priority int
15
-
16
-const (
17
-	errorFormat = "[%s] [%s] %s:%d %s\n"
18
-	logFormat   = "[%s] [%s] %s\n"
19
-
20
-	fatalPriority priority = iota
21
-	errorPriority
22
-	infoPriority
23
-	debugPriority
24
-)
25
-
26
-// A common interface to access the Fatal method of
27
-// both testing.B and testing.T.
28
-type Fataler interface {
29
-	Fatal(args ...interface{})
30
-}
31
-
32
-func (p priority) String() string {
33
-	switch p {
34
-	case fatalPriority:
35
-		return "fatal"
36
-	case errorPriority:
37
-		return "error"
38
-	case infoPriority:
39
-		return "info"
40
-	case debugPriority:
41
-		return "debug"
42
-	}
43
-
44
-	return ""
45
-}
46
-
47
-var DefaultLogger = Logger{Out: os.Stdout, Err: os.Stderr}
48
-
49
-// Debug function, if the debug flag is set, then display. Do nothing otherwise
50
-// If Docker is in damon mode, also send the debug info on the socket
51
-func Debugf(format string, a ...interface{}) (int, error) {
52
-	return DefaultLogger.Debugf(format, a...)
53
-}
54
-
55
-func Infof(format string, a ...interface{}) (int, error) {
56
-	return DefaultLogger.Infof(format, a...)
57
-}
58
-
59
-func Errorf(format string, a ...interface{}) (int, error) {
60
-	return DefaultLogger.Errorf(format, a...)
61
-}
62
-
63
-func Fatal(a ...interface{}) {
64
-	DefaultLogger.Fatalf("%s", a...)
65
-}
66
-
67
-func Fatalf(format string, a ...interface{}) {
68
-	DefaultLogger.Fatalf(format, a...)
69
-}
70
-
71
-type Logger struct {
72
-	Err io.Writer
73
-	Out io.Writer
74
-}
75
-
76
-func (l Logger) Debugf(format string, a ...interface{}) (int, error) {
77
-	if os.Getenv("DEBUG") != "" {
78
-		return l.logf(l.Err, debugPriority, format, a...)
79
-	}
80
-	return 0, nil
81
-}
82
-
83
-func (l Logger) Infof(format string, a ...interface{}) (int, error) {
84
-	return l.logf(l.Out, infoPriority, format, a...)
85
-}
86
-
87
-func (l Logger) Errorf(format string, a ...interface{}) (int, error) {
88
-	return l.logf(l.Err, errorPriority, format, a...)
89
-}
90
-
91
-func (l Logger) Fatalf(format string, a ...interface{}) {
92
-	l.logf(l.Err, fatalPriority, format, a...)
93
-	os.Exit(1)
94
-}
95
-
96
-func (l Logger) logf(stream io.Writer, level priority, format string, a ...interface{}) (int, error) {
97
-	var prefix string
98
-
99
-	if level <= errorPriority || level == debugPriority {
100
-		// Retrieve the stack infos
101
-		_, file, line, ok := runtime.Caller(2)
102
-		if !ok {
103
-			file = "<unknown>"
104
-			line = -1
105
-		} else {
106
-			file = file[strings.LastIndex(file, "/")+1:]
107
-		}
108
-		prefix = fmt.Sprintf(errorFormat, time.Now().Format(timeutils.RFC3339NanoFixed), level.String(), file, line, format)
109
-	} else {
110
-		prefix = fmt.Sprintf(logFormat, time.Now().Format(timeutils.RFC3339NanoFixed), level.String(), format)
111
-	}
112
-
113
-	return fmt.Fprintf(stream, prefix, a...)
114
-}
115 1
deleted file mode 100644
... ...
@@ -1,39 +0,0 @@
1
-package log
2
-
3
-import (
4
-	"bytes"
5
-	"regexp"
6
-
7
-	"testing"
8
-)
9
-
10
-var reRFC3339NanoFixed = "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{9}.([0-9]{2}:[0-9]{2})?"
11
-
12
-func TestLogFatalf(t *testing.T) {
13
-	var output *bytes.Buffer
14
-
15
-	tests := []struct {
16
-		Level           priority
17
-		Format          string
18
-		Values          []interface{}
19
-		ExpectedPattern string
20
-	}{
21
-		{fatalPriority, "%d + %d = %d", []interface{}{1, 1, 2}, "\\[" + reRFC3339NanoFixed + "\\] \\[fatal\\] testing.go:\\d+ 1 \\+ 1 = 2"},
22
-		{errorPriority, "%d + %d = %d", []interface{}{1, 1, 2}, "\\[" + reRFC3339NanoFixed + "\\] \\[error\\] testing.go:\\d+ 1 \\+ 1 = 2"},
23
-		{infoPriority, "%d + %d = %d", []interface{}{1, 1, 2}, "\\[" + reRFC3339NanoFixed + "\\] \\[info\\] 1 \\+ 1 = 2"},
24
-		{debugPriority, "%d + %d = %d", []interface{}{1, 1, 2}, "\\[" + reRFC3339NanoFixed + "\\] \\[debug\\] testing.go:\\d+ 1 \\+ 1 = 2"},
25
-	}
26
-
27
-	for i, test := range tests {
28
-		output = &bytes.Buffer{}
29
-		DefaultLogger.logf(output, test.Level, test.Format, test.Values...)
30
-
31
-		expected := regexp.MustCompile(test.ExpectedPattern)
32
-		if !expected.MatchString(output.String()) {
33
-			t.Errorf("[%d] Log output does not match expected pattern:\n\tExpected: %s\n\tOutput: %s",
34
-				i,
35
-				expected.String(),
36
-				output.String())
37
-		}
38
-	}
39
-}
... ...
@@ -6,7 +6,7 @@ import (
6 6
 	"sync/atomic"
7 7
 	"syscall"
8 8
 
9
-	"github.com/docker/docker/pkg/log"
9
+	log "github.com/Sirupsen/logrus"
10 10
 )
11 11
 
12 12
 // Trap sets up a simplified signal "trap", appropriate for common
... ...
@@ -5,7 +5,7 @@ import (
5 5
 	"errors"
6 6
 	"io"
7 7
 
8
-	"github.com/docker/docker/pkg/log"
8
+	log "github.com/Sirupsen/logrus"
9 9
 )
10 10
 
11 11
 const (
... ...
@@ -13,7 +13,7 @@ import (
13 13
 
14 14
 	"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
15 15
 
16
-	"github.com/docker/docker/pkg/log"
16
+	log "github.com/Sirupsen/logrus"
17 17
 )
18 18
 
19 19
 const (
... ...
@@ -9,7 +9,7 @@ import (
9 9
 	"net/url"
10 10
 	"strings"
11 11
 
12
-	"github.com/docker/docker/pkg/log"
12
+	log "github.com/Sirupsen/logrus"
13 13
 )
14 14
 
15 15
 // scans string for api version in the URL path. returns the trimmed hostname, if version found, string and API version.
... ...
@@ -15,7 +15,7 @@ import (
15 15
 
16 16
 	"github.com/gorilla/mux"
17 17
 
18
-	"github.com/docker/docker/pkg/log"
18
+	log "github.com/Sirupsen/logrus"
19 19
 )
20 20
 
21 21
 var (
... ...
@@ -17,8 +17,8 @@ import (
17 17
 	"strings"
18 18
 	"time"
19 19
 
20
+	log "github.com/Sirupsen/logrus"
20 21
 	"github.com/docker/docker/pkg/httputils"
21
-	"github.com/docker/docker/pkg/log"
22 22
 	"github.com/docker/docker/pkg/tarsum"
23 23
 	"github.com/docker/docker/utils"
24 24
 )
... ...
@@ -8,7 +8,7 @@ import (
8 8
 	"net/url"
9 9
 	"strconv"
10 10
 
11
-	"github.com/docker/docker/pkg/log"
11
+	log "github.com/Sirupsen/logrus"
12 12
 	"github.com/docker/docker/utils"
13 13
 	"github.com/gorilla/mux"
14 14
 )
... ...
@@ -3,8 +3,8 @@ package runconfig
3 3
 import (
4 4
 	"strings"
5 5
 
6
+	log "github.com/Sirupsen/logrus"
6 7
 	"github.com/docker/docker/nat"
7
-	"github.com/docker/docker/pkg/log"
8 8
 )
9 9
 
10 10
 func Merge(userConf, imageConf *Config) error {
... ...
@@ -4,8 +4,8 @@ import (
4 4
 	"fmt"
5 5
 	"time"
6 6
 
7
+	log "github.com/Sirupsen/logrus"
7 8
 	"github.com/docker/docker/engine"
8
-	"github.com/docker/docker/pkg/log"
9 9
 	"github.com/docker/libtrust"
10 10
 )
11 11
 
... ...
@@ -12,7 +12,7 @@ import (
12 12
 	"sync"
13 13
 	"time"
14 14
 
15
-	"github.com/docker/docker/pkg/log"
15
+	log "github.com/Sirupsen/logrus"
16 16
 	"github.com/docker/libtrust/trustgraph"
17 17
 )
18 18
 
... ...
@@ -5,7 +5,7 @@ import (
5 5
 	"net/http"
6 6
 	"strings"
7 7
 
8
-	"github.com/docker/docker/pkg/log"
8
+	log "github.com/Sirupsen/logrus"
9 9
 )
10 10
 
11 11
 // VersionInfo is used to model entities which has a version.
... ...
@@ -20,10 +20,10 @@ import (
20 20
 	"sync"
21 21
 	"syscall"
22 22
 
23
+	log "github.com/Sirupsen/logrus"
23 24
 	"github.com/docker/docker/dockerversion"
24 25
 	"github.com/docker/docker/pkg/fileutils"
25 26
 	"github.com/docker/docker/pkg/ioutils"
26
-	"github.com/docker/docker/pkg/log"
27 27
 )
28 28
 
29 29
 type KeyValuePair struct {
30 30
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+logrus
0 1
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+language: go
1
+go:
2
+  - 1.1
3
+  - 1.2
4
+  - tip
5
+before_script:
6
+  - go get github.com/stretchr/testify
0 7
new file mode 100644
... ...
@@ -0,0 +1,21 @@
0
+The MIT License (MIT)
1
+
2
+Copyright (c) 2014 Simon Eskildsen
3
+
4
+Permission is hereby granted, free of charge, to any person obtaining a copy
5
+of this software and associated documentation files (the "Software"), to deal
6
+in the Software without restriction, including without limitation the rights
7
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+copies of the Software, and to permit persons to whom the Software is
9
+furnished to do so, subject to the following conditions:
10
+
11
+The above copyright notice and this permission notice shall be included in
12
+all copies or substantial portions of the Software.
13
+
14
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+THE SOFTWARE.
0 21
new file mode 100644
... ...
@@ -0,0 +1,336 @@
0
+# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>&nbsp;[![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus)
1
+
2
+Logrus is a structured logger for Go (golang), completely API compatible with
3
+the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not
4
+yet stable (pre 1.0), the core API is unlikely change much but please version
5
+control your Logrus to make sure you aren't fetching latest `master` on every
6
+build.**
7
+
8
+Nicely color-coded in development (when a TTY is attached, otherwise just
9
+plain text):
10
+
11
+![Colored](http://i.imgur.com/PY7qMwd.png)
12
+
13
+With `log.Formatter = new(logrus.JSONFormatter)`, for easy parsing by logstash
14
+or Splunk:
15
+
16
+```json
17
+{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
18
+ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
19
+
20
+{"level":"warning","msg":"The group's number increased tremendously!",
21
+"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
22
+
23
+{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
24
+"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
25
+
26
+{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
27
+"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
28
+
29
+{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
30
+"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
31
+```
32
+
33
+With the default `log.Formatter = new(logrus.TextFormatter)` when a TTY is not
34
+attached, the output is compatible with the
35
+[l2met](http://r.32k.io/l2met-introduction) format:
36
+
37
+```text
38
+time="2014-04-20 15:36:23.830442383 -0400 EDT" level="info" msg="A group of walrus emerges from the ocean" animal="walrus" size=10
39
+time="2014-04-20 15:36:23.830584199 -0400 EDT" level="warning" msg="The group's number increased tremendously!" omg=true number=122
40
+time="2014-04-20 15:36:23.830596521 -0400 EDT" level="info" msg="A giant walrus appears!" animal="walrus" size=10
41
+time="2014-04-20 15:36:23.830611837 -0400 EDT" level="info" msg="Tremendously sized cow enters the ocean." animal="walrus" size=9
42
+time="2014-04-20 15:36:23.830626464 -0400 EDT" level="fatal" msg="The ice breaks!" omg=true number=100
43
+```
44
+
45
+#### Example
46
+
47
+The simplest way to use Logrus is simply the package-level exported logger:
48
+
49
+```go
50
+package main
51
+
52
+import (
53
+  log "github.com/Sirupsen/logrus"
54
+)
55
+
56
+func main() {
57
+  log.WithFields(log.Fields{
58
+    "animal": "walrus",
59
+  }).Info("A walrus appears")
60
+}
61
+```
62
+
63
+Note that it's completely api-compatible with the stdlib logger, so you can
64
+replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"`
65
+and you'll now have the flexibility of Logrus. You can customize it all you
66
+want:
67
+
68
+```go
69
+package main
70
+
71
+import (
72
+  "os"
73
+  log "github.com/Sirupsen/logrus"
74
+  "github.com/Sirupsen/logrus/hooks/airbrake"
75
+)
76
+
77
+func init() {
78
+  // Log as JSON instead of the default ASCII formatter.
79
+  log.SetFormatter(&log.JSONFormatter{})
80
+
81
+  // Use the Airbrake hook to report errors that have Error severity or above to
82
+  // an exception tracker. You can create custom hooks, see the Hooks section.
83
+  log.AddHook(logrus_airbrake.AirbrakeHook)
84
+
85
+  // Output to stderr instead of stdout, could also be a file.
86
+  log.SetOutput(os.Stderr)
87
+
88
+  // Only log the warning severity or above.
89
+  log.SetLevel(log.WarnLevel)
90
+}
91
+
92
+func main() {
93
+  log.WithFields(log.Fields{
94
+    "animal": "walrus",
95
+    "size":   10,
96
+  }).Info("A group of walrus emerges from the ocean")
97
+
98
+  log.WithFields(log.Fields{
99
+    "omg":    true,
100
+    "number": 122,
101
+  }).Warn("The group's number increased tremendously!")
102
+
103
+  log.WithFields(log.Fields{
104
+    "omg":    true,
105
+    "number": 100,
106
+  }).Fatal("The ice breaks!")
107
+}
108
+```
109
+
110
+For more advanced usage such as logging to multiple locations from the same
111
+application, you can also create an instance of the `logrus` Logger:
112
+
113
+```go
114
+package main
115
+
116
+import (
117
+  "github.com/Sirupsen/logrus"
118
+)
119
+
120
+// Create a new instance of the logger. You can have any number of instances.
121
+var log = logrus.New()
122
+
123
+func main() {
124
+  // The API for setting attributes is a little different than the package level
125
+  // exported logger. See Godoc.
126
+  log.Out = os.Stderr
127
+
128
+  log.WithFields(log.Fields{
129
+    "animal": "walrus",
130
+    "size":   10,
131
+  }).Info("A group of walrus emerges from the ocean")
132
+}
133
+```
134
+
135
+#### Fields
136
+
137
+Logrus encourages careful, structured logging though logging fields instead of
138
+long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
139
+to send event %s to topic %s with key %d")`, you should log the much more
140
+discoverable:
141
+
142
+```go
143
+log.WithFields(log.Fields{
144
+  "event": event,
145
+  "topic": topic,
146
+  "key": key,
147
+}).Fatal("Failed to send event")
148
+```
149
+
150
+We've found this API forces you to think about logging in a way that produces
151
+much more useful logging messages. We've been in countless situations where just
152
+a single added field to a log statement that was already there would've saved us
153
+hours. The `WithFields` call is optional.
154
+
155
+In general, with Logrus using any of the `printf`-family functions should be
156
+seen as a hint you should add a field, however, you can still use the
157
+`printf`-family functions with Logrus.
158
+
159
+#### Hooks
160
+
161
+You can add hooks for logging levels. For example to send errors to an exception
162
+tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
163
+multiple places simultaneously, e.g. syslog.
164
+
165
+```go
166
+// Not the real implementation of the Airbrake hook. Just a simple sample.
167
+import (
168
+  log "github.com/Sirupsen/logrus"
169
+)
170
+
171
+func init() {
172
+  log.AddHook(new(AirbrakeHook))
173
+}
174
+
175
+type AirbrakeHook struct{}
176
+
177
+// `Fire()` takes the entry that the hook is fired for. `entry.Data[]` contains
178
+// the fields for the entry. See the Fields section of the README.
179
+func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error {
180
+  err := airbrake.Notify(entry.Data["error"].(error))
181
+  if err != nil {
182
+    log.WithFields(log.Fields{
183
+      "source":   "airbrake",
184
+      "endpoint": airbrake.Endpoint,
185
+    }).Info("Failed to send error to Airbrake")
186
+  }
187
+
188
+  return nil
189
+}
190
+
191
+// `Levels()` returns a slice of `Levels` the hook is fired for.
192
+func (hook *AirbrakeHook) Levels() []log.Level {
193
+  return []log.Level{
194
+    log.ErrorLevel,
195
+    log.FatalLevel,
196
+    log.PanicLevel,
197
+  }
198
+}
199
+```
200
+
201
+Logrus comes with built-in hooks. Add those, or your custom hook, in `init`:
202
+
203
+```go
204
+import (
205
+  log "github.com/Sirupsen/logrus"
206
+  "github.com/Sirupsen/logrus/hooks/airbrake"
207
+  "github.com/Sirupsen/logrus/hooks/syslog"
208
+)
209
+
210
+func init() {
211
+  log.AddHook(new(logrus_airbrake.AirbrakeHook))
212
+  log.AddHook(logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, ""))
213
+}
214
+```
215
+
216
+* [`github.com/Sirupsen/logrus/hooks/airbrake`](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go).
217
+  Send errors to an exception tracking service compatible with the Airbrake API.
218
+  Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes.
219
+
220
+* [`github.com/Sirupsen/logrus/hooks/syslog`](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go).
221
+  Send errors to remote syslog server.
222
+  Uses standard library `log/syslog` behind the scenes.
223
+
224
+#### Level logging
225
+
226
+Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
227
+
228
+```go
229
+log.Debug("Useful debugging information.")
230
+log.Info("Something noteworthy happened!")
231
+log.Warn("You should probably take a look at this.")
232
+log.Error("Something failed but I'm not quitting.")
233
+// Calls os.Exit(1) after logging
234
+log.Fatal("Bye.")
235
+// Calls panic() after logging
236
+log.Panic("I'm bailing.")
237
+```
238
+
239
+You can set the logging level on a `Logger`, then it will only log entries with
240
+that severity or anything above it:
241
+
242
+```go
243
+// Will log anything that is info or above (warn, error, fatal, panic). Default.
244
+log.SetLevel(log.InfoLevel)
245
+```
246
+
247
+It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
248
+environment if your application has that.
249
+
250
+#### Entries
251
+
252
+Besides the fields added with `WithField` or `WithFields` some fields are
253
+automatically added to all logging events:
254
+
255
+1. `time`. The timestamp when the entry was created.
256
+2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
257
+   the `AddFields` call. E.g. `Failed to send event.`
258
+3. `level`. The logging level. E.g. `info`.
259
+
260
+#### Environments
261
+
262
+Logrus has no notion of environment.
263
+
264
+If you wish for hooks and formatters to only be used in specific environments,
265
+you should handle that yourself. For example, if your application has a global
266
+variable `Environment`, which is a string representation of the environment you
267
+could do:
268
+
269
+```go
270
+import (
271
+  log "github.com/Sirupsen/logrus"
272
+)
273
+
274
+init() {
275
+  // do something here to set environment depending on an environment variable
276
+  // or command-line flag
277
+  if Environment == "production" {
278
+    log.SetFormatter(logrus.JSONFormatter)
279
+  } else {
280
+    // The TextFormatter is default, you don't actually have to do this.
281
+    log.SetFormatter(logrus.TextFormatter)
282
+  }
283
+}
284
+```
285
+
286
+This configuration is how `logrus` was intended to be used, but JSON in
287
+production is mostly only useful if you do log aggregation with tools like
288
+Splunk or Logstash.
289
+
290
+#### Formatters
291
+
292
+The built-in logging formatters are:
293
+
294
+* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
295
+  without colors.
296
+  * *Note:* to force colored output when there is no TTY, set the `ForceColors`
297
+    field to `true`.  To force no colored output even if there is a TTY  set the 
298
+    `DisableColors` field to `true`
299
+* `logrus.JSONFormatter`. Logs fields as JSON.
300
+
301
+Third party logging formatters:
302
+
303
+* [`zalgo`](https://github.com/aybabtme/logzalgo): invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
304
+
305
+You can define your formatter by implementing the `Formatter` interface,
306
+requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
307
+`Fields` type (`map[string]interface{}`) with all your fields as well as the
308
+default ones (see Entries section above):
309
+
310
+```go
311
+type MyJSONFormatter struct {
312
+}
313
+
314
+log.SetFormatter(new(MyJSONFormatter))
315
+
316
+func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
317
+  // Note this doesn't include Time, Level and Message which are available on
318
+  // the Entry. Consult `godoc` on information about those fields or read the
319
+  // source of the official loggers.
320
+  serialized, err := json.Marshal(entry.Data)
321
+    if err != nil {
322
+      return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
323
+    }
324
+  return append(serialized, '\n'), nil
325
+}
326
+```
327
+
328
+#### Rotation
329
+
330
+Log rotation is not provided with Logrus. Log rotation should be done by an
331
+external program (like `logrotated(8)`) that can compress and delete old log
332
+entries. It should not be a feature of the application-level logger.
333
+
334
+
335
+[godoc]: https://godoc.org/github.com/Sirupsen/logrus
0 336
new file mode 100644
... ...
@@ -0,0 +1,242 @@
0
+package logrus
1
+
2
+import (
3
+	"bytes"
4
+	"fmt"
5
+	"io"
6
+	"os"
7
+	"time"
8
+)
9
+
10
+// An entry is the final or intermediate Logrus logging entry. It containts all
11
+// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
12
+// Warn, Error, Fatal or Panic is called on it. These objects can be reused and
13
+// passed around as much as you wish to avoid field duplication.
14
+type Entry struct {
15
+	Logger *Logger
16
+
17
+	// Contains all the fields set by the user.
18
+	Data Fields
19
+
20
+	// Time at which the log entry was created
21
+	Time time.Time
22
+
23
+	// Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic
24
+	Level Level
25
+
26
+	// Message passed to Debug, Info, Warn, Error, Fatal or Panic
27
+	Message string
28
+}
29
+
30
+var baseTimestamp time.Time
31
+
32
+func NewEntry(logger *Logger) *Entry {
33
+	return &Entry{
34
+		Logger: logger,
35
+		// Default is three fields, give a little extra room
36
+		Data: make(Fields, 5),
37
+	}
38
+}
39
+
40
+// Returns a reader for the entry, which is a proxy to the formatter.
41
+func (entry *Entry) Reader() (*bytes.Buffer, error) {
42
+	serialized, err := entry.Logger.Formatter.Format(entry)
43
+	return bytes.NewBuffer(serialized), err
44
+}
45
+
46
+// Returns the string representation from the reader and ultimately the
47
+// formatter.
48
+func (entry *Entry) String() (string, error) {
49
+	reader, err := entry.Reader()
50
+	if err != nil {
51
+		return "", err
52
+	}
53
+
54
+	return reader.String(), err
55
+}
56
+
57
+// Add a single field to the Entry.
58
+func (entry *Entry) WithField(key string, value interface{}) *Entry {
59
+	return entry.WithFields(Fields{key: value})
60
+}
61
+
62
+// Add a map of fields to the Entry.
63
+func (entry *Entry) WithFields(fields Fields) *Entry {
64
+	data := Fields{}
65
+	for k, v := range entry.Data {
66
+		data[k] = v
67
+	}
68
+	for k, v := range fields {
69
+		data[k] = v
70
+	}
71
+	return &Entry{Logger: entry.Logger, Data: data}
72
+}
73
+
74
+func (entry *Entry) log(level Level, msg string) string {
75
+	entry.Time = time.Now()
76
+	entry.Level = level
77
+	entry.Message = msg
78
+
79
+	if err := entry.Logger.Hooks.Fire(level, entry); err != nil {
80
+		fmt.Fprintf(os.Stderr, "Failed to fire hook", err)
81
+	}
82
+
83
+	reader, err := entry.Reader()
84
+	if err != nil {
85
+		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v", err)
86
+	}
87
+
88
+	entry.Logger.mu.Lock()
89
+	defer entry.Logger.mu.Unlock()
90
+
91
+	_, err = io.Copy(entry.Logger.Out, reader)
92
+	if err != nil {
93
+		fmt.Fprintf(os.Stderr, "Failed to write to log, %v", err)
94
+	}
95
+
96
+	return reader.String()
97
+}
98
+
99
+func (entry *Entry) Debug(args ...interface{}) {
100
+	if entry.Logger.Level >= DebugLevel {
101
+		entry.log(DebugLevel, fmt.Sprint(args...))
102
+	}
103
+}
104
+
105
+func (entry *Entry) Print(args ...interface{}) {
106
+	entry.Info(args...)
107
+}
108
+
109
+func (entry *Entry) Info(args ...interface{}) {
110
+	if entry.Logger.Level >= InfoLevel {
111
+		entry.log(InfoLevel, fmt.Sprint(args...))
112
+	}
113
+}
114
+
115
+func (entry *Entry) Warn(args ...interface{}) {
116
+	if entry.Logger.Level >= WarnLevel {
117
+		entry.log(WarnLevel, fmt.Sprint(args...))
118
+	}
119
+}
120
+
121
+func (entry *Entry) Error(args ...interface{}) {
122
+	if entry.Logger.Level >= ErrorLevel {
123
+		entry.log(ErrorLevel, fmt.Sprint(args...))
124
+	}
125
+}
126
+
127
+func (entry *Entry) Fatal(args ...interface{}) {
128
+	if entry.Logger.Level >= FatalLevel {
129
+		entry.log(FatalLevel, fmt.Sprint(args...))
130
+	}
131
+	os.Exit(1)
132
+}
133
+
134
+func (entry *Entry) Panic(args ...interface{}) {
135
+	if entry.Logger.Level >= PanicLevel {
136
+		msg := entry.log(PanicLevel, fmt.Sprint(args...))
137
+		panic(msg)
138
+	}
139
+	panic(fmt.Sprint(args...))
140
+}
141
+
142
+// Entry Printf family functions
143
+
144
+func (entry *Entry) Debugf(format string, args ...interface{}) {
145
+	if entry.Logger.Level >= DebugLevel {
146
+		entry.Debug(fmt.Sprintf(format, args...))
147
+	}
148
+}
149
+
150
+func (entry *Entry) Infof(format string, args ...interface{}) {
151
+	if entry.Logger.Level >= InfoLevel {
152
+		entry.Info(fmt.Sprintf(format, args...))
153
+	}
154
+}
155
+
156
+func (entry *Entry) Printf(format string, args ...interface{}) {
157
+	entry.Infof(format, args...)
158
+}
159
+
160
+func (entry *Entry) Warnf(format string, args ...interface{}) {
161
+	if entry.Logger.Level >= WarnLevel {
162
+		entry.Warn(fmt.Sprintf(format, args...))
163
+	}
164
+}
165
+
166
+func (entry *Entry) Warningf(format string, args ...interface{}) {
167
+	entry.Warnf(format, args...)
168
+}
169
+
170
+func (entry *Entry) Errorf(format string, args ...interface{}) {
171
+	if entry.Logger.Level >= ErrorLevel {
172
+		entry.Error(fmt.Sprintf(format, args...))
173
+	}
174
+}
175
+
176
+func (entry *Entry) Fatalf(format string, args ...interface{}) {
177
+	if entry.Logger.Level >= FatalLevel {
178
+		entry.Fatal(fmt.Sprintf(format, args...))
179
+	}
180
+}
181
+
182
+func (entry *Entry) Panicf(format string, args ...interface{}) {
183
+	if entry.Logger.Level >= PanicLevel {
184
+		entry.Panic(fmt.Sprintf(format, args...))
185
+	}
186
+}
187
+
188
+// Entry Println family functions
189
+
190
+func (entry *Entry) Debugln(args ...interface{}) {
191
+	if entry.Logger.Level >= DebugLevel {
192
+		entry.Debug(entry.sprintlnn(args...))
193
+	}
194
+}
195
+
196
+func (entry *Entry) Infoln(args ...interface{}) {
197
+	if entry.Logger.Level >= InfoLevel {
198
+		entry.Info(entry.sprintlnn(args...))
199
+	}
200
+}
201
+
202
+func (entry *Entry) Println(args ...interface{}) {
203
+	entry.Infoln(args...)
204
+}
205
+
206
+func (entry *Entry) Warnln(args ...interface{}) {
207
+	if entry.Logger.Level >= WarnLevel {
208
+		entry.Warn(entry.sprintlnn(args...))
209
+	}
210
+}
211
+
212
+func (entry *Entry) Warningln(args ...interface{}) {
213
+	entry.Warnln(args...)
214
+}
215
+
216
+func (entry *Entry) Errorln(args ...interface{}) {
217
+	if entry.Logger.Level >= ErrorLevel {
218
+		entry.Error(entry.sprintlnn(args...))
219
+	}
220
+}
221
+
222
+func (entry *Entry) Fatalln(args ...interface{}) {
223
+	if entry.Logger.Level >= FatalLevel {
224
+		entry.Fatal(entry.sprintlnn(args...))
225
+	}
226
+}
227
+
228
+func (entry *Entry) Panicln(args ...interface{}) {
229
+	if entry.Logger.Level >= PanicLevel {
230
+		entry.Panic(entry.sprintlnn(args...))
231
+	}
232
+}
233
+
234
+// Sprintlnn => Sprint no newline. This is to get the behavior of how
235
+// fmt.Sprintln where spaces are always added between operands, regardless of
236
+// their type. Instead of vendoring the Sprintln implementation to spare a
237
+// string allocation, we do the simplest thing.
238
+func (entry *Entry) sprintlnn(args ...interface{}) string {
239
+	msg := fmt.Sprintln(args...)
240
+	return msg[:len(msg)-1]
241
+}
0 242
new file mode 100644
... ...
@@ -0,0 +1,29 @@
0
+package main
1
+
2
+import (
3
+	"github.com/Sirupsen/logrus"
4
+)
5
+
6
+var log = logrus.New()
7
+
8
+func init() {
9
+	log.Formatter = new(logrus.JSONFormatter)
10
+	log.Formatter = new(logrus.TextFormatter) // default
11
+}
12
+
13
+func main() {
14
+	log.WithFields(logrus.Fields{
15
+		"animal": "walrus",
16
+		"size":   10,
17
+	}).Info("A group of walrus emerges from the ocean")
18
+
19
+	log.WithFields(logrus.Fields{
20
+		"omg":    true,
21
+		"number": 122,
22
+	}).Warn("The group's number increased tremendously!")
23
+
24
+	log.WithFields(logrus.Fields{
25
+		"omg":    true,
26
+		"number": 100,
27
+	}).Fatal("The ice breaks!")
28
+}
0 29
new file mode 100644
... ...
@@ -0,0 +1,35 @@
0
+package main
1
+
2
+import (
3
+	"github.com/Sirupsen/logrus"
4
+	"github.com/Sirupsen/logrus/hooks/airbrake"
5
+	"github.com/tobi/airbrake-go"
6
+)
7
+
8
+var log = logrus.New()
9
+
10
+func init() {
11
+	log.Formatter = new(logrus.TextFormatter) // default
12
+	log.Hooks.Add(new(logrus_airbrake.AirbrakeHook))
13
+}
14
+
15
+func main() {
16
+	airbrake.Endpoint = "https://exceptions.whatever.com/notifier_api/v2/notices.xml"
17
+	airbrake.ApiKey = "whatever"
18
+	airbrake.Environment = "production"
19
+
20
+	log.WithFields(logrus.Fields{
21
+		"animal": "walrus",
22
+		"size":   10,
23
+	}).Info("A group of walrus emerges from the ocean")
24
+
25
+	log.WithFields(logrus.Fields{
26
+		"omg":    true,
27
+		"number": 122,
28
+	}).Warn("The group's number increased tremendously!")
29
+
30
+	log.WithFields(logrus.Fields{
31
+		"omg":    true,
32
+		"number": 100,
33
+	}).Fatal("The ice breaks!")
34
+}
0 35
new file mode 100644
... ...
@@ -0,0 +1,177 @@
0
+package logrus
1
+
2
+import (
3
+	"io"
4
+)
5
+
6
+var (
7
+	// std is the name of the standard logger in stdlib `log`
8
+	std = New()
9
+)
10
+
11
+// SetOutput sets the standard logger output.
12
+func SetOutput(out io.Writer) {
13
+	std.mu.Lock()
14
+	defer std.mu.Unlock()
15
+	std.Out = out
16
+}
17
+
18
+// SetFormatter sets the standard logger formatter.
19
+func SetFormatter(formatter Formatter) {
20
+	std.mu.Lock()
21
+	defer std.mu.Unlock()
22
+	std.Formatter = formatter
23
+}
24
+
25
+// SetLevel sets the standard logger level.
26
+func SetLevel(level Level) {
27
+	std.mu.Lock()
28
+	defer std.mu.Unlock()
29
+	std.Level = level
30
+}
31
+
32
+// AddHook adds a hook to the standard logger hooks.
33
+func AddHook(hook Hook) {
34
+	std.mu.Lock()
35
+	defer std.mu.Unlock()
36
+	std.Hooks.Add(hook)
37
+}
38
+
39
+// WithField creates an entry from the standard logger and adds a field to
40
+// it. If you want multiple fields, use `WithFields`.
41
+//
42
+// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
43
+// or Panic on the Entry it returns.
44
+func WithField(key string, value interface{}) *Entry {
45
+	return std.WithField(key, value)
46
+}
47
+
48
+// WithFields creates an entry from the standard logger and adds multiple
49
+// fields to it. This is simply a helper for `WithField`, invoking it
50
+// once for each field.
51
+//
52
+// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
53
+// or Panic on the Entry it returns.
54
+func WithFields(fields Fields) *Entry {
55
+	return std.WithFields(fields)
56
+}
57
+
58
+// Debug logs a message at level Debug on the standard logger.
59
+func Debug(args ...interface{}) {
60
+	std.Debug(args...)
61
+}
62
+
63
+// Print logs a message at level Info on the standard logger.
64
+func Print(args ...interface{}) {
65
+	std.Print(args...)
66
+}
67
+
68
+// Info logs a message at level Info on the standard logger.
69
+func Info(args ...interface{}) {
70
+	std.Info(args...)
71
+}
72
+
73
+// Warn logs a message at level Warn on the standard logger.
74
+func Warn(args ...interface{}) {
75
+	std.Warn(args...)
76
+}
77
+
78
+// Warning logs a message at level Warn on the standard logger.
79
+func Warning(args ...interface{}) {
80
+	std.Warning(args...)
81
+}
82
+
83
+// Error logs a message at level Error on the standard logger.
84
+func Error(args ...interface{}) {
85
+	std.Error(args...)
86
+}
87
+
88
+// Panic logs a message at level Panic on the standard logger.
89
+func Panic(args ...interface{}) {
90
+	std.Panic(args...)
91
+}
92
+
93
+// Fatal logs a message at level Fatal on the standard logger.
94
+func Fatal(args ...interface{}) {
95
+	std.Fatal(args...)
96
+}
97
+
98
+// Debugf logs a message at level Debugf on the standard logger.
99
+func Debugf(format string, args ...interface{}) {
100
+	std.Debugf(format, args...)
101
+}
102
+
103
+// Printf logs a message at level Info on the standard logger.
104
+func Printf(format string, args ...interface{}) {
105
+	std.Printf(format, args...)
106
+}
107
+
108
+// Infof logs a message at level Info on the standard logger.
109
+func Infof(format string, args ...interface{}) {
110
+	std.Infof(format, args...)
111
+}
112
+
113
+// Warnf logs a message at level Warn on the standard logger.
114
+func Warnf(format string, args ...interface{}) {
115
+	std.Warnf(format, args...)
116
+}
117
+
118
+// Warningf logs a message at level Warn on the standard logger.
119
+func Warningf(format string, args ...interface{}) {
120
+	std.Warningf(format, args...)
121
+}
122
+
123
+// Errorf logs a message at level Error on the standard logger.
124
+func Errorf(format string, args ...interface{}) {
125
+	std.Errorf(format, args...)
126
+}
127
+
128
+// Panicf logs a message at level Pancf on the standard logger.
129
+func Panicf(format string, args ...interface{}) {
130
+	std.Panicf(format, args...)
131
+}
132
+
133
+// Fatalf logs a message at level Fatal on the standard logger.
134
+func Fatalf(format string, args ...interface{}) {
135
+	std.Fatalf(format, args...)
136
+}
137
+
138
+// Debugln logs a message at level Debug on the standard logger.
139
+func Debugln(args ...interface{}) {
140
+	std.Debugln(args...)
141
+}
142
+
143
+// Println logs a message at level Info on the standard logger.
144
+func Println(args ...interface{}) {
145
+	std.Println(args...)
146
+}
147
+
148
+// Infoln logs a message at level Info on the standard logger.
149
+func Infoln(args ...interface{}) {
150
+	std.Infoln(args...)
151
+}
152
+
153
+// Warnln logs a message at level Warn on the standard logger.
154
+func Warnln(args ...interface{}) {
155
+	std.Warnln(args...)
156
+}
157
+
158
+// Warningln logs a message at level Warn on the standard logger.
159
+func Warningln(args ...interface{}) {
160
+	std.Warningln(args...)
161
+}
162
+
163
+// Errorln logs a message at level Error on the standard logger.
164
+func Errorln(args ...interface{}) {
165
+	std.Errorln(args...)
166
+}
167
+
168
+// Panicln logs a message at level Panic on the standard logger.
169
+func Panicln(args ...interface{}) {
170
+	std.Panicln(args...)
171
+}
172
+
173
+// Fatalln logs a message at level Fatal on the standard logger.
174
+func Fatalln(args ...interface{}) {
175
+	std.Fatalln(args...)
176
+}
0 177
new file mode 100644
... ...
@@ -0,0 +1,54 @@
0
+package logrus
1
+
2
+import (
3
+	"time"
4
+)
5
+
6
+// The Formatter interface is used to implement a custom Formatter. It takes an
7
+// `Entry`. It exposes all the fields, including the default ones:
8
+//
9
+// * `entry.Data["msg"]`. The message passed from Info, Warn, Error ..
10
+// * `entry.Data["time"]`. The timestamp.
11
+// * `entry.Data["level"]. The level the entry was logged at.
12
+//
13
+// Any additional fields added with `WithField` or `WithFields` are also in
14
+// `entry.Data`. Format is expected to return an array of bytes which are then
15
+// logged to `logger.Out`.
16
+type Formatter interface {
17
+	Format(*Entry) ([]byte, error)
18
+}
19
+
20
+// This is to not silently overwrite `time`, `msg` and `level` fields when
21
+// dumping it. If this code wasn't there doing:
22
+//
23
+//  logrus.WithField("level", 1).Info("hello")
24
+//
25
+// Would just silently drop the user provided level. Instead with this code
26
+// it'll logged as:
27
+//
28
+//  {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
29
+//
30
+// It's not exported because it's still using Data in an opionated way. It's to
31
+// avoid code duplication between the two default formatters.
32
+func prefixFieldClashes(entry *Entry) {
33
+	_, ok := entry.Data["time"]
34
+	if ok {
35
+		entry.Data["fields.time"] = entry.Data["time"]
36
+	}
37
+
38
+	entry.Data["time"] = entry.Time.Format(time.RFC3339)
39
+
40
+	_, ok = entry.Data["msg"]
41
+	if ok {
42
+		entry.Data["fields.msg"] = entry.Data["msg"]
43
+	}
44
+
45
+	entry.Data["msg"] = entry.Message
46
+
47
+	_, ok = entry.Data["level"]
48
+	if ok {
49
+		entry.Data["fields.level"] = entry.Data["level"]
50
+	}
51
+
52
+	entry.Data["level"] = entry.Level.String()
53
+}
0 54
new file mode 100644
... ...
@@ -0,0 +1,122 @@
0
+package logrus
1
+
2
+import (
3
+	"testing"
4
+
5
+	"github.com/stretchr/testify/assert"
6
+)
7
+
8
+type TestHook struct {
9
+	Fired bool
10
+}
11
+
12
+func (hook *TestHook) Fire(entry *Entry) error {
13
+	hook.Fired = true
14
+	return nil
15
+}
16
+
17
+func (hook *TestHook) Levels() []Level {
18
+	return []Level{
19
+		DebugLevel,
20
+		InfoLevel,
21
+		WarnLevel,
22
+		ErrorLevel,
23
+		FatalLevel,
24
+		PanicLevel,
25
+	}
26
+}
27
+
28
+func TestHookFires(t *testing.T) {
29
+	hook := new(TestHook)
30
+
31
+	LogAndAssertJSON(t, func(log *Logger) {
32
+		log.Hooks.Add(hook)
33
+		assert.Equal(t, hook.Fired, false)
34
+
35
+		log.Print("test")
36
+	}, func(fields Fields) {
37
+		assert.Equal(t, hook.Fired, true)
38
+	})
39
+}
40
+
41
+type ModifyHook struct {
42
+}
43
+
44
+func (hook *ModifyHook) Fire(entry *Entry) error {
45
+	entry.Data["wow"] = "whale"
46
+	return nil
47
+}
48
+
49
+func (hook *ModifyHook) Levels() []Level {
50
+	return []Level{
51
+		DebugLevel,
52
+		InfoLevel,
53
+		WarnLevel,
54
+		ErrorLevel,
55
+		FatalLevel,
56
+		PanicLevel,
57
+	}
58
+}
59
+
60
+func TestHookCanModifyEntry(t *testing.T) {
61
+	hook := new(ModifyHook)
62
+
63
+	LogAndAssertJSON(t, func(log *Logger) {
64
+		log.Hooks.Add(hook)
65
+		log.WithField("wow", "elephant").Print("test")
66
+	}, func(fields Fields) {
67
+		assert.Equal(t, fields["wow"], "whale")
68
+	})
69
+}
70
+
71
+func TestCanFireMultipleHooks(t *testing.T) {
72
+	hook1 := new(ModifyHook)
73
+	hook2 := new(TestHook)
74
+
75
+	LogAndAssertJSON(t, func(log *Logger) {
76
+		log.Hooks.Add(hook1)
77
+		log.Hooks.Add(hook2)
78
+
79
+		log.WithField("wow", "elephant").Print("test")
80
+	}, func(fields Fields) {
81
+		assert.Equal(t, fields["wow"], "whale")
82
+		assert.Equal(t, hook2.Fired, true)
83
+	})
84
+}
85
+
86
+type ErrorHook struct {
87
+	Fired bool
88
+}
89
+
90
+func (hook *ErrorHook) Fire(entry *Entry) error {
91
+	hook.Fired = true
92
+	return nil
93
+}
94
+
95
+func (hook *ErrorHook) Levels() []Level {
96
+	return []Level{
97
+		ErrorLevel,
98
+	}
99
+}
100
+
101
+func TestErrorHookShouldntFireOnInfo(t *testing.T) {
102
+	hook := new(ErrorHook)
103
+
104
+	LogAndAssertJSON(t, func(log *Logger) {
105
+		log.Hooks.Add(hook)
106
+		log.Info("test")
107
+	}, func(fields Fields) {
108
+		assert.Equal(t, hook.Fired, false)
109
+	})
110
+}
111
+
112
+func TestErrorHookShouldFireOnError(t *testing.T) {
113
+	hook := new(ErrorHook)
114
+
115
+	LogAndAssertJSON(t, func(log *Logger) {
116
+		log.Hooks.Add(hook)
117
+		log.Error("test")
118
+	}, func(fields Fields) {
119
+		assert.Equal(t, hook.Fired, true)
120
+	})
121
+}
0 122
new file mode 100644
... ...
@@ -0,0 +1,34 @@
0
+package logrus
1
+
2
+// A hook to be fired when logging on the logging levels returned from
3
+// `Levels()` on your implementation of the interface. Note that this is not
4
+// fired in a goroutine or a channel with workers, you should handle such
5
+// functionality yourself if your call is non-blocking and you don't wish for
6
+// the logging calls for levels returned from `Levels()` to block.
7
+type Hook interface {
8
+	Levels() []Level
9
+	Fire(*Entry) error
10
+}
11
+
12
+// Internal type for storing the hooks on a logger instance.
13
+type levelHooks map[Level][]Hook
14
+
15
+// Add a hook to an instance of logger. This is called with
16
+// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
17
+func (hooks levelHooks) Add(hook Hook) {
18
+	for _, level := range hook.Levels() {
19
+		hooks[level] = append(hooks[level], hook)
20
+	}
21
+}
22
+
23
+// Fire all the hooks for the passed level. Used by `entry.log` to fire
24
+// appropriate hooks for a log entry.
25
+func (hooks levelHooks) Fire(level Level, entry *Entry) error {
26
+	for _, hook := range hooks[level] {
27
+		if err := hook.Fire(entry); err != nil {
28
+			return err
29
+		}
30
+	}
31
+
32
+	return nil
33
+}
0 34
new file mode 100644
... ...
@@ -0,0 +1,54 @@
0
+package logrus_airbrake
1
+
2
+import (
3
+	"github.com/Sirupsen/logrus"
4
+	"github.com/tobi/airbrake-go"
5
+)
6
+
7
+// AirbrakeHook to send exceptions to an exception-tracking service compatible
8
+// with the Airbrake API. You must set:
9
+// * airbrake.Endpoint
10
+// * airbrake.ApiKey
11
+// * airbrake.Environment (only sends exceptions when set to "production")
12
+//
13
+// Before using this hook, to send an error. Entries that trigger an Error,
14
+// Fatal or Panic should now include an "error" field to send to Airbrake.
15
+type AirbrakeHook struct{}
16
+
17
+func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error {
18
+	if entry.Data["error"] == nil {
19
+		entry.Logger.WithFields(logrus.Fields{
20
+			"source":   "airbrake",
21
+			"endpoint": airbrake.Endpoint,
22
+		}).Warn("Exceptions sent to Airbrake must have an 'error' key with the error")
23
+		return nil
24
+	}
25
+
26
+	err, ok := entry.Data["error"].(error)
27
+	if !ok {
28
+		entry.Logger.WithFields(logrus.Fields{
29
+			"source":   "airbrake",
30
+			"endpoint": airbrake.Endpoint,
31
+		}).Warn("Exceptions sent to Airbrake must have an `error` key of type `error`")
32
+		return nil
33
+	}
34
+
35
+	airErr := airbrake.Notify(err)
36
+	if airErr != nil {
37
+		entry.Logger.WithFields(logrus.Fields{
38
+			"source":   "airbrake",
39
+			"endpoint": airbrake.Endpoint,
40
+			"error":    airErr,
41
+		}).Warn("Failed to send error to Airbrake")
42
+	}
43
+
44
+	return nil
45
+}
46
+
47
+func (hook *AirbrakeHook) Levels() []logrus.Level {
48
+	return []logrus.Level{
49
+		logrus.ErrorLevel,
50
+		logrus.FatalLevel,
51
+		logrus.PanicLevel,
52
+	}
53
+}
0 54
new file mode 100644
... ...
@@ -0,0 +1,20 @@
0
+# Syslog Hooks for Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>
1
+
2
+## Usage
3
+
4
+```go
5
+import (
6
+  "log/syslog"
7
+  "github.com/Sirupsen/logrus"
8
+  "github.com/Sirupsen/logrus/hooks/syslog"
9
+)
10
+
11
+func main() {
12
+  log       := logrus.New()
13
+  hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
14
+
15
+  if err == nil {
16
+    log.Hooks.Add(hook)
17
+  }
18
+}
19
+```
0 20
\ No newline at end of file
1 21
new file mode 100644
... ...
@@ -0,0 +1,59 @@
0
+package logrus_syslog
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/Sirupsen/logrus"
5
+	"log/syslog"
6
+	"os"
7
+)
8
+
9
+// SyslogHook to send logs via syslog.
10
+type SyslogHook struct {
11
+	Writer        *syslog.Writer
12
+	SyslogNetwork string
13
+	SyslogRaddr   string
14
+}
15
+
16
+// Creates a hook to be added to an instance of logger. This is called with
17
+// `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")`
18
+// `if err == nil { log.Hooks.Add(hook) }`
19
+func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) {
20
+	w, err := syslog.Dial(network, raddr, priority, tag)
21
+	return &SyslogHook{w, network, raddr}, err
22
+}
23
+
24
+func (hook *SyslogHook) Fire(entry *logrus.Entry) error {
25
+	line, err := entry.String()
26
+	if err != nil {
27
+		fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
28
+		return err
29
+	}
30
+
31
+	switch entry.Data["level"] {
32
+	case "panic":
33
+		return hook.Writer.Crit(line)
34
+	case "fatal":
35
+		return hook.Writer.Crit(line)
36
+	case "error":
37
+		return hook.Writer.Err(line)
38
+	case "warn":
39
+		return hook.Writer.Warning(line)
40
+	case "info":
41
+		return hook.Writer.Info(line)
42
+	case "debug":
43
+		return hook.Writer.Debug(line)
44
+	default:
45
+		return nil
46
+	}
47
+}
48
+
49
+func (hook *SyslogHook) Levels() []logrus.Level {
50
+	return []logrus.Level{
51
+		logrus.PanicLevel,
52
+		logrus.FatalLevel,
53
+		logrus.ErrorLevel,
54
+		logrus.WarnLevel,
55
+		logrus.InfoLevel,
56
+		logrus.DebugLevel,
57
+	}
58
+}
0 59
new file mode 100644
... ...
@@ -0,0 +1,26 @@
0
+package logrus_syslog
1
+
2
+import (
3
+	"github.com/Sirupsen/logrus"
4
+	"log/syslog"
5
+	"testing"
6
+)
7
+
8
+func TestLocalhostAddAndPrint(t *testing.T) {
9
+	log := logrus.New()
10
+	hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
11
+
12
+	if err != nil {
13
+		t.Errorf("Unable to connect to local syslog.")
14
+	}
15
+
16
+	log.Hooks.Add(hook)
17
+
18
+	for _, level := range hook.Levels() {
19
+		if len(log.Hooks[level]) != 1 {
20
+			t.Errorf("SyslogHook was not added. The length of log.Hooks[%v]: %v", level, len(log.Hooks[level]))
21
+		}
22
+	}
23
+
24
+	log.Info("Congratulations!")
25
+}
0 26
new file mode 100644
... ...
@@ -0,0 +1,19 @@
0
+package logrus
1
+
2
+import (
3
+	"encoding/json"
4
+	"fmt"
5
+)
6
+
7
+type JSONFormatter struct {
8
+}
9
+
10
+func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
11
+	prefixFieldClashes(entry)
12
+
13
+	serialized, err := json.Marshal(entry.Data)
14
+	if err != nil {
15
+		return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
16
+	}
17
+	return append(serialized, '\n'), nil
18
+}
0 19
new file mode 100644
... ...
@@ -0,0 +1,161 @@
0
+package logrus
1
+
2
+import (
3
+	"io"
4
+	"os"
5
+	"sync"
6
+)
7
+
8
+type Logger struct {
9
+	// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
10
+	// file, or leave it default which is `os.Stdout`. You can also set this to
11
+	// something more adventorous, such as logging to Kafka.
12
+	Out io.Writer
13
+	// Hooks for the logger instance. These allow firing events based on logging
14
+	// levels and log entries. For example, to send errors to an error tracking
15
+	// service, log to StatsD or dump the core on fatal errors.
16
+	Hooks levelHooks
17
+	// All log entries pass through the formatter before logged to Out. The
18
+	// included formatters are `TextFormatter` and `JSONFormatter` for which
19
+	// TextFormatter is the default. In development (when a TTY is attached) it
20
+	// logs with colors, but to a file it wouldn't. You can easily implement your
21
+	// own that implements the `Formatter` interface, see the `README` or included
22
+	// formatters for examples.
23
+	Formatter Formatter
24
+	// The logging level the logger should log at. This is typically (and defaults
25
+	// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
26
+	// logged. `logrus.Debug` is useful in
27
+	Level Level
28
+	// Used to sync writing to the log.
29
+	mu sync.Mutex
30
+}
31
+
32
+// Creates a new logger. Configuration should be set by changing `Formatter`,
33
+// `Out` and `Hooks` directly on the default logger instance. You can also just
34
+// instantiate your own:
35
+//
36
+//    var log = &Logger{
37
+//      Out: os.Stderr,
38
+//      Formatter: new(JSONFormatter),
39
+//      Hooks: make(levelHooks),
40
+//      Level: logrus.Debug,
41
+//    }
42
+//
43
+// It's recommended to make this a global instance called `log`.
44
+func New() *Logger {
45
+	return &Logger{
46
+		Out:       os.Stdout,
47
+		Formatter: new(TextFormatter),
48
+		Hooks:     make(levelHooks),
49
+		Level:     InfoLevel,
50
+	}
51
+}
52
+
53
+// Adds a field to the log entry, note that you it doesn't log until you call
54
+// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
55
+// Ff you want multiple fields, use `WithFields`.
56
+func (logger *Logger) WithField(key string, value interface{}) *Entry {
57
+	return NewEntry(logger).WithField(key, value)
58
+}
59
+
60
+// Adds a struct of fields to the log entry. All it does is call `WithField` for
61
+// each `Field`.
62
+func (logger *Logger) WithFields(fields Fields) *Entry {
63
+	return NewEntry(logger).WithFields(fields)
64
+}
65
+
66
+func (logger *Logger) Debugf(format string, args ...interface{}) {
67
+	NewEntry(logger).Debugf(format, args...)
68
+}
69
+
70
+func (logger *Logger) Infof(format string, args ...interface{}) {
71
+	NewEntry(logger).Infof(format, args...)
72
+}
73
+
74
+func (logger *Logger) Printf(format string, args ...interface{}) {
75
+	NewEntry(logger).Printf(format, args...)
76
+}
77
+
78
+func (logger *Logger) Warnf(format string, args ...interface{}) {
79
+	NewEntry(logger).Warnf(format, args...)
80
+}
81
+
82
+func (logger *Logger) Warningf(format string, args ...interface{}) {
83
+	NewEntry(logger).Warnf(format, args...)
84
+}
85
+
86
+func (logger *Logger) Errorf(format string, args ...interface{}) {
87
+	NewEntry(logger).Errorf(format, args...)
88
+}
89
+
90
+func (logger *Logger) Fatalf(format string, args ...interface{}) {
91
+	NewEntry(logger).Fatalf(format, args...)
92
+}
93
+
94
+func (logger *Logger) Panicf(format string, args ...interface{}) {
95
+	NewEntry(logger).Panicf(format, args...)
96
+}
97
+
98
+func (logger *Logger) Debug(args ...interface{}) {
99
+	NewEntry(logger).Debug(args...)
100
+}
101
+
102
+func (logger *Logger) Info(args ...interface{}) {
103
+	NewEntry(logger).Info(args...)
104
+}
105
+
106
+func (logger *Logger) Print(args ...interface{}) {
107
+	NewEntry(logger).Info(args...)
108
+}
109
+
110
+func (logger *Logger) Warn(args ...interface{}) {
111
+	NewEntry(logger).Warn(args...)
112
+}
113
+
114
+func (logger *Logger) Warning(args ...interface{}) {
115
+	NewEntry(logger).Warn(args...)
116
+}
117
+
118
+func (logger *Logger) Error(args ...interface{}) {
119
+	NewEntry(logger).Error(args...)
120
+}
121
+
122
+func (logger *Logger) Fatal(args ...interface{}) {
123
+	NewEntry(logger).Fatal(args...)
124
+}
125
+
126
+func (logger *Logger) Panic(args ...interface{}) {
127
+	NewEntry(logger).Panic(args...)
128
+}
129
+
130
+func (logger *Logger) Debugln(args ...interface{}) {
131
+	NewEntry(logger).Debugln(args...)
132
+}
133
+
134
+func (logger *Logger) Infoln(args ...interface{}) {
135
+	NewEntry(logger).Infoln(args...)
136
+}
137
+
138
+func (logger *Logger) Println(args ...interface{}) {
139
+	NewEntry(logger).Println(args...)
140
+}
141
+
142
+func (logger *Logger) Warnln(args ...interface{}) {
143
+	NewEntry(logger).Warnln(args...)
144
+}
145
+
146
+func (logger *Logger) Warningln(args ...interface{}) {
147
+	NewEntry(logger).Warnln(args...)
148
+}
149
+
150
+func (logger *Logger) Errorln(args ...interface{}) {
151
+	NewEntry(logger).Errorln(args...)
152
+}
153
+
154
+func (logger *Logger) Fatalln(args ...interface{}) {
155
+	NewEntry(logger).Fatalln(args...)
156
+}
157
+
158
+func (logger *Logger) Panicln(args ...interface{}) {
159
+	NewEntry(logger).Panicln(args...)
160
+}
0 161
new file mode 100644
... ...
@@ -0,0 +1,72 @@
0
+package logrus
1
+
2
+import (
3
+	"log"
4
+)
5
+
6
+// Fields type, used to pass to `WithFields`.
7
+type Fields map[string]interface{}
8
+
9
+// Level type
10
+type Level uint8
11
+
12
+// Convert the Level to a string. E.g. PanicLevel becomes "panic".
13
+func (level Level) String() string {
14
+	switch level {
15
+	case DebugLevel:
16
+		return "debug"
17
+	case InfoLevel:
18
+		return "info"
19
+	case WarnLevel:
20
+		return "warning"
21
+	case ErrorLevel:
22
+		return "error"
23
+	case FatalLevel:
24
+		return "fatal"
25
+	case PanicLevel:
26
+		return "panic"
27
+	}
28
+
29
+	return "unknown"
30
+}
31
+
32
+// These are the different logging levels. You can set the logging level to log
33
+// on your instance of logger, obtained with `logrus.New()`.
34
+const (
35
+	// PanicLevel level, highest level of severity. Logs and then calls panic with the
36
+	// message passed to Debug, Info, ...
37
+	PanicLevel Level = iota
38
+	// FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the
39
+	// logging level is set to Panic.
40
+	FatalLevel
41
+	// ErrorLevel level. Logs. Used for errors that should definitely be noted.
42
+	// Commonly used for hooks to send errors to an error tracking service.
43
+	ErrorLevel
44
+	// WarnLevel level. Non-critical entries that deserve eyes.
45
+	WarnLevel
46
+	// InfoLevel level. General operational entries about what's going on inside the
47
+	// application.
48
+	InfoLevel
49
+	// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
50
+	DebugLevel
51
+)
52
+
53
+// Won't compile if StdLogger can't be realized by a log.Logger
54
+var _ StdLogger = &log.Logger{}
55
+
56
+// StdLogger is what your logrus-enabled library should take, that way
57
+// it'll accept a stdlib logger and a logrus logger. There's no standard
58
+// interface, this is the closest we get, unfortunately.
59
+type StdLogger interface {
60
+	Print(...interface{})
61
+	Printf(string, ...interface{})
62
+	Println(...interface{})
63
+
64
+	Fatal(...interface{})
65
+	Fatalf(string, ...interface{})
66
+	Fatalln(...interface{})
67
+
68
+	Panic(...interface{})
69
+	Panicf(string, ...interface{})
70
+	Panicln(...interface{})
71
+}
0 72
new file mode 100644
... ...
@@ -0,0 +1,173 @@
0
+package logrus
1
+
2
+import (
3
+	"bytes"
4
+	"encoding/json"
5
+	"testing"
6
+
7
+	"github.com/stretchr/testify/assert"
8
+)
9
+
10
+func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) {
11
+	var buffer bytes.Buffer
12
+	var fields Fields
13
+
14
+	logger := New()
15
+	logger.Out = &buffer
16
+	logger.Formatter = new(JSONFormatter)
17
+
18
+	log(logger)
19
+
20
+	err := json.Unmarshal(buffer.Bytes(), &fields)
21
+	assert.Nil(t, err)
22
+
23
+	assertions(fields)
24
+}
25
+
26
+func TestPrint(t *testing.T) {
27
+	LogAndAssertJSON(t, func(log *Logger) {
28
+		log.Print("test")
29
+	}, func(fields Fields) {
30
+		assert.Equal(t, fields["msg"], "test")
31
+		assert.Equal(t, fields["level"], "info")
32
+	})
33
+}
34
+
35
+func TestInfo(t *testing.T) {
36
+	LogAndAssertJSON(t, func(log *Logger) {
37
+		log.Info("test")
38
+	}, func(fields Fields) {
39
+		assert.Equal(t, fields["msg"], "test")
40
+		assert.Equal(t, fields["level"], "info")
41
+	})
42
+}
43
+
44
+func TestWarn(t *testing.T) {
45
+	LogAndAssertJSON(t, func(log *Logger) {
46
+		log.Warn("test")
47
+	}, func(fields Fields) {
48
+		assert.Equal(t, fields["msg"], "test")
49
+		assert.Equal(t, fields["level"], "warning")
50
+	})
51
+}
52
+
53
+func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) {
54
+	LogAndAssertJSON(t, func(log *Logger) {
55
+		log.Infoln("test", "test")
56
+	}, func(fields Fields) {
57
+		assert.Equal(t, fields["msg"], "test test")
58
+	})
59
+}
60
+
61
+func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) {
62
+	LogAndAssertJSON(t, func(log *Logger) {
63
+		log.Infoln("test", 10)
64
+	}, func(fields Fields) {
65
+		assert.Equal(t, fields["msg"], "test 10")
66
+	})
67
+}
68
+
69
+func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
70
+	LogAndAssertJSON(t, func(log *Logger) {
71
+		log.Infoln(10, 10)
72
+	}, func(fields Fields) {
73
+		assert.Equal(t, fields["msg"], "10 10")
74
+	})
75
+}
76
+
77
+func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
78
+	LogAndAssertJSON(t, func(log *Logger) {
79
+		log.Infoln(10, 10)
80
+	}, func(fields Fields) {
81
+		assert.Equal(t, fields["msg"], "10 10")
82
+	})
83
+}
84
+
85
+func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) {
86
+	LogAndAssertJSON(t, func(log *Logger) {
87
+		log.Info("test", 10)
88
+	}, func(fields Fields) {
89
+		assert.Equal(t, fields["msg"], "test10")
90
+	})
91
+}
92
+
93
+func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) {
94
+	LogAndAssertJSON(t, func(log *Logger) {
95
+		log.Info("test", "test")
96
+	}, func(fields Fields) {
97
+		assert.Equal(t, fields["msg"], "testtest")
98
+	})
99
+}
100
+
101
+func TestWithFieldsShouldAllowAssignments(t *testing.T) {
102
+	var buffer bytes.Buffer
103
+	var fields Fields
104
+
105
+	logger := New()
106
+	logger.Out = &buffer
107
+	logger.Formatter = new(JSONFormatter)
108
+
109
+	localLog := logger.WithFields(Fields{
110
+		"key1": "value1",
111
+	})
112
+
113
+	localLog.WithField("key2", "value2").Info("test")
114
+	err := json.Unmarshal(buffer.Bytes(), &fields)
115
+	assert.Nil(t, err)
116
+
117
+	assert.Equal(t, "value2", fields["key2"])
118
+	assert.Equal(t, "value1", fields["key1"])
119
+
120
+	buffer = bytes.Buffer{}
121
+	fields = Fields{}
122
+	localLog.Info("test")
123
+	err = json.Unmarshal(buffer.Bytes(), &fields)
124
+	assert.Nil(t, err)
125
+
126
+	_, ok := fields["key2"]
127
+	assert.Equal(t, false, ok)
128
+	assert.Equal(t, "value1", fields["key1"])
129
+}
130
+
131
+func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) {
132
+	LogAndAssertJSON(t, func(log *Logger) {
133
+		log.WithField("msg", "hello").Info("test")
134
+	}, func(fields Fields) {
135
+		assert.Equal(t, fields["msg"], "test")
136
+	})
137
+}
138
+
139
+func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) {
140
+	LogAndAssertJSON(t, func(log *Logger) {
141
+		log.WithField("msg", "hello").Info("test")
142
+	}, func(fields Fields) {
143
+		assert.Equal(t, fields["msg"], "test")
144
+		assert.Equal(t, fields["fields.msg"], "hello")
145
+	})
146
+}
147
+
148
+func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) {
149
+	LogAndAssertJSON(t, func(log *Logger) {
150
+		log.WithField("time", "hello").Info("test")
151
+	}, func(fields Fields) {
152
+		assert.Equal(t, fields["fields.time"], "hello")
153
+	})
154
+}
155
+
156
+func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) {
157
+	LogAndAssertJSON(t, func(log *Logger) {
158
+		log.WithField("level", 1).Info("test")
159
+	}, func(fields Fields) {
160
+		assert.Equal(t, fields["level"], "info")
161
+		assert.Equal(t, fields["fields.level"], 1)
162
+	})
163
+}
164
+
165
+func TestConvertLevelToString(t *testing.T) {
166
+	assert.Equal(t, "debug", DebugLevel.String())
167
+	assert.Equal(t, "info", InfoLevel.String())
168
+	assert.Equal(t, "warning", WarnLevel.String())
169
+	assert.Equal(t, "error", ErrorLevel.String())
170
+	assert.Equal(t, "fatal", FatalLevel.String())
171
+	assert.Equal(t, "panic", PanicLevel.String())
172
+}
0 173
new file mode 100644
... ...
@@ -0,0 +1,12 @@
0
+// Based on ssh/terminal:
1
+// Copyright 2013 The Go Authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style
3
+// license that can be found in the LICENSE file.
4
+
5
+package logrus
6
+
7
+import "syscall"
8
+
9
+const ioctlReadTermios = syscall.TIOCGETA
10
+
11
+type Termios syscall.Termios
0 12
new file mode 100644
... ...
@@ -0,0 +1,20 @@
0
+/*
1
+  Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin.
2
+*/
3
+package logrus
4
+
5
+import (
6
+	"syscall"
7
+)
8
+
9
+const ioctlReadTermios = syscall.TIOCGETA
10
+
11
+type Termios struct {
12
+	Iflag  uint32
13
+	Oflag  uint32
14
+	Cflag  uint32
15
+	Lflag  uint32
16
+	Cc     [20]uint8
17
+	Ispeed uint32
18
+	Ospeed uint32
19
+}
0 20
new file mode 100644
... ...
@@ -0,0 +1,12 @@
0
+// Based on ssh/terminal:
1
+// Copyright 2013 The Go Authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style
3
+// license that can be found in the LICENSE file.
4
+
5
+package logrus
6
+
7
+import "syscall"
8
+
9
+const ioctlReadTermios = syscall.TCGETS
10
+
11
+type Termios syscall.Termios
0 12
new file mode 100644
... ...
@@ -0,0 +1,21 @@
0
+// Based on ssh/terminal:
1
+// Copyright 2011 The Go Authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style
3
+// license that can be found in the LICENSE file.
4
+
5
+// +build linux,!appengine darwin freebsd
6
+
7
+package logrus
8
+
9
+import (
10
+	"syscall"
11
+	"unsafe"
12
+)
13
+
14
+// IsTerminal returns true if the given file descriptor is a terminal.
15
+func IsTerminal() bool {
16
+	fd := syscall.Stdout
17
+	var termios Termios
18
+	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
19
+	return err == 0
20
+}
0 21
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+// Based on ssh/terminal:
1
+// Copyright 2011 The Go Authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style
3
+// license that can be found in the LICENSE file.
4
+
5
+// +build windows
6
+
7
+package logrus
8
+
9
+import (
10
+	"syscall"
11
+	"unsafe"
12
+)
13
+
14
+var kernel32 = syscall.NewLazyDLL("kernel32.dll")
15
+
16
+var (
17
+	procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
18
+)
19
+
20
+// IsTerminal returns true if the given file descriptor is a terminal.
21
+func IsTerminal() bool {
22
+	fd := syscall.Stdout
23
+	var st uint32
24
+	r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
25
+	return r != 0 && e == 0
26
+}
0 27
new file mode 100644
... ...
@@ -0,0 +1,86 @@
0
+package logrus
1
+
2
+import (
3
+	"bytes"
4
+	"fmt"
5
+	"sort"
6
+	"strings"
7
+	"time"
8
+)
9
+
10
+const (
11
+	nocolor = 0
12
+	red     = 31
13
+	green   = 32
14
+	yellow  = 33
15
+	blue    = 34
16
+)
17
+
18
+func init() {
19
+	baseTimestamp = time.Now()
20
+}
21
+
22
+func miniTS() int {
23
+	return int(time.Since(baseTimestamp) / time.Second)
24
+}
25
+
26
+type TextFormatter struct {
27
+	// Set to true to bypass checking for a TTY before outputting colors.
28
+	ForceColors   bool
29
+	DisableColors bool
30
+}
31
+
32
+func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
33
+	b := &bytes.Buffer{}
34
+
35
+	prefixFieldClashes(entry)
36
+
37
+	if (f.ForceColors || IsTerminal()) && !f.DisableColors {
38
+		levelText := strings.ToUpper(entry.Data["level"].(string))[0:4]
39
+
40
+		levelColor := blue
41
+
42
+		if entry.Data["level"] == "warning" {
43
+			levelColor = yellow
44
+		} else if entry.Data["level"] == "error" ||
45
+			entry.Data["level"] == "fatal" ||
46
+			entry.Data["level"] == "panic" {
47
+			levelColor = red
48
+		}
49
+
50
+		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Data["msg"])
51
+
52
+		keys := make([]string, 0)
53
+		for k, _ := range entry.Data {
54
+			if k != "level" && k != "time" && k != "msg" {
55
+				keys = append(keys, k)
56
+			}
57
+		}
58
+		sort.Strings(keys)
59
+		for _, k := range keys {
60
+			v := entry.Data[k]
61
+			fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v)
62
+		}
63
+	} else {
64
+		f.AppendKeyValue(b, "time", entry.Data["time"].(string))
65
+		f.AppendKeyValue(b, "level", entry.Data["level"].(string))
66
+		f.AppendKeyValue(b, "msg", entry.Data["msg"].(string))
67
+
68
+		for key, value := range entry.Data {
69
+			if key != "time" && key != "level" && key != "msg" {
70
+				f.AppendKeyValue(b, key, value)
71
+			}
72
+		}
73
+	}
74
+
75
+	b.WriteByte('\n')
76
+	return b.Bytes(), nil
77
+}
78
+
79
+func (f *TextFormatter) AppendKeyValue(b *bytes.Buffer, key, value interface{}) {
80
+	if _, ok := value.(string); ok {
81
+		fmt.Fprintf(b, "%v=%q ", key, value)
82
+	} else {
83
+		fmt.Fprintf(b, "%v=%v ", key, value)
84
+	}
85
+}
... ...
@@ -7,8 +7,8 @@ import (
7 7
 	"path/filepath"
8 8
 	"sync"
9 9
 
10
+	log "github.com/Sirupsen/logrus"
10 11
 	"github.com/docker/docker/daemon/graphdriver"
11
-	"github.com/docker/docker/pkg/log"
12 12
 	"github.com/docker/docker/utils"
13 13
 )
14 14